Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 133 additions & 35 deletions docs/architecture/task-lifecycle-model.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"check-types": "turbo check-types --log-order grouped --output-logs new-only",
"test": "turbo test --log-order grouped --output-logs new-only",
"test:mutation-ci": "node --test scripts/stryker-diff.test.mjs",
"lifecycle:model-check": "tsx scripts/check-task-lifecycle.ts && tsx scripts/check-task-store-concurrency.ts && pnpm cleanup-protocol:model-check && pnpm parser-scope:model-check && tsx scripts/check-completion-persistence.ts",
"lifecycle:model-check": "pnpm --filter @roo-code/types build && tsx scripts/check-task-lifecycle.ts && tsx scripts/check-provider-handoff.ts && tsx scripts/check-task-store-concurrency.ts && pnpm cleanup-protocol:model-check && pnpm parser-scope:model-check && tsx scripts/check-completion-persistence.ts",
"cleanup-protocol:model-check": "tsx scripts/check-task-cleanup-protocol.ts",
"parser-scope:model-check": "node scripts/run-native-tool-call-parser-scoping.mjs",
"test:coverage": "turbo test:coverage --log-order grouped --output-logs new-only",
Expand Down
46 changes: 46 additions & 0 deletions packages/types/src/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,51 @@ export const pendingTaskActionSchema = z.discriminatedUnion("kind", [

export type PendingTaskAction = z.infer<typeof pendingTaskActionSchema>

/**
* Durable child-side write-ahead marker for an in-flight provider handoff.
*
* The child's history record is written with this marker BEFORE the parent's
* delegation record is committed, so a crash can never leave a parent
* durably pointing at a child whose handoff identity was lost. The marker
* carries the secret-free execution identity only (requested mode and the
* explicit profile projection intent); the full API configuration is
* deliberately NOT persisted — restart re-resolves it from the durable
* profile store by name, matching normal resumed-task behavior.
*
* - `set`: the handoff carries a named profile identity.
* - `preserve`: workspace profile pinning; the identity (if any) must not
* be rewritten by the handoff projection.
* - `clear`: the handoff carries no profile identity (explicit clear).
*
* Absence of the field means the record is not a pending handoff. The
* marker is stripped once the delegation commit is durable and the child's
* in-memory context is authoritative; restart reconciliation replays the
* strip for committed children and removes orphaned pre-commit children.
*/
export const pendingHandoffSchema = z.discriminatedUnion("kind", [
z.object({
kind: z.literal("set"),
version: z.literal(1),
mode: z.string().min(1),
// Parity with `isValidPendingHandoff`: a `set` marker without a
// durable identity must never parse as valid.
profileName: z.string().min(1),
}),
z.object({
kind: z.literal("preserve"),
version: z.literal(1),
mode: z.string().min(1),
profileName: z.string().optional(),
}),
z.object({
kind: z.literal("clear"),
version: z.literal(1),
mode: z.string().min(1),
}),
Comment thread
edelauna marked this conversation as resolved.
])

export type PendingHandoff = z.infer<typeof pendingHandoffSchema>

export const historyItemSchema = z.object({
id: z.string(),
rootTaskId: z.string().optional(),
Expand All @@ -49,6 +94,7 @@ export const historyItemSchema = z.object({
completedByChildId: z.string().optional(), // Child that completed and resumed this parent
completionResultSummary: z.string().optional(), // Summary from completed child
pendingAction: pendingTaskActionSchema.optional(),
pendingHandoff: pendingHandoffSchema.optional(), // Durable child-side write-ahead marker for an in-flight delegation
})

export type HistoryItem = z.infer<typeof historyItemSchema>
Loading
Loading