@ordewell/core 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/{IFileSystem-DgpX0BU7.d.mts → IFileSystem-B5asBRDa.d.mts} +1 -1
  2. package/dist/{IFileSystem-D4ADfTrX.d.ts → IFileSystem-CbCmUDBn.d.ts} +1 -1
  3. package/dist/{ModeResolver-Cv01ZDd9.d.mts → ModeResolver-DcqAho4y.d.mts} +1 -1
  4. package/dist/{ModeResolver-BEcRAz1c.d.ts → ModeResolver-DtfhWeVL.d.ts} +1 -1
  5. package/dist/{Task-CoKhvBa1.d.mts → Task-BpzIbunq.d.mts} +146 -22
  6. package/dist/{Task-CoKhvBa1.d.ts → Task-BpzIbunq.d.ts} +146 -22
  7. package/dist/{chunk-LSGY7AIZ.mjs → chunk-AJ5KDO6V.mjs} +219 -77
  8. package/dist/chunk-AJ5KDO6V.mjs.map +1 -0
  9. package/dist/chunk-H5O74L77.mjs +185 -0
  10. package/dist/chunk-H5O74L77.mjs.map +1 -0
  11. package/dist/{chunk-YU4UKKKI.mjs → chunk-MZYZS7B3.mjs} +14 -186
  12. package/dist/chunk-MZYZS7B3.mjs.map +1 -0
  13. package/dist/{chunk-J3YSTPCE.mjs → chunk-OUIQWNS3.mjs} +8 -5
  14. package/dist/chunk-OUIQWNS3.mjs.map +1 -0
  15. package/dist/{chunk-N66ZHFGN.mjs → chunk-OYUSADFM.mjs} +11 -1
  16. package/dist/chunk-OYUSADFM.mjs.map +1 -0
  17. package/dist/{chunk-HPNUFBIT.mjs → chunk-V3JHS7AX.mjs} +9 -1
  18. package/dist/chunk-V3JHS7AX.mjs.map +1 -0
  19. package/dist/index.d.mts +909 -459
  20. package/dist/index.d.ts +909 -459
  21. package/dist/index.js +5671 -3776
  22. package/dist/index.js.map +1 -1
  23. package/dist/index.mjs +5548 -3795
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/order-labels.d.mts +15 -2
  26. package/dist/order-labels.d.ts +15 -2
  27. package/dist/order-labels.js +12 -2
  28. package/dist/order-labels.js.map +1 -1
  29. package/dist/order-labels.mjs +7 -3
  30. package/dist/{parsing-DMdOq-BV.d.ts → parsing-COFGUEWx.d.ts} +2 -2
  31. package/dist/{parsing-B4OCHH1_.d.mts → parsing-D0uo-Icd.d.mts} +2 -2
  32. package/dist/parsing.d.mts +3 -3
  33. package/dist/parsing.d.ts +3 -3
  34. package/dist/parsing.js +6 -3
  35. package/dist/parsing.js.map +1 -1
  36. package/dist/parsing.mjs +4 -3
  37. package/dist/{plan-utils-CD1Xu65s.d.mts → plan-utils-CdW3mxt5.d.mts} +226 -9
  38. package/dist/{plan-utils-BdYPRBno.d.ts → plan-utils-D4Z8UyLL.d.ts} +226 -9
  39. package/dist/plan-utils.d.mts +4 -3
  40. package/dist/plan-utils.d.ts +4 -3
  41. package/dist/plan-utils.js +210 -3
  42. package/dist/plan-utils.js.map +1 -1
  43. package/dist/plan-utils.mjs +44 -3
  44. package/dist/testing.d.mts +17 -5
  45. package/dist/testing.d.ts +17 -5
  46. package/dist/testing.js +50 -3
  47. package/dist/testing.js.map +1 -1
  48. package/dist/testing.mjs +50 -5
  49. package/dist/testing.mjs.map +1 -1
  50. package/package.json +1 -1
  51. package/dist/chunk-HPNUFBIT.mjs.map +0 -1
  52. package/dist/chunk-J3YSTPCE.mjs.map +0 -1
  53. package/dist/chunk-LSGY7AIZ.mjs.map +0 -1
  54. package/dist/chunk-N66ZHFGN.mjs.map +0 -1
  55. package/dist/chunk-YU4UKKKI.mjs.map +0 -1
package/dist/index.d.mts CHANGED
@@ -1,15 +1,15 @@
1
- import { aX as RunnerId, av as PlanStatus, a9 as LegacyPlanState, L as IApproval, k as ApprovalDecision, p as ApprovalRequest, j as ApprovalAnswer, O as ITerminalRunner, M as IConfig, h as AiProvider, aJ as ProviderModelLists, m as ApprovalMode, _ as IsolationMergeResult, W as IsolationLandedTask, Q as IWorktreeIsolation, a5 as IsolationTaskStatus, as as PlanIsolation, b0 as RunnerTransport, aQ as RepoGroupLayout, P as ITerminalSession, y as DiscoveredModel, bc as Task, f as AgentProcessDeps, c as AgentAdapter, ap as OrdewellMcpServer, aS as ResearchProgress, aR as ResearchLogEntry, w as ConversationMessage, ay as PlannerToolHandler, bz as UsageRecord, bo as TaskSnapshot, r as AwaitingReason, T as IsolationHandoff, a2 as IsolationRun, a3 as IsolationTaskRecord, be as TaskIsolation, a6 as IsolationView, $ as IsolationOutcome, aP as RepairEvidence, bx as TreeSnapshot, bG as Verdict, a_ as RunnerRegistry, aO as QueuedTaskMessage, aN as QueuedMessage, b as ActiveTaskSession, ao as OrchestratorOption, bh as TaskModeAgentAdapter, g as AgentStartOptions, e as AgentEvent, aV as ResearchToolType, az as PlannerUsage, n as ApprovalPolicy, au as PlanState, bd as TaskCompleteArgs, t as CheckpointAnswer, aY as RunnerInvocation, A as AbstractRunner, a as AbstractTerminalSession, b1 as RunnerVersionProbe, b6 as StructuredSessionCapability, bp as TaskStartOptions, br as TaskTokenScope, b7 as StructuredTurnEnd, b5 as StructuredEvent, a$ as RunnerSpawnOptions, N as IPluginStore, aZ as RunnerPluginManifest, aW as ResolveContext, bn as TaskRunnerFlags } from './Task-CoKhvBa1.mjs';
2
- export { d as AgentAdapterFactory, i as AllProviderModels, l as ApprovalKind, o as ApprovalPolicyOptions, q as ApprovalSource, B as BlockingPrompt, C as CMD_EXE_MAX_COMMAND_LINE, s as CatalogModel, u as CheckpointArgs, v as CommandLineTooLongError, D as DENY_ALL, x as DiscoveredMode, z as DiscoveryCommand, E as EditPlanArgs, F as EmbeddedNewlineError, G as ExecutableNotFoundError, H as FetchAllProviderModelsOptions, I as HeadlessRunner, J as HeadlessRunnerDeps, K as HeadlessSession, R as IntegrationDisposal, S as IsolationAvailability, U as IsolationHandoffRepo, V as IsolationInactiveReason, X as IsolationLanding, Y as IsolationMergeBlock, Z as IsolationMergeBlockReason, a0 as IsolationPruneResult, a1 as IsolationRepo, a4 as IsolationTaskRepo, a7 as LaunchDeps, a8 as LaunchPlan, aa as LineBuffer, ab as ListModelsArgs, ac as ListRunnersArgs, ad as McpClientConfig, ae as McpCredential, af as McpTool, ag as McpToolContext, ah as McpToolReply, ai as Message, aj as ModelCatalog, ak as ModelShortcut, al as ORCHESTRATOR_SHORTCUTS, am as ORDEWELL_MCP_PATH, an as ORDEWELL_MCP_SERVER_NAME, aq as OwnerOnlyFile, ar as PLANNER_TOOLS, at as PlanModificationWarnings, aw as PlannerStartOptions, ax as PlannerTokenScope, aA as PluginCloneFn, aB as PluginEntry, aC as PluginFeatures, aD as PluginMode, aE as PluginModelDiscovery, aF as PluginRunnerDef, aG as PreparedLaunch, aH as PreparedTask, aI as ProviderCredentialSource, aK as ProviderModelsResult, aL as PtySize, aM as PtyWrapOptions, aT as ResearchStep, aU as ResearchStepOutcome, b2 as StreamEvent, b3 as StreamStepEvent, b4 as StreamThinkingEvent, b8 as SubagentLogEntry, b9 as SubagentOutcome, ba as SubmitPlanArgs, bb as TASK_TOOLS, bf as TaskIsolationState, bg as TaskMode, bi as TaskModeUnsupportedError, bj as TaskModelAssignment, bk as TaskOutputArgs, bl as TaskOutputSummary, bm as TaskQueryArgs, bq as TaskStatus, bs as TaskToolHandler, bt as TaskTransport, bu as TaskType, bv as TaskWithParent, bw as ThinkingBlock, by as UsageLine, bA as UsageTotals, bB as UserPromptEntry, bC as UserStep, bD as ValidationCheck, bE as ValidationContext, bF as ValidationResult, bH as VerificationCheck, bI as WINDOWS_MAX_COMMAND_LINE, bJ as addPlannerUsage, bK as addTaskToPlan, bL as addUsage, bM as approvalScopes, bN as buildShellInvocation, bO as collectProviderCredentials, bP as createEmptyPlan, bQ as createTask, bR as emptyWarnings, bS as enabledRunners, bT as fetchAllProviderModels, bU as flattenTasks, bV as flattenTasksWithParents, bW as isAwaitingReason, bX as isExecutableResolved, bY as isGranted, bZ as isMeasured, b_ as isReservedRunnerName, b$ as isRunnerApproval, c0 as isRunnerTransport, c1 as isStructuredSession, c2 as keepExecutionState, c3 as knownModelId, c4 as mcpClientConfig, c5 as migrateLegacyPlan, c6 as migratePlanState, c7 as migrateTask, c8 as ownerOnlyConfigFile, c9 as partedPromptUsage, ca as planDirectLaunch, cb as planShellLaunch, cc as plannerContextFill, cd as posixShellQuote, ce as removeTaskFromPlan, cf as renumberTasks, cg as resolveModelShortcut, ch as resolveProvider, ci as sharedMcpServer, cj as stripAnsi, ck as toApprovalDecision, cl as toOrchestratorOptions, cm as updateTaskInPlan, cn as usageLine, co as validateModifiedPlan, cp as warningsText, cq as windowsCommandLine, cr as wrapWithPty } from './Task-CoKhvBa1.mjs';
3
- import { x as TaskOp, M as MergeGateView, p as SessionBroadcaster, A as ApplyTaskOpsResult, q as SessionMessage, r as SessionNotice, v as TaskLogEvent } from './plan-utils-CD1Xu65s.mjs';
4
- export { a as ApprovalBlock, b as ApprovalStatus, C as CHECKPOINT_TRUNCATE_LENGTH, c as ConversationInput, d as ConversationView, D as DiffRow, e as DiffRowKind, f as DiffStat, g as DisplayBlock, E as EMPTY_CONVERSATION, h as EMPTY_HOLD, i as EMPTY_TASK_LOG, G as GatedConversation, L as LocalEntry, j as MessageBlock, k as MessageRole, N as NO_TURN, O as OutputPreview, P as PlanBlock, l as PlanMarkerStatus, m as PromptHold, S as SerializedPlan, n as SerializedTask, o as SerializedTaskStatus, s as SubagentBlock, t as SubagentChild, u as SubagentStatus, T as TakenPrompt, w as TaskLogView, y as TaskRef, z as ThinkingDisplayBlock, B as ToolBlock, F as ToolHeadline, H as ToolStatus, I as TurnGate, U as UsageBlock, J as aheadOfDraft, K as applyTaskOps, Q as canMergeTasks, R as canSetDependencies, V as canSplitTask, W as capConflictFiles, X as classifyOutcome, Y as coalesceTaskLog, Z as dependencyCandidates, _ as dependentsOf, $ as diffRows, a0 as diffStat, a1 as diffSummary, a2 as digestTaskLog, a3 as drainNext, a4 as executionSummary, a5 as followTurn, a6 as fromTranscript, a7 as hasHiddenDetail, a8 as holdPrompt, a9 as outputLines, aa as outputPreview, ab as parseTaskOpsJson, ac as reduceConversation, ad as reduceTaskLog, ae as replayTaskLog, af as runnerToolSubject, ag as serializePlan, ah as serializeTask, ai as serializeTaskStatus, aj as stopTurn, ak as summarizeToolCall, al as taskStartedNotice, am as textHasTaskOps, an as toTaskLogEvent, ao as toolHeadline, ap as trimToolOutput, aq as truncateCheckpointSummary, ar as unsendAll, as as unsendLatest } from './plan-utils-CD1Xu65s.mjs';
5
- import { I as IFileSystem, R as ReadFileOpts, T as ToolOutcome, b as GrepOptions, a as GlobOptions, F as FindSymbolOptions } from './IFileSystem-DgpX0BU7.mjs';
6
- export { G as GREP_DEFAULT_HEAD_LIMIT, c as GrepOutputMode, S as SEARCH_EXCLUSIONS } from './IFileSystem-DgpX0BU7.mjs';
7
- import { R as RunnerModeInfo } from './ModeResolver-Cv01ZDd9.mjs';
8
- export { M as ManifestLookup, a as autonomyLevelLabel, b as buildModeGuide, f as filteredBuildModes, p as parseAutonomyLevel, r as resolveDefaultMode, c as resolveTaskMode, d as runnerModesFrom } from './ModeResolver-Cv01ZDd9.mjs';
1
+ import { b0 as RunnerId, ay as PlanStatus, ac as LegacyPlanState, M as IApproval, p as ApprovalRequest, k as ApprovalDecision, j as ApprovalAnswer, P as ITerminalRunner, N as IConfig, h as AiProvider, aN as ProviderModelLists, m as ApprovalMode, $ as IsolationMergeResult, X as IsolationLandedTask, R as IWorktreeIsolation, a8 as IsolationTaskStatus, av as PlanIsolation, b5 as RunnerTransport, aV as RepoGroupLayout, Q as ITerminalSession, z as DiscoveredModel, bi as Task, f as AgentProcessDeps, c as AgentAdapter, as as OrdewellMcpServer, aX as ResearchProgress, aW as ResearchLogEntry, w as ConversationMessage, aB as PlannerToolHandler, bF as UsageRecord, bu as TaskSnapshot, r as AwaitingReason, U as IsolationHandoff, a4 as IsolationRun, a5 as IsolationTaskRecord, bk as TaskIsolation, a9 as IsolationView, a0 as IsolationOutcome, aU as RepairEvidence, bD as TreeSnapshot, bM as Verdict, b3 as RunnerRegistry, aS as QueuedTaskMessage, aR as QueuedMessage, b as ActiveTaskSession, ar as OrchestratorOption, bn as TaskModeAgentAdapter, g as AgentStartOptions, e as AgentEvent, a_ as ResearchToolType, aC as PlannerUsage, n as ApprovalPolicy, ax as PlanState, bj as TaskCompleteArgs, t as CheckpointAnswer, b1 as RunnerInvocation, A as AbstractRunner, a as AbstractTerminalSession, b6 as RunnerVersionProbe, bb as StructuredSessionCapability, bv as TaskStartOptions, bx as TaskTokenScope, bc as StructuredTurnEnd, ba as StructuredEvent, b4 as RunnerSpawnOptions, O as IPluginStore, b2 as RunnerPluginManifest, a$ as ResolveContext, bt as TaskRunnerFlags } from './Task-BpzIbunq.mjs';
2
+ export { d as AgentAdapterFactory, i as AllProviderModels, l as ApprovalKind, o as ApprovalPolicyOptions, q as ApprovalSource, B as BlockingPrompt, C as CMD_EXE_MAX_COMMAND_LINE, s as CatalogModel, u as CheckpointArgs, v as CommandLineTooLongError, D as DEFAULT_RUNNERS, x as DENY_ALL, y as DiscoveredMode, E as DiscoveryCommand, F as EditPlanArgs, G as EmbeddedNewlineError, H as ExecutableNotFoundError, I as FetchAllProviderModelsOptions, J as HeadlessRunner, K as HeadlessRunnerDeps, L as HeadlessSession, S as IntegrationDisposal, T as IsolationAvailability, V as IsolationHandoffRepo, W as IsolationInactiveReason, Y as IsolationLanding, Z as IsolationMergeBlock, _ as IsolationMergeBlockReason, a1 as IsolationPruneResult, a2 as IsolationRemoval, a3 as IsolationRepo, a6 as IsolationTaskRef, a7 as IsolationTaskRepo, aa as LaunchDeps, ab as LaunchPlan, ad as LineBuffer, ae as ListModelsArgs, af as ListRunnersArgs, ag as McpClientConfig, ah as McpCredential, ai as McpTool, aj as McpToolContext, ak as McpToolReply, al as Message, am as ModelCatalog, an as ModelShortcut, ao as ORCHESTRATOR_SHORTCUTS, ap as ORDEWELL_MCP_PATH, aq as ORDEWELL_MCP_SERVER_NAME, at as OwnerOnlyFile, au as PLANNER_TOOLS, aw as PlanModificationWarnings, az as PlannerStartOptions, aA as PlannerTokenScope, aD as PluginCloneFn, aE as PluginEntry, aF as PluginFeatures, aG as PluginMode, aH as PluginModelDiscovery, aI as PluginRunnerDef, aJ as PreparedLaunch, aK as PreparedTask, aL as PreservedWork, aM as ProviderCredentialSource, aO as ProviderModelsResult, aP as PtySize, aQ as PtyWrapOptions, aT as RefusedRemoval, aY as ResearchStep, aZ as ResearchStepOutcome, b7 as StreamEvent, b8 as StreamStepEvent, b9 as StreamThinkingEvent, bd as SubagentLogEntry, be as SubagentOutcome, bf as SubmitPlanArgs, bg as TASK_COMPLETE_TOOL, bh as TASK_TOOLS, bl as TaskIsolationState, bm as TaskMode, bo as TaskModeUnsupportedError, bp as TaskModelAssignment, bq as TaskOutputArgs, br as TaskOutputSummary, bs as TaskQueryArgs, bw as TaskStatus, by as TaskToolHandler, bz as TaskTransport, bA as TaskType, bB as TaskWithParent, bC as ThinkingBlock, bE as UsageLine, bG as UsageTotals, bH as UserPromptEntry, bI as UserStep, bJ as ValidationCheck, bK as ValidationContext, bL as ValidationResult, bN as VerificationCheck, bO as WINDOWS_MAX_COMMAND_LINE, bP as addPlannerUsage, bQ as addTaskToPlan, bR as addUsage, bS as approvalScopes, bT as buildShellInvocation, bU as collectProviderCredentials, bV as createEmptyPlan, bW as createTask, bX as emptyWarnings, bY as enabledRunners, bZ as fetchAllProviderModels, b_ as flattenTasks, b$ as flattenTasksWithParents, c0 as inheritedOps, c1 as isAwaitingReason, c2 as isExecutableResolved, c3 as isGranted, c4 as isMeasured, c5 as isReservedRunnerName, c6 as isRunnerApproval, c7 as isRunnerTransport, c8 as isStructuredSession, c9 as keepExecutionState, ca as knownModelId, cb as mcpClientConfig, cc as migrateLegacyPlan, cd as migratePlanState, ce as migrateTask, cf as opsFlag, cg as ownerOnlyConfigFile, ch as partedPromptUsage, ci as planDirectLaunch, cj as planShellLaunch, ck as plannerContextFill, cl as posixShellQuote, cm as removeTaskFromPlan, cn as renumberTasks, co as resolveModelShortcut, cp as resolveProvider, cq as sharedMcpServer, cr as stripAnsi, cs as toApprovalDecision, ct as toOrchestratorOptions, cu as updateTaskInPlan, cv as usageLine, cw as validateModifiedPlan, cx as warningsText, cy as windowsCommandLine, cz as wrapWithPty } from './Task-BpzIbunq.mjs';
3
+ import { B as TaskOp, M as MergeGateView, j as EditCatalog, s as SessionBroadcaster, A as ApplyTaskOpsResult, t as SessionMessage, u as SessionNotice, y as TaskLogEvent } from './plan-utils-CdW3mxt5.mjs';
4
+ export { a as ApprovalBlock, b as ApprovalStatus, C as CHECKPOINT_TRUNCATE_LENGTH, c as ConversationInput, d as ConversationView, D as DiffRow, e as DiffRowKind, f as DiffStat, g as DisplayBlock, E as EMPTY_CONVERSATION, h as EMPTY_HOLD, i as EMPTY_TASK_LOG, G as GatedConversation, L as LocalEntry, k as MessageBlock, l as MessageRole, N as NOT_STARTED, m as NO_TURN, n as NewTaskFields, O as OutputPreview, P as PlanBlock, o as PlanMarkerStatus, p as PromptHold, S as SerializedPlan, q as SerializedTask, r as SerializedTaskStatus, v as SubagentBlock, w as SubagentChild, x as SubagentStatus, T as TakenPrompt, z as TaskLogView, F as TaskRef, H as TaskRowAction, I as TaskRowContext, J as TaskRowIsolation, K as TaskRowKind, Q as TaskRowPlacement, R as TaskRowSubject, U as TaskRowView, V as TaskStatusKind, W as ThinkingDisplayBlock, X as ToolBlock, Y as ToolHeadline, Z as ToolStatus, _ as TurnGate, $ as UsageBlock, a0 as aheadOfDraft, a1 as applyTaskOps, a2 as approvalLabel, a3 as awaitingLabel, a4 as canMergeTasks, a5 as canSetDependencies, a6 as canSplitTask, a7 as capConflictFiles, a8 as classifyOutcome, a9 as coalesceTaskLog, aa as dependencyCandidates, ab as dependentsNotice, ac as dependentsOf, ad as diffRows, ae as diffStat, af as diffSummary, ag as digestTaskLog, ah as drainNext, ai as executionSummary, aj as followTurn, ak as fromTranscript, al as hasHiddenDetail, am as holdPrompt, an as isTaskRunning, ao as markAction, ap as markRequestFor, aq as namedRepos, ar as newTaskFields, as as opsEditable, at as outputLines, au as outputPreview, av as parseTaskOpsJson, aw as pastGateConfirmation, ax as reduceConversation, ay as reduceTaskLog, az as replayTaskLog, aA as runnerToolSubject, aB as serializePlan, aC as serializeTask, aD as serializeTaskStatus, aE as stopTurn, aF as summarizeToolCall, aG as taskRowActions, aH as taskRowView, aI as taskStartedNotice, aJ as taskStatusKind, aK as textHasTaskOps, aL as titledRefs, aM as toTaskLogEvent, aN as toolHeadline, aO as trimToolOutput, aP as truncateCheckpointSummary, aQ as unsendAll, aR as unsendLatest } from './plan-utils-CdW3mxt5.mjs';
5
+ import { I as IFileSystem, R as ReadFileOpts, T as ToolOutcome, b as GrepOptions, a as GlobOptions, F as FindSymbolOptions } from './IFileSystem-B5asBRDa.mjs';
6
+ export { G as GREP_DEFAULT_HEAD_LIMIT, c as GrepOutputMode, S as SEARCH_EXCLUSIONS } from './IFileSystem-B5asBRDa.mjs';
7
+ import { R as RunnerModeInfo } from './ModeResolver-DcqAho4y.mjs';
8
+ export { M as ManifestLookup, a as autonomyLevelLabel, b as buildModeGuide, f as filteredBuildModes, p as parseAutonomyLevel, r as resolveDefaultMode, c as resolveTaskMode, d as runnerModesFrom } from './ModeResolver-DcqAho4y.mjs';
9
9
  import { ChildProcess } from 'child_process';
10
- import { b as PlanParseError } from './parsing-B4OCHH1_.mjs';
11
- export { P as PLAN_ENVELOPE_KEY, a as PartialPlanTask, c as PlanTaskError, d as PlanTasksResult, T as TASK_OPS_ENVELOPE_KEY, e as TASK_QUERY_ENVELOPE_KEY, f as checkDepsResolve, g as checkImmutableLog, h as checkInProgress, i as checkNoCycles, j as checkUniqueIds, k as escapeControlCharsInStrings, l as extractJsonObject, m as extractObjectWithBalance, n as extractObjectsWithKey, o as looksLikePlanAttempt, p as opensWithJsonObject, q as parsePartialPlan, r as parsePlanJson, s as stripModelNoise, t as stripTrailingCommas, v as validatePlanModification, u as validatePlanTasks } from './parsing-B4OCHH1_.mjs';
12
- export { resolveOrderLabel, taskOrderLabel } from './order-labels.mjs';
10
+ import { b as PlanParseError } from './parsing-D0uo-Icd.mjs';
11
+ export { P as PLAN_ENVELOPE_KEY, a as PartialPlanTask, c as PlanTaskError, d as PlanTasksResult, T as TASK_OPS_ENVELOPE_KEY, e as TASK_QUERY_ENVELOPE_KEY, f as checkDepsResolve, g as checkImmutableLog, h as checkInProgress, i as checkNoCycles, j as checkUniqueIds, k as escapeControlCharsInStrings, l as extractJsonObject, m as extractObjectWithBalance, n as extractObjectsWithKey, o as looksLikePlanAttempt, p as opensWithJsonObject, q as parsePartialPlan, r as parsePlanJson, s as stripModelNoise, t as stripTrailingCommas, v as validatePlanModification, u as validatePlanTasks } from './parsing-D0uo-Icd.mjs';
12
+ export { resolveOrderLabel, taskOrderLabel, taskRef, titledTaskRef } from './order-labels.mjs';
13
13
  import 'events';
14
14
  import 'zod/v4';
15
15
 
@@ -650,6 +650,7 @@ type GitExecFn = (file: string, args: string[], opts: {
650
650
  stdout: string;
651
651
  stderr: string;
652
652
  }>;
653
+
653
654
  interface WorktreeIsolationDeps {
654
655
  config: Pick<IConfig, 'worktreeIsolation' | 'worktreeSetupCommand' | 'workspaceRepos' | 'worktreeLinks'>;
655
656
  /** Test seam: every git invocation goes through this. */
@@ -1106,10 +1107,13 @@ interface ConversationRequest {
1106
1107
  * atomically and answers queries out of its own state; the AI service only
1107
1108
  * parses them.
1108
1109
  */
1109
- type ConversationTurn = {
1110
+ type ConversationTurn =
1111
+ /** `aborted`: the call was stopped, and `text` is only what it had by then. */
1112
+ {
1110
1113
  kind: 'message';
1111
1114
  text: string;
1112
1115
  researchLog: ResearchLogEntry[];
1116
+ aborted?: boolean;
1113
1117
  } | {
1114
1118
  kind: 'plan';
1115
1119
  tasks: Task[];
@@ -1464,15 +1468,6 @@ declare class PlanStore {
1464
1468
  setTaskVerdict(id: string, verdict: Task['verdict']): void;
1465
1469
  setTaskOutputSummary(id: string, summary: Task['outputSummary']): void;
1466
1470
  setTaskTransport(id: string, transport: Task['transport']): void;
1467
- getPlanVisualization(): {
1468
- tasks: {
1469
- id: string;
1470
- title: string;
1471
- dependencies: string[];
1472
- parallelGroups: number[][];
1473
- }[];
1474
- parallelGroups: number[][];
1475
- };
1476
1471
  /**
1477
1472
  * Widen the plan's runner set. Retargeting a task onto a runner the plan has
1478
1473
  * not used before has to land here as well as on the plan state, or
@@ -1517,6 +1512,12 @@ interface IsolationRunControllerDeps {
1517
1512
  notifications: INotification;
1518
1513
  workspaceRoot: () => string;
1519
1514
  listener: IsolationRunListener;
1515
+ /**
1516
+ * The tasks no clean-up of the whole run may take a worktree from: an
1517
+ * attempt is running them, or the plan has them in progress or waiting on
1518
+ * the user mid-attempt.
1519
+ */
1520
+ liveTasks: () => ReadonlySet<string>;
1520
1521
  }
1521
1522
  /**
1522
1523
  * The lifecycle of a plan's isolation run (ADR-0013, ADR-0014, ADR-0020),
@@ -1533,6 +1534,7 @@ declare class IsolationRunController {
1533
1534
  private readonly notifications;
1534
1535
  private readonly workspaceRoot;
1535
1536
  private readonly listener;
1537
+ private readonly liveTasks;
1536
1538
  private run;
1537
1539
  /** Copied paths already reported for the current run: every task gets the same copies. */
1538
1540
  private reportedCopies;
@@ -1545,6 +1547,8 @@ declare class IsolationRunController {
1545
1547
  /** Resolver task id → the conflicted task it resolves; see {@link linkResolver}. */
1546
1548
  private resolvers;
1547
1549
  private opening;
1550
+ /** Bumped by an interrupt, so an activation in flight across it does not open the run it ended. */
1551
+ private activations;
1548
1552
  /** The start a dirty tree turned away, handed back once the user chooses how to go on. */
1549
1553
  private blockedStart;
1550
1554
  /** The dirty repos behind {@link blockedStart}, for the stash notice. */
@@ -1561,6 +1565,7 @@ declare class IsolationRunController {
1561
1565
  get blocked(): boolean;
1562
1566
  /** What the plan persists of isolated execution; null when no run ever isolated. */
1563
1567
  get planIsolation(): PlanIsolation | null;
1568
+ /** The one guard of every action on the run: a plan that never isolated is a request refused, not a fault. */
1564
1569
  requireRun(): IsolationRun;
1565
1570
  /** A task's record in the open run, only while that run isolates. */
1566
1571
  openRecord(taskId: string): IsolationTaskRecord | undefined;
@@ -1604,20 +1609,27 @@ declare class IsolationRunController {
1604
1609
  */
1605
1610
  continueBlocked(how: 'stash' | 'shared'): Promise<(() => Promise<void>) | null>;
1606
1611
  /**
1607
- * The one place an attempt's working directory is decided: the worktree
1608
- * prepared for it in an isolated run, the kept one for a conflict repair,
1609
- * else the workspace root. `worktree` says which. Does not itself report a
1610
- * worktree it creates as a change — the caller does once the attempt is
1611
- * committed as running, so a spawn abandoned or failed after this settles
1612
- * is not misreported as a change that stuck.
1612
+ * The working directory of an attempt that may run in a worktree — which
1613
+ * attempts do is the attempt kind's to say (see `attemptKind.attemptCwd`):
1614
+ * the worktree prepared for it in an isolated run, the kept one for a
1615
+ * conflict repair, else the workspace root. `worktree` says which. Does not
1616
+ * itself report a worktree it creates as a change — the caller does once
1617
+ * the attempt is committed as running, so a spawn abandoned or failed after
1618
+ * this settles is not misreported as a change that stuck.
1613
1619
  */
1614
1620
  attemptCwd(task: Task, opts: {
1615
1621
  repair: boolean;
1616
- ops?: boolean;
1617
1622
  }): Promise<{
1618
1623
  cwd: string;
1619
1624
  worktree: boolean;
1620
1625
  }>;
1626
+ /** The workspace root, for an attempt that runs outside any worktree. */
1627
+ workspaceCwd(task: Task): Promise<{
1628
+ cwd: string;
1629
+ worktree: boolean;
1630
+ }>;
1631
+ private reopenCwd;
1632
+ private prepareCwd;
1621
1633
  /**
1622
1634
  * Land a task's work on the run's integration branches. The record is
1623
1635
  * reported changed once the landing is set and before the first merge, so
@@ -1654,10 +1666,11 @@ declare class IsolationRunController {
1654
1666
  */
1655
1667
  refreshInHead(): Promise<boolean>;
1656
1668
  /**
1657
- * The tracked state of the workspace as an ops task starts, for
1658
- * {@link filesChangedSince} (ADR-0020). Null where the check does not run:
1659
- * a run that shares the workspace root, where every task's edits land in
1660
- * it, or a workspace git cannot isolate at all.
1669
+ * The tracked state of the workspace as an attempt whose tree is checked
1670
+ * starts (see `attemptKind.checksTree`), for {@link filesChangedSince}
1671
+ * (ADR-0020). Null where the check does not run: a run that shares the
1672
+ * workspace root, where every task's edits land in it, or a workspace git
1673
+ * cannot isolate at all.
1661
1674
  */
1662
1675
  snapshotWorkspace(): Promise<TreeSnapshot | null>;
1663
1676
  /** Tracked files changed since `snapshot`; none when the run has since gone on in the workspace root. */
@@ -1672,7 +1685,9 @@ declare class IsolationRunController {
1672
1685
  /**
1673
1686
  * Mark which added task resolves which conflict. The resolver merges the
1674
1687
  * conflicted task's branch by hand in its own worktree; once that lands, the
1675
- * conflicted task can land in turn.
1688
+ * conflicted task can land in turn — through the same merge, so a resolver
1689
+ * that did not really bring it along conflicts again instead of being taken
1690
+ * at its word.
1676
1691
  */
1677
1692
  linkResolver(resolverId: string, conflictedId: string): void;
1678
1693
  /** The conflicted task a landed resolver was added for, forgotten as it is returned. */
@@ -1687,11 +1702,28 @@ declare class IsolationRunController {
1687
1702
  * HEAD somehow does not contain stays for the next run's sweep.
1688
1703
  */
1689
1704
  merge(): Promise<IsolationMergeResult>;
1690
- /** Worktrees and task branches go; the integration branch and the record stay for review and merge. */
1705
+ /**
1706
+ * Worktrees and task branches go; the integration branch and the record stay
1707
+ * for review and merge. Refused while a task of the run is still live.
1708
+ */
1691
1709
  cleanup(): Promise<void>;
1692
- /** The run and everything it made go, and the plan forgets it; the next run starts afresh. */
1710
+ /**
1711
+ * The run and everything it made go, and the plan forgets it; the next run
1712
+ * starts afresh. Refused while a task of the run is still live.
1713
+ */
1693
1714
  discard(): Promise<void>;
1715
+ /** The records of tasks still live, whose worktrees no clean-up of the whole run may take. */
1716
+ private liveRecords;
1717
+ private refuseWhileLive;
1718
+ /**
1719
+ * Clear a settled run up once everything it landed is merged — and only
1720
+ * when nothing else is left in it: a live task, or a worktree holding work
1721
+ * that never landed, keeps the whole run, worktrees and branches, rather
1722
+ * than leaving that work to a deletion.
1723
+ */
1694
1724
  private clearMerged;
1725
+ /** Say what a removal kept back instead of deleting: where the work went, or which worktree stayed. */
1726
+ private report;
1695
1727
  private forget;
1696
1728
  private assess;
1697
1729
  private activate;
@@ -1711,6 +1743,10 @@ declare class IsolationRunController {
1711
1743
  * whole. One that cannot be continued for another reason — it ran from a
1712
1744
  * different workspace path — keeps its integration branch in each repo that
1713
1745
  * has not merged it: only the user gives landed work up.
1746
+ *
1747
+ * False when `interrupted` came while git was minting: a newer activation
1748
+ * may be minting its own run, so this one is never installed over it, and
1749
+ * is discarded rather than left as branches no record names.
1714
1750
  */
1715
1751
  private mint;
1716
1752
  private reportCopies;
@@ -1862,7 +1898,7 @@ interface OrchestratorObserver {
1862
1898
  }): void;
1863
1899
  onTick?(): void;
1864
1900
  onExecutionComplete?(): void;
1865
- /** Queued user messages are ready to be processed by the planner. */
1901
+ /** The run is parked behind queued user messages, with nothing live, until they are drained. */
1866
1902
  onQueueReady?(): void;
1867
1903
  onReviewNeeded?(data: {
1868
1904
  tasks: Task[];
@@ -1916,9 +1952,9 @@ interface TaskAttemptSnapshot {
1916
1952
  }
1917
1953
  /**
1918
1954
  * The wiring a {@link TaskOrchestrator} runs on, every collaborator resolved.
1919
- * {@link createTaskOrchestrator} is the only production caller; tests build one
1920
- * through that factory too. The constructor makes nothing itself, so there is
1921
- * exactly one place defaults live.
1955
+ * {@link TaskOrchestrator.compose} is the only production caller; tests build
1956
+ * one through it too. The constructor makes nothing itself, so there is exactly
1957
+ * one place defaults live.
1922
1958
  */
1923
1959
  interface TaskOrchestratorDeps {
1924
1960
  config: IConfig;
@@ -1943,9 +1979,9 @@ interface TaskOrchestratorDeps {
1943
1979
  previousAttemptFromLog: (taskId: string) => string | null;
1944
1980
  }
1945
1981
  /**
1946
- * What a caller supplies to {@link createTaskOrchestrator}; every optional one
1947
- * gets its default there. This is what a host or test chooses, not the resolved
1948
- * wiring the orchestrator itself runs on.
1982
+ * What a caller supplies to {@link TaskOrchestrator.compose}; every optional
1983
+ * one gets its default there. This is what a host or test chooses, not the
1984
+ * resolved wiring the orchestrator itself runs on.
1949
1985
  */
1950
1986
  interface TaskOrchestratorOptions {
1951
1987
  config: IConfig;
@@ -2008,8 +2044,18 @@ declare class TaskOrchestrator {
2008
2044
  * task whose spawn always throws.
2009
2045
  */
2010
2046
  private onHold;
2011
- /** The plan's isolation run and the open run's lifecycle (ADR-0013); see {@link IsolationRunController}. */
2012
- private runs;
2047
+ /**
2048
+ * Tasks Mark complete is landing after it ended their attempt: in progress
2049
+ * with no attempt for as long as that takes, and not orphans for it.
2050
+ */
2051
+ private completing;
2052
+ /**
2053
+ * The plan's isolation run and the open run's lifecycle (ADR-0013); see
2054
+ * {@link IsolationRunController}. Read and acted on there directly, except
2055
+ * Merge all: that goes through {@link mergeRun}, which keeps it apart from
2056
+ * ops tasks and starts the tasks its merge lets through.
2057
+ */
2058
+ readonly runs: IsolationRunController;
2013
2059
  /** A passed attempt's work onto the integration branch, and the conflict repair a landing may need. */
2014
2060
  private landing;
2015
2061
  private registry;
@@ -2054,13 +2100,24 @@ declare class TaskOrchestrator {
2054
2100
  private envForTask;
2055
2101
  approveCheckpoint(taskId: string): void;
2056
2102
  rejectCheckpoint(taskId: string, reason?: string): void;
2057
- private atCheckpoint;
2103
+ /** What the task's waiting checkpoint asks, whole; undefined once it settles or when none waits. */
2104
+ getCheckpointQuestion(taskId: string): string | undefined;
2105
+ awaitsCheckpoint(taskId: string): boolean;
2058
2106
  /**
2059
2107
  * Send a structured task a user message (ADR-0018, M1). Mid-turn it queues
2060
2108
  * behind the turn; a task waiting for input takes it at once and is back in
2061
2109
  * progress. Returns the message's id, for {@link removeQueuedTaskMessage}.
2062
2110
  */
2063
2111
  sendTaskMessage(taskId: string, text: string): string;
2112
+ /**
2113
+ * Force send (ADR-0023, F1–F3): interrupt the running turn and deliver the
2114
+ * message as the next one, ahead of anything queued, without the task
2115
+ * waiting for input in between. A task already waiting for input just takes it.
2116
+ */
2117
+ forceSendTaskMessage(taskId: string, text: string): string;
2118
+ private messageTask;
2119
+ /** Force send a message still queued behind the running turn; false once the runner has it. */
2120
+ forceSendQueuedTaskMessage(taskId: string, id: string): boolean;
2064
2121
  /** Take back a message still queued behind a turn; false once it was delivered. */
2065
2122
  removeQueuedTaskMessage(taskId: string, id: string): boolean;
2066
2123
  /** Stop a structured task's running turn; it then waits for input like any turn that ends without its marker. */
@@ -2095,30 +2152,12 @@ declare class TaskOrchestrator {
2095
2152
  /** Task id → session id of every attempt whose runner is up; a spawn in flight has no session yet. */
2096
2153
  get activeSessionMap(): Map<string, string>;
2097
2154
  getAttempt(taskId: string): TaskAttemptSnapshot | undefined;
2098
- /** Where a task's isolated work stands; null when the plan has no isolation run to speak of. */
2099
- getTaskIsolation(taskId: string): TaskIsolation | null;
2100
- /**
2101
- * The plan's isolation as a surface shows it, for one with no stream to have
2102
- * told it — a reconnected webview, a session just loaded. Null without a run.
2103
- */
2104
- isolationView(): IsolationView | null;
2105
- getAttemptSession(taskId: string): ITerminalSession | undefined;
2106
2155
  get queuedCount(): number;
2107
- /** A run is waiting on the user to stash or to go on without isolation. */
2108
- get awaitingIsolationChoice(): boolean;
2109
- /**
2110
- * Take over a plan's persisted isolation, or none for a plan that has not
2111
- * isolated yet, pruning what a crashed process left behind. Silent on the
2112
- * observer: adopting is not a change to persist.
2113
- */
2114
- adoptIsolation(state: PlanIsolation | null): Promise<void>;
2115
- reviewRunDiff(): Promise<string>;
2116
2156
  /**
2117
2157
  * "Merge all"; a settled run that merged everything is cleared up and
2118
2158
  * forgotten. During a run it merges what has landed and the run goes on:
2119
2159
  * the tasks waiting at a merge gate for that work then start (ADR-0020).
2120
- * Never while an ops task runs, and no ops task starts until it ends — the
2121
- * one overlap the plan cannot order, because the merge is the user's.
2160
+ * Never alongside ops work, either way round ({@link mergeExcludes}).
2122
2161
  */
2123
2162
  mergeRun(): Promise<IsolationMergeResult>;
2124
2163
  /**
@@ -2133,13 +2172,7 @@ declare class TaskOrchestrator {
2133
2172
  * the task has started, and for every other task.
2134
2173
  */
2135
2174
  getMergeGate(taskId: string): string[];
2136
- /** Worktrees and task branches go; the integration branch and the record stay for review and merge. */
2137
- cleanupRun(): Promise<void>;
2138
- /** The run and everything it made go, and the plan forgets it; the next run starts afresh. */
2139
- discardRun(): Promise<void>;
2140
2175
  private watchBlockingPrompts;
2141
- /** What the plan persists of isolated execution; null when no run ever isolated. */
2142
- get isolationRecord(): PlanIsolation | null;
2143
2176
  /** The transport the plan's latest run copied from the setting; null before its first run. */
2144
2177
  get runnerTransport(): RunnerTransport | null;
2145
2178
  /** Take over a saved plan's copied transport. The next run copies the setting afresh. */
@@ -2150,7 +2183,6 @@ declare class TaskOrchestrator {
2150
2183
  removeQueuedMessage(id: string): boolean;
2151
2184
  setQueuedMessages(messages: QueuedMessage[]): void;
2152
2185
  clearQueuedMessages(): void;
2153
- processNextQueuedMessage(): QueuedMessage | null;
2154
2186
  loadPlan(tasks: readonly Task[], planRunners?: RunnerId[]): void;
2155
2187
  /**
2156
2188
  * Adopt an edited plan without letting go of the run in progress. Everything
@@ -2167,7 +2199,40 @@ declare class TaskOrchestrator {
2167
2199
  start(): Promise<void>;
2168
2200
  private haltOnFailure;
2169
2201
  stop(): void;
2170
- onUserTaskComplete(taskId: string): Promise<void>;
2202
+ /** Name the tasks a stop took down, so none is mistaken for one still at work. */
2203
+ private sayStopped;
2204
+ /**
2205
+ * A task in progress with no attempt has nothing running it: a stop, a plan
2206
+ * load or anything else that ended its attempt left its status behind. It
2207
+ * is put back to not started, and said, so it can be retried rather than
2208
+ * read as running — or, in an isolated run, count as live work forever.
2209
+ */
2210
+ private recoverOrphans;
2211
+ /**
2212
+ * Task ids still live, whose worktrees no clean-up of the whole run may
2213
+ * take: an attempt is running them, or the plan has them in progress or
2214
+ * waiting on the user mid-attempt — at a checkpoint, or for input. One
2215
+ * waiting on the user's decision instead (a conflict, a usage limit, files
2216
+ * an ops task changed) is not: no runner is at work on it, and a clean-up
2217
+ * keeps what it holds on a branch before its worktree goes.
2218
+ */
2219
+ private liveTaskIds;
2220
+ /**
2221
+ * Whether the run may close with its handoff: no task is live. A handoff
2222
+ * tells the user the run has finished, and a Merge all after it clears the
2223
+ * run up, so neither may happen under a task the plan still shows at work.
2224
+ */
2225
+ private get settled();
2226
+ /**
2227
+ * The boundary a verdict crosses: nothing awaits the verifier's listener, so
2228
+ * a throw from reading the output, landing, releasing a worktree or the tick
2229
+ * after would be an unhandled rejection — and an attempt left integrating is
2230
+ * live work forever. One that never settled fails (a repair waits on the
2231
+ * user, as its conflict did); either way the run halts and says why.
2232
+ */
2233
+ private deliverVerdict;
2234
+ /** Work nothing awaits: a rejection is said, not left unhandled. */
2235
+ private unawaited;
2171
2236
  private onVerdict;
2172
2237
  private afterVerdict;
2173
2238
  /**
@@ -2192,24 +2257,17 @@ declare class TaskOrchestrator {
2192
2257
  * the ending attempt freed and never one more than the run allows.
2193
2258
  */
2194
2259
  private settleUnlanded;
2195
- /**
2196
- * Mark which added task resolves which conflict. The resolver merges the
2197
- * conflicted task's branch by hand in its own worktree; once that lands, the
2198
- * conflicted task's branch is already on the integration branch and it can
2199
- * land in turn — through the same merge, so a resolver that did not really
2200
- * bring it along conflicts again instead of being taken at its word.
2201
- */
2202
- linkConflictResolver(resolverId: string, conflictedId: string): void;
2203
2260
  private landResolved;
2204
2261
  getReadyTasks(): Task[];
2205
2262
  private readiness;
2206
2263
  /**
2207
- * Open the run a task starts in, and for a change task decide whether it
2208
- * isolates (ADR-0020): an ops task needs no worktree, so it never waits on
2209
- * that choice. False while a dirty tree waits on the user; `resume` is what
2210
- * their choice replays.
2264
+ * Open the run a task starts in, and decide whether it isolates if the
2265
+ * task's next attempt needs that ({@link decidesIsolation}). False while a
2266
+ * dirty tree waits on the user; `resume` is what their choice replays.
2211
2267
  */
2212
2268
  private openFor;
2269
+ /** The kind a task's next attempt starts as, short of a repair or a continue: what is weighed before one exists. */
2270
+ private nextAttemptKind;
2213
2271
  /**
2214
2272
  * Cancel a running (or scheduled) task: kill its session and return it to
2215
2273
  * 'pending' — "not executed". The task is put on hold so the scheduler
@@ -2231,7 +2289,6 @@ declare class TaskOrchestrator {
2231
2289
  */
2232
2290
  releaseTask(taskId: string): Promise<void>;
2233
2291
  markTaskComplete(taskId: string): Promise<void>;
2234
- markAiTaskComplete(taskId: string): Promise<void>;
2235
2292
  /**
2236
2293
  * Undo a completion: return the task to "not executed" — pending, verdict and
2237
2294
  * summary dropped, archive entry removed. It is put on hold like a cancel, so
@@ -2278,16 +2335,9 @@ declare class TaskOrchestrator {
2278
2335
  isAllComplete(): boolean;
2279
2336
  isAnyFailed(): boolean;
2280
2337
  approveReview(): Promise<void>;
2281
- getPlanVisualization(): {
2282
- tasks: {
2283
- id: string;
2284
- title: string;
2285
- dependencies: string[];
2286
- parallelGroups: number[][];
2287
- }[];
2288
- parallelGroups: number[][];
2289
- };
2290
2338
  tick(): Promise<void>;
2339
+ /** Whether the scheduler may start `id` now: it passes every readiness gate and a slot is free. */
2340
+ private stillReady;
2291
2341
  /**
2292
2342
  * A run paused at merge gates says so, as plainly as a task that waits on
2293
2343
  * the user: which tasks wait, and that Merge all lets them go on (ADR-0020).
@@ -2321,11 +2371,6 @@ declare class TaskOrchestrator {
2321
2371
  private abandonSpawn;
2322
2372
  private tell;
2323
2373
  private say;
2324
- /**
2325
- * Whether the run in force, or else the next one, gives each task its own
2326
- * worktrees, and of which repo group — what the planner is told.
2327
- */
2328
- plannerIsolation(): Promise<IsolatedExecution>;
2329
2374
  /**
2330
2375
  * Replay the start a dirty tree turned away. `stash` puts the user's tracked
2331
2376
  * changes on the git stash first, so the run isolates; `shared` runs this one
@@ -2350,12 +2395,6 @@ declare class TaskOrchestrator {
2350
2395
  private saveNativeSession;
2351
2396
  private endAllAttempts;
2352
2397
  }
2353
- /**
2354
- * Build a {@link TaskOrchestrator}, filling in every collaborator a caller did
2355
- * not inject. The one entry point for hosts, bench harnesses and tests alike;
2356
- * {@link TaskOrchestrator} itself constructs nothing.
2357
- */
2358
- declare function createTaskOrchestrator(options: TaskOrchestratorOptions): TaskOrchestrator;
2359
2398
 
2360
2399
  /**
2361
2400
  * Everything needed to produce a plan from a goal. Carries the planning context
@@ -2518,7 +2557,9 @@ declare class RunnerInstallation {
2518
2557
  * typing a real goal is the failure this exists to prevent.
2519
2558
  *
2520
2559
  * "Usable" is deliberately shallow: the binary answers `--version`, and the
2521
- * runner declares a planner transport. Whether the user's subscription is
2560
+ * runner has a connector to plan over. Plugin runners have none yet: a
2561
+ * manifest-level planner capability, so a plugin can declare itself
2562
+ * plannable, is a follow-up. Whether the user's subscription is
2522
2563
  * live cannot be known without spending a turn on it, so an expired login
2523
2564
  * surfaces where it actually bites — as the agent's own error text on the
2524
2565
  * first turn.
@@ -2594,6 +2635,37 @@ declare function isCliProvider(id: AiProvider): boolean;
2594
2635
  declare function runnerForProvider(id: AiProvider): string | null;
2595
2636
  /** The harness planner that wraps a given runner, or null when none does. */
2596
2637
  declare function providerForRunner(runner: string): AiProvider | null;
2638
+ /** One runner's planner usability, as `RunnerInstallation.plannerUsability` reports it. */
2639
+ interface PlannerUsability {
2640
+ usable: boolean;
2641
+ reason?: string;
2642
+ }
2643
+ /**
2644
+ * One entry of the planner backend list (ADR-0009). `id` is the `AiProvider`
2645
+ * either way; `runner` is set only for a harness planner, whose model catalog
2646
+ * comes from that runner, and `reason` is the second line a surface renders —
2647
+ * the block detail for a harness planner, the API-key variable for a vendor.
2648
+ */
2649
+ interface PlannerBackendEntry {
2650
+ id: AiProvider;
2651
+ label: string;
2652
+ kind: 'harness' | 'vendor';
2653
+ runner?: string;
2654
+ usable: boolean;
2655
+ reason?: string;
2656
+ }
2657
+ /** The detail a usable harness planner is always shown with. */
2658
+ declare const HARNESS_PLANNER_REASON = "coding agent \u00B7 no API key needed";
2659
+ /**
2660
+ * The planner backends every surface offers, in order: the harness planners
2661
+ * first, always listed so a missing CLI is greyed with its reason rather than
2662
+ * hidden, then the configured vendors in `PROVIDER_PRIORITY` order. Pure: the
2663
+ * caller supplies what it knows of each runner (VS Code its `plannerUsability`
2664
+ * results, the TUI its installed set) and maps the entries to its own item
2665
+ * type, keeping surface-specific affordances — e.g. the CLI's empty-vendor
2666
+ * placeholder — to itself.
2667
+ */
2668
+ declare function plannerBackendEntries(usability: Readonly<Record<string, PlannerUsability>>, configured: readonly AiProvider[]): PlannerBackendEntry[];
2597
2669
  /**
2598
2670
  * The single definition of which providers count as "configured" — one API
2599
2671
  * key each, or (for `openai_compatible`) an explicit base URL. Every surface
@@ -2672,7 +2744,7 @@ interface EnvAdmission {
2672
2744
  */
2673
2745
  declare function admitSettingsEnv(env: Record<string, unknown>): EnvAdmission;
2674
2746
 
2675
- /** Whether a runner's tasks can run on the structured transport. */
2747
+ /** Whether a runner's tasks can run on the structured transport (ADR-0018, S3). */
2676
2748
  declare function supportsTaskMode(runner: string): boolean;
2677
2749
  /** The adapter that drives one task. Throws {@link TaskModeUnsupportedError} for a runner without a connector. */
2678
2750
  declare function createTaskAdapter(runner: string, deps: AgentProcessDeps): TaskModeAgentAdapter;
@@ -2693,8 +2765,8 @@ interface SpawnSpec {
2693
2765
  *
2694
2766
  * Subclasses own their agent's protocol: what to spawn, how to phrase a user
2695
2767
  * turn, and how to read one protocol line. Everything below — line framing,
2696
- * turn lifecycle, abort, stderr capture, premature-exit detection — is the
2697
- * same for all of them.
2768
+ * turn lifecycle, abort — is the same for all of them, and the process itself
2769
+ * is a {@link RunnerProcess}.
2698
2770
  */
2699
2771
  declare abstract class StdioAgentAdapter implements AgentAdapter {
2700
2772
  protected deps: AgentProcessDeps;
@@ -2702,8 +2774,7 @@ declare abstract class StdioAgentAdapter implements AgentAdapter {
2702
2774
  protected process: ChildProcess | null;
2703
2775
  protected sessionId: string | null;
2704
2776
  private readonly stdout;
2705
- private stderrTail;
2706
- private exited;
2777
+ private readonly runner;
2707
2778
  /** Set for the duration of a turn. Outside one, events are buffered rather than dropped. */
2708
2779
  private turnEmit;
2709
2780
  /** Set for the duration of a turn. Pinged on every raw line, whether or not `handleLine` turns it into an event. */
@@ -2725,11 +2796,6 @@ declare abstract class StdioAgentAdapter implements AgentAdapter {
2725
2796
  /** A task's session, which wants what the agent says on its own after a turn closed; a planner registers none and drops it. */
2726
2797
  private outOfTurnListener;
2727
2798
  private disposed;
2728
- /** Resolves when the process ends, so a handshake can lose the race instead of waiting out its timeout. */
2729
- protected processEnded: Promise<void>;
2730
- /** The environment the agent was spawned under, for any side process a handshake needs. */
2731
- protected spawnEnv: NodeJS.ProcessEnv;
2732
- private markEnded;
2733
2799
  /** What this process is to the user — a planner or a task's runner — for the words a failure is reported in. */
2734
2800
  protected role: 'planner' | 'task';
2735
2801
  constructor(deps: AgentProcessDeps);
@@ -2745,6 +2811,17 @@ declare abstract class StdioAgentAdapter implements AgentAdapter {
2745
2811
  /** Protocol handshake, if the agent needs one before it accepts a turn. */
2746
2812
  protected handshake(_opts: AgentStartOptions): Promise<void>;
2747
2813
  start(opts: AgentStartOptions): Promise<void>;
2814
+ /**
2815
+ * Every protocol line goes to `handleLine`, turn or no turn: an agent's
2816
+ * handshake and its session-id announcement both arrive before the first
2817
+ * message is sent, and dropping them left the handshake waiting on a reply
2818
+ * that had already come and gone.
2819
+ */
2820
+ private readProtocol;
2821
+ /** Resolves when the process ends, so a handshake can lose the race instead of waiting out its timeout. */
2822
+ protected get processEnded(): Promise<void>;
2823
+ /** The environment the agent was spawned under, for any side process a handshake needs. */
2824
+ protected get spawnEnv(): NodeJS.ProcessEnv;
2748
2825
  send(message: string, onEvent: (event: AgentEvent) => void, signal?: AbortSignal, onActivity?: () => void): Promise<void>;
2749
2826
  onOutOfTurn(listener: (event: AgentEvent) => void): void;
2750
2827
  nativeSessionId(): string | null;
@@ -2802,8 +2879,12 @@ declare class ClaudeCodeAdapter extends StdioAgentAdapter implements TaskModeAge
2802
2879
  private pendingResume;
2803
2880
  /** The `--mcp-config` file of the running process, which holds its token; removed with the process. */
2804
2881
  private mcpConfig;
2805
- /** `mcp__ordewell__`, once this process was given the server. */
2806
- private ordewellToolPrefix;
2882
+ /** Whether this process was given the server, so its tools' requests are Ordewell's own. */
2883
+ private givenOrdewell;
2884
+ /** Messages written into a running task turn, by the `uuid` their echo will carry, until it does (ADR-0023). */
2885
+ private readonly steers;
2886
+ /** The last turn's `result` ended it, so a steer's echo now opens a turn the CLI started for that message. */
2887
+ private turnClosed;
2807
2888
  protected spawnSpec(opts: AgentStartOptions): SpawnSpec;
2808
2889
  /**
2809
2890
  * A task's run: the manifest decides what its mode and effort mean
@@ -2814,16 +2895,9 @@ declare class ClaudeCodeAdapter extends StdioAgentAdapter implements TaskModeAge
2814
2895
  * without it `-p` refuses them silently and nothing can ever surface one.
2815
2896
  */
2816
2897
  private taskSpawnSpec;
2817
- /**
2818
- * `--mcp-config` takes a path as well as inline JSON; the path keeps the
2819
- * token out of the argv every local user can list (ADR-0022, A5). The file
2820
- * lives exactly as long as the process that reads it. `alwaysLoad` because
2821
- * the CLI otherwise defers MCP tools behind its tool search, and a model
2822
- * that has to look an Ordewell tool up first tends to go on without it.
2823
- */
2898
+ /** The file holding the token lives exactly as long as the process that reads it. */
2824
2899
  private ordewellServerArgs;
2825
2900
  private removeMcpConfig;
2826
- start(opts: AgentStartOptions): Promise<void>;
2827
2901
  /** Asks the CLI itself, which reports `pending` until its connection attempt settles. */
2828
2902
  mcpAttached(): Promise<boolean>;
2829
2903
  /**
@@ -2833,6 +2907,15 @@ declare class ClaudeCodeAdapter extends StdioAgentAdapter implements TaskModeAge
2833
2907
  * as an interrupted `turn_end`.
2834
2908
  */
2835
2909
  interrupt(timeoutMs: number): Promise<boolean>;
2910
+ /**
2911
+ * A `user` line written mid-turn is Claude Code's own queue (ADR-0023): the
2912
+ * CLI attaches it to the next tool result, or runs it as a turn of its own
2913
+ * after `result` — an interrupted one included — so it never lets go of one,
2914
+ * and nothing is ever reported dropped. Refused while an interrupt is in
2915
+ * flight, and before a `--resume` is taken up: a refused resume closes stdin
2916
+ * with the message unread.
2917
+ */
2918
+ steer(id: string, text: string): Promise<boolean>;
2836
2919
  /** Send a control request and wait for its answer; null when none came in time or the process ended. */
2837
2920
  private control;
2838
2921
  /**
@@ -2852,6 +2935,13 @@ declare class ClaudeCodeAdapter extends StdioAgentAdapter implements TaskModeAge
2852
2935
  dispose(): void;
2853
2936
  private clearFollowOnTimer;
2854
2937
  protected turnPayload(message: string): string;
2938
+ private openTurnState;
2939
+ /**
2940
+ * The echo of a steered message: the model has it. After a closed turn it
2941
+ * opens the CLI's own turn for the message, which then runs like one Ordewell
2942
+ * sent. The first prompt is echoed too, and is no steer.
2943
+ */
2944
+ private steerDelivered;
2855
2945
  protected handleLine(line: string, emit: (event: AgentEvent) => void): void;
2856
2946
  /**
2857
2947
  * `--permission-mode auto` on a model or account without auto mode is not
@@ -2913,25 +3003,22 @@ declare class CodexAdapter extends StdioAgentAdapter implements TaskModeAgentAda
2913
3003
  private threadId;
2914
3004
  /** The model Codex opened the thread with — usage records name it. */
2915
3005
  private threadModel;
2916
- private nextRequestId;
2917
- private settleHandshake;
2918
- private handshakeError;
3006
+ private readonly rpc;
2919
3007
  private startOpts;
2920
3008
  /** Whether this turn has already emitted prose — see the `agentMessage` case. */
2921
3009
  private turnHasText;
2922
- /**
2923
- * The turn in flight. Its id arrives with `turn/started` or the `turn/start`
2924
- * response, whichever lands first; `turn/interrupt` cannot be sent without it.
2925
- */
3010
+ /** The turn in flight. `turn/interrupt` and `turn/steer` cannot be sent before it has an id. */
2926
3011
  private turn;
2927
- /** The JSON-RPC id of the `turn/start` that opened {@link turn}. */
2928
- private turnRequestId;
2929
3012
  /** The last turn this adapter ended — so its second end signal, landing late, ends nothing. */
2930
3013
  private endedTurnId;
2931
3014
  /** An interrupt was asked for during this turn, so however it ends, it was cut short. */
2932
3015
  private interruptRequested;
2933
- /** An interrupt asked for before Codex named the turn, sent once it does. */
2934
- private interruptOnStart;
3016
+ /**
3017
+ * Turns an interrupt cut short. Codex leaves their command running and
3018
+ * reports it when it ends, inside whatever turn is live by then — a report
3019
+ * that belongs to no turn of the agent's any more (ADR-0023, F4).
3020
+ */
3021
+ private readonly interruptedTurnIds;
2935
3022
  /** A task's approval requests still waiting for an answer, by the request id as a string. */
2936
3023
  private readonly openPermissions;
2937
3024
  /**
@@ -2941,10 +3028,6 @@ declare class CodexAdapter extends StdioAgentAdapter implements TaskModeAgentAda
2941
3028
  private readonly fileChangePaths;
2942
3029
  /** The row each announced file of an in-flight `fileChange` item is shown under, by the file's shown path. */
2943
3030
  private readonly fileChangeRows;
2944
- /** Requests this adapter sent and awaits an answer to, by JSON-RPC id. */
2945
- private readonly pendingRequests;
2946
- private resumeAttempted;
2947
- private resumeFallbackSent;
2948
3031
  private sandbox;
2949
3032
  /** The Ordewell server's startup state, as Codex last reported it for this thread. */
2950
3033
  private mcpStartup;
@@ -2955,6 +3038,7 @@ declare class CodexAdapter extends StdioAgentAdapter implements TaskModeAgentAda
2955
3038
  * one stream; the thread id is what tells them apart (see {@link subagentOf}).
2956
3039
  */
2957
3040
  private readonly subagents;
3041
+ constructor(deps: AgentProcessDeps);
2958
3042
  protected spawnSpec(opts: AgentStartOptions): SpawnSpec;
2959
3043
  /**
2960
3044
  * `initialize`, then `thread/start`, both before the first user message. A
@@ -2964,16 +3048,18 @@ declare class CodexAdapter extends StdioAgentAdapter implements TaskModeAgentAda
2964
3048
  * construction. A task's runs as its mode says (see {@link threadParams}).
2965
3049
  */
2966
3050
  protected handshake(opts: AgentStartOptions): Promise<void>;
3051
+ private handshakeIncomplete;
2967
3052
  /**
2968
3053
  * Open this session's thread. A resume id means the previous
2969
3054
  * process died mid-session: `thread/resume` puts the agent back in front of
2970
3055
  * the context it already paid to read. A planner's failed resume is not an
2971
- * error — the response handler falls back to a fresh thread, which is the
2972
- * same degradation `restoreChat` performs on every surface (T4). A task's
2973
- * is: Continue (K1) offers the session that did the work, and a fresh thread
2974
- * in its place would pretend it was resumed.
3056
+ * error — it falls back to a fresh thread, which is the same degradation
3057
+ * `restoreChat` performs on every surface (T4). A task's is: Continue (K1)
3058
+ * offers the session that did the work, and a fresh thread in its place
3059
+ * would pretend it was resumed.
2975
3060
  */
2976
- private startThread;
3061
+ private openThread;
3062
+ private requestThread;
2977
3063
  private threadParams;
2978
3064
  /**
2979
3065
  * Codex's own startup report for the server, which it sends once the thread
@@ -2984,10 +3070,23 @@ declare class CodexAdapter extends StdioAgentAdapter implements TaskModeAgentAda
2984
3070
  private effort;
2985
3071
  /**
2986
3072
  * `turn/interrupt` names the turn as well as the thread, so an interrupt
2987
- * asked for before Codex has named the turn waits for it. Codex acknowledges
2988
- * with an empty result, then ends the turn as `interrupted`.
3073
+ * asked for before Codex has named the turn waits for it, as a steer does.
3074
+ * Codex acknowledges with an empty result, then ends the turn as `interrupted`.
2989
3075
  */
2990
3076
  interrupt(timeoutMs: number): Promise<boolean>;
3077
+ /** Run `send` now if Codex has named the turn, and once it does if not. */
3078
+ private whenNamed;
3079
+ /**
3080
+ * `turn/steer` into the running turn (ADR-0023). Codex answers with the
3081
+ * turn's id at once — acceptance, not delivery — and shows the input to the
3082
+ * model after the item in flight, as a `userMessage` item carrying the
3083
+ * `clientUserMessageId` sent; that item is the delivery. The request needs
3084
+ * the turn's id, so a steer asked for before Codex named the turn waits for
3085
+ * it, as an interrupt does. Refused when the turn has ended or is another one.
3086
+ */
3087
+ steer(id: string, text: string): Promise<boolean>;
3088
+ /** The one way input reaches a running turn: a task message, or a deny note. */
3089
+ private requestSteer;
2991
3090
  /**
2992
3091
  * Answer an open approval in its own result schema. Codex's decline carries
2993
3092
  * no message, so a deny note reaches the agent as input steered into the
@@ -2995,9 +3094,19 @@ declare class CodexAdapter extends StdioAgentAdapter implements TaskModeAgentAda
2995
3094
  */
2996
3095
  answerPermission(id: string, decision: ApprovalDecision): boolean;
2997
3096
  protected turnPayload(message: string): string;
3097
+ /** `turn/start`'s answer. One for a turn that has since closed or been replaced names and closes nothing. */
3098
+ private turnAnswered;
2998
3099
  protected handleLine(line: string, emit: (event: AgentEvent) => void): void;
2999
3100
  private turnStarted;
3101
+ /**
3102
+ * Ahead of the event that ends the turn: a steer Codex accepted and the turn
3103
+ * never consumed is gone — Codex keeps no queue across turns — and one not
3104
+ * yet accepted never will be (ADR-0023, D4).
3105
+ */
3000
3106
  private closeTurn;
3107
+ private fromInterruptedTurn;
3108
+ /** The `userMessage` item a steer turns into once the item before it completes: the model has it. */
3109
+ private steerDelivered;
3001
3110
  /**
3002
3111
  * The child thread id when `threadId` names a subagent's thread, or undefined
3003
3112
  * for the planner's own. Codex replays both threads on one stream and only
@@ -3065,8 +3174,8 @@ declare class CodexAdapter extends StdioAgentAdapter implements TaskModeAgentAda
3065
3174
  * (ADR-0009, ADR-0018).
3066
3175
  *
3067
3176
  * The odd one out: `opencode serve` is a real server rather than a stdio
3068
- * protocol, so this adapter owns both halves of the boundary — it spawns the
3069
- * process through the same injected `spawn` every other adapter uses, then
3177
+ * protocol, so this adapter owns both halves of the boundary — it starts the
3178
+ * process as the same {@link RunnerProcess} every other adapter uses, then
3070
3179
  * talks to it through the injected `fetch`. Both are part of the one seam the
3071
3180
  * tests drive.
3072
3181
  *
@@ -3086,29 +3195,36 @@ declare class OpenCodeAdapter implements TaskModeAgentAdapter {
3086
3195
  /** Sent on every request to a task's server, which a per-task password secures. */
3087
3196
  private authorization;
3088
3197
  private sessionId;
3089
- private stderrTail;
3090
- private exited;
3091
- private exitCode;
3198
+ private readonly runner;
3092
3199
  private disposed;
3093
- private markEnded;
3094
- private readonly processEnded;
3095
3200
  private planner;
3096
3201
  private task;
3097
- /** Whether this turn has already emitted reply text — see {@link emitPart}. */
3098
- private turnHasText;
3099
3202
  /** The last assistant message already settled — the baseline {@link recoverReply} measures a new reply against. */
3100
3203
  private lastAssistantId;
3101
3204
  private taskTurn;
3102
3205
  /** An abort was posted during the current task turn, so the idle that follows ends it as interrupted. */
3103
3206
  private interruptRequested;
3104
- /** A task's requests waiting for an answer, by request id, with the session that asked. */
3105
- private readonly openPermissions;
3207
+ private readonly permissions;
3208
+ /** Messages handed into the running task turn, until the model reads them (ADR-0023). Kept across turns: the server keeps the message too. */
3209
+ private readonly steers;
3106
3210
  /** Set once the server turns out to speak the 2.x API, which then owns the session. */
3107
3211
  private v2;
3108
3212
  /** The Ordewell server this process was configured with; null when none was given or its config could not be merged. */
3109
3213
  private ordewell;
3110
3214
  constructor(deps: AgentProcessDeps);
3215
+ private get processEnded();
3216
+ private get exited();
3111
3217
  start(opts: AgentStartOptions): Promise<void>;
3218
+ private launch;
3219
+ /**
3220
+ * Where `serve` listens, or null once it ends or gives up. Stdout only: that
3221
+ * is where `serve` prints its address, and stderr can carry another URL — a
3222
+ * warning about a provider it could not reach. Read a line at a time, and
3223
+ * only until the address is known.
3224
+ */
3225
+ private serverAddress;
3226
+ /** From the server's address to an open session — the 2.x API's, or a 1.x one created or resumed. */
3227
+ private connect;
3112
3228
  send(message: string, onEvent: (event: AgentEvent) => void, signal?: AbortSignal, onActivity?: () => void): Promise<void>;
3113
3229
  /**
3114
3230
  * One task turn. `prompt_async` only queues the work, so the turn is over
@@ -3120,6 +3236,20 @@ declare class OpenCodeAdapter implements TaskModeAgentAdapter {
3120
3236
  * {@link recoverReply}, never applies.
3121
3237
  */
3122
3238
  private sendTask;
3239
+ /**
3240
+ * The prompt that carries a task message: the mode as the agent, the task
3241
+ * model and effort, and only `question` withheld. A steer names its own
3242
+ * `messageID` so the frames can be told apart from the first prompt's.
3243
+ */
3244
+ private taskPromptBody;
3245
+ /** Hand a message into the running task turn (ADR-0023, D2), naming it so its delivery is observable. */
3246
+ steer(id: string, text: string): Promise<boolean>;
3247
+ /**
3248
+ * Messages handed over but unread when the turn ended. Each is deleted from
3249
+ * the session — so the next turn's re-send is not a duplicate — and reported
3250
+ * dropped, which is what puts it back in Ordewell's queue.
3251
+ */
3252
+ private dropUnreadSteers;
3123
3253
  /**
3124
3254
  * Whatever of the turn's own messages the stream missed. Each part is
3125
3255
  * emitted once, so a turn the stream saw whole adds nothing here; one that
@@ -3207,48 +3337,15 @@ declare class OpenCodeAdapter implements TaskModeAgentAdapter {
3207
3337
  * none of this turn's business.
3208
3338
  */
3209
3339
  private onFrame;
3210
- /**
3211
- * Stream one piece of a reply text part. The part's paragraph break goes out
3212
- * with its first visible delta, so the deltas add up to exactly the text the
3213
- * completed part then re-sends; a part that is only whitespace so far is
3214
- * held back, for the reason {@link emitPart} drops one.
3215
- */
3216
- private onTextDelta;
3217
- /**
3218
- * Deny one permission request (T1). OpenCode blocks the turn until the
3219
- * request is answered, so this must answer — `reject` rather than a silent
3220
- * drop, which is the same "absent answer is a denial" invariant ADR-0008
3221
- * states for Ordewell's own tools. The refusal is announced so the timeline
3222
- * shows the planner reaching for something it may not have.
3223
- */
3224
- private denyPermission;
3225
- /**
3226
- * A task's request. Under a mode whose manifest sets `approvals: auto` —
3227
- * `build`, which the terminal transport runs with `--auto` — it is answered
3228
- * at once with what `run --auto` answers, so the same plan behaves the same
3229
- * on both transports (ADR-0001), and announced already decided so the log
3230
- * still shows it. Any other mode leaves it open for an approval card.
3231
- */
3232
- private askPermission;
3340
+ /** A planner's request is refused (T1); a task's is answered at once or left open for a card. */
3341
+ private onPermissionAsked;
3233
3342
  /**
3234
3343
  * `POST /permission/:id/reply` is the current answer. The per-session path
3235
3344
  * it replaced is tried only when a server too old to know the new one 404s.
3236
3345
  */
3237
3346
  private replyPermission;
3238
- /**
3239
- * Open `/event` for one turn and wait until it is connected. The stream
3240
- * stopped being best-effort the moment permission answers moved onto it: a
3241
- * request raised before we connect is one nobody answers, and the turn
3242
- * hangs on it. Waiting is bounded so a server that never opens `/event`
3243
- * still gets its turn.
3244
- */
3347
+ /** `/event` for one turn — see {@link openEventStream}. Load-bearing for {@link onPermissionAsked}. */
3245
3348
  private openStream;
3246
- /**
3247
- * Server-sent events from `/event`: the turn's live text, reasoning, tool
3248
- * activity and usage, and the only channel permission requests arrive on —
3249
- * so the stream is load-bearing for {@link denyPermission}.
3250
- */
3251
- private streamEvents;
3252
3349
  private json;
3253
3350
  private request;
3254
3351
  private headers;
@@ -3277,12 +3374,155 @@ declare function mapAgentTool(name: string): MappedTool;
3277
3374
  */
3278
3375
  declare function normalizeAgentArgs(tool: ResearchToolType, args: Record<string, unknown>): Record<string, unknown>;
3279
3376
 
3377
+ /** What a runner offers a task: the models discovered for it and the modes its manifest declares. */
3378
+ interface RunnerCatalog {
3379
+ models: DiscoveredModel[];
3380
+ modes: RunnerModeInfo[];
3381
+ /**
3382
+ * The mode a task lands on when it carries none this runner offers: the
3383
+ * manifest's `autonomous`/`safe`-tagged mode under the user's toggle
3384
+ * (ADR-0001). Absent, `modes[0]` stands in — which for Codex is its
3385
+ * workspace-write sandbox, not the full access an unattended run needs.
3386
+ */
3387
+ defaultMode?: string;
3388
+ }
3389
+ /**
3390
+ * The model, thinking effort and mode a runner offers for a task.
3391
+ *
3392
+ * A task's model, effort and mode are all scoped to its runner:
3393
+ * `claude-sonnet-4-5` on Codex, or `acceptEdits` on OpenCode, are not degraded
3394
+ * choices but unspawnable ones. So this is where any task acquires a runnable
3395
+ * assignment — a runner change (below) and a hand-added task derive it the same
3396
+ * way. Each field in `current` is preserved when the runner also offers it, and
3397
+ * otherwise snapped to that runner's preferred entry (discovery already sorts
3398
+ * models by the manifest's `preferredPatterns`; the mode is the catalog's
3399
+ * `defaultMode`).
3400
+ *
3401
+ * An empty catalog means discovery failed or the runner is a plugin we have no
3402
+ * list for — not that the runner offers nothing. That field is left out of the
3403
+ * patch and the runner validates last, matching `coerceAssignments`.
3404
+ */
3405
+ declare function runnerAssignment(catalog: RunnerCatalog, current?: Pick<Task, 'assignedModel' | 'taskMode'>): Partial<Task>;
3406
+ /**
3407
+ * Move a task onto a different runner, carrying its model, thinking effort and
3408
+ * mode over to values that runner actually offers. A runner change is never a
3409
+ * single-field edit — it either brings the other three with it or leaves the
3410
+ * task unrunnable.
3411
+ *
3412
+ * Returns the patch to apply, or `{}` when there is nothing to change.
3413
+ */
3414
+ declare function retargetTaskRunner(task: Task, runner: RunnerId, catalog: RunnerCatalog): Partial<Task>;
3415
+
3416
+ type ModelsByRunner = Partial<Record<RunnerId, DiscoveredModel[]>>;
3417
+ interface SessionCatalogDeps {
3418
+ config: Pick<IConfig, 'enabledRunners' | 'autonomousMode'>;
3419
+ registry: Pick<RunnerRegistry, 'getManifest'>;
3420
+ modelResolver: Pick<ModelResolver, 'modelsForRunners' | 'getCachedRunnerModels'>;
3421
+ /** Read at each call: a toggle or allowlist edit made since the session was built counts (#69). */
3422
+ settings: () => {
3423
+ enabledRunners?: RunnerId[];
3424
+ modelAllowlist?: Record<string, string[]>;
3425
+ };
3426
+ /** The runners of the plan being edited; empty while the session has none. */
3427
+ planRunners: () => RunnerId[];
3428
+ }
3429
+ /**
3430
+ * What a session may assign: the enabled runners, their modes, the models
3431
+ * discovery found for them and the allowlist narrowing those. Every view of
3432
+ * it — the planner's per-turn block, its tools, a user's direct edit — reads
3433
+ * it here, so none can be shown or checked against something the others are not.
3434
+ *
3435
+ * The discovered models are remembered per runner and only ever merged into:
3436
+ * discovering one runner must not forget another's, since coercion later clamps
3437
+ * thinking efforts against whatever was last found.
3438
+ */
3439
+ declare class SessionCatalog {
3440
+ private readonly deps;
3441
+ private discovered;
3442
+ constructor(deps: SessionCatalogDeps);
3443
+ /** The runners enabled right now. */
3444
+ enabledRunners(): RunnerId[];
3445
+ /**
3446
+ * The allowlist in force right now. Unset means no restriction — falling
3447
+ * back to the one planning started under would keep a restriction the user
3448
+ * has since cleared.
3449
+ */
3450
+ allowlist(): Record<string, string[]>;
3451
+ /** The models the user allows on one runner; unset means no restriction. */
3452
+ allowlistFor(runner: RunnerId): string[] | undefined;
3453
+ /**
3454
+ * The discovered catalog as the resolver holds it now, per runner, with this
3455
+ * session's own discovery as the fallback where the resolver has nothing
3456
+ * cached. The resolver's cache outlives this session's snapshot in both
3457
+ * directions — an allowlist picker re-discovers into it mid-session — and a
3458
+ * model allowed after that must reach the planner with its real label and
3459
+ * variants, not as an id-only stub. Never triggers discovery itself.
3460
+ */
3461
+ models(): ModelsByRunner;
3462
+ /** What this session's own discovery found for a runner; empty when it never ran there. */
3463
+ known(runner: RunnerId): DiscoveredModel[];
3464
+ /** Discover the runners' models, spawning their CLIs where the resolver has none cached, and remember them. */
3465
+ discover(runners: RunnerId[]): Promise<ModelsByRunner>;
3466
+ /**
3467
+ * Keep what an admitted runner was found to offer, for the next coercion's
3468
+ * effort clamping. An empty list is discovery having found nothing, not a
3469
+ * runner offering nothing, so it never displaces what is known.
3470
+ */
3471
+ admit(runner: RunnerId, models: DiscoveredModel[]): void;
3472
+ /** Session boundaries are hard: nothing discovered for one goal informs the next. */
3473
+ reset(): void;
3474
+ modes(runners: RunnerId[]): Record<RunnerId, RunnerModeInfo[]>;
3475
+ /**
3476
+ * What a planner may assign right now: every enabled runner, its modes, and
3477
+ * its allowlisted models. Discovers a runner's models the first time it is
3478
+ * asked for.
3479
+ */
3480
+ live(): Promise<TaskQueryCatalog>;
3481
+ queryCatalog(runners: RunnerId[]): TaskQueryCatalog;
3482
+ /**
3483
+ * The catalog a model/task-mode edit is checked against — the same
3484
+ * discovered models and manifest modes the per-turn catalog block shows the
3485
+ * planner, so a refusal here can never name something invalid that the
3486
+ * planner was never told about. The catalog is not filtered by the
3487
+ * allowlist here — `checkModelAndModeValidity` narrows by allowlist itself,
3488
+ * the same way `coerceAssignments` does.
3489
+ */
3490
+ edit(runners?: RunnerId[]): EditCatalog;
3491
+ /** What a runner offers, as `runnerAssignment` needs it. Spawns the runner's CLI to list models. */
3492
+ runner(runner: RunnerId): Promise<RunnerCatalog>;
3493
+ /**
3494
+ * The one place planning discovers what it may draw from: the runners'
3495
+ * models and their modes. `filteredModels` is the allowlisted view a prompt
3496
+ * may show; `modelsByRunner` stays whole because coercion needs real labels
3497
+ * and variants.
3498
+ */
3499
+ planning(runners: RunnerId[]): Promise<{
3500
+ modelsByRunner: Partial<Record<string, DiscoveredModel[]>>;
3501
+ filteredModels: Partial<Record<string, DiscoveredModel[]>>;
3502
+ runnerModes: Record<string, RunnerModeInfo[]>;
3503
+ }>;
3504
+ }
3505
+
3280
3506
  /** The dialogue record a fork carries — what {@link PlannerConversation.clone} returns. */
3281
3507
  interface ForkedDialogue {
3282
3508
  conversationHistory: ConversationMessage[];
3283
3509
  researchLog: ResearchLogEntry[];
3284
3510
  }
3285
3511
 
3512
+ /**
3513
+ * The planner turn (see CONTEXT.md): the one piece of planner work that holds
3514
+ * the conversation, from its claim to its settled outcome. Its signal is what a
3515
+ * stop aborts.
3516
+ */
3517
+ declare class PlannerTurn {
3518
+ private readonly controller;
3519
+ private dropped;
3520
+ get signal(): AbortSignal;
3521
+ /** The plan it was answering was dropped under it, so nothing it settles may land. */
3522
+ get abandoned(): boolean;
3523
+ stop(): void;
3524
+ abandon(): void;
3525
+ }
3286
3526
  /** A read made through a planner tool: refused once the turn's budget is gone, else answered, told to land when the budget asks it to. */
3287
3527
  type ToolRead<T> = {
3288
3528
  status: 'refused';
@@ -3323,7 +3563,7 @@ interface PlannerConversationHost {
3323
3563
  * conversation never reaches into execution state directly.
3324
3564
  */
3325
3565
  liveOutput: LiveOutputLookup;
3326
- hasLiveWork(): boolean;
3566
+ isExecuting(): boolean;
3327
3567
  /** The session's single mutation ritual: op → persist → notify (default: the plan). */
3328
3568
  mutate(op: () => boolean, notify?: () => void): LegacyPlanState | null;
3329
3569
  broadcast: SessionBroadcaster;
@@ -3338,6 +3578,8 @@ interface PlannerConversationHost {
3338
3578
  queueEdit(userMessage: string): number;
3339
3579
  /** Wake a scheduler an applied edit may have unblocked. */
3340
3580
  afterEdit(): Promise<void>;
3581
+ /** The planner turn was stopped or abandoned: whatever it is waiting on (a parked approval) will never be answered. */
3582
+ turnAborted(): void;
3341
3583
  }
3342
3584
  /**
3343
3585
  * A conversation edit (rewind, fork) the conversation refused because the
@@ -3356,6 +3598,25 @@ declare class ConversationEditError extends Error {
3356
3598
  declare class ConversationBusyError extends ConversationEditError {
3357
3599
  constructor(operation: string);
3358
3600
  }
3601
+ /**
3602
+ * A planner turn that settled after the plan it was answering was dropped (a
3603
+ * new plan, another session adopted, the session closed), or after its call
3604
+ * was cut off by something other than its own stop. Nothing it produced was
3605
+ * committed: landing it would write one session's reply into another's
3606
+ * transcript.
3607
+ */
3608
+ declare class PlannerTurnDiscardedError extends Error {
3609
+ constructor(options?: ErrorOptions);
3610
+ }
3611
+ /**
3612
+ * The user stopped the turn and the backend reported that as a thrown error
3613
+ * rather than a settled reply. Typed so a surface can stay quiet for it without
3614
+ * reading the message: some SDKs' abort errors are plain `Error`s by name, and
3615
+ * a real failure can mention "aborted" too.
3616
+ */
3617
+ declare class PlannerTurnStoppedError extends Error {
3618
+ constructor(options?: ErrorOptions);
3619
+ }
3359
3620
  /** A user message a rewind can fork the conversation from just before. */
3360
3621
  interface RewindTarget {
3361
3622
  /** Position in the transcript — what {@link PlannerConversation.cloneBefore} takes. */
@@ -3411,13 +3672,32 @@ declare class PlannerConversation {
3411
3672
  private persisted;
3412
3673
  /** Saves an execution event made while a turn may be in flight; see {@link restore}. */
3413
3674
  private savedInBackground;
3414
- private turnsInFlight;
3415
- private compacting;
3675
+ private live;
3416
3676
  private openTurn;
3417
3677
  constructor(host: PlannerConversationHost);
3418
3678
  get transcript(): readonly ConversationMessage[];
3419
- /** Whether a user turn is between its transcript append and its settled outcome. */
3679
+ /** Whether a planner turn holds the conversation. */
3420
3680
  get isTurnInFlight(): boolean;
3681
+ /** Stop the planner turn in flight; it settles as stopped. False when none is. */
3682
+ stopTurn(): boolean;
3683
+ /**
3684
+ * Drop the planner turn in flight together with the plan it was answering:
3685
+ * it is stopped, whatever it settles is discarded, and the conversation is
3686
+ * free at once rather than when the abandoned turn finishes unwinding.
3687
+ */
3688
+ abandonTurn(): void;
3689
+ /**
3690
+ * Run planner work that is not a conversation turn — one-shot plan
3691
+ * generation — as the planner turn, so it is stopped, refused and superseded
3692
+ * like one. It starts a fresh plan, so it supersedes the turn in flight.
3693
+ */
3694
+ hold<T>(signal: AbortSignal | undefined, body: (turn: PlannerTurn) => Promise<T>): Promise<T>;
3695
+ /**
3696
+ * Take the conversation for a new planner turn. A caller's own signal stops
3697
+ * the turn too; the relay is detached on release because a caller may reuse
3698
+ * that signal, and a stale listener would stop the next turn with it.
3699
+ */
3700
+ private claim;
3421
3701
  /** The user turn being answered, for what the host raises during it — an approval the turn's research asks for. */
3422
3702
  get currentTurnId(): string | undefined;
3423
3703
  /**
@@ -3526,8 +3806,11 @@ declare class PlannerConversation {
3526
3806
  * inside the host's mutation ritual.
3527
3807
  */
3528
3808
  recordQueuedEditsFailed(messages: string[], reason: string): void;
3529
- /** Open the conversation on a fresh plan: the goal is its first message. */
3530
- start(goal: string, opening: ConversationOpening, signal?: AbortSignal): Promise<LegacyPlanState>;
3809
+ /**
3810
+ * Open the conversation on the host's fresh plan: the goal is its first
3811
+ * message. `prepare` (discovery) runs inside the turn, so a stop reaches it.
3812
+ */
3813
+ start(goal: string, prepare: () => Promise<ConversationOpening>, signal?: AbortSignal): Promise<LegacyPlanState>;
3531
3814
  /**
3532
3815
  * Every later user message, from the transcript append to a persisted
3533
3816
  * outcome. A turn that throws before anything was persisted takes its own
@@ -3544,7 +3827,20 @@ declare class PlannerConversation {
3544
3827
  */
3545
3828
  private editTouchesLiveWork;
3546
3829
  private assertIdle;
3547
- private inTurn;
3830
+ /**
3831
+ * Refuse to land a turn that no longer belongs here: it was abandoned, its
3832
+ * plan is no longer the host's, or its call was cut off by something other
3833
+ * than its own stop (`IAiService.reset` under a plan change) — what such a
3834
+ * call hands back is a fragment of an answer to a dialogue that is gone.
3835
+ */
3836
+ private assertCurrent;
3837
+ /**
3838
+ * A live conversation is only safe to continue in-place when it also
3839
+ * matches the model/effort configured right now — a harness planner's
3840
+ * running process was spawned with the old one baked in and cannot pick up
3841
+ * a switch (ADR-0009). Otherwise the turn resumes from the transcript.
3842
+ */
3843
+ private canContinueLive;
3548
3844
  /**
3549
3845
  * Bracket one user turn with its start and end, under an id minted here:
3550
3846
  * the turn is where the stream a surface draws begins and ends, and only the
@@ -3589,10 +3885,11 @@ declare class PlannerConversation {
3589
3885
  */
3590
3886
  private taskQueryAnswer;
3591
3887
  /**
3592
- * Drive a planner turn to a persisted, broadcast outcome. Task edits apply
3593
- * atomically; validation failures are fed back to the model for up to 2
3594
- * silent retries, then surfaced as a message with the plan untouched. The
3595
- * first turn and every later turn route through here — one path, not two.
3888
+ * Drive a planner turn, its reads already drained, to a persisted, broadcast
3889
+ * outcome. Task edits apply atomically; validation failures are fed back to
3890
+ * the model for up to 2 silent retries, then surfaced as a message with the
3891
+ * plan untouched. The first turn and every later turn route through here —
3892
+ * one path, not two.
3596
3893
  */
3597
3894
  private settle;
3598
3895
  /** Validate + commit a task_ops turn atomically. Returns the errors on rejection (plan untouched). */
@@ -3694,14 +3991,21 @@ interface SessionEventRelayDeps {
3694
3991
  /** Where isolation notices go, for a host whose notifications are not seen by the user. */
3695
3992
  onNotice?: (notice: SessionNotice) => void;
3696
3993
  store: Pick<PlanStore, 'allTasks' | 'snapshot'>;
3697
- orchestrator: Pick<TaskOrchestrator, 'getIdleSince' | 'getTaskIsolation' | 'getQueuedTaskMessages' | 'getMergeGate' | 'mergeGateView'>;
3994
+ orchestrator: Pick<TaskOrchestrator, 'getIdleSince' | 'getQueuedTaskMessages' | 'getMergeGate' | 'mergeGateView'>;
3995
+ runs: Pick<IsolationRunController, 'taskIsolation'>;
3698
3996
  /** Shared with the Session, which snapshots, restores and clears it. */
3699
3997
  usage: PlannerUsageLedger;
3700
3998
  /** How many of a task's runner requests wait for an answer (ADR-0018, A1). */
3701
3999
  awaitingApproval?: (taskId: string) => number;
4000
+ /** What a task's waiting checkpoint asks, whole. */
4001
+ checkpointQuestion?: (taskId: string) => string | undefined;
3702
4002
  }
3703
- /** Every orchestrator event the relay announces; `onTaskSettled` is persistence only, so it is the Session's. */
3704
- type RelayObserver = Required<Omit<OrchestratorObserver, 'onTaskSettled'>>;
4003
+ /**
4004
+ * Every orchestrator event the relay announces. `onTaskSettled` is persistence
4005
+ * only and `onQueueReady` is the Session's cue to drain its queue, so both are
4006
+ * the Session's alone.
4007
+ */
4008
+ type RelayObserver = Required<Omit<OrchestratorObserver, 'onTaskSettled' | 'onQueueReady'>>;
3705
4009
  /**
3706
4010
  * Turns orchestrator and planner events into {@link SessionMessage}
3707
4011
  * broadcasts — the one place a surface's view of a Session is produced. It
@@ -3713,8 +4017,10 @@ declare class SessionEventRelay {
3713
4017
  private readonly onNotice?;
3714
4018
  private readonly store;
3715
4019
  private readonly orchestrator;
4020
+ private readonly runs;
3716
4021
  private readonly usage;
3717
4022
  private readonly awaitingApproval;
4023
+ private readonly checkpointQuestion;
3718
4024
  /**
3719
4025
  * The in-flight turn's subagent activity, grouped one run per subagent so a
3720
4026
  * replay nests each step under its own brief/result. Folded into the plan's
@@ -3780,16 +4086,6 @@ declare function openTaskLog(location: TaskLogLocation, taskId: string): TaskLog
3780
4086
  */
3781
4087
  declare function readTaskLog(location: TaskLogLocation, taskId: string, attempt: number): TaskLogEvent[];
3782
4088
 
3783
- /**
3784
- * A direct (non-planner) plan edit the session refused. Distinct from a plain
3785
- * Error so a surface can tell "you asked for something invalid" from "something
3786
- * broke" and say which — the HTTP routes used to collapse both into 404/500,
3787
- * which read to the TUI and VS Code as the edit silently doing nothing. Carries
3788
- * no status code: core is transport-agnostic, the route maps it.
3789
- */
3790
- declare class PlanEditError extends Error {
3791
- constructor(message: string);
3792
- }
3793
4089
  /**
3794
4090
  * Options for plan generation. Progress is not overridable: every planner
3795
4091
  * progress event is translated to a SessionMessage inside the Session and
@@ -3909,7 +4205,7 @@ declare function createSession(deps: SessionDeps): Session;
3909
4205
  /** What {@link createSession} hands a Session: every collaborator, already built and wired. */
3910
4206
  interface SessionParts {
3911
4207
  config: IConfig;
3912
- registry: RunnerRegistry;
4208
+ catalog: SessionCatalog;
3913
4209
  workspace: string;
3914
4210
  fsAdapter: IFileSystem;
3915
4211
  broadcast: SessionBroadcaster;
@@ -3921,6 +4217,8 @@ interface SessionParts {
3921
4217
  planner: SessionPlanner;
3922
4218
  store: PlanStore;
3923
4219
  orchestrator: TaskOrchestrator;
4220
+ /** The orchestrator's own: Session reads the run and acts on it there, Merge all aside. */
4221
+ runs: IsolationRunController;
3924
4222
  events: SessionEventRelay;
3925
4223
  usage: PlannerUsageLedger;
3926
4224
  approvals: PendingApprovals;
@@ -3949,9 +4247,10 @@ declare class Session {
3949
4247
  private readonly events;
3950
4248
  private readonly planner;
3951
4249
  private readonly orchestrator;
4250
+ private readonly runs;
3952
4251
  private readonly store;
3953
4252
  private readonly config;
3954
- private readonly registry;
4253
+ private readonly catalog;
3955
4254
  private plan;
3956
4255
  private goal;
3957
4256
  private workspace;
@@ -3964,13 +4263,18 @@ declare class Session {
3964
4263
  private readonly fetcher;
3965
4264
  private readonly settingsFn;
3966
4265
  private readonly save;
3967
- /** Last discovered model catalog — lets sync plan commits clamp thinking efforts to real variants. */
3968
- private modelsCache;
4266
+ /** The queue drain in flight, which a second call joins; see {@link processQueuedMessages}. */
4267
+ private queueDrain;
4268
+ /** The queued messages the planner has been given, no longer the user's to take back. */
4269
+ private drainingIds;
4270
+ /** User controls in flight; see {@link withSave}. */
4271
+ private controlsInFlight;
3969
4272
  private unsubObserver;
3970
4273
  private readonly hostSessionId?;
3971
4274
  private currentSessionId;
3972
4275
  private readonly skillsService;
3973
4276
  private readonly conversation;
4277
+ private readonly editor;
3974
4278
  private readonly plannerTools;
3975
4279
  constructor(parts: SessionParts);
3976
4280
  /**
@@ -3997,7 +4301,7 @@ declare class Session {
3997
4301
  /**
3998
4302
  * The relay announces every orchestrator event; this adds the saves some of
3999
4303
  * them owe, each made before the announcement so no surface sees state the
4000
- * disk lacks.
4304
+ * disk lacks, and drains the queue the scheduler parked behind.
4001
4305
  */
4002
4306
  private observer;
4003
4307
  /**
@@ -4032,41 +4336,8 @@ declare class Session {
4032
4336
  * session.
4033
4337
  */
4034
4338
  reset(): void;
4035
- /**
4036
- * What a planner may assign right now: every enabled runner, its modes, and
4037
- * its allowlisted models — read as of this call, so a runner enabled or an
4038
- * allowlist edited since planning started is in it (#69). Discovers a
4039
- * runner's models the first time it is asked for.
4040
- */
4339
+ /** What a planner may assign right now; see {@link SessionCatalog.live}. */
4041
4340
  liveCatalog(): Promise<TaskQueryCatalog>;
4042
- private catalogOf;
4043
- private runnerModesFor;
4044
- /**
4045
- * The catalog a model/task-mode edit is checked against — the same
4046
- * discovered models and manifest modes the per-turn catalog block shows the
4047
- * planner, so a refusal here can never name something invalid that the
4048
- * planner was never told about. The catalog is not filtered by the
4049
- * allowlist here — {@link checkModelAndModeValidity} narrows by
4050
- * allowlist itself, the same way `coerceAssignments` does.
4051
- */
4052
- private editCatalog;
4053
- /** The runners enabled right now — a toggle made since the session was built counts (#69). */
4054
- private enabledRunners;
4055
- /**
4056
- * The allowlist in force right now. Unset means no restriction — falling
4057
- * back to the one planning started under would keep a restriction the user
4058
- * has since cleared.
4059
- */
4060
- private allowlist;
4061
- /**
4062
- * The discovered catalog as the resolver holds it now, per runner, with this
4063
- * session's own discovery as the fallback where the resolver has nothing
4064
- * cached. The resolver's cache outlives this session's snapshot in both
4065
- * directions — an allowlist picker re-discovers into it mid-session — and a
4066
- * model allowed after that must reach the planner with its real label and
4067
- * variants, not as an id-only stub. Never triggers discovery itself.
4068
- */
4069
- private models;
4070
4341
  get planState(): LegacyPlanState | null;
4071
4342
  /**
4072
4343
  * The live plan in the shape a saved session is read back as. The disk
@@ -4082,24 +4353,16 @@ declare class Session {
4082
4353
  * nothing executing, and reporting it as executing is what left the plan
4083
4354
  * unstartable after its last live task was cancelled.
4084
4355
  */
4085
- get isExecuting(): boolean;
4086
4356
  /** See {@link TaskOrchestrator.hasLiveWork} — a spawned runner, not merely an armed scheduler. */
4087
- get hasLiveWork(): boolean;
4357
+ get isExecuting(): boolean;
4088
4358
  get status(): 'approved' | 'running' | 'completed';
4359
+ /** Whether a planner turn — a reply, a compaction, plan generation — holds the conversation. */
4360
+ get isPlannerBusy(): boolean;
4089
4361
  /**
4090
- * Deny every parked approval as soon as a planning turn is aborted, for the
4091
- * same reason `beginFreshPlan` does it: a prompt raised by a turn nobody is
4092
- * waiting on has no one left to serve, and denying it unblocks the research
4093
- * loop instead of stranding it. Without this the request sat out its
4094
- * five-minute timeout and the loop then carried on as if nothing had
4095
- * happened — the abort was real, but invisible until long after the user
4096
- * pressed stop.
4097
- *
4098
- * The listener is returned as a disposer rather than left attached: callers
4099
- * own the signal and may reuse it, and a leaked listener would deny the
4100
- * *next* turn's prompts the moment that stale signal aborted.
4362
+ * Stop the planner turn in flight. It still settles — a backend hands back
4363
+ * what it had — and ends as stopped. False when no turn is in flight.
4101
4364
  */
4102
- private denyApprovalsOnAbort;
4365
+ abortPlannerTurn(): boolean;
4103
4366
  generatePlan(goal: string, runners: RunnerId[], options?: GeneratePlanOptions): Promise<LegacyPlanState>;
4104
4367
  /**
4105
4368
  * Kick off the planner conversation (ADR-0002): research + the first planner
@@ -4169,13 +4432,6 @@ declare class Session {
4169
4432
  * bare /unknown through to be mis-resolved.
4170
4433
  */
4171
4434
  private resolveSkillInvocation;
4172
- /**
4173
- * The one place planning discovers what it may draw from: the runners'
4174
- * models (cached for later effort clamping), their modes, and the settings
4175
- * in force. `filteredModels` is the allowlisted view a prompt may show;
4176
- * `modelsByRunner` stays whole because coercion needs real labels and variants.
4177
- */
4178
- private plannerCatalog;
4179
4435
  private conversationOpening;
4180
4436
  /**
4181
4437
  * Load planner-produced tasks, coerced to the allowlist read live — it may
@@ -4212,7 +4468,17 @@ declare class Session {
4212
4468
  cancelTask(taskId: string): Promise<void>;
4213
4469
  markTaskComplete(taskId: string): Promise<void>;
4214
4470
  markTaskIncomplete(taskId: string): Promise<void>;
4471
+ /**
4472
+ * Hand the queued edits to the planner. The Session does this itself each
4473
+ * time the scheduler parks behind the queue, so no surface has to; a caller
4474
+ * only needs it to await the drain. The messages stay queued until the
4475
+ * planner's answer is applied, so the scheduler stays paused behind them
4476
+ * meanwhile; one drain runs at a time, and a call made during it joins it.
4477
+ * Whatever was queued while it ran is drained after, before the run goes on.
4478
+ */
4215
4479
  processQueuedMessages(): Promise<void>;
4480
+ private drainQueue;
4481
+ private applyQueuedEdits;
4216
4482
  /**
4217
4483
  * Every finished task as the log records it: this run's own entries, plus
4218
4484
  * the tasks earlier runs completed, which the log dropped when this run
@@ -4221,16 +4487,25 @@ declare class Session {
4221
4487
  private finishedWork;
4222
4488
  approveCheckpoint(taskId: string): void;
4223
4489
  rejectCheckpoint(taskId: string, reason?: string): void;
4490
+ /** Whether an answer would reach the task: it waits at a checkpoint and a runner is left to hear it. */
4491
+ awaitsCheckpoint(taskId: string): boolean;
4224
4492
  /**
4225
4493
  * A user message to a structured task (ADR-0018, M1); throws
4226
4494
  * `TaskControlError` for one that cannot take it. A task waiting for input
4227
4495
  * is back in progress, and saved so before any surface is told.
4228
4496
  */
4229
4497
  sendTaskMessage(taskId: string, text: string): string;
4498
+ /**
4499
+ * Force send (ADR-0023, F1): interrupt the task's running turn and deliver
4500
+ * this message next. Throws `TaskControlError` where a plain message would,
4501
+ * and for a task on the terminal transport.
4502
+ */
4503
+ forceSendTaskMessage(taskId: string, text: string): string;
4504
+ forceSendQueuedTaskMessage(taskId: string, id: string): boolean;
4230
4505
  removeQueuedTaskMessage(taskId: string, id: string): boolean;
4231
4506
  interruptTask(taskId: string): Promise<void>;
4232
4507
  getQueuedMessages(): QueuedMessage[];
4233
- /** Take back one unsent message; the plan's persisted queue follows so a reload cannot resurrect it. */
4508
+ /** Take back one unsent message — false once the planner has it; the plan's persisted queue follows so a reload cannot resurrect it. */
4234
4509
  removeQueuedMessage(id: string): boolean;
4235
4510
  setQueuedMessages(msgs: QueuedMessage[]): void;
4236
4511
  /** Replay a run a dirty tree blocked, after stashing the tracked changes. */
@@ -4267,104 +4542,43 @@ declare class Session {
4267
4542
  */
4268
4543
  discardRun(): Promise<void>;
4269
4544
  private requireSettledRun;
4270
- /**
4271
- * The opt-in way through a merge conflict: add an AI task, on the conflicted
4272
- * task's own runner and model, that merges its branch by hand in a worktree
4273
- * of its own. When that task lands, the conflicted one lands through it (see
4274
- * {@link TaskOrchestrator.linkConflictResolver}). The automatic answer to a
4275
- * conflict is a repair, a new attempt of the same task (ADR-0015); a task is
4276
- * only ever added to the plan by this call.
4277
- */
4545
+ /** Add the task that merges a conflicted task's branch by hand; see {@link PlanEditor.addConflictResolver}. */
4278
4546
  resolveConflictAsTask(taskId: string): Promise<LegacyPlanState | null>;
4279
4547
  stopExecution(): void;
4548
+ /**
4549
+ * Run a user control and save what it changed before any surface hears of
4550
+ * it. Not a status hold, as {@link saved} is: these await spawns and git, so
4551
+ * a hold would stall every task's status for as long, and an
4552
+ * `execution_complete` the control triggers would overtake the status held
4553
+ * back — which a surface treats as the end of the stream. Instead each status
4554
+ * announced while one runs is saved first (see {@link observer}), as the
4555
+ * background save of an execution event: it may land mid planner turn. So
4556
+ * is the last one where the control is itself execution, not the user's
4557
+ * word on the plan (`background`).
4558
+ */
4559
+ private withSave;
4560
+ private saveForControl;
4561
+ /** A control that finishes synchronously: its status is held until it is saved. */
4562
+ private saved;
4280
4563
  /**
4281
4564
  * The one mutation seam: every structural plan mutation runs the same
4282
4565
  * ritual — store op → persist (which snapshots tasks from PlanStore) →
4283
4566
  * broadcast — in that order, once. A store op returning false aborts
4284
4567
  * before anything is persisted. PlanStore is the single authority for
4285
4568
  * task state; LegacyPlanState.tasks is populated only at persist time.
4569
+ * Direct edits reach it through the {@link PlanEditor}, which adds the
4570
+ * reschedule they owe.
4286
4571
  */
4287
4572
  private mutatePlan;
4288
- /**
4289
- * The direct-edit seam: {@link mutatePlan} plus the reschedule every
4290
- * structural edit owes an armed scheduler. Nothing else wakes one after a
4291
- * hand edit — the queue-drain path never runs, because a direct edit never
4292
- * queues — so a task the edit just unblocked would sit ready and never
4293
- * start. `tick()` no-ops while the scheduler is idle, so this costs nothing
4294
- * during plain planning. The planner-driven path re-ticks in
4295
- * {@link PlannerConversation} instead (`afterEdit`); it must not tick twice.
4296
- */
4297
- private editPlan;
4298
- /**
4299
- * Patch one task's fields. A hand-set dependency list, or a type flip
4300
- * between AI and MAN, are the patches that can leave a task incoherent
4301
- * (unschedulable, or carrying fields that mean nothing for its new type),
4302
- * so both go through the same {@link validateTaskEdit} guard the planner's
4303
- * task-ops applier uses (as the 'direct' actor, which skips the lock rule)
4304
- * rather than a second copy of the rules — and throws, so the surface can
4305
- * say why. Gated on the task existing so an edit to an unknown id still
4306
- * falls through to the no-op `store.update` below instead of throwing.
4307
- */
4573
+ /** Patch one task's fields, checked as the planner's task ops are; see {@link PlanEditor.updateTask}. */
4308
4574
  updateTask(taskId: string, changes: Partial<Task>): Promise<LegacyPlanState | null>;
4309
- /**
4310
- * Move one task onto a different runner. Distinct from {@link updateTask}
4311
- * because a runner change is never a single-field edit: the task's model,
4312
- * thinking effort and mode are all scoped to its runner, so they are
4313
- * re-derived from the new runner's catalog (see {@link retargetTaskRunner}).
4314
- * That needs discovery, which is why it is not a branch inside `updateTask`.
4315
- *
4316
- * The runner is also admitted into `plan.runners` — see {@link admitRunner}.
4317
- */
4575
+ /** Move one task onto a different runner, re-deriving what is scoped to it; see {@link PlanEditor.setTaskRunner}. */
4318
4576
  setTaskRunner(taskId: string, runner: RunnerId): Promise<LegacyPlanState | null>;
4319
- /** What a runner offers, as {@link runnerAssignment} needs it. Spawns the runner's CLI to list models. */
4320
- private catalogFor;
4321
- /**
4322
- * What a *derived* assignment may draw from: the runner's catalog narrowed to
4323
- * the user's allowlist. Deriving from the full catalog would hand a task the
4324
- * runner's first model regardless of a restriction the user set — the next
4325
- * planner turn's `coerceAssignments` would snap it back anyway, so the user
4326
- * would see their pick silently change instead of never being offered.
4327
- *
4328
- * `catalogFor` stays unnarrowed because {@link admitRunner} caches it as what
4329
- * the runner really offers, which is what effort clamping needs.
4330
- */
4331
- private allowedCatalog;
4332
- /**
4333
- * Make a runner a first-class member of this plan. Without this, the next
4334
- * planner turn's `coerceAssignments` would treat it as disallowed and snap
4335
- * every task on it back, silently undoing the user's choice; and that same
4336
- * pass clamps efforts against `modelsCache`, so a catalog missing from there
4337
- * makes the effort we just derived read as unverifiable.
4338
- */
4339
- private admitRunner;
4340
- /**
4341
- * Replace one task's dependency list — the named entry point the surfaces'
4342
- * dependency pickers call. The guard itself lives in {@link updateTask}, so
4343
- * a dependency list arriving as a plain field patch is rejected by the same
4344
- * rule instead of slipping past it.
4345
- */
4577
+ /** Replace one task's dependency list, under the same guard as a field patch; see {@link PlanEditor.setTaskDependencies}. */
4346
4578
  setTaskDependencies(taskId: string, dependencies: string[]): Promise<LegacyPlanState | null>;
4347
- /**
4348
- * Delete one task. A running task is cancelled first: the plan can drop it
4349
- * either way, but nothing can reach its runner afterwards — the tmux session
4350
- * outlives the plan and the orchestrator keeps counting it as active. The
4351
- * planner-driven path refuses instead (see {@link applyTaskOps}); a user
4352
- * deleting their own task means it.
4353
- */
4579
+ /** Delete one task, cancelling its runner first if it runs; see {@link PlanEditor.removeTask}. */
4354
4580
  removeTask(taskId: string): Promise<LegacyPlanState | null>;
4355
- /**
4356
- * Add one task, filling in whatever the caller left unset. A task with no
4357
- * runnable assignment is not a lighter task but an unspawnable one, so the
4358
- * runner falls back to the plan's first and the model, effort and mode are
4359
- * derived from that runner's catalog — the same derivation a runner change
4360
- * uses ({@link runnerAssignment}), which is why this is async like
4361
- * {@link setTaskRunner}. Anything the caller did choose survives when the
4362
- * runner offers it.
4363
- *
4364
- * Dependencies naming tasks that don't exist are dropped rather than rejected:
4365
- * the caller is a picker over the current plan, so a stale id means the plan
4366
- * moved on, not that the whole task should be refused.
4367
- */
4581
+ /** Add one task, deriving what the caller left unset; see {@link PlanEditor.addTask}. */
4368
4582
  addTask(draft: Partial<Task>): Promise<LegacyPlanState | null>;
4369
4583
  /**
4370
4584
  * Planner-driven merge: validate compatibility up front, then ask the planner
@@ -4389,6 +4603,274 @@ declare class Session {
4389
4603
  get aiServiceInstance(): IAiService;
4390
4604
  }
4391
4605
 
4606
+ /**
4607
+ * A request on the plan the session refused: a direct (non-planner) edit that
4608
+ * is not valid, or an isolation action on a plan with no isolated run to act
4609
+ * on. Distinct from a plain Error so a surface can tell "you asked for
4610
+ * something invalid" from "something broke" and say which — the HTTP routes
4611
+ * used to collapse both into 404/500, which read to the TUI and VS Code as the
4612
+ * edit silently doing nothing. Carries no status code: core is
4613
+ * transport-agnostic, the route maps it.
4614
+ */
4615
+ declare class PlanEditError extends Error {
4616
+ constructor(message: string);
4617
+ }
4618
+
4619
+ /** What the user last picked for one planner backend. */
4620
+ interface PlannerModelChoice {
4621
+ model: string;
4622
+ effort?: string;
4623
+ }
4624
+ /**
4625
+ * A candidate model as this planner's catalog lists it. Deliberately plain
4626
+ * data: a harness planner's catalog is its runner's own model list and a
4627
+ * vendor's is a picker list (ADR-0009), and the rule below is the same for
4628
+ * both — so the caller normalizes, and this module never reaches for a
4629
+ * ModelResolver, a config or the network.
4630
+ */
4631
+ interface PlannerModelCandidate {
4632
+ id: string;
4633
+ variants?: {
4634
+ id: string;
4635
+ }[];
4636
+ }
4637
+ interface PlannerModelRecall {
4638
+ model: string;
4639
+ effort: string;
4640
+ source: 'remembered' | 'catalog-default' | 'none';
4641
+ }
4642
+ /** The slice of `SettingsService` this needs, so tests can pass a plain object. */
4643
+ interface PlannerModelStore {
4644
+ getPlannerModel(provider: string): PlannerModelChoice | undefined;
4645
+ setPlannerModel(provider: string, entry: PlannerModelChoice | undefined): void;
4646
+ }
4647
+ /**
4648
+ * Remembers, per planner backend, the model the user last chose for it.
4649
+ *
4650
+ * Switching the planner has to clear the model — an id is scoped to the agent
4651
+ * that listed it, so an OpenRouter slug handed to Claude Code points it at
4652
+ * something it cannot spawn. That clear is correct and stays; what was missing
4653
+ * is that switching *back* then forced a re-pick of a model the user had
4654
+ * already chosen. Both surfaces' clears and the restore now read one rule from
4655
+ * here rather than three copies drifting apart.
4656
+ */
4657
+ declare class PlannerModelMemory {
4658
+ private store;
4659
+ constructor(store: PlannerModelStore);
4660
+ /** Record what the user picked for `provider`; a blank model forgets it instead. */
4661
+ remember(provider: string, model: string, effort?: string): void;
4662
+ /**
4663
+ * What the model and effort should become when `provider` takes over the
4664
+ * planner, given the models it actually serves. The `source` is what the
4665
+ * three surfaces word their notice from — restored, defaulted, or nothing.
4666
+ */
4667
+ recall(provider: string, catalog: PlannerModelCandidate[]): PlannerModelRecall;
4668
+ }
4669
+
4670
+ /**
4671
+ * The daemon contract: every HTTP body the local daemon sends, in one place.
4672
+ *
4673
+ * The daemon's routes build their bodies against these types and the CLI's
4674
+ * `ApiClient` reads them back, so a field added on one side is a compile error
4675
+ * on the other. Before this there were three hand-written copies — the routes'
4676
+ * inline `c.json`, the client's per-method generics, and the TUI's slice of the
4677
+ * client — and they had drifted (the client declared a `headless` field the
4678
+ * daemon never sent, and typed `GET /sessions/:id`'s plan as the wrong shape).
4679
+ *
4680
+ * Types only. Core is the one package both the daemon and its clients already
4681
+ * depend on; importing from the daemon would pull its runtime into the CLI.
4682
+ */
4683
+ /**
4684
+ * Why a request was refused, as a stable code a client switches on instead of
4685
+ * reading the message. Absent on a refusal nobody needs to tell apart — a
4686
+ * missing field, say — where the message is all there is.
4687
+ */
4688
+ type DaemonErrorCode = 'session_not_found' | 'task_not_found' | 'no_plan' | 'already_executing' | 'workspace_not_found' | 'workspace_not_a_project' | 'conversation_busy'
4689
+ /** The user stopped the planner turn: the turn ended as asked, so a client stays quiet. */
4690
+ | 'planner_turn_stopped'
4691
+ /** The planner turn settled after its plan was dropped; nothing it produced was kept. */
4692
+ | 'planner_turn_discarded'
4693
+ /** The request asked for something the plan, task or conversation does not allow. */
4694
+ | 'refused' | 'checkpoint_not_waiting' | 'approval_not_outstanding';
4695
+ /** The body of every non-2xx answer a route gives. */
4696
+ interface ErrorBody {
4697
+ error: string;
4698
+ code?: DaemonErrorCode;
4699
+ /** Set with `workspace_not_a_project`: the directory the daemon refused, so a client can offer to initialize it. */
4700
+ workspace?: string;
4701
+ }
4702
+ interface OkResponse {
4703
+ ok: boolean;
4704
+ }
4705
+ /**
4706
+ * A plan as the planner routes return it: the session's plan state itself, not
4707
+ * the `SerializedPlan` a websocket `plan_generated` carries. The two differ —
4708
+ * this one holds raw tasks and the dialogue's bookkeeping — and a client that
4709
+ * reads either reads only the fields it needs.
4710
+ */
4711
+ interface PlanResponse {
4712
+ plan: LegacyPlanState;
4713
+ }
4714
+ /** `POST /plans/:id/resolve-conflict`: `null` when the session had no conflict to hand a resolver task. */
4715
+ interface ResolveConflictResponse {
4716
+ plan: LegacyPlanState | null;
4717
+ }
4718
+ /** What `GET /api/models` returns, and what the pool's provider discovery produces. */
4719
+ interface ModelsResponse {
4720
+ models: DiscoveredModel[];
4721
+ modelsByRunner: Record<string, DiscoveredModel[]>;
4722
+ /** Each runner's manifest modes, so a surface can offer a per-task mode picker. */
4723
+ modesByRunner: Record<string, RunnerModeInfo[]>;
4724
+ orchestratorModel: string;
4725
+ providers: AiProvider[];
4726
+ /** Full cross-provider catalog for the orchestrator (planner) model picker. */
4727
+ orchestratorModels: OrchestratorOption[];
4728
+ /** Per-provider catalog-fetch failures, keyed by provider id. */
4729
+ providerErrors: Record<string, string>;
4730
+ }
4731
+ /** One-shot planning answers in the phase-tagged shape a saved session is read back in. */
4732
+ interface GeneratePlanResponse extends Pick<ModelsResponse, 'models' | 'modelsByRunner'> {
4733
+ plan: PlanState;
4734
+ }
4735
+ interface ExecuteResponse {
4736
+ status: 'running';
4737
+ }
4738
+ interface StopResponse {
4739
+ status: 'stopped';
4740
+ }
4741
+ interface CancelPlanningResponse {
4742
+ cancelled: boolean;
4743
+ }
4744
+ interface TaskMessageResponse {
4745
+ id: string;
4746
+ }
4747
+ interface ForceSendResponse {
4748
+ sent: boolean;
4749
+ }
4750
+ interface RemoveMessageResponse {
4751
+ removed: boolean;
4752
+ }
4753
+ interface MergeGateEntry {
4754
+ id: string;
4755
+ order: number;
4756
+ title: string;
4757
+ }
4758
+ interface MergeGateResponse {
4759
+ mergeGate: MergeGateEntry[];
4760
+ }
4761
+ interface IsolationDiffResponse {
4762
+ diff: string;
4763
+ }
4764
+ /** A merge that conflicts or is blocked is an outcome, not a refusal, so it answers 200 with the result. */
4765
+ type IsolationMergeResponse = IsolationMergeResult;
4766
+ /** A fork the daemon has already adopted. */
4767
+ interface ConversationForkResponse {
4768
+ sessionId: string;
4769
+ goal: string;
4770
+ plan: LegacyPlanState;
4771
+ }
4772
+ /** A fork made by a rewind, with the full text of the message it was made just before. */
4773
+ interface ConversationRewindResponse extends ConversationForkResponse {
4774
+ rewoundMessage: string;
4775
+ }
4776
+ interface RewindTargetsResponse {
4777
+ targets: RewindTarget[];
4778
+ }
4779
+ interface ConversationCompactResponse extends ConversationCompaction, PlanResponse {
4780
+ }
4781
+ interface PrdResponse {
4782
+ prdMarkdown: string;
4783
+ }
4784
+ interface RunnerEntry {
4785
+ id: string;
4786
+ name: string;
4787
+ enabled: boolean;
4788
+ }
4789
+ interface RunnersResponse {
4790
+ runners: RunnerEntry[];
4791
+ orchestratorModel: string;
4792
+ }
4793
+ /** Sessions: a saved one comes off its file, a live one from the pool's store. */
4794
+ interface SessionResponse {
4795
+ meta: SessionMeta;
4796
+ plan: PlanState;
4797
+ }
4798
+ interface AdoptSessionResponse extends OkResponse {
4799
+ plan: LegacyPlanState;
4800
+ goal: string;
4801
+ }
4802
+ interface TaskLogAttemptsResponse {
4803
+ attempts: number[];
4804
+ }
4805
+ interface TaskLogResponse {
4806
+ attempt: number;
4807
+ events: TaskLogEvent[];
4808
+ }
4809
+ type SessionListResponse = SessionMeta[];
4810
+ interface SettingsResponse {
4811
+ orchestratorModel: string;
4812
+ /** Who plans (ADR-0009) — a vendor id, or one of the three harness planners. */
4813
+ aiProvider: AiProvider;
4814
+ plannerThinkingEffort: string;
4815
+ maxParallel: number;
4816
+ tdd: UserSettings['tdd'];
4817
+ verification: UserSettings['verification'];
4818
+ modelAllowlist: UserSettings['modelAllowlist'];
4819
+ plannerModels: UserSettings['plannerModels'];
4820
+ runnerTransport: RunnerTransport;
4821
+ }
4822
+ /** What `PATCH /api/settings` answers: the settings as they now stand, plus what the write itself decided. */
4823
+ interface SettingsUpdateResponse extends SettingsResponse {
4824
+ /**
4825
+ * Which model a planner switch landed on and why — remembered for that
4826
+ * backend, its catalog default, or none. Present only when the write changed
4827
+ * the planner; absent for every other write, where nothing was recalled.
4828
+ */
4829
+ switchRecall?: PlannerModelRecall;
4830
+ /** Env keys the daemon refused to apply, so a client does not persist what the daemon would not. */
4831
+ rejectedEnvKeys?: string[];
4832
+ }
4833
+ interface CommandDescriptor {
4834
+ name: string;
4835
+ description: string;
4836
+ }
4837
+ interface CommandsResponse {
4838
+ commands: CommandDescriptor[];
4839
+ }
4840
+ interface CommandResponse extends OkResponse {
4841
+ settings: SettingsResponse;
4842
+ }
4843
+ interface WorkspacesResponse {
4844
+ workspaces: string[];
4845
+ }
4846
+ interface ApprovalsResponse {
4847
+ pending: PendingApproval[];
4848
+ approvedScopes: string[];
4849
+ }
4850
+ interface ApprovalAnswerResponse extends OkResponse {
4851
+ granted: boolean;
4852
+ decision: ApprovalDecision['decision'];
4853
+ }
4854
+
4855
+ /**
4856
+ * Why a call on a session could not be made. Each is a class rather than a
4857
+ * message so the daemon can map it to a status and a stable code in one place,
4858
+ * and a client can switch on that code instead of reading the wording. Like
4859
+ * `PlanEditError`, none carries a status: core is transport-agnostic.
4860
+ */
4861
+ /** No session with that id is held — never planned in this process, or dropped by a restart. */
4862
+ declare class SessionNotFoundError extends Error {
4863
+ constructor();
4864
+ }
4865
+ /** The session holds no plan (or an empty one) for the operation to act on. */
4866
+ declare class NoPlanError extends Error {
4867
+ constructor(operation: 'execute' | 'review');
4868
+ }
4869
+ /** A run is already live, and starting another would reset the one in flight. */
4870
+ declare class AlreadyExecutingError extends Error {
4871
+ constructor();
4872
+ }
4873
+
4392
4874
  /**
4393
4875
  * Where Ordewell keeps machine-global data (settings, daemon tokens, caches,
4394
4876
  * user plugins). A single home for everything not tied to a project directory,
@@ -4419,6 +4901,8 @@ type CheckpointWithdrawnListener = (taskId: string) => void;
4419
4901
  type IdleListener = (taskId: string, idleSince: string | null) => void;
4420
4902
  declare class VerdictEngine {
4421
4903
  private markerSeen;
4904
+ private pendingVerdicts;
4905
+ private structuredSessions;
4422
4906
  private markerTails;
4423
4907
  private checkpointCarry;
4424
4908
  private pausedSessions;
@@ -4431,6 +4915,8 @@ declare class VerdictEngine {
4431
4915
  * answer is typed into the session instead.
4432
4916
  */
4433
4917
  private toolCheckpoints;
4918
+ /** The question a task's checkpoint asks, whole, for as long as it waits — by either route. */
4919
+ private checkpointQuestions;
4434
4920
  private idleListeners;
4435
4921
  /**
4436
4922
  * Per-task generation. Replaced on every watch(), clear() and verdict.
@@ -4479,6 +4965,8 @@ declare class VerdictEngine {
4479
4965
  * `\n`-terminated line, and the leading newline flushes a partial line.
4480
4966
  */
4481
4967
  private resumeToken;
4968
+ /** What a task's waiting checkpoint asks, untruncated; undefined when none waits. */
4969
+ getCheckpointQuestion(taskId: string): string | undefined;
4482
4970
  approveCheckpoint(taskId: string): void;
4483
4971
  rejectCheckpoint(taskId: string, reason: string): void;
4484
4972
  /**
@@ -4497,6 +4985,10 @@ declare class VerdictEngine {
4497
4985
  * task's current one or another signal already settled it.
4498
4986
  */
4499
4987
  signalComplete(taskId: string, generation: number, report: TaskCompleteArgs): void;
4988
+ /** The runner was told something after its evidence so far, so only what it reports from here counts. */
4989
+ private supersede;
4990
+ private acceptVerdict;
4991
+ private publishVerdict;
4500
4992
  /**
4501
4993
  * The runner's `checkpoint` call (ADR-0022, V5): raised through the same
4502
4994
  * listeners as the marker, so the task waits on the user the same way, and
@@ -4504,6 +4996,8 @@ declare class VerdictEngine {
4504
4996
  * withdrawn, never left hanging, once the attempt is over or the call goes.
4505
4997
  */
4506
4998
  raiseCheckpoint(taskId: string, generation: number, question: string, signal: AbortSignal): Promise<CheckpointAnswer>;
4999
+ /** The open `checkpoint` call can no longer be answered: refuse it, and let the task out of the wait. */
5000
+ private callWentAway;
4507
5001
  private withdrawToolCheckpoint;
4508
5002
  private approvalOpened;
4509
5003
  private approvalClosed;
@@ -4515,6 +5009,8 @@ declare class VerdictEngine {
4515
5009
  markComplete(task: Task): Verdict;
4516
5010
  /** Clear verification state for a task (used on retry). */
4517
5011
  clear(task: Task): void;
5012
+ /** Drop a task's verification state and invalidate any callbacks still holding its generation. */
5013
+ private ceaseTracking;
4518
5014
  /** Drop all tracking state (used on stop / loadPlan). */
4519
5015
  reset(): void;
4520
5016
  private decide;
@@ -4601,45 +5097,6 @@ declare function clampThinkingEffort(effort: string | undefined, variants: {
4601
5097
  }[]): string | undefined;
4602
5098
  declare function coerceAssignments(tasks: readonly Task[], perRunnerAllowlist: Partial<Record<RunnerId, string[]>>, allowedRunners?: RunnerId[], modelsByRunner?: Partial<Record<RunnerId, DiscoveredModel[]>>): Task[];
4603
5099
 
4604
- /** What a runner offers a task: the models discovered for it and the modes its manifest declares. */
4605
- interface RunnerCatalog {
4606
- models: DiscoveredModel[];
4607
- modes: RunnerModeInfo[];
4608
- /**
4609
- * The mode a task lands on when it carries none this runner offers: the
4610
- * manifest's `autonomous`/`safe`-tagged mode under the user's toggle
4611
- * (ADR-0001). Absent, `modes[0]` stands in — which for Codex is its
4612
- * workspace-write sandbox, not the full access an unattended run needs.
4613
- */
4614
- defaultMode?: string;
4615
- }
4616
- /**
4617
- * The model, thinking effort and mode a runner offers for a task.
4618
- *
4619
- * A task's model, effort and mode are all scoped to its runner:
4620
- * `claude-sonnet-4-5` on Codex, or `acceptEdits` on OpenCode, are not degraded
4621
- * choices but unspawnable ones. So this is where any task acquires a runnable
4622
- * assignment — a runner change (below) and a hand-added task derive it the same
4623
- * way. Each field in `current` is preserved when the runner also offers it, and
4624
- * otherwise snapped to that runner's preferred entry (discovery already sorts
4625
- * models by the manifest's `preferredPatterns`; the mode is the catalog's
4626
- * `defaultMode`).
4627
- *
4628
- * An empty catalog means discovery failed or the runner is a plugin we have no
4629
- * list for — not that the runner offers nothing. That field is left out of the
4630
- * patch and the runner validates last, matching `coerceAssignments`.
4631
- */
4632
- declare function runnerAssignment(catalog: RunnerCatalog, current?: Pick<Task, 'assignedModel' | 'taskMode'>): Partial<Task>;
4633
- /**
4634
- * Move a task onto a different runner, carrying its model, thinking effort and
4635
- * mode over to values that runner actually offers. A runner change is never a
4636
- * single-field edit — it either brings the other three with it or leaves the
4637
- * task unrunnable.
4638
- *
4639
- * Returns the patch to apply, or `{}` when there is nothing to change.
4640
- */
4641
- declare function retargetTaskRunner(task: Task, runner: RunnerId, catalog: RunnerCatalog): Partial<Task>;
4642
-
4643
5100
  declare function buildResearchToolsPrompt(): string;
4644
5101
  /**
4645
5102
  * System prompt for one read-only research subagent (issue #34). The digest
@@ -5067,8 +5524,9 @@ interface SessionLaunch {
5067
5524
  * an `ITerminalSession`, so everything downstream of `onOutput` is unchanged;
5068
5525
  * what a terminal cannot do sits on {@link StructuredSessionCapability}.
5069
5526
  *
5070
- * Ordewell owns the message queue (M1): a message sent mid-turn waits for the
5071
- * turn to end rather than being typed into a runner that is busy.
5527
+ * Ordewell owns the message queue (M1). A message sent mid-turn is handed to a
5528
+ * runner that can take one at its next step (ADR-0023); otherwise it waits for
5529
+ * the turn to end rather than being typed into a runner that is busy.
5072
5530
  */
5073
5531
  declare class StructuredSession extends AbstractTerminalSession implements StructuredSessionCapability {
5074
5532
  private readonly launch;
@@ -5112,13 +5570,36 @@ declare class StructuredSession extends AbstractTerminalSession implements Struc
5112
5570
  private issueTools;
5113
5571
  private revokeTools;
5114
5572
  protected baseHandleExit(code: number): void;
5115
- /** A reply typed at the task — a checkpoint answer, most often — is a user message. */
5573
+ /**
5574
+ * A reply typed at the task — a checkpoint answer, most often — is a user
5575
+ * message like any other: into the running turn where the runner takes one,
5576
+ * else the next turn. A runner that stopped to ask reads it at once either way.
5577
+ */
5116
5578
  write(text: string): void;
5117
5579
  kill(): void;
5118
5580
  turnState(): 'working' | 'idle';
5119
5581
  onTurnEnd(listener: (reason: StructuredTurnEnd) => void): void;
5120
5582
  onEvent(listener: (event: StructuredEvent) => void): void;
5121
5583
  sendMessage(text: string): string;
5584
+ /**
5585
+ * Force send (ADR-0023, F1–F3): interrupt the running turn — and the tool
5586
+ * call in flight — then deliver this message as the turn that replaces it,
5587
+ * ahead of everything still queued. With no turn to interrupt it is a plain
5588
+ * {@link sendMessage}.
5589
+ */
5590
+ forceSend(text: string): string;
5591
+ /** Force send a message still waiting in the queue. False once the runner has it, or it is gone. */
5592
+ forceSendQueued(id: string): boolean;
5593
+ /**
5594
+ * Behind earlier forced messages and ahead of the rest, handed-over ones
5595
+ * included: what the runner already has is the runner's to order (F2). Held
5596
+ * back from steering while the interrupt runs, it opens the next turn — and
5597
+ * a queue that is not empty when the turn ends is what keeps the task out of
5598
+ * "waiting for input" between the two (F3).
5599
+ */
5600
+ private force;
5601
+ private runningTurn;
5602
+ /** No runner can recall a message once offered to it, so only a queued one comes back. */
5122
5603
  removeQueued(id: string): boolean;
5123
5604
  queued(): QueuedTaskMessage[];
5124
5605
  nativeSessionId(): string | null;
@@ -5136,6 +5617,16 @@ declare class StructuredSession extends AbstractTerminalSession implements Struc
5136
5617
  private startAdapter;
5137
5618
  private openTurn;
5138
5619
  private deliver;
5620
+ /**
5621
+ * Offer the oldest queued message to the running turn (ADR-0023, D2), one at
5622
+ * a time so the runner takes them in the order they were sent. A refusal
5623
+ * leaves it, and everything behind it, for the turn's end.
5624
+ */
5625
+ private offerNext;
5626
+ private steered;
5627
+ private delivered;
5628
+ /** Back to waiting in Ordewell's queue, removable again; the log hears of it only if it had shown as handed over. */
5629
+ private requeue;
5139
5630
  private route;
5140
5631
  /**
5141
5632
  * The runner spoke with no message of ours in flight. When it is working — a
@@ -5150,9 +5641,17 @@ declare class StructuredSession extends AbstractTerminalSession implements Struc
5150
5641
  /**
5151
5642
  * A queued message goes out as the turn closes, and the state never passes
5152
5643
  * through `idle` on the way, so a listener told the turn ended can already
5153
- * see the task is not waiting.
5644
+ * see the task is not waiting. Nor does a message the runner owes, or one
5645
+ * still being offered: the runner is about to read it, or Ordewell to send
5646
+ * it (ADR-0023, D5).
5154
5647
  */
5155
5648
  private endTurn;
5649
+ /**
5650
+ * Between turns the oldest message opens the next one — but only once it is
5651
+ * back in Ordewell's hands: while it is on offer or owed, nothing overtakes it.
5652
+ */
5653
+ private deliverNext;
5654
+ private undeliverQueued;
5156
5655
  private emitEvent;
5157
5656
  }
5158
5657
  /**
@@ -5371,6 +5870,8 @@ interface KillTreeDeps {
5371
5870
  unref?: () => void;
5372
5871
  };
5373
5872
  clearTimeoutImpl?: (handle: unknown) => void;
5873
+ /** Signals a pid, or a process group as its negated id. Injected so the group path needs no real processes. */
5874
+ killImpl?: (pid: number, signal: NodeJS.Signals | 0) => void;
5374
5875
  }
5375
5876
  /**
5376
5877
  * Terminate `proc` and everything it started.
@@ -5600,57 +6101,6 @@ declare function sanitizeSlug(raw: string): string;
5600
6101
  /** Save the PRD to `<workspace>/.scratch/<slug>/PRD.md` (to-prd native convention). */
5601
6102
  declare function savePrdMarkdown(workspace: string, slug: string, markdown: string): string;
5602
6103
 
5603
- /** What the user last picked for one planner backend. */
5604
- interface PlannerModelChoice {
5605
- model: string;
5606
- effort?: string;
5607
- }
5608
- /**
5609
- * A candidate model as this planner's catalog lists it. Deliberately plain
5610
- * data: a harness planner's catalog is its runner's own model list and a
5611
- * vendor's is a picker list (ADR-0009), and the rule below is the same for
5612
- * both — so the caller normalizes, and this module never reaches for a
5613
- * ModelResolver, a config or the network.
5614
- */
5615
- interface PlannerModelCandidate {
5616
- id: string;
5617
- variants?: {
5618
- id: string;
5619
- }[];
5620
- }
5621
- interface PlannerModelRecall {
5622
- model: string;
5623
- effort: string;
5624
- source: 'remembered' | 'catalog-default' | 'none';
5625
- }
5626
- /** The slice of `SettingsService` this needs, so tests can pass a plain object. */
5627
- interface PlannerModelStore {
5628
- getPlannerModel(provider: string): PlannerModelChoice | undefined;
5629
- setPlannerModel(provider: string, entry: PlannerModelChoice | undefined): void;
5630
- }
5631
- /**
5632
- * Remembers, per planner backend, the model the user last chose for it.
5633
- *
5634
- * Switching the planner has to clear the model — an id is scoped to the agent
5635
- * that listed it, so an OpenRouter slug handed to Claude Code points it at
5636
- * something it cannot spawn. That clear is correct and stays; what was missing
5637
- * is that switching *back* then forced a re-pick of a model the user had
5638
- * already chosen. Both surfaces' clears and the restore now read one rule from
5639
- * here rather than three copies drifting apart.
5640
- */
5641
- declare class PlannerModelMemory {
5642
- private store;
5643
- constructor(store: PlannerModelStore);
5644
- /** Record what the user picked for `provider`; a blank model forgets it instead. */
5645
- remember(provider: string, model: string, effort?: string): void;
5646
- /**
5647
- * What the model and effort should become when `provider` takes over the
5648
- * planner, given the models it actually serves. The `source` is what the
5649
- * three surfaces word their notice from — restored, defaulted, or nothing.
5650
- */
5651
- recall(provider: string, catalog: PlannerModelCandidate[]): PlannerModelRecall;
5652
- }
5653
-
5654
6104
  /** How many AI tasks may run at once: `ORDEWELL_MAX_PARALLEL`, `/parallel`, `ordewell.maxParallelSessions`. */
5655
6105
  declare const DEFAULT_MAX_PARALLEL = 3;
5656
6106
  /**
@@ -5661,4 +6111,4 @@ declare const DEFAULT_MAX_PARALLEL = 3;
5661
6111
  */
5662
6112
  declare function parseMaxParallel(value: unknown): number | null;
5663
6113
 
5664
- export { ALLOWED_PLUGIN_HOSTS, ALL_PROVIDERS, AUTO_COMMANDS, AbstractRunner, AbstractTerminalSession, ActiveTaskSession, type Adr0013IsolationRun, type Adr0013PlanIsolation, type Adr0013TaskRecord, AgentAdapter, AgentEvent, AgentProcessDeps, AgentStartOptions, AiProvider, ApplyTaskOpsResult, ApprovalAnswer, ApprovalDecision, ApprovalMode, ApprovalPolicy, ApprovalRequest, type AskOptions, AwaitingReason, BUILTIN_SKILL_NAMES, BaseAiService, BaseConfig, BaseFileSystem, BufferedTaskOutputSource, CLAUDE_CODE_MANIFEST, CLI_PROVIDERS, CORE_PLANNER_PROMPT, type CappedRows, CheckpointAnswer, type CheckpointListener, ClaudeCodeAdapter, CliAgentAiService, type CliAgentAiServiceDeps, CodexAdapter, type CommandClassification, type CommandTier, type ComposeOptions, ConsoleLogger, ContextCollector, type Continuability, ConversationBusyError, type ConversationCompaction, ConversationEditError, type ConversationFork, ConversationMessage, type ConversationRequest, type ConversationRewind, type ConversationTurn, type ConversationVariant, DAEMON_SUBPROTOCOL, DAEMON_TOKEN_SUBPROTOCOL_PREFIX, DEFAULT_MAX_PARALLEL, DiscoveredModel, type EnvAdmission, EnvConfig, type ExecFileFn, type ExecImpl, FindSymbolOptions, FsPluginStore, GIT_READONLY_SUBCOMMANDS, GREP_PCRE_PROBE_ARGS, GeminiService, type GitExecFn, GlobOptions, type GrepInvocation, GrepOptions, type GrepRegex, type HasBinFn, HomeTranscriptReader, type IAiService, IApproval, IConfig, IFileSystem, type ILogger, type INotification, IPluginStore, ITerminalRunner, ITerminalSession, IWorktreeIsolation, IsolationHandoff, IsolationLandedTask, IsolationMergeResult, IsolationOutcome, IsolationRun, IsolationTaskRecord, IsolationTaskStatus, IsolationView, JSON_REPAIR_INSTRUCTION, type KillTreeDeps, LegacyPlanState, type LiveOutputLookup, type LiveTail, type LiveTailOptions, type MappedTool, MergeGateView, ModelResolver, type ModelResolverDeps, type ModifyPlanRequest, type NotificationAction, OPENCODE_MANIFEST, ORDEWELL_SETTABLE_ENV, OUTPUT_LINES_DEFAULT, OUTPUT_LINES_MAX, OpenAiService, OpenCodeAdapter, type OrchestratorObserver, OrchestratorOption, OrdewellMcpServer, PLUGIN_NAME_PATTERN, PROVIDER_CREDENTIAL_ENV, PROVIDER_DETECT_PRIORITY, PROVIDER_LABEL, PROVIDER_PRIORITY, PROVIDER_SHORT_LABEL, type PendingApproval, PendingApprovals, type PendingApprovalsOptions, PlanEditError, PlanIsolation, PlanParseError, type PlanRequest, PlanState, PlanStatus, PlanStore, Planner, type PlannerModelCandidate, type PlannerModelChoice, PlannerModelMemory, type PlannerModelRecall, type PlannerModelStore, type PlannerReplyClassification, type PlannerRuntimeToggles, type PlannerSubmission, PlannerToolHandler, PlannerUsage, type PluginSource, type PrdBlock, type PriorOutput, type ProbeFn, ProviderModelLists, type ProviderRegistration, QueuedMessage, QueuedTaskMessage, REFUSED_COMMANDS, ReadFileOpts, RepairEvidence, type RepairLoopOpts, type RepairVerdict, RepoGroupLayout, type ResearchChat, ResearchLogEntry, ResearchProgress, type ResearchShell, type ResearchShellDeps, ResearchToolType, type ResearchTurn, ResolveContext, type RewindTarget, RunnerApprovals, type RunnerCatalog, RunnerId, RunnerInstallation, RunnerInvocation, type RunnerMode, RunnerModeInfo, RunnerPluginManifest, RunnerRegistry, RunnerSpawnOptions, RunnerTransport, SETTINGS_ENV_ALLOWLIST, SETTINGS_ENV_REFUSED, STATE_DIR, STOPPED_TOOL_RESULT, SYMBOL_LANGUAGES, type SaveSession, Session, SessionBroadcaster, type SessionData, type SessionDeps, SessionMessage, type SessionMeta, SessionNotice, type SessionPlanner, type SessionRuntimeSettings, SettingsService, type ShellDialect, type SkillInfo, type SkillMetadata, SkillsService, type SpawnSpec, StdioAgentAdapter, StructuredEvent, StructuredRunner, type StructuredRunnerDeps, StructuredSession, StructuredSessionCapability, StructuredTurnEnd, TASK_QUERY_ANSWER_MAX_CHARS, TASK_QUERY_ANSWER_OR_OPS, TASK_QUERY_FIELDS, TASK_QUERY_PROTOCOL, TASK_QUERY_REMINDER, TRUNCATED_PLAN_REPAIR_INSTRUCTION, Task, type TaskAttemptSnapshot, TaskCompleteArgs, TaskControlError, TaskIsolation, TaskLogEvent, type TaskLogFile, type TaskLogLocation, TaskLogRecorder, type TaskLogRecorderDeps, TaskModeAgentAdapter, TaskOp, TaskOrchestrator, type TaskOrchestratorDeps, type TaskOrchestratorOptions, type TaskOutputAttempt, type TaskOutputSource, type TaskQuery, type TaskQueryCatalog, type TaskQueryField, TaskRunnerFlags, TaskSnapshot, TaskStartOptions, TaskTokenScope, TmuxRunner, type TmuxRunnerDeps, type TokenCarriers, type ToolCall, ToolOutcome, type ToolResult, type TranscriptQuery, type TranscriptReader, type TransportRoute, TransportRouter, TreeSnapshot, UsageRecord, type UserSettings, Verdict, VerdictEngine, type VerdictListener, type WorkspaceCheckDeps, WorkspaceNotAProjectError, WorkspaceNotFoundError, type WorkspaceProjectCheckDeps, type WorktreeIsolationDeps, admitSettingsEnv, applyHeadLimit, assertInstallablePluginUrl, assertPlainPluginName, assertWorkspaceExists, assertWorkspaceIsProject, augmentPromptWithPriorOutputs, augmentedPath, bearerHeaderValue, buildConflictRepairPrompt, buildConflictResolutionPrompt, buildConversationSystemPrompt, buildFallbackGrepArgs, buildGlobArgs, buildGrepArgs, buildMergePrompt, buildModifyDuringExecutionPrompt, buildModifyPlanPrompt, buildPlanWithResults, buildResearchPrompt, buildResearchToolsPrompt, buildRunnerInvocation, buildSplitPrompt, buildSubagentSystemPrompt, canContinue, clampThinkingEffort, classifyCommand, classifyPlannerReply, classifyPluginSource, clearAugmentedPathCache, clearDaemonToken, clearResearchShellCache, clearState, clipboardCopyCommand, coerceAssignments, collectDirectDependencyOutputs, composeAugmentedPrompt, composeContinuationPrompt, configuredProviders, continuability, createAiService, createSession, createSkillsService, createTaskAdapter, createTaskOrchestrator, createWorktreeIsolation, daemonTokenPath, defaultLogger, definitionPattern, defuseMarkers, deleteSession, describeMergeResult, discoverGeminiModels, effectiveAllowlist, ensureDir, ensurePrivateDir, ensureStateDirIgnored, executionLogBlock, extractPrdBlock, extractPresentedToken, filterFallbackByAnchoredInclude, filterModelsForPrompt, formatSearchOutput, generatePlanWithRepair, getLatestSession, getProviderMeta, getSettingsPath, getStateDir, globalDataDir, grantScopeFor, grepRegexFromProbe, hasTmux, idSegment, includeGlobFor, isCliProvider, isOpenAiProvider, isPlainPluginName, isValidManifest, killTree, languageForId, listSessions, listTaskLogAttempts, loadSession, loadSessionPlanState, loadState, mapAgentTool, migrateOldConfigDir, migratePlanIsolation, mintDaemonToken, mintSessionId, modelContextBlock, modifyValidationFeedback, normalizeAgentArgs, normalizeGeminiModel, openTaskLog, parseMaxParallel, parseTaskQueryJson, pendingEditRulesBlock, prefixModelId, providerForRunner, reEmitPlanPrompt, reEmitTaskOpsPrompt, reEmitTaskQueryPrompt, readDaemonToken, readTaskLog, referencePattern, renderPlanMap, renderPriorOutputs, renderTaskQueryAnswer, repairLoop, researchShellWarning, researchToolsPath, resolveArgs, resolvePluginInstallDir, resolveProviderFromPrefix, resolveResearchShell, resolveSkillInvocation, resolveTaskRunnerFlags, resolveWithin, retargetTaskRunner, routeTransport, runnerAssignment, runnerForProvider, sanitizeSlug, savePrdMarkdown, saveSession, saveState, sessionDataDir, sessionRuntimeSettings, stateExists, stripModelPrefix, summarizeOutput, supportsTaskMode, taskOpsRejectedPrompt, taskQuerySignature, textHasTaskQuery, tmuxSessionName, tmuxSocketName, tmuxWindowName, tokenSubprotocols, tokensMatch, truncatedPlanReEmitPrompt, wellKnownBinDirs, withPath, writePrivateFile };
6114
+ export { ALLOWED_PLUGIN_HOSTS, ALL_PROVIDERS, AUTO_COMMANDS, AbstractRunner, AbstractTerminalSession, ActiveTaskSession, type AdoptSessionResponse, type Adr0013IsolationRun, type Adr0013PlanIsolation, type Adr0013TaskRecord, AgentAdapter, AgentEvent, AgentProcessDeps, AgentStartOptions, AiProvider, AlreadyExecutingError, ApplyTaskOpsResult, ApprovalAnswer, type ApprovalAnswerResponse, ApprovalDecision, ApprovalMode, ApprovalPolicy, ApprovalRequest, type ApprovalsResponse, type AskOptions, AwaitingReason, BUILTIN_SKILL_NAMES, BaseAiService, BaseConfig, BaseFileSystem, BufferedTaskOutputSource, CLAUDE_CODE_MANIFEST, CLI_PROVIDERS, CORE_PLANNER_PROMPT, type CancelPlanningResponse, type CappedRows, CheckpointAnswer, type CheckpointListener, ClaudeCodeAdapter, CliAgentAiService, type CliAgentAiServiceDeps, CodexAdapter, type CommandClassification, type CommandDescriptor, type CommandResponse, type CommandTier, type CommandsResponse, type ComposeOptions, ConsoleLogger, ContextCollector, type Continuability, ConversationBusyError, type ConversationCompactResponse, type ConversationCompaction, ConversationEditError, type ConversationFork, type ConversationForkResponse, ConversationMessage, type ConversationRequest, type ConversationRewind, type ConversationRewindResponse, type ConversationTurn, type ConversationVariant, DAEMON_SUBPROTOCOL, DAEMON_TOKEN_SUBPROTOCOL_PREFIX, DEFAULT_MAX_PARALLEL, type DaemonErrorCode, DiscoveredModel, type EnvAdmission, EnvConfig, type ErrorBody, type ExecFileFn, type ExecImpl, type ExecuteResponse, FindSymbolOptions, type ForceSendResponse, FsPluginStore, GIT_READONLY_SUBCOMMANDS, GREP_PCRE_PROBE_ARGS, GeminiService, type GeneratePlanResponse, type GitExecFn, GlobOptions, type GrepInvocation, GrepOptions, type GrepRegex, HARNESS_PLANNER_REASON, type HasBinFn, HomeTranscriptReader, type IAiService, IApproval, IConfig, IFileSystem, type ILogger, type INotification, IPluginStore, ITerminalRunner, ITerminalSession, IWorktreeIsolation, type IsolationDiffResponse, IsolationHandoff, IsolationLandedTask, type IsolationMergeResponse, IsolationMergeResult, IsolationOutcome, IsolationRun, IsolationTaskRecord, IsolationTaskStatus, IsolationView, JSON_REPAIR_INSTRUCTION, type KillTreeDeps, LegacyPlanState, type LiveOutputLookup, type LiveTail, type LiveTailOptions, type MappedTool, type MergeGateEntry, type MergeGateResponse, MergeGateView, ModelResolver, type ModelResolverDeps, type ModelsResponse, type ModifyPlanRequest, NoPlanError, type NotificationAction, OPENCODE_MANIFEST, ORDEWELL_SETTABLE_ENV, OUTPUT_LINES_DEFAULT, OUTPUT_LINES_MAX, type OkResponse, OpenAiService, OpenCodeAdapter, type OrchestratorObserver, OrchestratorOption, OrdewellMcpServer, PLUGIN_NAME_PATTERN, PROVIDER_CREDENTIAL_ENV, PROVIDER_DETECT_PRIORITY, PROVIDER_LABEL, PROVIDER_PRIORITY, PROVIDER_SHORT_LABEL, type PendingApproval, PendingApprovals, type PendingApprovalsOptions, PlanEditError, PlanIsolation, PlanParseError, type PlanRequest, type PlanResponse, PlanState, PlanStatus, PlanStore, Planner, type PlannerBackendEntry, type PlannerModelCandidate, type PlannerModelChoice, PlannerModelMemory, type PlannerModelRecall, type PlannerModelStore, type PlannerReplyClassification, type PlannerRuntimeToggles, type PlannerSubmission, PlannerToolHandler, PlannerTurnDiscardedError, PlannerTurnStoppedError, type PlannerUsability, PlannerUsage, type PluginSource, type PrdBlock, type PrdResponse, type PriorOutput, type ProbeFn, ProviderModelLists, type ProviderRegistration, QueuedMessage, QueuedTaskMessage, REFUSED_COMMANDS, ReadFileOpts, type RemoveMessageResponse, RepairEvidence, type RepairLoopOpts, type RepairVerdict, RepoGroupLayout, type ResearchChat, ResearchLogEntry, ResearchProgress, type ResearchShell, type ResearchShellDeps, ResearchToolType, type ResearchTurn, type ResolveConflictResponse, ResolveContext, type RewindTarget, type RewindTargetsResponse, RunnerApprovals, type RunnerCatalog, type RunnerEntry, RunnerId, RunnerInstallation, RunnerInvocation, type RunnerMode, RunnerModeInfo, RunnerPluginManifest, RunnerRegistry, RunnerSpawnOptions, RunnerTransport, type RunnersResponse, SETTINGS_ENV_ALLOWLIST, SETTINGS_ENV_REFUSED, STATE_DIR, STOPPED_TOOL_RESULT, SYMBOL_LANGUAGES, type SaveSession, Session, SessionBroadcaster, type SessionData, type SessionDeps, type SessionListResponse, SessionMessage, type SessionMeta, SessionNotFoundError, SessionNotice, type SessionPlanner, type SessionResponse, type SessionRuntimeSettings, type SettingsResponse, SettingsService, type SettingsUpdateResponse, type ShellDialect, type SkillInfo, type SkillMetadata, SkillsService, type SpawnSpec, StdioAgentAdapter, type StopResponse, StructuredEvent, StructuredRunner, type StructuredRunnerDeps, StructuredSession, StructuredSessionCapability, StructuredTurnEnd, TASK_QUERY_ANSWER_MAX_CHARS, TASK_QUERY_ANSWER_OR_OPS, TASK_QUERY_FIELDS, TASK_QUERY_PROTOCOL, TASK_QUERY_REMINDER, TRUNCATED_PLAN_REPAIR_INSTRUCTION, Task, type TaskAttemptSnapshot, TaskCompleteArgs, TaskControlError, TaskIsolation, type TaskLogAttemptsResponse, TaskLogEvent, type TaskLogFile, type TaskLogLocation, TaskLogRecorder, type TaskLogRecorderDeps, type TaskLogResponse, type TaskMessageResponse, TaskModeAgentAdapter, TaskOp, TaskOrchestrator, type TaskOrchestratorDeps, type TaskOrchestratorOptions, type TaskOutputAttempt, type TaskOutputSource, type TaskQuery, type TaskQueryCatalog, type TaskQueryField, TaskRunnerFlags, TaskSnapshot, TaskStartOptions, TaskTokenScope, TmuxRunner, type TmuxRunnerDeps, type TokenCarriers, type ToolCall, ToolOutcome, type ToolResult, type TranscriptQuery, type TranscriptReader, type TransportRoute, TransportRouter, TreeSnapshot, UsageRecord, type UserSettings, Verdict, VerdictEngine, type VerdictListener, type WorkspaceCheckDeps, WorkspaceNotAProjectError, WorkspaceNotFoundError, type WorkspaceProjectCheckDeps, type WorkspacesResponse, type WorktreeIsolationDeps, admitSettingsEnv, applyHeadLimit, assertInstallablePluginUrl, assertPlainPluginName, assertWorkspaceExists, assertWorkspaceIsProject, augmentPromptWithPriorOutputs, augmentedPath, bearerHeaderValue, buildConflictRepairPrompt, buildConflictResolutionPrompt, buildConversationSystemPrompt, buildFallbackGrepArgs, buildGlobArgs, buildGrepArgs, buildMergePrompt, buildModifyDuringExecutionPrompt, buildModifyPlanPrompt, buildPlanWithResults, buildResearchPrompt, buildResearchToolsPrompt, buildRunnerInvocation, buildSplitPrompt, buildSubagentSystemPrompt, canContinue, clampThinkingEffort, classifyCommand, classifyPlannerReply, classifyPluginSource, clearAugmentedPathCache, clearDaemonToken, clearResearchShellCache, clearState, clipboardCopyCommand, coerceAssignments, collectDirectDependencyOutputs, composeAugmentedPrompt, composeContinuationPrompt, configuredProviders, continuability, createAiService, createSession, createSkillsService, createTaskAdapter, createWorktreeIsolation, daemonTokenPath, defaultLogger, definitionPattern, defuseMarkers, deleteSession, describeMergeResult, discoverGeminiModels, effectiveAllowlist, ensureDir, ensurePrivateDir, ensureStateDirIgnored, executionLogBlock, extractPrdBlock, extractPresentedToken, filterFallbackByAnchoredInclude, filterModelsForPrompt, formatSearchOutput, generatePlanWithRepair, getLatestSession, getProviderMeta, getSettingsPath, getStateDir, globalDataDir, grantScopeFor, grepRegexFromProbe, hasTmux, idSegment, includeGlobFor, isCliProvider, isOpenAiProvider, isPlainPluginName, isValidManifest, killTree, languageForId, listSessions, listTaskLogAttempts, loadSession, loadSessionPlanState, loadState, mapAgentTool, migrateOldConfigDir, migratePlanIsolation, mintDaemonToken, mintSessionId, modelContextBlock, modifyValidationFeedback, normalizeAgentArgs, normalizeGeminiModel, openTaskLog, parseMaxParallel, parseTaskQueryJson, pendingEditRulesBlock, plannerBackendEntries, prefixModelId, providerForRunner, reEmitPlanPrompt, reEmitTaskOpsPrompt, reEmitTaskQueryPrompt, readDaemonToken, readTaskLog, referencePattern, renderPlanMap, renderPriorOutputs, renderTaskQueryAnswer, repairLoop, researchShellWarning, researchToolsPath, resolveArgs, resolvePluginInstallDir, resolveProviderFromPrefix, resolveResearchShell, resolveSkillInvocation, resolveTaskRunnerFlags, resolveWithin, retargetTaskRunner, routeTransport, runnerAssignment, runnerForProvider, sanitizeSlug, savePrdMarkdown, saveSession, saveState, sessionDataDir, sessionRuntimeSettings, stateExists, stripModelPrefix, summarizeOutput, supportsTaskMode, taskOpsRejectedPrompt, taskQuerySignature, textHasTaskQuery, tmuxSessionName, tmuxSocketName, tmuxWindowName, tokenSubprotocols, tokensMatch, truncatedPlanReEmitPrompt, wellKnownBinDirs, withPath, writePrivateFile };