@ordewell/core 0.10.5 → 0.10.7

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 (44) hide show
  1. package/dist/{IRunner-Crkt3tHD.d.ts → IFileSystem-C4_mpfze.d.ts} +97 -14
  2. package/dist/{IRunner-lMQIa5Wc.d.mts → IFileSystem-DUgeWWQN.d.mts} +97 -14
  3. package/dist/{ModeResolver-B4hhABNf.d.mts → ModeResolver-4qNjxcDs.d.mts} +1 -1
  4. package/dist/{ModeResolver-CUR5jCSU.d.ts → ModeResolver-DGK2rWHE.d.ts} +1 -1
  5. package/dist/{Task-D4QHjdGv.d.mts → Task-C0o4i3Nn.d.mts} +23 -4
  6. package/dist/{Task-D4QHjdGv.d.ts → Task-C0o4i3Nn.d.ts} +23 -4
  7. package/dist/{chunk-6LEA6WAM.mjs → chunk-DMG4ISUG.mjs} +923 -45
  8. package/dist/chunk-DMG4ISUG.mjs.map +1 -0
  9. package/dist/{chunk-RT4NQ2BZ.mjs → chunk-EEE2GBLG.mjs} +114 -27
  10. package/dist/chunk-EEE2GBLG.mjs.map +1 -0
  11. package/dist/chunk-H7QX32BH.mjs +647 -0
  12. package/dist/chunk-H7QX32BH.mjs.map +1 -0
  13. package/dist/{chunk-F3KIX7WR.mjs → chunk-LTHKFBKM.mjs} +4 -4
  14. package/dist/chunk-LTHKFBKM.mjs.map +1 -0
  15. package/dist/index.d.mts +278 -1701
  16. package/dist/index.d.ts +278 -1701
  17. package/dist/index.js +2744 -1915
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.mjs +2186 -1535
  20. package/dist/index.mjs.map +1 -1
  21. package/dist/parsing.d.mts +2 -2
  22. package/dist/parsing.d.ts +2 -2
  23. package/dist/parsing.js +231 -75
  24. package/dist/parsing.js.map +1 -1
  25. package/dist/parsing.mjs +3 -3
  26. package/dist/plan-utils.d.mts +20 -4
  27. package/dist/plan-utils.d.ts +20 -4
  28. package/dist/plan-utils.js +255 -97
  29. package/dist/plan-utils.js.map +1 -1
  30. package/dist/plan-utils.mjs +33 -19
  31. package/dist/{plan-utils-wI8MuDBC.d.mts → runSettingsText-BgOmLxdw.d.mts} +1941 -108
  32. package/dist/{plan-utils-BVJCzNvm.d.ts → runSettingsText-BgXvajj2.d.ts} +1941 -108
  33. package/dist/testing.d.mts +2 -3
  34. package/dist/testing.d.ts +2 -3
  35. package/dist/testing.js.map +1 -1
  36. package/dist/testing.mjs +1 -1
  37. package/package.json +1 -1
  38. package/dist/IFileSystem-ClBdp59f.d.ts +0 -78
  39. package/dist/IFileSystem-Db34maxC.d.mts +0 -78
  40. package/dist/chunk-4QLFM73A.mjs +0 -1407
  41. package/dist/chunk-4QLFM73A.mjs.map +0 -1
  42. package/dist/chunk-6LEA6WAM.mjs.map +0 -1
  43. package/dist/chunk-F3KIX7WR.mjs.map +0 -1
  44. package/dist/chunk-RT4NQ2BZ.mjs.map +0 -1
package/dist/index.d.mts CHANGED
@@ -1,13 +1,11 @@
1
- import { ah as Task, E as ExecutionActor, r as IsolationMergeResult, f as ExecutionRecord, g as GuardTrip, ag as SupervisorLevel, e as ExecutionCommandName, G as GuardTarget, a3 as RunnerId, d as DiscoveredModel, _ as ResearchProgress, Z as ResearchLogEntry, C as ConversationMessage, a9 as SkillInfo, aa as SkillLoad, ae as SkillsService, L as LegacyPlanState, N as PlanStatus, n as IsolationLandedTask, I as IWorktreeIsolation, F as IsolationTaskStatus, J as PlanIsolation, au as UsageRecord, a4 as RunnerSessionCost, a1 as ResearchToolType, R as PlannerUsage, ao as TaskSkillSnapshot, ap as TaskSnapshot, K as PlanState, H as IsolationView, aw as Verdict } from './Task-D4QHjdGv.mjs';
2
- export { A as ApprovalRecord, a as AttemptUsage, b as AwaitingReason, B as BUILTIN_SKILL_NAMES, D as DEFAULT_RUNNERS, c as DEFAULT_SUPERVISOR_RETRY_LIMIT, h as IntegrationDisposal, i as InvalidSkill, j as IsolationAvailability, k as IsolationHandoff, l as IsolationHandoffRepo, m as IsolationInactiveReason, o as IsolationLanding, p as IsolationMergeBlock, q as IsolationMergeBlockReason, s as IsolationOutcome, t as IsolationPruneResult, u as IsolationRemoval, v as IsolationRepo, w as IsolationRun, x as IsolationTaskRecord, y as IsolationTaskRef, z as IsolationTaskRepo, M as ModelUsage, O as PlanUsage, Q as PlannerResendState, S as PrepareIntent, T as PreparedTask, U as PreservedWork, V as PreviousWork, W as RefusedRemoval, X as RepairEvidence, Y as RepoGroupLayout, $ as ResearchStep, a0 as ResearchStepOutcome, a2 as RunSettings, a5 as SetupFailure, a6 as ShadowedSkill, a7 as SkillAppliesTo, a8 as SkillCatalog, ab as SkillLoadNotice, ac as SkillMetadata, ad as SkillSource, ak as TaskIsolation, al as TaskIsolationState, am as TaskModelAssignment, aq as TaskStatus, ar as TaskUsageSummary, as as TreeSnapshot, at as UsageLine, av as UsageTotals, ax as VerificationCheck, ay as addAttemptUsage, az as addModelUsage, aA as addPlannerUsage, aB as addUsage, aC as addUsageTotals, aD as countedSessionCost, aE as createEmptyPlan, aF as createSkillsService, aG as createTask, aH as endAttemptUsage, aI as flattenTasks, aJ as flattenTasksWithParents, aK as isAwaitingReason, aL as isMeasured, aM as isSupervisorLevel, aN as isUserMessage, aO as migratePlanState, aP as partedPromptUsage, aQ as pausedOnLimit, aR as planUsage, aS as plannerContextFill, aT as rejoinPlanTree, aU as resumedSessionWindow, aV as startAttemptUsage, aW as taskUsageSummary, aX as usageLine, aY as waitsForApproval } from './Task-D4QHjdGv.mjs';
3
- import { aj as StartAdmission, y as LiveTailOptions, L as LiveTail, W as PlannerModes, av as TaskOp, w as IsolatedExecution, f as AssignmentScope, ac as SessionBroadcaster, b as ApplyTaskOpsResult, ah as SkillLookup, ae as SessionNotice, s as EditCatalog, ad as SessionMessage, V as PlanStore, aw as TaskOrchestrator, x as IsolationRunController, Q as OrchestratorObserver, at as TaskLogEvent, I as INotification, u as GlobalRunSettings, a3 as RunView, $ as RunMergeView, az as TaskOutputSource, aQ as UserSettings, an as SurfacePlan, ao as SurfacePlanState } from './plan-utils-wI8MuDBC.mjs';
4
- export { A as ACT_RISK, a as AUTORUN_RISK, c as ApprovalBlock, d as ApprovalStatus, e as AssignmentIssue, g as AutoMergeTrigger, B as BlockingSubject, C as CHECKPOINT_TRUNCATE_LENGTH, h as CommandBlock, i as CommandStatus, j as CompactionBlock, k as ConversationInput, l as ConversationView, D as DiffRow, m as DiffRowKind, n as DiffStat, o as DisplayBlock, E as EMPTY_CONVERSATION, p as EMPTY_HOLD, q as EMPTY_RUN, r as EMPTY_TASK_LOG, t as ExecutionSummary, G as GatedConversation, H as HeldEdit, v as HeldEditMark, z as LocalEntry, M as MarkdownToken, F as MessageBlock, J as MessageRole, N as NOT_STARTED, K as NO_TURN, O as NewTaskFields, P as NotificationAction, R as OutputPreview, S as PlanBlock, T as PlanMarkerStatus, U as PlanSnapshot, X as PromptHold, Y as RUN_SETTINGS_DEFAULTS, _ as RunInput, a0 as RunSettingsError, a1 as RunTask, a2 as RunTaskStatus, a4 as RunnerLoadedBlock, a5 as STOPPED_BEFORE_START, a6 as SUPERVISOR_LEVEL_LABEL, a7 as SerializedConversationMessage, a8 as SerializedPendingTurn, a9 as SerializedPlan, aa as SerializedTask, ab as SerializedTaskStatus, af as SettingsService, ag as SkillLoadBlock, ai as SkillToken, ak as SubagentBlock, al as SubagentChild, am as SubagentStatus, ap as SurfaceTask, aq as TakenPrompt, ar as TaskAttemptSnapshot, as as TaskControlError, au as TaskLogView, ax as TaskOrchestratorDeps, ay as TaskOrchestratorOptions, aA as TaskRef, aB as TaskRowAction, aC as TaskRowContext, aD as TaskRowIsolation, aE as TaskRowKind, aF as TaskRowPlacement, aG as TaskRowSubject, aH as TaskRowUsage, aI as TaskRowView, aJ as TaskStatusKind, aK as ThinkingDisplayBlock, aL as ToolBlock, aM as ToolHeadline, aN as ToolStatus, aO as TurnGate, aP as UsageBlock, aR as WAITS_FOR_APPROVAL_LABEL, aS as aheadOfDraft, aT as applyTaskOps, aU as approvalLabel, aV as assignmentIssue, aW as assignmentIssueText, aX as automergeSummary, aY as autorunSummary, aZ as awaitingLabel, a_ as blockedByLabel, a$ as blockingFailures, b0 as canMergeTasks, b1 as canSetDependencies, b2 as canSplitTask, b3 as capConflictFiles, b4 as classifyOutcome, b5 as coalesceTaskLog, b6 as dependencyCandidates, b7 as dependentsNotice, b8 as dependentsOf, b9 as diffRows, ba as diffStat, bb as diffSummary, bc as digestTaskLog, bd as drainNext, be as executionSummary, bf as followTurn, bg as fromTranscript, bh as getSettingsPath, bi as hasHiddenDetail, bj as holdPrompt, bk as isTaskRunning, bl as loadedSkillTokens, bm as markAction, bn as markRequestFor, bo as markdownHtml, bp as markdownTokens, bq as namedRepos, br as newTaskFields, bs as opsEditable, bt as outputLines, bu as outputPreview, bv as parseCount, bw as parseSwitch, bx as parseTaskOpsJson, by as pastGateConfirmation, bz as reconcileApprovals, bA as reduceConversation, bB as reduceRun, bC as reduceTaskLog, bD as replayTaskLog, bE as resolveTaskRef, bF as resumeLabel, bG as resumeTime, bH as runSettingsOf, bI as runnerToolSubject, bJ as serializePlan, bK as serializeTask, bL as serializeTaskStatus, bM as skillTokens, bN as skippedLabel, bO as stopTurn, bP as summarizeToolCall, bQ as supervisorSummary, bR as surfacePlan, bS as surfacePlanState, bT as takeAt, bU as taskLogForSurface, bV as taskOrderLabel, bW as taskRef, bX as taskRefOf, bY as taskRowActions, bZ as taskRowView, b_ as taskStartedNotice, b$ as taskStatusKind, c0 as textHasTaskOps, c1 as titledRefs, c2 as titledTaskRef, c3 as titledTaskRefOf, c4 as toTaskLogEvent, c5 as toolHeadline, c6 as trimToolOutput, c7 as truncateCheckpointSummary, c8 as unsendAll, c9 as unsendLatest } from './plan-utils-wI8MuDBC.mjs';
5
- import { I as IApproval, y as IConfig, d as AgentProcessDeps, a as AgentAdapter, _ as OrdewellMcpServer, aM as UsageLimitSignal, a6 as PlannerToolHandler, ad as ResearchShell, p as ApprovalRequest, j as ApprovalDecision, i as ApprovalAnswer, z as IRunner, a3 as PlannerBackendId, a9 as ProviderModelLists, f as AiProvider, m as ApprovalModeSetting, ar as RunnerRegistry, Z as OrchestratorOption, aE as TaskModeAgentAdapter, e as AgentStartOptions, c as AgentEvent, ai as RunnerCommand, a7 as ProjectMcpServer, B as IRunnerSession, n as ApprovalPolicy, aB as TaskCompleteArgs, r as CheckpointAnswer, aw as StructuredTurnEnd, av as StructuredEvent, aD as TaskMessageFrom, ab as QueuedTaskMessage, aJ as TaskStartOptions, aK as TaskTokenScope, am as RunnerManifest, aI as TaskRunnerFlags } from './IRunner-lMQIa5Wc.mjs';
6
- export { A as AUTO_COMMANDS, b as AgentAdapterFactory, g as AllProviderModels, h as AnswerApprovalArgs, k as ApprovalKind, l as ApprovalMode, o as ApprovalPolicyOptions, q as ApprovalSource, C as CatalogModel, s as CheckpointArgs, t as CommandClassification, u as CommandTier, v as CommandWords, D as DEFAULT_PLANNER_ALLOWLIST, w as DENY_ALL, x as DiscoveryCommand, E as EditPlanArgs, F as FetchAllProviderModelsOptions, G as GIT_READONLY_SUBCOMMANDS, H as InvokedCommand, L as LineBuffer, J as ListModelsArgs, K as ListRunnersArgs, M as LoadSkillArgs, N as McpClientConfig, O as McpCredential, P as McpTool, Q as McpToolContext, R as McpToolReply, S as MessageTaskArgs, T as ModelCatalog, U as ModelShortcut, V as ORCHESTRATOR_SHORTCUTS, W as ORDEWELL_MCP_PATH, X as ORDEWELL_MCP_SERVER_NAME, Y as OneTaskArgs, $ as OwnerOnlyFile, a0 as PLANNER_EXECUTION_TOOLS, a1 as PLANNER_TOOLS, a2 as PlannerAllowlist, a4 as PlannerStartOptions, a5 as PlannerTokenScope, a8 as ProviderCredentialSource, aa as ProviderModelsResult, ac as REFUSED_COMMANDS, ae as ResearchShellDeps, af as RetryTaskArgs, ag as RunCommandArgs, ah as RunPlanArgs, aj as RunnerCommandKind, ak as RunnerEntry, al as RunnerFeatures, an as RunnerMcpServer, ao as RunnerMode, ap as RunnerModelDiscovery, aq as RunnerModelRef, as as ShellDialect, at as SpawnFn, au as StandingDecision, ax as SubmitPlanArgs, ay as TASK_COMPLETE_TOOL, az as TASK_COMPLETION_CONTRACT, aA as TASK_TOOLS, aC as TaskDiffArgs, aF as TaskModeUnsupportedError, aG as TaskOutputArgs, aH as TaskQueryArgs, aL as TaskToolHandler, aN as approvalScopes, aO as classifyCommand, aP as clearResearchShellCache, aQ as collectProviderCredentials, aR as commandAllowedBy, aS as effectiveApprovalMode, aT as enabledRunners, aU as fetchAllProviderModels, aV as findRunnerCommand, aW as isGranted, aX as isRunnerApproval, aY as knownModelId, aZ as mcpClientConfig, a_ as ownerOnlyConfigFile, a$ as parseAllowlist, b0 as parseApprovalModeSetting, b1 as researchShellWarning, b2 as researchToolsPath, b3 as resolveModelShortcut, b4 as resolveProvider, b5 as resolveResearchShell, b6 as scopeAllowed, b7 as sharedMcpServer, b8 as toApprovalDecision, b9 as toOrchestratorOptions, ba as toolAllowed } from './IRunner-lMQIa5Wc.mjs';
7
- import { T as ToolOutcome, I as IFileSystem, R as ReadFileOpts, b as GrepOptions, a as GlobOptions, F as FindSymbolOptions } from './IFileSystem-Db34maxC.mjs';
8
- export { G as GREP_DEFAULT_HEAD_LIMIT, c as GrepOutputMode, S as SEARCH_EXCLUSIONS } from './IFileSystem-Db34maxC.mjs';
9
- import { R as RunnerModeInfo } from './ModeResolver-B4hhABNf.mjs';
10
- export { a as autonomyLevelLabel, p as parseAutonomyLevel, r as runnerModesFrom } from './ModeResolver-B4hhABNf.mjs';
1
+ import { r as IsolationMergeResult, ai as Task, E as ExecutionActor, f as ExecutionRecord, g as GuardTrip, ah as SupervisorLevel, e as ExecutionCommandName, G as GuardTarget, n as IsolationLandedTask, I as IWorktreeIsolation, F as IsolationTaskStatus, J as PlanIsolation, a4 as RunnerId, $ as ResearchProgress, av as UsageRecord, _ as ResearchLogEntry, d as DiscoveredModel, L as LegacyPlanState, a5 as RunnerSessionCost, a2 as ResearchToolType, R as PlannerUsage, ap as TaskSkillSnapshot, af as SkillsService, aq as TaskSnapshot, K as PlanState, H as IsolationView, ax as Verdict } from './Task-C0o4i3Nn.mjs';
2
+ export { A as ApprovalRecord, a as AttemptUsage, b as AwaitingReason, B as BUILTIN_SKILL_NAMES, C as ConversationMessage, D as DEFAULT_RUNNERS, c as DEFAULT_SUPERVISOR_RETRY_LIMIT, h as IntegrationDisposal, i as InvalidSkill, j as IsolationAvailability, k as IsolationHandoff, l as IsolationHandoffRepo, m as IsolationInactiveReason, o as IsolationLanding, p as IsolationMergeBlock, q as IsolationMergeBlockReason, s as IsolationOutcome, t as IsolationPruneResult, u as IsolationRemoval, v as IsolationRepo, w as IsolationRun, x as IsolationTaskRecord, y as IsolationTaskRef, z as IsolationTaskRepo, M as ModelUsage, N as PlanStatus, O as PlanUsage, Q as PlannerResendState, S as PrepareIntent, T as PreparedTask, U as PreservedWork, V as PreviousWork, W as RefusedRemoval, X as RepairEvidence, Y as ReplyRun, Z as RepoGroupLayout, a0 as ResearchStep, a1 as ResearchStepOutcome, a3 as RunSettings, a6 as SetupFailure, a7 as ShadowedSkill, a8 as SkillAppliesTo, a9 as SkillCatalog, aa as SkillInfo, ab as SkillLoad, ac as SkillLoadNotice, ad as SkillMetadata, ae as SkillSource, al as TaskIsolation, am as TaskIsolationState, an as TaskModelAssignment, ar as TaskStatus, as as TaskUsageSummary, at as TreeSnapshot, au as UsageLine, aw as UsageTotals, ay as VerificationCheck, az as addAttemptUsage, aA as addModelUsage, aB as addPlannerUsage, aC as addUsage, aD as addUsageTotals, aE as countedSessionCost, aF as createEmptyPlan, aG as createSkillsService, aH as createTask, aI as endAttemptUsage, aJ as flattenTasks, aK as flattenTasksWithParents, aL as isAwaitingReason, aM as isMeasured, aN as isSupervisorLevel, aO as isUserMessage, aP as migratePlanState, aQ as partedPromptUsage, aR as pausedOnLimit, aS as planUsage, aT as plannerContextFill, aU as rejoinPlanTree, aV as resumedSessionWindow, aW as startAttemptUsage, aX as taskUsageSummary, aY as usageLine, aZ as waitsForApproval } from './Task-C0o4i3Nn.mjs';
3
+ import { bv as StartAdmission, bw as StartWait, aY as ResolveAll, a8 as IWebFetcher, cp as WebReadPolicy, cn as WebRead, co as WebReadDecision, aw as PendingApprovals, K as ConversationTurn, aL as PlannerModes, a6 as IAiService, I as ConversationRequest, n as AssignmentWriter, m as AssignmentScope, c1 as TaskQueryCatalog, k as AssignmentCatalog, b7 as RunnerCatalog, bl as SessionMessage, bh as SessionBroadcaster, bn as SessionNotice, aC as PlanStore, bX as TaskOrchestrator, ab as IsolationRunController, at as OrchestratorObserver, bQ as TaskLogEvent, a7 as INotification, a2 as GlobalRunSettings, aF as PlannerConversationHost, aE as PlannerConversation, av as PendingApproval, E as ConversationCompaction, a_ as RewindTarget, b6 as RunView, b1 as RunMergeView, b_ as TaskOutputSource, cl as UserSettings, ae as LiveTailOptions, ad as LiveTail, bm as SessionMeta, aK as PlannerModelStore, aJ as PlannerModelRecall, aG as PlannerModelCandidate } from './runSettingsText-BgOmLxdw.mjs';
4
+ export { A as ACT_RISK, a as AUTORUN_RISK, b as AdoptSessionResponse, c as ApplyTaskOpsResult, d as ApprovalAnswerResponse, e as ApprovalBlock, f as ApprovalRequestMessage, g as ApprovalStatus, h as ApprovalsResponse, i as ApproveReviewResponse, j as AskOptions, l as AssignmentIssue, o as AutoMergeTrigger, B as BlockingSubject, C as CHAT_COMMANDS, p as CHECKPOINT_TRUNCATE_LENGTH, q as CancelPlanningResponse, r as CancelResumeResponse, s as CliAgentAiService, t as CliAgentAiServiceDeps, u as CommandBlock, v as CommandDescriptor, w as CommandStatus, x as CommandsResponse, y as CompactionBlock, z as ConversationBusyError, D as ConversationCompactResponse, F as ConversationEditError, G as ConversationForkResponse, H as ConversationInput, J as ConversationRewindResponse, L as ConversationView, M as DEFAULT_WEB_READ_BUDGET, N as DaemonErrorCode, O as DecideOptions, P as DiffRow, Q as DiffRowKind, R as DiffStat, S as DisplayBlock, T as EMPTY_CONVERSATION, U as EMPTY_HOLD, V as EMPTY_RUN, W as EMPTY_TASK_LOG, X as EMPTY_TASK_LOG_FOLLOW, Y as ErrorBody, Z as ExecuteResponse, _ as ExecutionSummary, $ as ForceSendResponse, a0 as GatedConversation, a1 as GeneratePlanResponse, a3 as HEADLESS_CLI_COMMANDS, a4 as HeldEdit, a5 as HeldEditMark, a9 as IsolationDiffResponse, aa as IsolationMergeResponse, ac as LiveOutputLookup, af as LocalEntry, ag as MarkdownToken, ah as MergeGateEntry, ai as MergeGateResponse, aj as MessageBlock, ak as MessageRole, al as ModelsResponse, am as NOT_STARTED, an as NO_TURN, ao as NewTaskFields, ap as NotificationAction, aq as OUTPUT_LINES_DEFAULT, ar as OUTPUT_LINES_MAX, as as OkResponse, au as OutputPreview, ax as PendingApprovalsOptions, ay as PlanBlock, az as PlanMarkerStatus, aA as PlanResponse, aB as PlanSnapshot, aD as PlannerCommandSyntax, aH as PlannerModelChoice, aI as PlannerModelMemory, aM as PlannerNotReady, aN as PlannerNotReadyError, aO as PlannerReadinessConfig, aP as PlannerSubmission, aQ as PlannerTurnDiscardedError, aR as PlannerTurnStoppedError, aS as PlannerUsageLimitError, aT as PrdResponse, aU as PromptHold, aV as RUN_SETTINGS_DEFAULTS, aX as RemoveMessageResponse, aZ as ResolveConflictResponse, a$ as RewindTargetsResponse, b0 as RunInput, b2 as RunSettingsError, b3 as RunSnapshotFrame, b4 as RunTask, b5 as RunTaskStatus, b8 as RunnerLoadedBlock, b9 as RunnersResponse, ba as STOPPED_BEFORE_START, bb as SUPERVISOR_LEVEL_LABEL, bc as SerializedConversationMessage, bd as SerializedPendingTurn, be as SerializedPlan, bf as SerializedTask, bg as SerializedTaskStatus, bi as SessionData, bj as SessionFrame, bk as SessionListResponse, bo as SessionResponse, bp as SettingsResponse, bq as SettingsService, br as SettingsUpdateResponse, bs as SkillInvocation, bt as SkillLoadBlock, bu as SkillToken, bx as StopResponse, by as StreamStamp, bz as SubagentBlock, bA as SubagentChild, bB as SubagentStatus, bC as SurfacePlan, bD as SurfacePlanState, bE as SurfaceTask, bF as TASK_QUERY_ANSWER_MAX_CHARS, bG as TASK_QUERY_ANSWER_OR_OPS, bH as TASK_QUERY_FIELDS, bI as TASK_QUERY_PROTOCOL, bJ as TASK_QUERY_REMINDER, bK as TERMINAL_UI_COMMANDS, bL as TakenPrompt, bM as TaskAttemptSnapshot, bN as TaskControlError, bO as TaskLogAttemptsResponse, bP as TaskLogBatch, bR as TaskLogFollow, bS as TaskLogFollowInput, bT as TaskLogResponse, bU as TaskLogView, bV as TaskMessageResponse, bW as TaskOp, bY as TaskOrchestratorDeps, bZ as TaskOrchestratorOptions, b$ as TaskPlacement, c0 as TaskQuery, c2 as TaskQueryField, c3 as TaskRef, c4 as TaskRowAction, c5 as TaskRowContext, c6 as TaskRowIsolation, c7 as TaskRowKind, c8 as TaskRowPlacement, c9 as TaskRowSubject, ca as TaskRowUsage, cb as TaskRowView, cc as TaskStartResponse, cd as TaskStatusKind, ce as ThinkingDisplayBlock, cf as ToolBlock, cg as ToolHeadline, ch as ToolStatus, ci as TreeTask, cj as TurnGate, ck as UsageBlock, cm as WAITS_FOR_APPROVAL_LABEL, cq as Worded, cr as WorkspacesResponse, cs as abbreviateHome, ct as aheadOfDraft, cu as applyTaskOps, cv as approvalLabel, cw as assignmentIssue, cx as assignmentIssueText, cy as automergeSummary, cz as autorunSummary, cA as awaitingLabel, cB as blockedByLabel, cC as blockingFailures, cD as canMergeTasks, cE as canSetDependencies, cF as canSplitTask, cG as capConflictFiles, cH as classifyOutcome, cI as coalesceTaskLog, cJ as createAiService, cK as dependencyCandidates, cL as dependentsNotice, cM as dependentsOf, cN as describePlannerNotReady, cO as diffRows, cP as diffStat, cQ as diffSummary, cR as digestTaskLog, cS as drainNext, cT as executionSummary, cU as followTaskLog, cV as followTurn, cW as fromTranscript, cX as getSettingsPath, cY as hasHiddenDetail, cZ as holdPrompt, c_ as isTaskRunning, c$ as loadedSkillTokens, d0 as markAction, d1 as markRequestFor, d2 as markdownHtml, d3 as markdownTokens, d4 as namedRepos, d5 as newTaskFields, d6 as opsEditable, d7 as outputLines, d8 as outputPreview, d9 as parseCount, da as parseSwitch, db as parseTaskOpsJson, dc as parseTaskQueryJson, dd as pastGateConfirmation, de as plannerMessage, df as plannerNotReady, dg as plannerNotReadyFor, dh as plannerTranscript, di as reconcileApprovals, dj as reduceConversation, dk as reduceRun, dl as reduceTaskLog, dm as renderTaskQueryAnswer, dn as replayTaskLog, dp as resolveSkillInvocation, dq as resolveTaskRef, dr as resolveUserTaskRef, ds as resumeLabel, dt as resumeTime, du as runSettingsOf, dv as runnerToolSubject, dw as serializePlan, dx as serializeTask, dy as serializeTaskStatus, dz as skillTokens, dA as skippedLabel, dB as stopTurn, dC as subtreeIds, dD as summarizeToolCall, dE as supervisorSummary, dF as surfacePlan, dG as surfacePlanState, dH as takeAt, dI as taskLogForSurface, dJ as taskOrderLabel, dK as taskPlacement, dL as taskQuerySignature, dM as taskRef, dN as taskRefOf, dO as taskRowActions, dP as taskRowView, dQ as taskStartedNotice, dR as taskStatusKind, dS as textHasTaskOps, dT as textHasTaskQuery, dU as titledRefs, dV as titledTaskRef, dW as titledTaskRefOf, dX as toTaskLogEvent, dY as toolHeadline, dZ as trimToolOutput, d_ as truncateCheckpointSummary, d$ as unsendAll, e0 as unsendLatest, e1 as walkPlan } from './runSettingsText-BgOmLxdw.mjs';
5
+ import { j as ApprovalDecision, J as IApproval, L as IFileSystem, ak as ResearchShell, aj as ReadFileOpts, aU as ToolOutcome, H as GrepOptions, B as GlobOptions, y as FindSymbolOptions, M as IRunner, K as IConfig, a9 as PlannerBackendId, af as ProviderModelLists, f as AiProvider, m as ApprovalModeSetting, ac as PlannerToolHandler, ay as RunnerRegistry, a3 as OrchestratorOption, d as AgentProcessDeps, aM as TaskModeAgentAdapter, a as AgentAdapter, e as AgentStartOptions, c as AgentEvent, ap as RunnerCommand, ad as ProjectMcpServer, N as IRunnerSession, n as ApprovalPolicy, i as ApprovalAnswer, a4 as OrdewellMcpServer, aJ as TaskCompleteArgs, r as CheckpointAnswer, aE as StructuredTurnEnd, aD as StructuredEvent, aL as TaskMessageFrom, ah as QueuedTaskMessage, aR as TaskStartOptions, aS as TaskTokenScope, at as RunnerManifest, aQ as TaskRunnerFlags } from './IFileSystem-DUgeWWQN.mjs';
6
+ export { A as AUTO_COMMANDS, b as AgentAdapterFactory, g as AllProviderModels, h as AnswerApprovalArgs, k as ApprovalKind, l as ApprovalMode, o as ApprovalPolicyOptions, p as ApprovalRequest, q as ApprovalSource, C as CatalogModel, s as CheckpointArgs, t as CommandClassification, u as CommandTier, v as CommandWords, D as DEFAULT_PLANNER_ALLOWLIST, w as DENY_ALL, x as DiscoveryCommand, E as EditPlanArgs, F as FetchAllProviderModelsOptions, G as GIT_READONLY_SUBCOMMANDS, z as GREP_DEFAULT_HEAD_LIMIT, I as GrepOutputMode, O as InvokedCommand, P as LineBuffer, Q as ListModelsArgs, R as ListRunnersArgs, S as LoadSkillArgs, T as McpClientConfig, U as McpCredential, V as McpTool, W as McpToolContext, X as McpToolReply, Y as MessageTaskArgs, Z as ModelCatalog, _ as ModelShortcut, $ as ORCHESTRATOR_SHORTCUTS, a0 as ORDEWELL_MCP_PATH, a1 as ORDEWELL_MCP_SERVER_NAME, a2 as OneTaskArgs, a5 as OwnerOnlyFile, a6 as PLANNER_EXECUTION_TOOLS, a7 as PLANNER_TOOLS, a8 as PlannerAllowlist, aa as PlannerStartOptions, ab as PlannerTokenScope, ae as ProviderCredentialSource, ag as ProviderModelsResult, ai as REFUSED_COMMANDS, al as ResearchShellDeps, am as RetryTaskArgs, an as RunCommandArgs, ao as RunPlanArgs, aq as RunnerCommandKind, ar as RunnerEntry, as as RunnerFeatures, au as RunnerMcpServer, av as RunnerMode, aw as RunnerModelDiscovery, ax as RunnerModelRef, az as SEARCH_EXCLUSIONS, aA as ShellDialect, aB as SpawnFn, aC as StandingDecision, aF as SubmitPlanArgs, aG as TASK_COMPLETE_TOOL, aH as TASK_COMPLETION_CONTRACT, aI as TASK_TOOLS, aK as TaskDiffArgs, aN as TaskModeUnsupportedError, aO as TaskOutputArgs, aP as TaskQueryArgs, aT as TaskToolHandler, aW as approvalScopes, aX as classifyCommand, aY as clearResearchShellCache, aZ as collectProviderCredentials, a_ as commandAllowedBy, a$ as effectiveApprovalMode, b0 as enabledRunners, b1 as fetchAllProviderModels, b2 as findRunnerCommand, b3 as isGranted, b4 as isRunnerApproval, b5 as knownModelId, b6 as mcpClientConfig, b7 as ownerOnlyConfigFile, b8 as parseAllowlist, b9 as parseApprovalModeSetting, ba as researchShellWarning, bb as researchToolsPath, bc as resolveModelShortcut, bd as resolveProvider, be as resolveResearchShell, bf as scopeAllowed, bg as sharedMcpServer, bh as toApprovalDecision, bi as toOrchestratorOptions, bj as toolAllowed } from './IFileSystem-DUgeWWQN.mjs';
7
+ import { R as RunnerModeInfo } from './ModeResolver-4qNjxcDs.mjs';
8
+ export { a as autonomyLevelLabel, p as parseAutonomyLevel, r as runnerModesFrom } from './ModeResolver-4qNjxcDs.mjs';
11
9
  import { ChildProcess } from 'child_process';
12
10
  import { EventEmitter } from 'events';
13
11
  import 'zod/v4';
@@ -42,10 +40,13 @@ type ExecutionCommand = {
42
40
  task: string;
43
41
  from: 'fresh' | 'session';
44
42
  message?: string;
45
- } | {
43
+ }
44
+ /** `force`: the user's force send (ADR-0023, F1), which interrupts the running turn and is read next. */
45
+ | {
46
46
  command: 'message_task';
47
47
  task: string;
48
48
  text: string;
49
+ force?: boolean;
49
50
  }
50
51
  /** `answer` is checked here rather than typed: an `allow` must reach the seam to be refused with its reason. */
51
52
  | {
@@ -64,20 +65,89 @@ type ExecutionCommand = {
64
65
  command: 'skip_task';
65
66
  task: string;
66
67
  reason?: string;
68
+ }
69
+ /** The user's force start: past a failed dependency or a merge gate, queued behind dependencies still under way (ADR-0035). */
70
+ | {
71
+ command: 'force_start';
72
+ task: string;
73
+ }
74
+ /** The user vouches for the task's work, which lands as a passed verdict's would; never anyone else (ADR-0029). */
75
+ | {
76
+ command: 'mark_complete';
77
+ task: string;
78
+ }
79
+ /** The user takes a completion back: the task is not started again, and held until it is retried or started. */
80
+ | {
81
+ command: 'mark_not_done';
82
+ task: string;
83
+ }
84
+ /** Stop a structured task's running turn, keeping its session: it then waits for the user's message. */
85
+ | {
86
+ command: 'interrupt_task';
87
+ task: string;
88
+ }
89
+ /** Force send a message queued behind the running turn (ADR-0023, F1); `id` is the one `message_task` answered with. */
90
+ | {
91
+ command: 'force_send_queued';
92
+ task: string;
93
+ id: string;
94
+ }
95
+ /** Take back a message still queued behind the running turn. */
96
+ | {
97
+ command: 'remove_queued';
98
+ task: string;
99
+ id: string;
100
+ }
101
+ /** Keep a task paused on a usage limit from resuming on its own at the reset: it waits for the user's Resume now or Retry. */
102
+ | {
103
+ command: 'cancel_resume';
104
+ task: string;
105
+ }
106
+ /** The user's answer to the checkpoint a task waits at: it goes on, or is told `reason` to act on. */
107
+ | {
108
+ command: 'answer_checkpoint';
109
+ task: string;
110
+ answer: 'approve' | 'reject';
111
+ reason?: string;
112
+ }
113
+ /** The user signs off the plan's review: every task in it is approved, as executing it approves them, and it runs. */
114
+ | {
115
+ command: 'approve_review';
116
+ };
117
+ /** What a control answers besides its line, for the surface whose button asked. */
118
+ type CommandPayload =
119
+ /** `message_task`: the queued message's id, which the surface's button answers with. */
120
+ {
121
+ kind: 'message';
122
+ id: string;
123
+ }
124
+ /** `force_start` queued behind dependencies still under way, rather than started: the line saying so. */
125
+ | {
126
+ kind: 'queued';
127
+ line: string;
128
+ }
129
+ /**
130
+ * The user's `merge_all`, merged or stopped: what the Merge all card
131
+ * renders. Whether it closed the run is the Session's `run_closed` (ADR-0013).
132
+ */
133
+ | {
134
+ kind: 'merge';
135
+ result: IsolationMergeResult;
67
136
  };
68
137
  /**
69
138
  * What a call came to: `message` is written for the caller, a model included,
70
- * to act on. `id` is the queued message's id for `message_task`, the value the
71
- * surface's button already answered with.
139
+ * to act on. A refusal carries a payload only where the act ran and stopped:
140
+ * a Merge all that git would not take.
72
141
  */
73
142
  type CommandOutcome = {
74
143
  ok: true;
75
144
  message: string;
76
- id?: string;
145
+ payload?: CommandPayload;
77
146
  } | {
78
147
  ok: false;
79
148
  outcome: 'refused' | 'declined';
80
149
  message: string;
150
+ payload?: CommandPayload;
81
151
  };
82
152
  /** What the seam does with a valid command: run it, ask the user first, or refuse with a note the caller can act on. */
83
153
  type Consent = {
@@ -114,9 +184,9 @@ interface WaitingRequest {
114
184
  subject: string;
115
185
  }
116
186
  /**
117
- * What the seam reads and calls. The Session supplies it from its own public
118
- * operations, the ones the surfaces' buttons call, so a command lands exactly
119
- * as the matching button press does.
187
+ * What the seam reads and calls. The Session supplies it from the operations
188
+ * behind the surfaces' buttons, so a command lands exactly as the matching
189
+ * button press does, whoever asked.
120
190
  */
121
191
  interface ExecutionHost {
122
192
  /** The plan's task tree; null when there is no plan. */
@@ -148,12 +218,12 @@ interface ExecutionHost {
148
218
  maxParallel(): number;
149
219
  /** The run waits on the user to stash or skip isolation before anything starts. */
150
220
  runBlocked(): boolean;
151
- /** The tasks a task's dependencies still hold it back for: unmet dependencies, or the unfinished work behind a skipped one. */
152
- unmetDependencies(task: Readonly<Task>): string[];
153
- /** The ancestors a task would wait on at its merge gate if it started now, whatever its status (ADR-0020). */
154
- mergeGate(taskId: string): string[];
155
- /** The failed tasks a task not started waits on through its dependencies (ADR-0034). */
156
- blockedBy(taskId: string): string[];
221
+ /**
222
+ * Every reason the task cannot start now, from the scheduler's one start
223
+ * rule (ADR-0034). Takes the task, not its id: one a held edit changes or
224
+ * adds is judged as the edit leaves it.
225
+ */
226
+ startWaits(task: Readonly<Task>): readonly StartWait[];
157
227
  /** The runner requests waiting on a task for an answer. */
158
228
  waitingRequests(taskId: string): readonly WaitingRequest[];
159
229
  /** A start is told who asked: whether a run it opens continues the last one depends on it (ADR-0029). */
@@ -165,6 +235,11 @@ interface ExecutionHost {
165
235
  */
166
236
  joinRun(approved: number): Promise<string>;
167
237
  startTask(taskId: string, actor: ExecutionActor): Promise<void>;
238
+ /** Force start the task; answers the line saying it waits for its dependencies, when it was queued behind them rather than started. */
239
+ forceStart(taskId: string): Promise<string | undefined>;
240
+ markComplete(taskId: string): Promise<void>;
241
+ markNotDone(taskId: string): Promise<void>;
242
+ interrupt(taskId: string): Promise<void>;
168
243
  stopTask(taskId: string): Promise<void>;
169
244
  stopRun(): void;
170
245
  retryTask(taskId: string, actor: ExecutionActor): Promise<void>;
@@ -174,8 +249,22 @@ interface ExecutionHost {
174
249
  /** Resume a task paused on a usage limit now; see {@link TaskOrchestrator.resumeNow}. */
175
250
  resumeNow(taskId: string, message: string | undefined, actor: ExecutionActor): Promise<void>;
176
251
  messageTask(taskId: string, text: string): string;
177
- denyRequest(requestId: string, note?: string): boolean;
178
- /** Merge all, through the queue landings and the user's Merge all share. */
252
+ /** Force send (ADR-0023, F1): interrupt the running turn and deliver this next. */
253
+ forceMessage(taskId: string, text: string): string;
254
+ /** False once the message was delivered. */
255
+ forceSendQueued(taskId: string, id: string): boolean;
256
+ /** False once the message was delivered or taken back. */
257
+ removeQueued(taskId: string, id: string): boolean;
258
+ /** False when no resume was scheduled. */
259
+ cancelResume(taskId: string): Promise<boolean>;
260
+ /** The task waits at a checkpoint, and a runner is left to hear the answer. */
261
+ awaitsCheckpoint(taskId: string): boolean;
262
+ answerCheckpoint(taskId: string, approve: boolean, reason?: string): void;
263
+ /** Sign off the review: the plan is the user's, and runs. */
264
+ approveReview(): Promise<void>;
265
+ /** Answer a runner's request, as its card does; false once it was answered or withdrawn. */
266
+ answerRequest(requestId: string, decision: ApprovalDecision): boolean;
267
+ /** Merge all, through the queue it shares with landings. */
179
268
  mergeAll(): Promise<IsolationMergeResult>;
180
269
  /** Keep the call in the timeline, worded as `text`. */
181
270
  record(record: ExecutionRecord, text: string): void;
@@ -208,12 +297,13 @@ interface ExecutionCommandsDeps {
208
297
  now?: () => string;
209
298
  }
210
299
  /**
211
- * The command seam (ADR-0029, #91): the one place an execution action from
212
- * anyone but a button reaches the scheduler. It resolves the task, checks the
213
- * call against the plan's state, who asked and the user's approval, asks the
214
- * user where consent needs a person, runs the Session operation the matching
215
- * button runs, and records the call in the timeline with who asked. A refusal
216
- * names the task and the reason, worded for a model to act on.
300
+ * The command seam (ADR-0029, #91): the one place an execution action
301
+ * reaches the scheduler, whether the user, a planner turn or the supervisor
302
+ * asked. It resolves the task, checks the call against the plan's state, who
303
+ * asked and the user's approval, asks the user where consent needs a person,
304
+ * runs the Session operation behind the matching button, and records the call
305
+ * in the timeline with who asked. A refusal names the task and the reason,
306
+ * worded for whoever asked to act on ({@link voiceOf}).
217
307
  */
218
308
  declare class ExecutionCommands {
219
309
  private readonly deps;
@@ -269,8 +359,9 @@ declare class ExecutionCommands {
269
359
  private asksToApprove;
270
360
  /**
271
361
  * Who may do what without the mandate (#23): the user's own call needs no
272
- * consent; a planner turn may always end or steer work, and starts it only
273
- * as the autonomy level says; the supervisor's level decides for it.
362
+ * consent, and their own controls are theirs alone; a planner turn may
363
+ * always end or steer work, and starts it only as the autonomy level says;
364
+ * the supervisor's level decides for it.
274
365
  */
275
366
  private consent;
276
367
  /**
@@ -282,6 +373,19 @@ declare class ExecutionCommands {
282
373
  private prepareRunPlan;
283
374
  /** A start of one task: `run_task`, or a fresh `retry_task`, which also takes a task that failed or waits on the user. */
284
375
  private prepareStart;
376
+ /**
377
+ * The user's force start goes past what the scheduler waits for, so only
378
+ * what no start could get past is refused here; the start rule decides the
379
+ * rest at the claim, and a dependency still under way queues it.
380
+ */
381
+ private prepareForceStart;
382
+ /** Signing off the review approves every task in the plan, as executing it does: the seam records that before it acts. */
383
+ private prepareApproveReview;
384
+ private prepareMarkComplete;
385
+ private prepareMarkNotDone;
386
+ private prepareInterrupt;
387
+ private prepareCancelResume;
388
+ private prepareCheckpoint;
285
389
  private prepareContinue;
286
390
  /**
287
391
  * A pause is not a failure: the attempt stopped on its account's limit, so
@@ -292,6 +396,13 @@ declare class ExecutionCommands {
292
396
  private prepareStop;
293
397
  private prepareStopRun;
294
398
  private prepareMessage;
399
+ /** A message queued behind the running turn, sent now or taken back; one delivered meanwhile is past either. */
400
+ private prepareQueued;
401
+ /**
402
+ * The user answers a runner's request as its card does. Anyone else only
403
+ * narrows or hands it on: allowing would widen the mode the plan gave the
404
+ * task (ADR-0029).
405
+ */
295
406
  private prepareAnswer;
296
407
  private prepareMergeAll;
297
408
  /**
@@ -326,1150 +437,10 @@ declare class ExecutionCommands {
326
437
  private located;
327
438
  /** A reference read against the plan as the held edit leaves it, where the handles its batch gives come first. */
328
439
  private locateAsEdited;
329
- /** A reference as a caller writes one — see {@link resolveTaskRef}. */
440
+ /** A reference as a caller writes one — see {@link resolveTaskRef}; the user's as they type one ({@link resolveUserTaskRef}). */
330
441
  private locate;
331
442
  }
332
443
 
333
- /**
334
- * The planner's read channel: a `{"taskQuery":{...}}` reply asking Ordewell to
335
- * show it what the per-turn plan block deliberately leaves out.
336
- *
337
- * The plan block carries only short fields, so a planner asked to "add a line
338
- * to task 3's prompt" would be rewriting text it has never read. Inlining every
339
- * body on every turn was rejected — a twenty-task plan re-sends thousands of
340
- * tokens per message — so the planner asks instead, and pays for the detail
341
- * only on the turns it uses it.
342
- *
343
- * This module owns the wire shape and the rendering of the answer; the loop,
344
- * the budgets and the injection live in the Session, and the classification
345
- * lives in PlanRepair alongside the plan and task-ops envelopes.
346
- */
347
- /** The long fields a query may ask for. Everything else is already in the plan block. */
348
- declare const TASK_QUERY_FIELDS: readonly ["description", "prompt", "userSteps", "verdict", "outputSummary", "userStoriesCovered", "skills", "output"];
349
- type TaskQueryField = typeof TASK_QUERY_FIELDS[number];
350
- /** Lines a `output` read returns when the query sets no count. */
351
- declare const OUTPUT_LINES_DEFAULT = 80;
352
- /** Hard cap on `outputLines` — the tail read is bounded however the planner asks. */
353
- declare const OUTPUT_LINES_MAX = 400;
354
- /**
355
- * Character budget the rendered answer converges to. A running output tail is
356
- * the only part with no natural bound — a runner may print for minutes — so
357
- * the tail absorbs the clamp; everything else (task bodies, the catalog cap)
358
- * is bounded upstream.
359
- */
360
- declare const TASK_QUERY_ANSWER_MAX_CHARS = 20000;
361
- interface TaskQuery {
362
- /** Task references to read in full, as {@link resolveTaskRef} reads them. */
363
- tasks: string[];
364
- /** Narrows what comes back. Omitted means every field in {@link TASK_QUERY_FIELDS}. */
365
- fields?: TaskQueryField[];
366
- /** Also return the full runner/model catalog with labels, variants and mode descriptions. */
367
- catalog: boolean;
368
- /** Line count for `output` reads. Clamped at render to [1, {@link OUTPUT_LINES_MAX}]. */
369
- outputLines?: number;
370
- /** A previous answer's next offset: the output tail then covers only what came after it. */
371
- outputSince?: number;
372
- }
373
- declare function textHasTaskQuery(text: string): boolean;
374
- /** Parse a `{"taskQuery":...}` reply. Throws PlanParseError when the JSON is unusable. */
375
- declare function parseTaskQueryJson(text: string): TaskQuery;
376
- /** Stable identity of a query, so a turn can recognise the planner asking the same thing twice. */
377
- declare function taskQuerySignature(query: TaskQuery): string;
378
- /** What a full-catalog read is answered from. Models arrive already allowlist-filtered. */
379
- interface TaskQueryCatalog {
380
- runners: RunnerId[];
381
- models: Partial<Record<RunnerId, DiscoveredModel[]>>;
382
- modes: Record<RunnerId, RunnerModeInfo[]>;
383
- autonomousDefault: boolean;
384
- }
385
- /**
386
- * Appended when the planner has spent its read budget, or asked the identical
387
- * question twice. The detail still comes back — refusing it would leave the
388
- * planner editing text it has not read, which is the whole failure this
389
- * channel exists to prevent — but the turn is told to land.
390
- */
391
- declare const TASK_QUERY_ANSWER_OR_OPS: string;
392
- /**
393
- * The protocol as the planner is taught it, owned here beside the parser so the
394
- * two cannot drift. Both planner backends get these lines verbatim (ADR-0009):
395
- * a harness planner has no tool loop to reach, so the envelope is the channel.
396
- */
397
- declare const TASK_QUERY_PROTOCOL: string[];
398
- /** The one-line reminder the per-turn plan block carries, so the protocol has a single owner. */
399
- declare const TASK_QUERY_REMINDER = "- To READ a task in full (prompt, user steps, verdict, output, user stories) or the whole model/mode catalog before editing, reply with ONLY {\"taskQuery\":{\"tasks\":[\"<id or #order>\"],\"catalog\":true}} \u2014 it changes nothing, and you then reply again with your ops.";
400
- /**
401
- * Where an `output` read is answered from: the orchestrator's live capture of
402
- * the task's latest attempt. Injected so this module stays planner-side and
403
- * never imports the orchestrator.
404
- */
405
- type LiveOutputLookup = (taskId: string, opts: LiveTailOptions) => LiveTail | null;
406
- /**
407
- * Render the injected answer to one read. Never persisted — it is context for
408
- * the planner's next reply and nothing else — so it is built fresh from live
409
- * state on every query rather than cached. `liveOutput` backs the `output`
410
- * field; omitting it reads as "nothing captured".
411
- */
412
- declare function renderTaskQueryAnswer(query: TaskQuery, tasks: readonly Task[], catalog: TaskQueryCatalog, liveOutput?: LiveOutputLookup): string;
413
-
414
- /**
415
- * The SSRF guard for anything that leaves the machine on the planner's behalf.
416
- * An unguarded `http://169.254.169.254/` would hand a prompt-injected planner
417
- * the cloud metadata credentials, so every outbound host is vetted here: IP
418
- * literals by range, names by every address DNS answers with.
419
- */
420
- type ResolveAll = (host: string) => Promise<string[]>;
421
-
422
- /**
423
- * The one decision every planner web read goes through, whichever runner
424
- * plans and whichever path carries the bytes (ADR-0027). A fetch names the
425
- * URL it reads; a search names its query and the provider that receives it.
426
- */
427
- type WebRead = {
428
- kind: 'fetch';
429
- url: string;
430
- }
431
- /** `provider` keys the grant; `providerLabel`, when given, names it to the person asked. */
432
- | {
433
- kind: 'search';
434
- query: string;
435
- provider: string;
436
- providerLabel?: string;
437
- note?: string;
438
- };
439
- /** A refusal's `reason` is written for the planner: it says what to do next. */
440
- type WebReadDecision = {
441
- allowed: true;
442
- } | {
443
- allowed: false;
444
- reason: string;
445
- };
446
- interface DecideOptions {
447
- /**
448
- * Research subagents run in the background where nothing can prompt
449
- * (ADR-0008, T6). When false, a read that would ask is refused with the gap
450
- * to report, rather than raising a prompt nobody can answer.
451
- */
452
- prompting?: boolean;
453
- }
454
- interface WebReadPolicyOptions {
455
- approval: IApproval;
456
- /** The secrets Ordewell holds — provider keys, MCP tokens — read per request. */
457
- secrets?: () => Iterable<string>;
458
- /** Defaults to `process.env`; its credential-named values are secrets too. */
459
- env?: () => Record<string, string | undefined>;
460
- /** How many web reads one planner turn may make; read per request, so a settings change applies at once. */
461
- budget?: () => number;
462
- /** The planner turn in progress; a new one starts a new budget. */
463
- turn?: () => string | undefined;
464
- /** Override DNS resolution (every answer for a name) in tests. */
465
- resolveHost?: ResolveAll;
466
- }
467
- declare const DEFAULT_WEB_READ_BUDGET = 40;
468
- declare class WebReadPolicy {
469
- private readonly approval;
470
- private readonly resolveHost;
471
- private readonly secrets;
472
- private readonly env;
473
- private readonly budget;
474
- private readonly turn;
475
- private spent;
476
- constructor(opts: WebReadPolicyOptions);
477
- /**
478
- * Every check that needs no network runs first, then the budget and the
479
- * approval, and only then is a fetch's host resolved: a lookup is itself a
480
- * request, so `<data>.attacker.example` would leave through DNS even for a
481
- * read that is then excluded, over budget or declined.
482
- */
483
- decide(read: WebRead, opts?: DecideOptions): Promise<WebReadDecision>;
484
- private subagentRefusal;
485
- /**
486
- * Whether a search through `provider` would run without asking anyone, for
487
- * a runner whose own search raises no request and is set on or off when
488
- * its session starts (ADR-0027). A zero budget disables it; no query is
489
- * known yet, so secrets and individual reads cannot be checked or counted.
490
- */
491
- searchesUnasked(provider: string): boolean;
492
- /** The last check, the one that leaves the machine: every DNS answer for the host must be public. */
493
- private resolved;
494
- private refuse;
495
- private vetFetch;
496
- private vetSearch;
497
- private spend;
498
- private refund;
499
- private carriesSecret;
500
- }
501
-
502
- interface IWebFetcher {
503
- /**
504
- * The web-read policy's decision (ADR-0027), for a read this fetcher will
505
- * carry or one a runner carries natively, such as Claude's `WebFetch`.
506
- * `fetch` and `search` decide for themselves; never call both.
507
- */
508
- decide(read: WebRead): Promise<WebReadDecision>;
509
- /** Whether a runner's own search through `provider` would run unasked; see `WebReadPolicy.searchesUnasked`. */
510
- searchesUnasked?(provider: string): boolean;
511
- fetch(url: string): Promise<ToolOutcome>;
512
- /**
513
- * Optional web search. A fetcher without it makes the `web_search` tool
514
- * report itself unavailable rather than failing the turn — the same
515
- * degradation `fetch` already uses when no fetcher is wired at all.
516
- */
517
- search?(query: string): Promise<ToolOutcome>;
518
- /**
519
- * A variant of this fetcher for a research subagent: same policy, cache,
520
- * transport and budget, but a read that would ask a person is refused
521
- * instead (ADR-0027, ADR-0008 T6). Absent means subagents get no web tools
522
- * and the `fetch`/`web_search` tools report themselves unavailable.
523
- */
524
- forSubagent?(): IWebFetcher;
525
- }
526
-
527
- /**
528
- * A person's approvals of project MCP servers, kept across sessions
529
- * (ADR-0028). Keyed by repository root and scope, which names the server
530
- * and hashes its definition: a changed definition is a new question.
531
- */
532
- interface ProjectServerApprovals {
533
- approved(repoRoot: string, scope: string): boolean;
534
- remember(repoRoot: string, scope: string): void;
535
- }
536
-
537
- interface CliAgentAiServiceDeps extends Partial<AgentProcessDeps> {
538
- /** Overrides adapter construction. Tests supply a fake agent; production picks by runner id. */
539
- createAdapter?: (runner: string, deps: AgentProcessDeps) => AgentAdapter | null;
540
- /** Workspace root the agent explores. Defaults to the host process's cwd. */
541
- workspaceRoot?: () => string;
542
- /** Where a conversation's planner tools are served (ADR-0022). Absent: no conversation can start. */
543
- mcpServer?: OrdewellMcpServer;
544
- /** A person's approvals of project MCP servers, kept across sessions (ADR-0028). Defaults to Ordewell's data directory. */
545
- projectServerApprovals?: ProjectServerApprovals;
546
- }
547
- /**
548
- * A coding agent driven as Ordewell's planner (ADR-0009).
549
- *
550
- * The second transport behind `IAiService`, sitting beside `OpenAiService` and
551
- * `GeminiService`. It deliberately does **not** extend {@link BaseAiService}:
552
- * that class's body is Ordewell executing research tools on a model's behalf,
553
- * which is precisely the part a coding agent replaces. What it reuses instead
554
- * is everything above the transport — `settleReply` (the empty-reply nudge
555
- * and the turn's settling), the plan validator, the `ResearchProgress` events the
556
- * four surfaces already render. That is why this backend reaches VS Code, the
557
- * web UI, the CLI and the TUI without any of them learning a coding agent is
558
- * on the other end.
559
- */
560
- declare class CliAgentAiService implements IAiService {
561
- private config;
562
- private readonly runner;
563
- private readonly processDeps;
564
- private readonly makeAdapter;
565
- private readonly workspaceRoot;
566
- private environmentSecrets;
567
- /** The conversation's session, from its start until the next session boundary. */
568
- private session;
569
- private readonly mcpServer;
570
- private readonly projectServerApprovals;
571
- private activeAbort;
572
- constructor(config: IConfig, deps?: CliAgentAiServiceDeps);
573
- hasActiveConversation(): boolean;
574
- /**
575
- * False when the running agent process was spawned under a model/effort the
576
- * user has since changed in the picker. Unlike a vendor backend, the model
577
- * is a spawn-time argument to the agent CLI (`--model`), not a per-request
578
- * field — `continueConversation` sends the next turn to whatever process is
579
- * already running, so a plain config read here would silently keep planning
580
- * on the old model. No conversation yet is vacuously "current".
581
- */
582
- conversationMatchesConfig(): boolean;
583
- /** Always: a conversation whose planner cannot reach the Ordewell MCP server fails to start rather than falling back to the envelopes. */
584
- plannerToolsAttached(): boolean;
585
- /** The agent's own session id, a resumption hint only — Ordewell's transcript is authoritative (T4). */
586
- nativeSessionId(): string | null;
587
- heldSecrets(): readonly string[];
588
- restart(): void;
589
- reset(): void;
590
- startConversation(req: ConversationRequest): Promise<ConversationTurn>;
591
- continueConversation(userMessage: string, onProgress: (progress: ResearchProgress) => void, signal?: AbortSignal): Promise<ConversationTurn>;
592
- private openingMessage;
593
- /**
594
- * Drive one user message to a settled planner turn through
595
- * {@link settleReply}, the loop the API backend settles through too. The
596
- * tool rounds belong to the agent now, so one call is one agent turn —
597
- * continued while it left subagents running in the background.
598
- */
599
- private runConversation;
600
- /**
601
- * Run one agent turn: stream its events into `ResearchProgress`, collect the
602
- * reply text, and return the research steps produced. Tool calls are matched
603
- * to their results by the agent's own call id — matching by tool name puts
604
- * one file's body on another file's row the moment an agent runs two reads
605
- * at once, which all three of these do routinely.
606
- */
607
- /**
608
- * A turn, and then — while the agent ended it with subagents still in the
609
- * background — a turn asking it to wait for them: whatever they find would
610
- * otherwise be said into a closed turn, or, in a one-shot, into a session
611
- * already thrown away. Bounded by `waits`, shared across a reply's sends.
612
- */
613
- private runTurnAwaitingAgents;
614
- private runTurn;
615
- /**
616
- * The only way this service starts an agent, and it takes a planner start by
617
- * type: the read-only boundary (ADR-0008/0009) cannot be crossed into task
618
- * mode from here without changing this signature. A planner that cannot
619
- * reach its tools cannot submit a plan, so it is respawned once and never
620
- * left to plan without them (ADR-0025).
621
- */
622
- private startAdapter;
623
- private newAdapter;
624
- /** Whether the web-read policy lets the runner's own session-wide search run unasked now (ADR-0027). */
625
- private nativeSearch;
626
- /**
627
- * The live agent process, restarted from its own session id if it died
628
- * between turns. Resume is a hint: when it fails, the caller's next
629
- * `startConversation` reseeds from Ordewell's transcript, which is the same
630
- * degradation `restoreChat` already performs on every surface.
631
- *
632
- * Also restarted when the policy's decision on the runner's own search has
633
- * changed since the process was spawned: the running thread would otherwise
634
- * keep searching unasked after Full turned Guarded. Codex 0.162 applies
635
- * `thread/resume`'s `config.web_search` either way (checked live).
636
- */
637
- private ensureAdapter;
638
- /**
639
- * What the planner gets of the repository's config, its runner being
640
- * launched without it (ADR-0028): the instruction files, which are text, in
641
- * its system prompt, and the project MCP servers it may start. Decided once
642
- * per conversation, so a respawn starts the same servers without asking.
643
- */
644
- private repoConfig;
645
- private plannerModel;
646
- /**
647
- * A plan from one-shot agent sessions: only what a submit_plan call
648
- * accepted, never the reply text, so a plan the tool refused cannot come
649
- * back in as the JSON the agent echoed. A session that ends without an
650
- * accepted call is followed by one corrective session, then fails.
651
- */
652
- private submittedPlan;
653
- /**
654
- * A single agent session that answers one prompt and exits. Used by every
655
- * non-conversational entry point. A plan-shaped envelope in its text
656
- * streams to the plan display, as a vendor planner's one-shot does, and the
657
- * prose around it is not streamed at all, since no turn is open to show it in.
658
- */
659
- private oneShot;
660
- researchAndPlan(userDescription: string, runners: RunnerId[], modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>, fs: IFileSystem, onProgress: (progress: ResearchProgress) => void, _fetcher?: IWebFetcher, runnerModes?: Record<RunnerId, RunnerModeInfo[]>, modes?: PlannerModes, signal?: AbortSignal): Promise<{
661
- tasks: Task[];
662
- researchLog: ResearchLogEntry[];
663
- researchResults: string;
664
- }>;
665
- generatePlanDirect(userDescription: string, runners?: RunnerId[], modelsByRunner?: Partial<Record<RunnerId, DiscoveredModel[]>>, onToken?: (token: string) => void, _fs?: IFileSystem, _fetcher?: IWebFetcher, runnerModes?: Record<RunnerId, RunnerModeInfo[]>, modes?: PlannerModes, signal?: AbortSignal): Promise<Task[]>;
666
- }
667
-
668
- /**
669
- * The planner tools a session offers its conversation (ADR-0022). A backend
670
- * that can inject the Ordewell MCP server into its planner issues a planner
671
- * token bound to `sessionId` and these handlers; API planners use the same task read handlers directly in their tool loop.
672
- */
673
- interface PlannerToolsOffer {
674
- sessionId: string;
675
- handler: PlannerToolHandler;
676
- }
677
- /**
678
- * Everything the conversation loop needs to start planning (ADR-0002).
679
- * The AI service keeps the tool-use message history internally across turns;
680
- * the surfaces only exchange user/assistant messages with it.
681
- */
682
- interface ConversationRequest {
683
- goal: string;
684
- runners: RunnerId[];
685
- modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>;
686
- fs: IFileSystem;
687
- onProgress: (progress: ResearchProgress) => void;
688
- fetcher?: IWebFetcher;
689
- runnerModes?: Record<RunnerId, RunnerModeInfo[]>;
690
- autonomousDefault?: boolean;
691
- /** Where tasks will run (ADR-0013, ADR-0014): in worktrees, the prompt drops file-overlap ordering and describes a repo group. */
692
- isolatedExecution?: IsolatedExecution;
693
- /** A plan the planner submits starts at once (ADR-0030), so the opening prompt has no outline gate. */
694
- autorun?: boolean;
695
- signal?: AbortSignal;
696
- /**
697
- * Persisted dialogue to seed a resumed conversation (session reload). The
698
- * turns are injected into the message history verbatim — no LLM call happens
699
- * for them; the first call is the turn opened by `initialMessage`.
700
- */
701
- priorHistory?: ConversationMessage[];
702
- /**
703
- * The user message that opens this turn. Defaults to `goal` — set it when
704
- * resuming so the system prompt keeps the original goal while the turn
705
- * carries the user's new message.
706
- */
707
- initialMessage?: string;
708
- plannerTools?: PlannerToolsOffer;
709
- /**
710
- * The session's approval policy, which a harness planner's own tool
711
- * requests are decided against as the API planner's are (ADR-0026). Absent:
712
- * nobody can be asked, so they are denied.
713
- */
714
- approval?: IApproval;
715
- /**
716
- * The workspace's skill catalog. Every planner is shown the task skills it
717
- * may attach; only one with tools is shown the planner skills, which load
718
- * through `load_skill` alone (ADR-0024).
719
- */
720
- skills?: readonly SkillInfo[];
721
- }
722
- /**
723
- * One planner turn's outcome. The planner talks to the user (`message`),
724
- * commits a full plan (`plan`, a `{tasks:[...]}` JSON object), emits targeted
725
- * task edits (`task_ops`, a `{taskOps:[...]}` JSON object), or asks to read
726
- * task bodies and the full catalog before editing (`task_query`, a
727
- * `{taskQuery:{...}}` JSON object). The Session validates and applies task ops
728
- * atomically and answers queries out of its own state; the AI service only
729
- * parses them.
730
- */
731
- type ConversationTurn =
732
- /**
733
- * `aborted`: the call was stopped, and `text` is only what it had by then.
734
- * `failed`: the call ended without a reply (a dead agent, a usage limit),
735
- * and `text` says why; `usageLimit`, where the runner also said in a field
736
- * whether that was its usage limit (see {@link TurnFailure}).
737
- */
738
- {
739
- kind: 'message';
740
- text: string;
741
- researchLog: ResearchLogEntry[];
742
- aborted?: boolean;
743
- failed?: boolean;
744
- usageLimit?: UsageLimitSignal | false;
745
- } | {
746
- kind: 'plan';
747
- tasks: Task[];
748
- text: string;
749
- researchLog: ResearchLogEntry[];
750
- } | {
751
- kind: 'task_ops';
752
- ops: TaskOp[];
753
- text: string;
754
- researchLog: ResearchLogEntry[];
755
- } | {
756
- kind: 'task_query';
757
- query: TaskQuery;
758
- text: string;
759
- researchLog: ResearchLogEntry[];
760
- };
761
- interface IAiService {
762
- /** Credential values from the harness planner's effective environment, for the outbound secret check. */
763
- heldSecrets?(): readonly string[];
764
- /**
765
- * Begin the planner conversation: collect workspace context, run the
766
- * research/tool loop, and return the first planner turn. The service
767
- * retains the full API message history for subsequent
768
- * {@link continueConversation} calls.
769
- */
770
- startConversation(req: ConversationRequest): Promise<ConversationTurn>;
771
- /**
772
- * Feed the user's reply into the active conversation and return the next
773
- * planner turn. Throws if no conversation is active.
774
- */
775
- continueConversation(userMessage: string, onProgress: (progress: ResearchProgress) => void, signal?: AbortSignal): Promise<ConversationTurn>;
776
- /**
777
- * Whether a planner conversation is currently held in memory. A branching
778
- * predicate only — never a test for whether there is anything to release.
779
- * The harness backend holds an OS process that outlives its conversation, so
780
- * guarding {@link reset} with this leaks an agent process; call `reset`
781
- * unconditionally instead.
782
- */
783
- hasActiveConversation(): boolean;
784
- /**
785
- * Optional: end the planner's live process but keep its own session id, so
786
- * the next turn starts a fresh one resumed from it (ADR-0009). What a
787
- * supervisor does to a planner that crashed or hung (ADR-0029). Absent where
788
- * there is no process to end: the next call is a fresh request anyway.
789
- */
790
- restart?(): void;
791
- /**
792
- * Whether the active conversation (if any) still matches the planner
793
- * model/effort currently configured. Optional, and true when absent: a
794
- * vendor backend re-reads its model from config on every turn, so there is
795
- * nothing to drift. A harness planner (ADR-0009) is the exception — its
796
- * model is baked into the agent process at spawn — so only
797
- * {@link CliAgentAiService} answers this for real. False tells the caller
798
- * the same thing an absent conversation would: don't call
799
- * `continueConversation`, restart instead so the new model takes effect.
800
- */
801
- conversationMatchesConfig?(): boolean;
802
- /**
803
- * Whether the planner reads the catalog and submits plans through Ordewell's
804
- * MCP tools (ADR-0022). Optional, and false when absent: only a harness
805
- * planner the server attached to has them, and every other planner keeps
806
- * the per-turn catalog and the envelopes.
807
- */
808
- plannerToolsAttached?(): boolean;
809
- /**
810
- * Optional: prune bulky raw tool output out of the live conversation, in
811
- * place, keeping subagent digests. Returns the characters removed. Absent
812
- * where the backend holds no tool transcript to prune (a harness planner's
813
- * agent owns its own context).
814
- */
815
- pruneContext?(): number;
816
- /**
817
- * One-shot research + plan for non-conversational surfaces (CLI `plan --goal`,
818
- * web REST). Never asks questions — it plans with what it can find.
819
- */
820
- researchAndPlan(userDescription: string, runners: RunnerId[], modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>, fs: IFileSystem, onProgress: (progress: ResearchProgress) => void, fetcher?: IWebFetcher, runnerModes?: Record<RunnerId, RunnerModeInfo[]>, modes?: PlannerModes, signal?: AbortSignal): Promise<{
821
- tasks: Task[];
822
- researchLog: ResearchLogEntry[];
823
- researchResults: string;
824
- }>;
825
- generatePlanDirect(userDescription: string, runners: RunnerId[], modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>, onToken?: (token: string) => void, fs?: IFileSystem, fetcher?: IWebFetcher, runnerModes?: Record<RunnerId, RunnerModeInfo[]>, modes?: PlannerModes, signal?: AbortSignal): Promise<Task[]>;
826
- /**
827
- * Release everything this service holds: the conversation, any in-flight
828
- * turn, and — on the harness backend — the agent process itself. Idempotent
829
- * and cheap in every implementation, so callers never gate it.
830
- */
831
- reset(): void;
832
- }
833
- /**
834
- * The single branch point between planner transports. Two of them talk HTTP to
835
- * an LLM vendor; the third (ADR-0009) drives a coding agent already installed
836
- * on the machine. Everything downstream — the plan contract, the research-step
837
- * stream, the four surfaces — is shared.
838
- */
839
- declare function createAiService(config: IConfig, deps?: CliAgentAiServiceDeps): IAiService;
840
-
841
- /** The dialogue record a fork carries — what {@link PlannerConversation.clone} returns. */
842
- interface ForkedDialogue {
843
- conversationHistory: ConversationMessage[];
844
- researchLog: ResearchLogEntry[];
845
- }
846
-
847
- /** A user's message as sent, and the skills its `/name` tokens load. */
848
- interface SkillInvocation {
849
- text: string;
850
- skills: SkillLoad[];
851
- }
852
- /**
853
- * A path with the home directory written `~`, for a notice or listing. On
854
- * Windows the drive letter's case is not significant and the separators are
855
- * rendered `/`, so the same skill reads alike wherever the session reloads.
856
- */
857
- declare function abbreviateHome(file: string, home?: string, platform?: NodeJS.Platform): string;
858
- /**
859
- * Resolve the `/skill-name` tokens in a message to snapshots of the skills they
860
- * name. Pure and exported so surfaces can resolve without a full Session. The text itself is never rewritten.
861
- *
862
- * Every distinct skill named loads, once, in the order first named. A token
863
- * naming no skill is plain text, wherever it stands — a whole message of
864
- * `/unknown` included: embedded in a sentence it is as likely to be incidental
865
- * text (a path, an example command) as a typo'd invocation. So is one naming
866
- * a skill with `user-invocable: false`, which only the model may invoke.
867
- */
868
- declare function resolveSkillInvocation(text: string, skillsService: Pick<SkillsService, 'findSkill'>, home?: string): SkillInvocation;
869
- /**
870
- * What the planner receives for a user message: the bodies of the skills it
871
- * loaded, then the message as the user wrote it. The one composition for a
872
- * live send and for a replay of the transcript, so a resumed conversation
873
- * hands the planner what the live one did.
874
- */
875
- declare function plannerMessage(text: string, loads: readonly SkillLoad[]): string;
876
- /**
877
- * The transcript as the planner is to see it on a replay: each user message
878
- * carries the skills loaded after it, composed by {@link plannerMessage}, and
879
- * the load entries themselves are folded away. Sessions saved before skill
880
- * loads existed hold the expanded text in the message and replay unchanged.
881
- * A failed turn's entry is left out: the planner never said it.
882
- */
883
- declare function plannerTranscript(history: readonly ConversationMessage[]): ConversationMessage[];
884
-
885
- /**
886
- * What a stopped runner says about why it stopped, and what ending an attempt
887
- * does to the runner it leaves behind. TaskOrchestrator acts on the answers.
888
- */
889
-
890
- /**
891
- * A turn's failure as its runner reported it: its words, and `usageLimit`
892
- * where its protocol also says in a field whether it was the limit (Codex's
893
- * `codexErrorInfo`).
894
- */
895
- interface TurnFailure {
896
- message: string;
897
- usageLimit?: UsageLimitSignal | false;
898
- }
899
-
900
- /** What a usage limit that ended a planner turn led to. */
901
- interface PlannerLimit {
902
- /** The line the transcript keeps, drawn as a failure. */
903
- text: string;
904
- /** When what the turn took goes again on its own; null when it waits for the user's next message. */
905
- resendAt: Date | null;
906
- /** Why nothing goes again on its own, when nothing does: what the planner did, as in "the planner hit its usage limit and …". */
907
- held?: string;
908
- }
909
-
910
- /**
911
- * The planner turn (see CONTEXT.md): the one piece of planner work that holds
912
- * the conversation, from its claim to its settled outcome. Its signal is what a
913
- * stop aborts.
914
- */
915
- declare class PlannerTurn {
916
- private readonly controller;
917
- private dropped;
918
- private forced;
919
- get signal(): AbortSignal;
920
- /** The plan it was answering was dropped under it, so nothing it settles may land. */
921
- get abandoned(): boolean;
922
- /** A force send stopped it, not the user: the forced turn follows. A turn already stopped stays the user's stop. */
923
- get forceSent(): boolean;
924
- stop(by?: 'force_send'): void;
925
- abandon(): void;
926
- }
927
- /** What a planner turn may change: the whole plan, or tasks through edits. */
928
- type PlanChange = 'plan' | 'edit';
929
- /**
930
- * A wake-up turn (ADR-0029): a supervisor's turn on wake-up digests alone, no
931
- * user message in it. What it may do is its level's, decided by the host.
932
- */
933
- interface WakeUpTurn {
934
- /** Reads the turn may make, in place of a user message's caps. */
935
- readBudget: number;
936
- /**
937
- * Why a whole plan, or a task edit, is refused in this turn, worded for the
938
- * model; undefined when its level allows it. Asked when the change is made
939
- * and again as it lands, so a level lowered during the turn applies at once.
940
- */
941
- refused(change: PlanChange): string | undefined;
942
- /** Told to the model after the digests and never saved, like the plan block: who it is in this turn, and what it may do. */
943
- brief?: string;
944
- }
945
- /** 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. */
946
- type ToolRead<T> = {
947
- status: 'refused';
948
- } | {
949
- status: 'answered';
950
- value: T;
951
- landNow: boolean;
952
- };
953
- /** A plan or task edit handed to the open turn over a channel other than its reply (ADR-0022). */
954
- type PlannerSubmission = {
955
- kind: 'plan';
956
- tasks: Task[];
957
- } | {
958
- kind: 'task_ops';
959
- ops: TaskOp[];
960
- };
961
- /** Everything (re)opening a conversation needs besides the dialogue itself. */
962
- type ConversationOpening = Omit<ConversationRequest, 'goal' | 'onProgress' | 'signal' | 'priorHistory' | 'initialMessage'>;
963
- /**
964
- * What the conversation needs from the session that hosts it. The session keeps
965
- * plan state, persistence and scheduling; the conversation reaches them only
966
- * through here, so a fake host is enough to drive it.
967
- */
968
- interface PlannerConversationHost {
969
- /** The plan the transcript lives on (`conversationHistory`, `researchLog`). */
970
- plan(): LegacyPlanState | null;
971
- goal(): string;
972
- aiService(): IAiService;
973
- onProgress(progress: ResearchProgress): void;
974
- /** Fresh discovery, allowlist-filtered the way the system prompt shows it. */
975
- opening(runners: RunnerId[]): Promise<ConversationOpening>;
976
- /** What the per-turn catalog block and every read draw from, as of now. */
977
- catalog(): TaskQueryCatalog;
978
- /** Whether autorun is on now (ADR-0030); the planner is told in the turn after it changes. */
979
- autorun(): boolean;
980
- /** The enabled runners and allowlist as of now; the planner is told in the turn after either changes. */
981
- assignmentScope(): AssignmentScope;
982
- /** Run updates no turn has carried yet, taken by the turn that asks: the next turn is told, whoever opens it. */
983
- takeRunUpdates(): string[];
984
- tasks(): readonly Task[];
985
- /**
986
- * The orchestrator's live capture for a task's latest attempt, backing the
987
- * `output` field of a read. Injected the same way as the catalog so the
988
- * conversation never reaches into execution state directly.
989
- */
990
- liveOutput: LiveOutputLookup;
991
- isExecuting(): boolean;
992
- /** The session's single mutation ritual: op → persist → notify (default: the plan). */
993
- mutate(op: () => boolean, notify?: () => void): LegacyPlanState | null;
994
- broadcast: SessionBroadcaster;
995
- /** `turnId`: the planner turn whose commit this is, when one is. */
996
- broadcastPlan(turnId?: string): void;
997
- /** Validate a batch against live state. Pure — nothing is applied. */
998
- validateOps(ops: TaskOp[]): ApplyTaskOpsResult;
999
- /** The workspace's skill catalog under its repo group's layout, which the skills a plan or edit attaches are checked against. */
1000
- taskSkills(): SkillLookup;
1001
- /** Tell the user something the transcript does not carry. */
1002
- notice(level: SessionNotice['level'], message: string): void;
1003
- /** Load planner-produced tasks: an edit keeps run state, a commit starts over. Returns how many landed. */
1004
- adoptTasks(tasks: readonly Task[], how: 'edit' | 'commit'): number;
1005
- capturePrd(text: string): void;
1006
- /** The planner turn was stopped or abandoned: whatever it is waiting on (a parked approval) will never be answered. */
1007
- turnAborted(): void;
1008
- /** A planner turn let the conversation go, so the next one may take it. */
1009
- turnReleased?(): void;
1010
- /** A turn ended on the planner's usage limit, which it reported as `said`: what is sent again, and when. */
1011
- usageLimit(said: TurnFailure): PlannerLimit;
1012
- }
1013
- /**
1014
- * A conversation edit (rewind, fork) the conversation refused because the
1015
- * request itself is wrong — no such message, nothing to fork. Transport-agnostic
1016
- * like `PlanEditError`: a route maps it to a status, core carries none.
1017
- */
1018
- declare class ConversationEditError extends Error {
1019
- constructor(message: string);
1020
- }
1021
- /**
1022
- * Refused because a planner turn is in flight. Distinct from a bad request:
1023
- * the same call succeeds once the turn settles, and the turn would otherwise
1024
- * append its reply to a transcript that no longer holds the message it
1025
- * answers.
1026
- */
1027
- declare class ConversationBusyError extends ConversationEditError {
1028
- constructor(operation: string);
1029
- }
1030
- /**
1031
- * A planner turn that settled after the plan it was answering was dropped (a
1032
- * new plan, another session adopted, the session closed), or after its call
1033
- * was cut off by something other than its own stop. Nothing it produced was
1034
- * committed: landing it would write one session's reply into another's
1035
- * transcript.
1036
- */
1037
- declare class PlannerTurnDiscardedError extends Error {
1038
- constructor(options?: ErrorOptions);
1039
- }
1040
- /**
1041
- * A wake-up turn's backend ended without a reply — a dead agent, a usage
1042
- * limit — and said why. A user's turn shows that in the chat instead; a
1043
- * wake-up has nobody waiting on it, so its supervisor is told (ADR-0029).
1044
- */
1045
- declare class PlannerTurnFailedError extends Error {
1046
- constructor(message: string);
1047
- }
1048
- /**
1049
- * A planner turn ended on the planner's usage limit (#84). Nothing of it
1050
- * lands: the transcript keeps the failure instead, out of the planner's own
1051
- * dialogue, and the turn's inputs wait to be sent again — at `resendAt`, or
1052
- * with the next input when it is null.
1053
- */
1054
- declare class PlannerUsageLimitError extends PlannerTurnFailedError {
1055
- readonly resendAt: Date | null;
1056
- /** Why nothing is sent again on its own, when nothing is. */
1057
- readonly held?: string;
1058
- constructor(limit: PlannerLimit);
1059
- }
1060
- /**
1061
- * The user stopped the turn and the backend reported that as a thrown error
1062
- * rather than a settled reply. Typed so a surface can stay quiet for it without
1063
- * reading the message: some SDKs' abort errors are plain `Error`s by name, and
1064
- * a real failure can mention "aborted" too.
1065
- */
1066
- declare class PlannerTurnStoppedError extends Error {
1067
- constructor(options?: ErrorOptions);
1068
- }
1069
- /** A user message a rewind can fork the conversation from just before. */
1070
- interface RewindTarget {
1071
- /** Position in the transcript — what {@link PlannerConversation.cloneBefore} takes. */
1072
- index: number;
1073
- preview: string;
1074
- /** The whole message — the preview cuts it to one line, and a surface confirming the rewind quotes it. */
1075
- content: string;
1076
- timestamp: string;
1077
- }
1078
- /** What a rewind forks from, and the message it lands just before. */
1079
- interface RewoundDialogue {
1080
- dialogue: ForkedDialogue;
1081
- rewoundMessage: string;
1082
- }
1083
- /** What a compaction left behind. */
1084
- interface ConversationCompaction {
1085
- summary: string;
1086
- /** Transcript entries kept verbatim after the summary entry. */
1087
- keptMessages: number;
1088
- }
1089
- /** A point a failed turn returns the dialogue to. Opaque outside this module. */
1090
- interface TranscriptSnapshot {
1091
- readonly plan: LegacyPlanState;
1092
- readonly history: ConversationMessage[] | undefined;
1093
- readonly researchLog: ResearchLogEntry[] | undefined;
1094
- readonly persisted: number;
1095
- readonly savedInBackground: number;
1096
- /** What the planner had been told of the user's runners when the turn began. */
1097
- readonly toldScope: AssignmentScope | null;
1098
- }
1099
- interface ReplyOptions {
1100
- signal?: AbortSignal;
1101
- /**
1102
- * Run inside the mutation that lands the turn's outcome — the edit, the
1103
- * plan or the reply — so what the caller drops with it (the inputs the turn
1104
- * answered) leaves disk in the same save.
1105
- */
1106
- onCommit?: () => void;
1107
- /** Wake-up digests delivered with this turn (ADR-0029), oldest first, ahead of the user's message if there is one. */
1108
- wakeUps?: readonly string[];
1109
- /** Set when the turn is a wake-up's alone. */
1110
- wakeUp?: WakeUpTurn;
1111
- /** The user's message was force sent: the turn replaces the one it stopped (ADR-0023). */
1112
- forced?: boolean;
1113
- }
1114
- /**
1115
- * The planner conversation (ADR-0002), end to end: the persisted dialogue
1116
- * record, the live model context behind it, and every turn from the user's
1117
- * message to a settled, persisted outcome.
1118
- *
1119
- * It is the only writer of `conversationHistory` and of the planner's
1120
- * `researchLog`. The writes land on the host's plan object — which is what the
1121
- * session persists and broadcasts — but only ever inside the host's mutation
1122
- * ritual or ahead of a turn that will either reach it or be rolled back.
1123
- *
1124
- * The live model context (a vendor service's message list, a harness planner's
1125
- * process and native session id) is disposable: {@link reset} drops it and the
1126
- * next turn is replayed from the transcript. That is what lets the transcript
1127
- * be replaced by a summary, or copied into a fork, without the model's memory
1128
- * drifting from it.
1129
- */
1130
- declare class PlannerConversation {
1131
- private readonly host;
1132
- /** Bumped on every persist, so a rollback can tell whether its writes already reached disk. */
1133
- private persisted;
1134
- /** Saves an execution event made while a turn may be in flight; see {@link restore}. */
1135
- private savedInBackground;
1136
- /** What the planner was last told of autorun: by its opening prompt, or a turn's block since. */
1137
- private toldAutorun;
1138
- /** The runners and allowlist the planner was last told of: its opening prompt, or a turn's block since. Null until a conversation opens. */
1139
- private toldScope;
1140
- private live;
1141
- private openTurn;
1142
- constructor(host: PlannerConversationHost);
1143
- get transcript(): readonly ConversationMessage[];
1144
- /** Whether a planner turn holds the conversation. */
1145
- get isTurnInFlight(): boolean;
1146
- /** Stop the planner turn in flight; it settles as stopped. False when none is. */
1147
- stopTurn(by?: 'force_send'): boolean;
1148
- /**
1149
- * Drop the planner turn in flight together with the plan it was answering:
1150
- * it is stopped, whatever it settles is discarded, and the conversation is
1151
- * free at once rather than when the abandoned turn finishes unwinding.
1152
- */
1153
- abandonTurn(): void;
1154
- /**
1155
- * Free the conversation from a turn that did not end when stopped — a hung
1156
- * wake-up (ADR-0029). Unlike {@link abandonTurn} the plan stays, so what the
1157
- * turn wrote is taken back as its failure would have, and its end is
1158
- * announced: the turn itself may never get there.
1159
- */
1160
- abandonHungTurn(): void;
1161
- /**
1162
- * Run planner work that is not a conversation turn — one-shot plan
1163
- * generation — as the planner turn, so it is stopped, refused and superseded
1164
- * like one. It starts a fresh plan, so it supersedes the turn in flight.
1165
- */
1166
- hold<T>(signal: AbortSignal | undefined, body: (turn: PlannerTurn) => Promise<T>): Promise<T>;
1167
- /**
1168
- * Take the conversation for a new planner turn. A caller's own signal stops
1169
- * the turn too; the relay is detached on release because a caller may reuse
1170
- * that signal, and a stale listener would stop the next turn with it.
1171
- */
1172
- private claim;
1173
- /** The user turn being answered, for what the host raises during it — an approval the turn's research asks for. */
1174
- get currentTurnId(): string | undefined;
1175
- /** The open turn's wake-up rules, when the supervisor is answering it rather than the user's message. */
1176
- get currentWakeUp(): WakeUpTurn | undefined;
1177
- recordPlannerSkill(skill: SkillLoad): boolean;
1178
- /**
1179
- * Hand the open turn a plan (already validated) or a task edit. The turn
1180
- * settles with it exactly as with the same plan or ops parsed out of its
1181
- * reply, and it wins over any envelope that reply carries. A later
1182
- * submission replaces an earlier one. False when no user turn is open.
1183
- */
1184
- submit(submission: PlannerSubmission): boolean;
1185
- /**
1186
- * Tell surfaces what the turn's edit would leave, from the same applier that
1187
- * lands it, so an edit that would not apply shows nothing. It stays shown
1188
- * from the moment it is handed in until it lands, is refused, or the turn
1189
- * ends: the backend's reply claims it, but it has not landed by then.
1190
- */
1191
- private showHeld;
1192
- private clearHeld;
1193
- /**
1194
- * The edit the open turn already holds, for a further edit in the same reply
1195
- * to join rather than replace. Empty when it holds nothing; null when it holds
1196
- * a whole plan, which an edit cannot be layered on.
1197
- */
1198
- pendingOps(): TaskOp[] | null;
1199
- /**
1200
- * A read the planner makes through a tool. It spends the same per-turn budget
1201
- * as an envelope read, so a planner cannot read more by switching channels:
1202
- * past the soft limit, or on a repeated question, the answer still comes but
1203
- * is told to land the turn; at the hard limit it is refused. `signature`
1204
- * names the question, for the repeat check.
1205
- */
1206
- read<T>(signature: string, answer: () => Promise<T>): Promise<ToolRead<T>>;
1207
- /** Whether the model still holds this conversation in memory. */
1208
- get isActive(): boolean;
1209
- append(role: ConversationMessage['role'], content: string, opts?: {
1210
- timestamp?: string;
1211
- kind?: ConversationMessage['kind'];
1212
- }): void;
1213
- private appendEntries;
1214
- /** Swap the whole transcript. The live context no longer matches it, so pair with {@link reset}. */
1215
- replace(messages: readonly ConversationMessage[]): void;
1216
- snapshot(): TranscriptSnapshot | null;
1217
- /**
1218
- * Put the dialogue back where {@link snapshot} found it — unless anything
1219
- * was persisted since, because then memory already matches disk and every
1220
- * surface, and undoing it would erase work the user has seen land. Also a
1221
- * no-op once a different plan has been adopted.
1222
- *
1223
- * A background save (a task settling mid-turn) wrote the turn's writes to
1224
- * disk without landing anything the user saw, so it does not stop the undo;
1225
- * the undo is saved in turn, or a reload would bring the writes back.
1226
- */
1227
- restore(snapshot: TranscriptSnapshot): boolean;
1228
- /**
1229
- * The host calls this after every persist. `background` is a save no call
1230
- * of the user's made — an execution event landing mid-turn.
1231
- */
1232
- markPersisted(opts?: {
1233
- background?: boolean;
1234
- }): void;
1235
- /**
1236
- * Drop the live model context. Idempotent; the harness backend holds an OS
1237
- * process here, so callers never gate it on {@link isActive}.
1238
- */
1239
- reset(): void;
1240
- /**
1241
- * The user messages a rewind may land before. The opening message is the
1242
- * goal: cutting it leaves a conversation about nothing, which is a new
1243
- * session, not a rewind. After a compaction the summary entry, always
1244
- * first, plays the goal's part: what it replaced is gone, so a rewind stops
1245
- * at it.
1246
- */
1247
- rewindTargets(): RewindTarget[];
1248
- /**
1249
- * Replace the transcript with a summary of it, keeping the last two
1250
- * exchanges as they were, and drop the live context so the next message
1251
- * replays from the shorter record.
1252
- *
1253
- * The summary is one hidden turn through whichever planner is configured —
1254
- * on the live context when it still matches, replayed from the transcript
1255
- * when not — so the compaction is the same for a vendor API and a harness
1256
- * agent. Whatever the turn emits besides the summary is discarded, ops
1257
- * included: it condenses the conversation, never the plan. Nothing is
1258
- * written until the summary is in hand, so a failed or stopped turn leaves
1259
- * the transcript exactly as it was.
1260
- */
1261
- compact(signal?: AbortSignal): Promise<ConversationCompaction>;
1262
- private summarize;
1263
- /**
1264
- * A copy of the dialogue record for a forked session to carry. Refused
1265
- * mid-turn: the copy would hold the user's message without the reply to it.
1266
- * The live context is not part of it — the fork replays from this record on
1267
- * its first turn, like any adopted session.
1268
- */
1269
- clone(): ForkedDialogue;
1270
- /**
1271
- * A copy of the dialogue as it stood just before the user message at
1272
- * `index` (a position from {@link rewindTargets}) — what a rewind forks
1273
- * from — plus that message's full text, for a surface to offer back. This
1274
- * conversation is not touched. The research trace is cut at the same point
1275
- * by time: its entries carry no link to the message that caused them.
1276
- */
1277
- cloneBefore(index: number): RewoundDialogue;
1278
- /**
1279
- * What the skill check of a landed plan or edit warned about, kept in the
1280
- * transcript so every surface shows it, on reload too, and not only the
1281
- * planner that may have read it in a tool result. Call inside the host's
1282
- * mutation ritual, so the broadcast plan carries it.
1283
- */
1284
- recordSkillWarnings(warnings: readonly string[], timestamp?: string): void;
1285
- /**
1286
- * A call at the command seam (ADR-0029), kept where it happened in the
1287
- * dialogue: a reload shows it, and a planner replayed from the transcript
1288
- * knows what was started or stopped, and by whom. Call inside the host's
1289
- * mutation ritual.
1290
- */
1291
- recordExecution(record: ExecutionRecord, text: string): void;
1292
- /**
1293
- * Open the conversation on the host's fresh plan: the goal is its first
1294
- * message. `prepare` (discovery) runs inside the turn, so a stop reaches it.
1295
- */
1296
- start(goal: SkillInvocation, prepare: () => Promise<ConversationOpening>, signal?: AbortSignal): Promise<LegacyPlanState>;
1297
- /**
1298
- * Every later user message, from the transcript append to a persisted
1299
- * outcome. A turn that throws before anything was persisted takes its own
1300
- * writes back out, so session memory never drifts from disk and the UI.
1301
- */
1302
- reply(message: string | SkillInvocation, options?: ReplyOptions): Promise<LegacyPlanState>;
1303
- private replyTurn;
1304
- /**
1305
- * A wake-up turn lands only a real answer. Stopped — by the user, or by its
1306
- * timeout — it lands nothing, not the fragment a backend hands back; a
1307
- * failure is thrown rather than shown, for the supervisor to retry
1308
- * (ADR-0029).
1309
- */
1310
- private assertWakeUpAnswered;
1311
- /**
1312
- * A turn that ended on the planner's usage limit lands nothing. What it
1313
- * wrote is taken back, its failure is kept in its place, and the limit's
1314
- * wait is armed in the same save. Only a turn Ordewell started gets here,
1315
- * so a limit is never acted on for a planner nobody asked anything of.
1316
- */
1317
- private assertNotLimited;
1318
- /**
1319
- * A failure kept in the transcript, drawn as one on reload too, and never
1320
- * replayed to the planner. Stamped after the newest entry: surfaces take
1321
- * only entries newer than the last they synced.
1322
- */
1323
- private recordFailure;
1324
- private assertIdle;
1325
- /**
1326
- * Refuse to land a turn that no longer belongs here: it was abandoned, its
1327
- * plan is no longer the host's, or its call was cut off by something other
1328
- * than its own stop (`IAiService.reset` under a plan change) — what such a
1329
- * call hands back is a fragment of an answer to a dialogue that is gone.
1330
- */
1331
- private assertCurrent;
1332
- /**
1333
- * {@link assertCurrent} on the far side of an await that precedes a plan
1334
- * mutation: the skill check waits on git, and a stop, rewind or session
1335
- * switch in that window drops the edit instead of landing it on whatever
1336
- * plan is current by then. A stop counts here because nothing has landed
1337
- * yet — the user asked for the turn to end before it changed the plan.
1338
- */
1339
- private assertStillCurrent;
1340
- /**
1341
- * A live conversation is only safe to continue in-place when it also
1342
- * matches the model/effort configured right now — a harness planner's
1343
- * running process was spawned with the old one baked in and cannot pick up
1344
- * a switch (ADR-0009). Otherwise the turn resumes from the transcript.
1345
- */
1346
- private canContinueLive;
1347
- /**
1348
- * Bracket one user turn with its start and end, under an id minted here:
1349
- * the turn is where the stream a surface draws begins and ends, and only the
1350
- * conversation sees all of it — every backend call, read and retry.
1351
- */
1352
- private userTurn;
1353
- private requirePlan;
1354
- /** The message as typed, then one entry per skill it loaded, sharing its timestamp so a reload keeps them together. */
1355
- private userEntries;
1356
- private recordUser;
1357
- /**
1358
- * Write the turn's held inputs inside the mutation that lands the outcome —
1359
- * call at its top, before the assistant entries, so the transcript reads in
1360
- * the order it happened and the inputs leave the plan in the save that
1361
- * carried their answer (ADR-0029). They go back where the turn opened, ahead
1362
- * of whatever it wrote as it ran (a planner skill load, a command at the
1363
- * seam), not at the end.
1364
- */
1365
- private commitInput;
1366
- private recordResearch;
1367
- /**
1368
- * Reopen the model context from the transcript — because the in-memory one
1369
- * is gone (session reload, extension restart), or because it is stale
1370
- * against the planner config now in effect. No LLM call happens for the
1371
- * replayed turns; the first call is the one the user's message opens.
1372
- */
1373
- private resume;
1374
- /**
1375
- * Answer every read the planner emits until it says something else.
1376
- *
1377
- * The channel is a text envelope rather than a registered tool because the
1378
- * protocol has to be identical on both planner backends (ADR-0009): Ordewell
1379
- * owns a tool loop only in the API case, and a harness planner running as a
1380
- * coding-agent subprocess can only be reached this way.
1381
- *
1382
- * Draining here — outside {@link repairLoop} — is what keeps reads free of
1383
- * the repair budget. A planner that looks a task up and *then* fumbles its
1384
- * ops JSON still gets its two corrective retries; charging it for the read
1385
- * would cost it the chance to fix the edit.
1386
- */
1387
- private drainTaskQueries;
1388
- /**
1389
- * The backend's answer, with what was handed in while it ran standing in
1390
- * for whatever its reply carried. Taken once: a corrective re-send after a
1391
- * rejected edit must not settle on the same submission again. A stopped
1392
- * turn commits nothing, the same as a stopped reply with a plan in it.
1393
- */
1394
- private claimSubmission;
1395
- /**
1396
- * Render one read out of live state. Never persisted to the transcript: the
1397
- * detail is context for the planner's next reply, and re-sending it on every
1398
- * later turn is exactly the cost this channel exists to avoid.
1399
- */
1400
- private taskQueryAnswer;
1401
- /**
1402
- * Drive a planner turn, its reads already drained, to a persisted, broadcast
1403
- * outcome. Task edits apply atomically; validation failures are fed back to
1404
- * the model for up to 2 silent retries, then surfaced as a message with the
1405
- * plan untouched. The first turn and every later turn route through here —
1406
- * one path, not two.
1407
- */
1408
- private settle;
1409
- /** A name not found at submit only warns: a task the attaching one depends on may create it. */
1410
- private warnSkills;
1411
- /** Validate + commit a task_ops turn atomically. Returns the errors on rejection (plan untouched). */
1412
- private applyTaskOps;
1413
- /** The edit was refused as it landed: what it showed is not coming. */
1414
- private refusedHeld;
1415
- /** Commit a settled (non-task_ops) turn through the host's mutation ritual. */
1416
- private commit;
1417
- /**
1418
- * The autorun rule (ADR-0030) when it changed since the planner was last
1419
- * told — in the opening prompt or an earlier turn — so a switch mid-
1420
- * conversation reaches its next turn without repeating in every one.
1421
- */
1422
- private autorunBlock;
1423
- /**
1424
- * The user's runner or allowlist change, when the planner was not told yet —
1425
- * the same once-per-change rule as autorun, for API and harness planners
1426
- * alike. Tasks the change leaves outside the scope are named, not touched.
1427
- *
1428
- * A reloaded session is not told: {@link resume} takes the scope as it is
1429
- * then, as it does the autorun rule, since the catalog the first turn carries
1430
- * and `list_runners` already show the runners as they are now.
1431
- */
1432
- private runnersChangedBlock;
1433
- private runUpdatesBlock;
1434
- /**
1435
- * The catalog the planner may actually draw from — model ids and task-mode
1436
- * ids, per runner in the plan — emitted on EVERY turn, before any plan exists
1437
- * or after. The system prompt shows this once at conversation start; a long
1438
- * clarifying conversation outlives that single showing and the planner
1439
- * starts misquoting it, so this re-states it per turn instead.
1440
- *
1441
- * The host's catalog is allowlist-filtered, so a restricted allowlist stays a
1442
- * hard bound on every turn, and reads the plan's runners live, so a runner
1443
- * admitted mid-session by a retarget is shown like every other. A planner
1444
- * with Ordewell's tools gets a reminder to read the catalog instead.
1445
- */
1446
- private catalogBlock;
1447
- /**
1448
- * The "you are here" block for post-plan chat: current tasks with stable
1449
- * references, plus the read and edit protocols. Injected per turn (never
1450
- * persisted) so the model always sees live statuses — including which tasks
1451
- * are locked by a running execution.
1452
- */
1453
- private planContextBlock;
1454
- /** One line per task, subtasks indented under their parent, with stable references — null until the plan has tasks. */
1455
- private currentPlanLines;
1456
- }
1457
-
1458
- interface SessionMeta {
1459
- id: string;
1460
- goal: string;
1461
- runners: RunnerId[];
1462
- taskCount: number;
1463
- status: PlanStatus;
1464
- createdAt: string;
1465
- updatedAt: string;
1466
- }
1467
- interface SessionData {
1468
- meta: SessionMeta;
1469
- /** The full plan state — including conversationHistory, prdMarkdown, and queuedMessages. */
1470
- plan: LegacyPlanState;
1471
- }
1472
-
1473
444
  /**
1474
445
  * The policy half of the planner's filesystem: path confinement, the tiered
1475
446
  * `bash` gate, and the definition-first symbol lookup. Adapters supply only the
@@ -1782,78 +753,6 @@ declare class HttpWebFetcher implements IWebFetcher {
1782
753
  private hop;
1783
754
  }
1784
755
 
1785
- /**
1786
- * The bridge between "core needs an answer" and "a human somewhere is looking
1787
- * at a UI". Core cannot prompt: the human may be at a browser, a TUI, a CLI
1788
- * stream, or a VS Code webview, and on the web server they are on the far end
1789
- * of a socket. So the Session parks a promise here, announces the request
1790
- * through the normal broadcast seam, and every surface answers through the
1791
- * same `resolve(id, granted)` call.
1792
- *
1793
- * Timeouts are load-bearing rather than defensive: a planner turn that blocks
1794
- * forever on an unanswered prompt would hang the whole research loop with no
1795
- * visible cause. On expiry the request resolves to denied and the model gets a
1796
- * normal, actionable tool result. A task runner's request is the exception
1797
- * (ADR-0018, A1): the runner is parked on it rather than a research loop, and
1798
- * it waits for a person — or the supervisor — however long that takes.
1799
- */
1800
- interface PendingApproval {
1801
- id: string;
1802
- request: ApprovalRequest;
1803
- createdAt: string;
1804
- }
1805
- interface PendingApprovalsOptions {
1806
- /** Denies and resolves after this long with no answer. Default 5 minutes. */
1807
- timeoutMs?: number;
1808
- /** Announce a new request to the surfaces. */
1809
- onRequest?: (pending: PendingApproval) => void;
1810
- /** Announce that a request is no longer actionable (answered or expired). */
1811
- onSettled?: (id: string, granted: boolean, settled: {
1812
- request: ApprovalRequest;
1813
- decision: ApprovalDecision;
1814
- }) => void;
1815
- }
1816
- interface AskOptions {
1817
- /**
1818
- * An id the caller already announced the request under. One still in use is
1819
- * refused — denied at once — rather than overwriting the request that has it.
1820
- */
1821
- id?: string;
1822
- /** Wait for an answer however long it takes. */
1823
- noTimeout?: boolean;
1824
- /**
1825
- * Given the answer as the request settles, before anything awaiting the
1826
- * promise runs: a caller about to tear down what asked (a runner being
1827
- * stopped) must deliver the answer before it goes.
1828
- */
1829
- onDecision?: (decision: ApprovalDecision) => void;
1830
- }
1831
- declare class PendingApprovals {
1832
- private readonly opts;
1833
- private readonly entries;
1834
- private readonly listeners;
1835
- constructor(opts?: PendingApprovalsOptions);
1836
- /** Hear each new request once it is announced — the supervisor wakes on a runner's (ADR-0029). */
1837
- listen(listener: (pending: PendingApproval) => void): () => void;
1838
- /** Park a request and return the promise the approval policy awaits. */
1839
- ask(request: ApprovalRequest, options?: AskOptions): Promise<boolean>;
1840
- /** Park a request whose answer is more than yes or no. */
1841
- decide(request: ApprovalRequest, options?: AskOptions): Promise<ApprovalDecision>;
1842
- /**
1843
- * Answer one request. Returns false when the id is unknown or already
1844
- * settled. "Allow for this task" on a request that did not offer it is a
1845
- * plain allow: no grant is made that the requester never proposed.
1846
- */
1847
- resolve(id: string, answer: ApprovalAnswer): boolean;
1848
- /** Everything still awaiting an answer — replayed to a surface that connects late. */
1849
- outstanding(): PendingApproval[];
1850
- /**
1851
- * Deny everything in flight, or only the requests `which` picks. Called on
1852
- * abort and on session reset.
1853
- */
1854
- clear(which?: (request: ApprovalRequest) => boolean, note?: string): void;
1855
- }
1856
-
1857
756
  /**
1858
757
  * Carries a structured task's tool requests to the session's one approval
1859
758
  * seam (ADR-0018, A1), so a runner's request is answered through
@@ -2297,6 +1196,8 @@ interface ResearchTurn {
2297
1196
  finishReason?: string;
2298
1197
  /** Exact prompt tokens this turn consumed, when the provider reports usage — drives proactive compaction. */
2299
1198
  promptTokens?: number;
1199
+ /** The segment `text` streamed under, where the provider streams it. */
1200
+ segmentId?: string;
2300
1201
  }
2301
1202
  interface ResearchChat {
2302
1203
  sendMessage(text: string, signal?: AbortSignal): Promise<ResearchTurn>;
@@ -2931,12 +1832,13 @@ declare class ClaudeCodeAdapter extends StdioAgentAdapter implements TaskModeAge
2931
1832
  /** An interrupt was sent during the current turn, so an aborted result is that interrupt, not a failure. */
2932
1833
  private interruptRequested;
2933
1834
  private readonly permissions;
2934
- /** Whether this turn has already emitted reply text — see {@link handleLine}. */
2935
- private turnHasText;
2936
- /** The text block streaming now follows earlier reply text, so its first delta opens the paragraph. */
2937
- private pendingBreak;
2938
- /** The model answering the planner's current message, as its `message_start` named it. */
1835
+ /** This turn's runs of reply text, each a text block named `<message id>:<block index>`. */
1836
+ private replyRuns;
1837
+ /** The model answering the planner's current message, and the message's id, as its `message_start` named them. */
2939
1838
  private plannerModel;
1839
+ private streamingMessage;
1840
+ /** The message the last `assistant` line was part of, and how many of its blocks the lines have carried. */
1841
+ private lineMessage;
2940
1842
  /**
2941
1843
  * The session's `total_cost_usd` as last counted. A resume starts from what
2942
1844
  * was counted of that session before, and is undefined without it: the CLI
@@ -3135,6 +2037,13 @@ declare class ClaudeCodeAdapter extends StdioAgentAdapter implements TaskModeAge
3135
2037
  * complete usage instead.
3136
2038
  */
3137
2039
  private finishForegroundSubagent;
2040
+ /**
2041
+ * The id of the next block an `assistant` line carries, `<message>:<index>`.
2042
+ * The CLI sends a message as one line per content block, in order, so a
2043
+ * block's place among its message's lines is the `index` its stream events
2044
+ * carry (checked against every recorded transcript).
2045
+ */
2046
+ private blockId;
3138
2047
  /**
3139
2048
  * Partial output of the planner's own message. The complete `assistant` line
3140
2049
  * for each block follows its deltas and is authoritative for that block (see
@@ -3179,8 +2088,8 @@ declare class CodexAdapter extends StdioAgentAdapter implements TaskModeAgentAda
3179
2088
  private threadModel;
3180
2089
  private readonly rpc;
3181
2090
  private startOpts;
3182
- /** Whether this turn has already emitted prose — see the `agentMessage` case. */
3183
- private turnHasText;
2091
+ /** The planner thread's runs of reply text this turn, each under its `agentMessage` item's id. */
2092
+ private replyRuns;
3184
2093
  /** The turn in flight. `turn/interrupt` and `turn/steer` cannot be sent before it has an id. */
3185
2094
  private turn;
3186
2095
  /** The last turn this adapter ended — so its second end signal, landing late, ends nothing. */
@@ -3507,9 +2416,10 @@ declare class OpenCodeAdapter implements TaskModeAgentAdapter {
3507
2416
  private dropUnreadSteers;
3508
2417
  /**
3509
2418
  * Whatever of the turn's own messages the stream missed. Each part is
3510
- * emitted once, so a turn the stream saw whole adds nothing here; one that
3511
- * lost its last text part still reaches the plain-text channel, which a
3512
- * summary falls back to.
2419
+ * emitted once, so a turn the stream saw whole adds nothing here; a text
2420
+ * part whose completion was lost completes as the run its deltas streamed
2421
+ * under, its part's id; one that lost every frame still reaches the
2422
+ * plain-text channel, which a summary falls back to.
3513
2423
  */
3514
2424
  private readBack;
3515
2425
  /**
@@ -3531,7 +2441,7 @@ declare class OpenCodeAdapter implements TaskModeAgentAdapter {
3531
2441
  */
3532
2442
  interrupt(timeoutMs: number): Promise<boolean>;
3533
2443
  onProcessExit(listener: (code: number, lastWords?: string) => void): void;
3534
- answerPermission(id: string, decision: ApprovalDecision): boolean;
2444
+ answerPermission(id: string, decision: ApprovalDecision): boolean | Promise<boolean>;
3535
2445
  /**
3536
2446
  * OpenCode 2.x replaced the 1.x HTTP surface rather than extending it, and
3537
2447
  * only 2.x answers `/api/info` with its version. A 1.x server — or one that
@@ -3634,17 +2544,48 @@ declare function mapAgentTool(name: string): MappedTool;
3634
2544
  */
3635
2545
  declare function normalizeAgentArgs(tool: ResearchToolType, args: Record<string, unknown>): Record<string, unknown>;
3636
2546
 
3637
- /** What a runner offers a task: the models discovered for it and the modes its manifest declares. */
3638
- interface RunnerCatalog {
3639
- models: DiscoveredModel[];
3640
- modes: RunnerModeInfo[];
3641
- /**
3642
- * The mode a task lands on when it carries none this runner offers: the
3643
- * manifest's `autonomous`/`safe`-tagged mode under the user's toggle
3644
- * (ADR-0001). Absent, `modes[0]` stands in — which for Codex is its
3645
- * workspace-write sandbox, not the full access an unattended run needs.
3646
- */
3647
- defaultMode?: string;
2547
+ interface PlannerBackendOptions {
2548
+ /** The planner provider configured now. */
2549
+ provider(): PlannerBackendId;
2550
+ /** A backend for the provider configured now. */
2551
+ create(): IAiService;
2552
+ /** Whether a planner turn is running on the backend. */
2553
+ busy(): boolean;
2554
+ }
2555
+ /**
2556
+ * The planner transport for the provider configured *right now* (ADR-0009),
2557
+ * and the one place it is let go.
2558
+ *
2559
+ * Resolved on every read rather than once at construction, because a Session
2560
+ * outlives the choice: VS Code hosts exactly one for the whole window, and the
2561
+ * webview pills and `/planner` switch backends underneath it. A service
2562
+ * captured at construction kept the old backend and was handed the new one's
2563
+ * model — an OpenCode model id spawned as `claude --model opencode/…`.
2564
+ *
2565
+ * Letting a backend go — a switch to another provider, or settings that its
2566
+ * client was built from — waits until no planner turn is running on it:
2567
+ * `reset()` cuts the call in flight off, and the turn would end with neither an
2568
+ * answer nor its inputs. A model or effort change needs no reset: a turn on a
2569
+ * live context that no longer matches the configuration replays instead
2570
+ * (`conversationMatchesConfig`). A harness planner holds an OS process, so a
2571
+ * backend replaced is always reset, never just dropped.
2572
+ */
2573
+ declare class PlannerBackend {
2574
+ private readonly options;
2575
+ private live;
2576
+ private liveProvider;
2577
+ /** Settings it was built from changed while a turn ran on it. */
2578
+ private stale;
2579
+ /** `pinned`: a backend injected whole, which is reset but never replaced. */
2580
+ constructor(options: PlannerBackendOptions | {
2581
+ pinned: IAiService;
2582
+ busy(): boolean;
2583
+ });
2584
+ current(): IAiService;
2585
+ /** What the backend was built from changed: it is reset now, or as soon as the turn running on it ends. */
2586
+ settingsChanged(): void;
2587
+ /** Make the switch or the reset that waited, if one did. Call while no turn runs: as one is about to take the conversation, and as one lets it go. */
2588
+ settle(): void;
3648
2589
  }
3649
2590
 
3650
2591
  type ModelsByRunner = Partial<Record<RunnerId, DiscoveredModel[]>>;
@@ -3678,6 +2619,8 @@ declare class SessionCatalog {
3678
2619
  constructor(deps: SessionCatalogDeps);
3679
2620
  /** The runners enabled right now. */
3680
2621
  enabledRunners(): RunnerId[];
2622
+ /** The runners `writer` may put a task on now ({@link assignableRunners}); undefined means any. */
2623
+ runnersFor(writer: AssignmentWriter): RunnerId[] | undefined;
3681
2624
  /**
3682
2625
  * The allowlist in force right now. Unset means no restriction — falling
3683
2626
  * back to the one planning started under would keep a restriction the user
@@ -3686,8 +2629,6 @@ declare class SessionCatalog {
3686
2629
  allowlist(): Record<string, string[]>;
3687
2630
  /** The enabled runners and allowlist together, for whoever compares an assignment with them. */
3688
2631
  scope(): AssignmentScope;
3689
- /** The models the user allows on one runner; unset means no restriction. */
3690
- allowlistFor(runner: RunnerId): string[] | undefined;
3691
2632
  /**
3692
2633
  * The discovered catalog as the resolver holds it now, per runner, with this
3693
2634
  * session's own discovery as the fallback where the resolver has nothing
@@ -3724,16 +2665,21 @@ declare class SessionCatalog {
3724
2665
  current(): TaskQueryCatalog;
3725
2666
  queryCatalog(runners: RunnerId[]): TaskQueryCatalog;
3726
2667
  /**
3727
- * The catalog a model/task-mode edit is checked against — the same
3728
- * discovered models and manifest modes the per-turn catalog block shows the
3729
- * planner, so a refusal here can never name something invalid that the
3730
- * planner was never told about. The catalog is not filtered by the
3731
- * allowlist here — `checkModelAndModeValidity` narrows by allowlist itself,
3732
- * the same way `coerceAssignments` does.
2668
+ * The catalog an assignment is resolved against — the same discovered
2669
+ * models and manifest modes the per-turn catalog block shows the planner,
2670
+ * so a refusal can never name something invalid that the planner was never
2671
+ * told about. The models are not filtered by the allowlist here: the
2672
+ * resolver reads the allowlist itself (`resolveAssignment`).
2673
+ */
2674
+ edit(runners?: RunnerId[]): AssignmentCatalog & {
2675
+ runnerModes: Record<RunnerId, RunnerModeInfo[]>;
2676
+ };
2677
+ /**
2678
+ * What a runner offers a task moved to it or added on it. Spawns the
2679
+ * runner's CLI to list models; null for a runner no manifest declares,
2680
+ * which has no CLI to ask.
3733
2681
  */
3734
- edit(runners?: RunnerId[]): EditCatalog;
3735
- /** What a runner offers, as `runnerAssignment` needs it. Spawns the runner's CLI to list models. */
3736
- runner(runner: RunnerId): Promise<RunnerCatalog>;
2682
+ runner(runner: RunnerId): Promise<RunnerCatalog | null>;
3737
2683
  /**
3738
2684
  * The one place planning discovers what it may draw from: the runners'
3739
2685
  * models and their modes. `filteredModels` is the allowlisted view a prompt
@@ -3813,12 +2759,14 @@ interface SessionEventRelayDeps {
3813
2759
  * and remembered for a surface joining late, so Session announces it too. A
3814
2760
  * model switch is a plan edit, which Session's editor announces as one.
3815
2761
  */
3816
- type RelayObserver = Required<Omit<OrchestratorObserver, 'onTaskSettled' | 'onRunStarted' | 'onAutoMerge' | 'onUsageLimitWait' | 'onModelSwitched' | 'onTaskIdle' | 'onIsolationHandoff'>>;
2762
+ type RelayObserver = Required<Omit<OrchestratorObserver, 'onTaskSettled' | 'onRunStarted' | 'onAutoMerge' | 'onUsageLimitWait' | 'onModelSwitched' | 'onTaskIdle' | 'onIsolationHandoff' | 'onRunClosed'>>;
3817
2763
  /**
3818
2764
  * Turns orchestrator and planner events into {@link SessionMessage}
3819
2765
  * broadcasts — the one place a surface's view of a Session is produced. It
3820
- * never persists: where an event also owes a save, the Session does that
3821
- * before handing the event on, so no surface sees state the disk lacks.
2766
+ * never persists, and holds nothing of a planner turn: a turn keeps what it
2767
+ * writes until it lands (`PlannerConversation`). Where an event also owes a
2768
+ * save, the Session does that before handing the event on, so no surface sees
2769
+ * state the disk lacks.
3822
2770
  */
3823
2771
  declare class SessionEventRelay {
3824
2772
  private readonly broadcast;
@@ -3830,12 +2778,6 @@ declare class SessionEventRelay {
3830
2778
  private readonly awaitingApproval;
3831
2779
  private readonly checkpointQuestion;
3832
2780
  private readonly waitsForApproval;
3833
- /**
3834
- * The in-flight turn's subagent activity, grouped one run per subagent so a
3835
- * replay nests each step under its own brief/result. Folded into the plan's
3836
- * researchLog at persist — see {@link flushSubagentRuns}.
3837
- */
3838
- private pendingSubagents;
3839
2781
  private statusHeld;
3840
2782
  private statusOwed;
3841
2783
  /** The planner's requests announced and not yet settled, as they were announced: a surface joining now is owed them. */
@@ -3868,20 +2810,6 @@ declare class SessionEventRelay {
3868
2810
  /** `turnId`: the planner turn whose commit this is, when one is. */
3869
2811
  planGenerated(plan: LegacyPlanState | null, goal: string, turnId?: string): void;
3870
2812
  progress(progress: ResearchProgress): void;
3871
- /** The run for `subagentId`, created on first sighting so a step arriving
3872
- * before (or without) its started event still gets a home. */
3873
- private subagentRun;
3874
- /**
3875
- * Fold the turn's subagent runs into the plan's researchLog as one contiguous
3876
- * group per subagent — its entry then its steps, in the order they started —
3877
- * so a replay nests each step under its own subagent however the live stream
3878
- * interleaved. A harness planner already logs its child steps through the
3879
- * turn's researchLog; they are pulled out of that position and re-grouped
3880
- * rather than duplicated.
3881
- */
3882
- flushSubagentRuns(plan: LegacyPlanState): void;
3883
- /** Forget a turn's subagent runs that will never be persisted. */
3884
- dropSubagentRuns(): void;
3885
2813
  }
3886
2814
 
3887
2815
  /** Which session's logs, in which workspace. */
@@ -4052,7 +2980,7 @@ interface SessionParts {
4052
2980
  modelResolver: ModelResolver;
4053
2981
  settings: () => SessionRuntimeSettings;
4054
2982
  hostSessionId?: string;
4055
- aiService: () => IAiService;
2983
+ backend: PlannerBackend;
4056
2984
  planner: SessionPlanner;
4057
2985
  store: PlanStore;
4058
2986
  orchestrator: TaskOrchestrator;
@@ -4083,7 +3011,7 @@ interface SessionParts {
4083
3011
  * (setModels, setRunnerList, …) stay on the host; Session never emits them.
4084
3012
  */
4085
3013
  declare class Session {
4086
- private readonly aiService;
3014
+ private readonly backend;
4087
3015
  private readonly usage;
4088
3016
  private readonly events;
4089
3017
  private readonly planner;
@@ -4103,6 +3031,8 @@ declare class Session {
4103
3031
  * supervisor has first.
4104
3032
  */
4105
3033
  private sentHandoff;
3034
+ /** The run was forgotten and no surface has been told yet; see {@link announceClosed}. */
3035
+ private closedUnannounced;
4106
3036
  /** The edit the open planner turn holds, as last announced: a surface that joins mid-turn is shown it. */
4107
3037
  private sentHeld;
4108
3038
  private readonly notifications;
@@ -4139,8 +3069,6 @@ declare class Session {
4139
3069
  private turnEffects;
4140
3070
  /** Every turn's effects with starts still owed: a turn's may still be running its starts when the next opens. */
4141
3071
  private readonly owingEffects;
4142
- /** What the planner's next turn is told before anything else; see {@link PlannerConversationHost.takeRunUpdates}. */
4143
- private readonly untoldRunUpdates;
4144
3072
  private unlistenApprovals;
4145
3073
  private readonly plannerTools;
4146
3074
  constructor(parts: SessionParts);
@@ -4185,9 +3113,9 @@ declare class Session {
4185
3113
  /** The open planner turn is the supervisor's, acting at Act: its edits and added tasks are its own (ADR-0029). */
4186
3114
  private supervisorEditing;
4187
3115
  /**
4188
- * The runners a planner edit may put a task on: the plan's own, and every
4189
- * runner enabled now. An edit is checked as it is made and again as it lands
4190
- * against this one set, so what was accepted cannot be refused at commit.
3116
+ * The runners a planner edit may put a task on (`assignableRunners`). An
3117
+ * edit is checked as it is made and again as it lands against this one set,
3118
+ * so what was accepted cannot be refused at commit.
4191
3119
  */
4192
3120
  private editRunners;
4193
3121
  /**
@@ -4227,7 +3155,6 @@ declare class Session {
4227
3155
  * task under the same id (ADR-0029).
4228
3156
  */
4229
3157
  private keepApprovals;
4230
- private approveTask;
4231
3158
  /**
4232
3159
  * Answer an outstanding approval. Every surface funnels here — the web
4233
3160
  * server's HTTP route, the VS Code webview, the TUI prompt — so the decision
@@ -4268,9 +3195,9 @@ declare class Session {
4268
3195
  */
4269
3196
  private syncPlanTasks;
4270
3197
  /**
4271
- * Persists PlanStore state to disk. PlanStore is the single authority.
4272
- * `background`: an execution event's save, which may land in the middle of a
4273
- * planner turn without settling it (see {@link PlannerConversation.restore}).
3198
+ * Persists PlanStore state to disk. PlanStore is the single authority. Any
3199
+ * save may come in the middle of a planner turn, and finds nothing of it to
3200
+ * write: a turn holds what it writes until the mutation that lands it.
4274
3201
  */
4275
3202
  private persist;
4276
3203
  /** A new plan on a long-lived Session gets its own persisted identity (unless the host fixed one). */
@@ -4279,8 +3206,8 @@ declare class Session {
4279
3206
  * The plan scope ends: everything that belongs to the plan this Session
4280
3207
  * holds goes, so nothing of it reaches the next one — a new plan, another
4281
3208
  * session adopted, or the Session closed. Its run is stopped, the planner
4282
- * turn in flight is abandoned with its uncommitted subagent steps, what that
4283
- * turn deferred is settled, and the conversation, supervision, usage ledger,
3209
+ * turn in flight is abandoned with everything it held, what that turn
3210
+ * deferred is settled, and the conversation, supervision, usage ledger,
4284
3211
  * execution log and planner prompts are cleared. A new plan on the same
4285
3212
  * Session keeps the scopes the user granted (`keepGrants`); a session
4286
3213
  * boundary does not (ADR-0008).
@@ -4424,12 +3351,6 @@ declare class Session {
4424
3351
  */
4425
3352
  compactConversation(signal?: AbortSignal): Promise<ConversationCompaction>;
4426
3353
  rewindTargets(): RewindTarget[];
4427
- /**
4428
- * Hand the planner turn in flight a validated plan or a task edit; the turn
4429
- * commits it as it would the same JSON in the planner's reply. False when
4430
- * no turn is open.
4431
- */
4432
- submitToTurn(submission: PlannerSubmission): boolean;
4433
3354
  /** A plan the planner submitted through its tool, already checked against the live catalog. */
4434
3355
  private submitPlanFromTool;
4435
3356
  /**
@@ -4486,7 +3407,6 @@ declare class Session {
4486
3407
  * level at each call. Other sessions' runs keep theirs until they end.
4487
3408
  */
4488
3409
  applyRunSettings(changes: Partial<GlobalRunSettings>): void;
4489
- executePlan(): Promise<void>;
4490
3410
  private idlePlan;
4491
3411
  private runWholePlan;
4492
3412
  /**
@@ -4496,11 +3416,7 @@ declare class Session {
4496
3416
  * would make it.
4497
3417
  */
4498
3418
  private joinRun;
4499
- approveReview(): Promise<LegacyPlanState>;
4500
- /** Answers the line saying the task waits for its dependencies, when it was queued behind them rather than started. */
4501
- forceStartTask(taskId: string): Promise<string | undefined>;
4502
3419
  private startOneTask;
4503
- runTask(taskId: string): Promise<void>;
4504
3420
  /**
4505
3421
  * A stale id — a task removed since the surface last drew it — is refused.
4506
3422
  * Reloading the plan to find it never could, and reset the verifier under
@@ -4509,20 +3425,8 @@ declare class Session {
4509
3425
  private requireTask;
4510
3426
  /** Start whatever the scheduler can now fit — after the parallel limit was raised mid-run, say. */
4511
3427
  reschedule(): Promise<void>;
4512
- retryTask(taskId: string): Promise<void>;
4513
3428
  private restartTask;
4514
- /**
4515
- * Continue a finished structured task in its saved runner session with the
4516
- * user's message (ADR-0018, K1); throws `TaskControlError` for a task that
4517
- * cannot be continued.
4518
- */
4519
- continueTask(taskId: string, message: string): Promise<void>;
4520
3429
  private resumeTask;
4521
- cancelTask(taskId: string): Promise<void>;
4522
- markTaskComplete(taskId: string): Promise<void>;
4523
- markTaskIncomplete(taskId: string): Promise<void>;
4524
- /** Resume a task paused on a usage limit now, once the user says it has reset; see {@link TaskOrchestrator.resumeNow}. */
4525
- resumeNow(taskId: string): Promise<void>;
4526
3430
  /**
4527
3431
  * Keep what waits on the planner's usage limit from being sent at the reset:
4528
3432
  * it goes with the user's next message instead. False when nothing waits on
@@ -4530,7 +3434,7 @@ declare class Session {
4530
3434
  */
4531
3435
  cancelPlannerResend(): boolean;
4532
3436
  /** Keep a task paused on a usage limit from resuming on its own; see {@link TaskOrchestrator.cancelScheduledResume}. */
4533
- cancelScheduledResume(taskId: string): boolean;
3437
+ private cancelScheduledResume;
4534
3438
  /**
4535
3439
  * The plan follows a model switch the user made in the runner (#106), and
4536
3440
  * the task's log says what the plan holds once that settled: a write-back
@@ -4541,8 +3445,8 @@ declare class Session {
4541
3445
  private followRunnerModel;
4542
3446
  /** Said through both channels: a host shows one or the other, never both (the daemon's toasts are no-ops). */
4543
3447
  private notice;
4544
- approveCheckpoint(taskId: string): void;
4545
- rejectCheckpoint(taskId: string, reason?: string): void;
3448
+ private approveCheckpoint;
3449
+ private rejectCheckpoint;
4546
3450
  /** Whether an answer would reach the task: it waits at a checkpoint and a runner is left to hear it. */
4547
3451
  awaitsCheckpoint(taskId: string): boolean;
4548
3452
  /**
@@ -4550,20 +3454,21 @@ declare class Session {
4550
3454
  * `TaskControlError` for one that cannot take it. A task waiting for input
4551
3455
  * is back in progress, and saved so before any surface is told.
4552
3456
  */
4553
- sendTaskMessage(taskId: string, text: string): string;
3457
+ private sendTaskMessage;
4554
3458
  /**
4555
3459
  * Force send (ADR-0023, F1): interrupt the task's running turn and deliver
4556
3460
  * this message next. Throws `TaskControlError` where a plain message would.
4557
3461
  */
4558
- forceSendTaskMessage(taskId: string, text: string): string;
3462
+ private forceSendTaskMessage;
4559
3463
  /**
4560
- * The task runner's own command `/name args` (#66), as a turn of its own.
4561
- * Throws `TaskControlError` where a plain message would.
3464
+ * The task runner's own command `/name args` (#66), as a turn of its own;
3465
+ * not an execution control, so it carries no command at the seam and the
3466
+ * daemon hands it straight to the runner. Throws `TaskControlError` where a
3467
+ * plain message would.
4562
3468
  */
4563
3469
  invokeTaskCommand(taskId: string, name: string, args?: string): string;
4564
- forceSendQueuedTaskMessage(taskId: string, id: string): boolean;
4565
- removeQueuedTaskMessage(taskId: string, id: string): boolean;
4566
- interruptTask(taskId: string): Promise<void>;
3470
+ private forceSendQueuedTaskMessage;
3471
+ private removeQueuedTaskMessage;
4567
3472
  /** Replay a run a dirty tree blocked, after stashing the tracked changes. */
4568
3473
  continueWithStash(): Promise<void>;
4569
3474
  /** Replay a run a dirty tree blocked, in the workspace root, for this run only. */
@@ -4589,13 +3494,20 @@ declare class Session {
4589
3494
  /**
4590
3495
  * "Merge all": each repo's integration branch into whatever the user has
4591
3496
  * checked out there — every repo, or none if any cannot take it. The one
4592
- * irreversible step of isolated execution. Automatic gate merging and the
4593
- * supervisor's merge_all share this path. During a run it merges what has
4594
- * landed so far,
4595
- * which is what opens a merge gate (ADR-0020); it waits for no ops task,
4596
- * and refuses while one runs.
3497
+ * irreversible step of isolated execution. The user's Merge all and the
3498
+ * supervisor's merge_all reach it through the seam; automatic gate merging
3499
+ * shares the scheduler's half of it. During a run it merges what has landed
3500
+ * so far, which is what opens a merge gate (ADR-0020); it waits for no ops
3501
+ * task, and refuses while one runs.
4597
3502
  */
4598
- mergeRun(): Promise<IsolationMergeResult>;
3503
+ private mergeAll;
3504
+ /**
3505
+ * Say so when the isolated run is gone: a merge that left nothing to hand
3506
+ * over clears it up (ADR-0013), and so does a discard. Surfaces fold this
3507
+ * instead of reading the merge result, which cannot tell a merge that ended
3508
+ * the run from one that only opened a gate (ADR-0020).
3509
+ */
3510
+ private announceClosed;
4599
3511
  /** Remove the run's worktrees and task branches; keep its integration branch to review or merge. */
4600
3512
  cleanupRun(): Promise<void>;
4601
3513
  /**
@@ -4622,10 +3534,7 @@ declare class Session {
4622
3534
  * a hold would stall every task's status for as long, and an
4623
3535
  * `execution_complete` the control triggers would overtake the status held
4624
3536
  * back — which a surface treats as the end of the stream. Instead each status
4625
- * announced while one runs is saved first (see {@link observer}), as the
4626
- * background save of an execution event: it may land mid planner turn. So
4627
- * is the last one where the control is itself execution, not the user's
4628
- * word on the plan (`background`).
3537
+ * announced while one runs is saved first (see {@link observer}).
4629
3538
  */
4630
3539
  private withSave;
4631
3540
  private saveForControl;
@@ -4671,7 +3580,12 @@ declare class Session {
4671
3580
  persist?: boolean;
4672
3581
  }): void;
4673
3582
  destroy(): void;
4674
- get aiServiceInstance(): IAiService;
3583
+ /**
3584
+ * The planner's settings or credentials changed: its backend is let go and
3585
+ * built afresh from them, once no planner turn is running on it (see
3586
+ * {@link PlannerBackend}).
3587
+ */
3588
+ plannerSettingsChanged(): void;
4675
3589
  }
4676
3590
 
4677
3591
  /**
@@ -4687,345 +3601,6 @@ declare class PlanEditError extends Error {
4687
3601
  constructor(message: string);
4688
3602
  }
4689
3603
 
4690
- /** What the preflight reads: the planner choice and whatever it needs to be reachable. */
4691
- type PlannerReadinessConfig = Pick<IConfig, 'aiProvider' | 'planningModel' | 'openaiCompatibleBaseUrl' | 'getProviderApiKey'>;
4692
- /**
4693
- * Why the selected planner cannot plan yet, as data. Each surface words it with
4694
- * its own command spelling (`describePlannerNotReady`), so a daemon, a chat and
4695
- * a shell script can all say the same thing without sharing a syntax.
4696
- */
4697
- type PlannerNotReady = {
4698
- code: 'no_key';
4699
- provider: AiProvider;
4700
- label: string;
4701
- keyEnvVar: string;
4702
- } | {
4703
- code: 'no_base_url';
4704
- provider: AiProvider;
4705
- label: string;
4706
- baseUrlEnvVar: string;
4707
- } | {
4708
- code: 'no_model';
4709
- provider: AiProvider;
4710
- label: string;
4711
- };
4712
- /**
4713
- * What the selected planner still needs before it can plan, or null when it is
4714
- * ready. There are two ways in (ADR-0009), and only one of them involves a
4715
- * credential: a harness planner drives a coding agent already installed on the
4716
- * machine, authenticated by the user's own subscription, so demanding an API
4717
- * key of it turns a working setup into a wall. Its model is optional too: an
4718
- * unset id means "the agent's own default".
4719
- */
4720
- declare function plannerNotReady(config: PlannerReadinessConfig): PlannerNotReady | null;
4721
- /**
4722
- * `plannerNotReady` judged on a host's own config — the one way every host
4723
- * builds the preflight's input, so none can hand it a key the selected
4724
- * planner's transport will not read (a stored OpenRouter key passing for an
4725
- * OpenAI planner, which then failed inside the transport). Every planner turn
4726
- * judges through this too, so none can let through what another refuses.
4727
- */
4728
- declare function plannerNotReadyFor(config: Pick<IConfig, 'aiProvider' | 'orchestratorModel' | 'planningModel' | 'openaiCompatibleBaseUrl' | 'getProviderApiKey'>): PlannerNotReady | null;
4729
- /** How one surface spells the commands a not-ready message points at. */
4730
- interface PlannerCommandSyntax {
4731
- pickPlanner: string;
4732
- setKey(provider: AiProvider): string;
4733
- setModel: string;
4734
- }
4735
- declare const TERMINAL_UI_COMMANDS: PlannerCommandSyntax;
4736
- /** The VS Code chat shares the terminal's slash commands, except that its `/model` needs `set`. */
4737
- declare const CHAT_COMMANDS: PlannerCommandSyntax;
4738
- declare const HEADLESS_CLI_COMMANDS: PlannerCommandSyntax;
4739
- declare function describePlannerNotReady(reason: PlannerNotReady, commands: PlannerCommandSyntax): string;
4740
- /**
4741
- * A planner turn refused before it began. Carries the reason as data so a
4742
- * client that spells commands differently (the headless CLI) can re-word it;
4743
- * the message is the terminal UI's spelling, which is what most clients show.
4744
- */
4745
- declare class PlannerNotReadyError extends Error {
4746
- readonly reason: PlannerNotReady;
4747
- constructor(reason: PlannerNotReady);
4748
- }
4749
-
4750
- /** What the user last picked for one planner backend. */
4751
- interface PlannerModelChoice {
4752
- model: string;
4753
- effort?: string;
4754
- }
4755
- /**
4756
- * A candidate model as this planner's catalog lists it. Deliberately plain
4757
- * data: a harness planner's catalog is its runner's own model list and a
4758
- * vendor's is a picker list (ADR-0009), and the rule below is the same for
4759
- * both — so the caller normalizes, and this module never reaches for a
4760
- * ModelResolver, a config or the network.
4761
- */
4762
- interface PlannerModelCandidate {
4763
- id: string;
4764
- variants?: {
4765
- id: string;
4766
- }[];
4767
- }
4768
- interface PlannerModelRecall {
4769
- model: string;
4770
- effort: string;
4771
- source: 'remembered' | 'catalog-default' | 'none';
4772
- }
4773
- /** The slice of `SettingsService` this needs, so tests can pass a plain object. */
4774
- interface PlannerModelStore {
4775
- getPlannerModel(provider: string): PlannerModelChoice | undefined;
4776
- setPlannerModel(provider: string, entry: PlannerModelChoice | undefined): void;
4777
- }
4778
- /**
4779
- * Remembers, per planner backend, the model the user last chose for it.
4780
- *
4781
- * Switching the planner has to clear the model — an id is scoped to the agent
4782
- * that listed it, so an OpenRouter slug handed to Claude Code points it at
4783
- * something it cannot spawn. That clear is correct and stays; what was missing
4784
- * is that switching *back* then forced a re-pick of a model the user had
4785
- * already chosen. The rule itself is `selectPlanner`'s; this is the storage
4786
- * the surfaces that keep their memory in the settings file hand it.
4787
- */
4788
- declare class PlannerModelMemory {
4789
- private store;
4790
- constructor(store: PlannerModelStore);
4791
- /** Record what the user picked for `provider`; a blank model forgets it instead. */
4792
- remember(provider: string, model: string, effort?: string): void;
4793
- getPlannerModel(provider: string): PlannerModelChoice | undefined;
4794
- /**
4795
- * What the model and effort should become when `provider` takes over the
4796
- * planner, given the models it actually serves. The `source` is what the
4797
- * three surfaces word their notice from — restored, defaulted, or nothing.
4798
- */
4799
- recall(provider: string, catalog: PlannerModelCandidate[]): PlannerModelRecall;
4800
- }
4801
-
4802
- /**
4803
- * The daemon contract: every HTTP body the local daemon sends, in one place.
4804
- *
4805
- * The daemon's routes build their bodies against these types and the CLI's
4806
- * `ApiClient` reads them back, so a field added on one side is a compile error
4807
- * on the other. Before this there were three hand-written copies — the routes'
4808
- * inline `c.json`, the client's per-method generics, and the TUI's slice of the
4809
- * client — and they had drifted (the client declared a `headless` field the
4810
- * daemon never sent, and typed `GET /sessions/:id`'s plan as the wrong shape).
4811
- *
4812
- * Types only. Core is the one package both the daemon and its clients already
4813
- * depend on; importing from the daemon would pull its runtime into the CLI.
4814
- */
4815
- /**
4816
- * Why a request was refused, as a stable code a client switches on instead of
4817
- * reading the message. Absent on a refusal nobody needs to tell apart — a
4818
- * missing field, say — where the message is all there is.
4819
- */
4820
- type DaemonErrorCode = 'session_not_found' | 'task_not_found' | 'no_plan' | 'already_executing' | 'workspace_not_found' | 'workspace_not_a_project' | 'conversation_busy'
4821
- /** The user stopped the planner turn: the turn ended as asked, so a client stays quiet. */
4822
- | 'planner_turn_stopped'
4823
- /** The planner turn settled after its plan was dropped; nothing it produced was kept. */
4824
- | 'planner_turn_discarded'
4825
- /** The selected planner cannot plan yet (no key, no base URL, no model); `plannerNotReady` says which. */
4826
- | 'planner_not_ready'
4827
- /** The request asked for something the plan, task or conversation does not allow. */
4828
- | 'refused' | 'checkpoint_not_waiting' | 'approval_not_outstanding';
4829
- /** The body of every non-2xx answer a route gives. */
4830
- interface ErrorBody {
4831
- error: string;
4832
- code?: DaemonErrorCode;
4833
- /** Set with `workspace_not_a_project`: the directory the daemon refused, so a client can offer to initialize it. */
4834
- workspace?: string;
4835
- /** Set with `planner_not_ready`: the reason as data, so a client can word it in its own commands. */
4836
- plannerNotReady?: PlannerNotReady;
4837
- }
4838
- interface OkResponse {
4839
- ok: boolean;
4840
- }
4841
- /** A task start's answer: `queued` says why it did not start now, when the task waits for its dependencies instead. */
4842
- interface TaskStartResponse extends OkResponse {
4843
- queued?: string;
4844
- }
4845
- /**
4846
- * A plan as the planner routes return it: the session's plan state, not the
4847
- * `SerializedPlan` a websocket `plan_generated` carries. The two differ —
4848
- * this one holds raw tasks and the dialogue's bookkeeping — and a client that
4849
- * reads either reads only the fields it needs. Skill bodies stay in the
4850
- * session: loads and attempt snapshots arrive as notices.
4851
- */
4852
- interface PlanResponse {
4853
- plan: SurfacePlan;
4854
- }
4855
- /** `POST /plans/:id/resolve-conflict`: `null` when the session had no conflict to hand a resolver task. */
4856
- interface ResolveConflictResponse {
4857
- plan: SurfacePlan | null;
4858
- }
4859
- /** What `GET /api/models` returns, and what the pool's provider discovery produces. */
4860
- interface ModelsResponse {
4861
- models: DiscoveredModel[];
4862
- modelsByRunner: Record<string, DiscoveredModel[]>;
4863
- /** Each runner's manifest modes, so a surface can offer a per-task mode picker. */
4864
- modesByRunner: Record<string, RunnerModeInfo[]>;
4865
- orchestratorModel: string;
4866
- providers: AiProvider[];
4867
- /** Full cross-provider catalog for the orchestrator (planner) model picker. */
4868
- orchestratorModels: OrchestratorOption[];
4869
- /** Per-provider catalog-fetch failures, keyed by provider id. */
4870
- providerErrors: Record<string, string>;
4871
- }
4872
- /** One-shot planning answers in the phase-tagged shape a saved session is read back in. */
4873
- interface GeneratePlanResponse extends Pick<ModelsResponse, 'models' | 'modelsByRunner'> {
4874
- plan: SurfacePlanState;
4875
- /** The host notes the plan's transcript records, a skill check's warnings among them, which the phase-tagged plan does not carry. */
4876
- notes?: string[];
4877
- }
4878
- interface ExecuteResponse {
4879
- status: 'running';
4880
- }
4881
- interface StopResponse {
4882
- status: 'stopped';
4883
- }
4884
- interface CancelPlanningResponse {
4885
- cancelled: boolean;
4886
- }
4887
- /** `POST /api/plans/:id/tasks/:taskId/resume/cancel` — false when no resume was scheduled for the task. */
4888
- interface CancelResumeResponse {
4889
- cancelled: boolean;
4890
- }
4891
- interface TaskMessageResponse {
4892
- id: string;
4893
- }
4894
- interface ForceSendResponse {
4895
- sent: boolean;
4896
- }
4897
- interface RemoveMessageResponse {
4898
- removed: boolean;
4899
- }
4900
- interface MergeGateEntry {
4901
- id: string;
4902
- order: number;
4903
- title: string;
4904
- }
4905
- interface MergeGateResponse {
4906
- mergeGate: MergeGateEntry[];
4907
- }
4908
- interface IsolationDiffResponse {
4909
- diff: string;
4910
- }
4911
- /** A merge that conflicts or is blocked is an outcome, not a refusal, so it answers 200 with the result. */
4912
- type IsolationMergeResponse = IsolationMergeResult;
4913
- /** A fork the daemon has already adopted. */
4914
- interface ConversationForkResponse {
4915
- sessionId: string;
4916
- goal: string;
4917
- plan: SurfacePlan;
4918
- }
4919
- /** A fork made by a rewind, with the full text of the message it was made just before. */
4920
- interface ConversationRewindResponse extends ConversationForkResponse {
4921
- rewoundMessage: string;
4922
- }
4923
- interface RewindTargetsResponse {
4924
- targets: RewindTarget[];
4925
- }
4926
- interface ConversationCompactResponse extends ConversationCompaction, PlanResponse {
4927
- }
4928
- interface PrdResponse {
4929
- prdMarkdown: string;
4930
- }
4931
- interface RunnerEntry {
4932
- id: string;
4933
- name: string;
4934
- enabled: boolean;
4935
- }
4936
- interface RunnersResponse {
4937
- runners: RunnerEntry[];
4938
- orchestratorModel: string;
4939
- }
4940
- /** Sessions: a saved one comes off its file, a live one from the pool's store. */
4941
- interface SessionResponse {
4942
- meta: SessionMeta;
4943
- plan: SurfacePlanState;
4944
- }
4945
- interface AdoptSessionResponse extends OkResponse {
4946
- plan: SurfacePlan;
4947
- goal: string;
4948
- }
4949
- interface TaskLogAttemptsResponse {
4950
- attempts: number[];
4951
- }
4952
- interface TaskLogResponse {
4953
- attempt: number;
4954
- events: TaskLogEvent[];
4955
- }
4956
- type SessionListResponse = SessionMeta[];
4957
- interface SettingsResponse {
4958
- orchestratorModel: string;
4959
- /** Who plans (ADR-0009) — a vendor id, or a runner serving as a harness planner. */
4960
- aiProvider: PlannerBackendId;
4961
- plannerThinkingEffort: string;
4962
- maxParallel: number;
4963
- modelAllowlist: UserSettings['modelAllowlist'];
4964
- plannerModels: UserSettings['plannerModels'];
4965
- /** The user's run settings (ADR-0030), each at its default until set. */
4966
- run: GlobalRunSettings;
4967
- }
4968
- /** What `PATCH /api/settings` answers: the settings as they now stand, plus what the write itself decided. */
4969
- interface SettingsUpdateResponse extends SettingsResponse {
4970
- /**
4971
- * Which model a planner switch landed on and why — remembered for that
4972
- * backend, its catalog default, or none. Present only when the write changed
4973
- * the planner; absent for every other write, where nothing was recalled.
4974
- */
4975
- switchRecall?: PlannerModelRecall;
4976
- /** Env keys the daemon refused to apply, so a client does not persist what the daemon would not. */
4977
- rejectedEnvKeys?: string[];
4978
- }
4979
- interface CommandDescriptor {
4980
- name: string;
4981
- description: string;
4982
- }
4983
- interface CommandsResponse {
4984
- commands: CommandDescriptor[];
4985
- }
4986
- interface WorkspacesResponse {
4987
- workspaces: string[];
4988
- }
4989
- interface ApprovalsResponse {
4990
- pending: PendingApproval[];
4991
- approvedScopes: string[];
4992
- }
4993
- interface ApprovalAnswerResponse extends OkResponse {
4994
- granted: boolean;
4995
- decision: ApprovalDecision['decision'];
4996
- }
4997
- /**
4998
- * Where a frame stands in its session's broadcast stream. A client holding
4999
- * several sockets on one session gets every broadcast on each, and a frame's
5000
- * content is no identity: two clears of a held edit are the same JSON. `seq`
5001
- * rises by one per broadcast of the session for as long as the daemon runs,
5002
- * whichever Session object holds the id; `stream` names the daemon process,
5003
- * so a restarted daemon's numbering is never compared with the last one's.
5004
- */
5005
- interface StreamStamp {
5006
- stream: string;
5007
- seq: number;
5008
- }
5009
- /** A broadcast as the session's socket carries it. Unstamped from a daemon older than the stamp. */
5010
- type SessionFrame = (SessionMessage | SessionNotice) & Partial<StreamStamp>;
5011
- type ApprovalRequestMessage = Extract<SessionMessage, {
5012
- type: 'approval_request';
5013
- }>;
5014
- /**
5015
- * What a session's socket sends right after `connected`: its run as it stands,
5016
- * so a client that joins mid-run has the run view before the next update —
5017
- * the held edit's preview included — and the planner's approval requests
5018
- * still waiting, which no later frame would announce again. `seq` is the last
5019
- * broadcast it accounts for. Every frame after it is a {@link SessionFrame}.
5020
- */
5021
- interface RunSnapshotFrame extends Partial<StreamStamp> {
5022
- type: 'run_snapshot';
5023
- sessionId: string;
5024
- view: RunView;
5025
- /** Absent from a daemon older than the field: nothing is known, not "none waiting". */
5026
- approvals?: ApprovalRequestMessage[];
5027
- }
5028
-
5029
3604
  /**
5030
3605
  * Why a call on a session could not be made. Each is a class rather than a
5031
3606
  * message so the daemon can map it to a status and a stable code in one place,
@@ -5211,7 +3786,7 @@ declare abstract class AbstractRunnerSession implements IRunnerSession {
5211
3786
  abstract nativeSessionId(): string | null;
5212
3787
  abstract commands(): RunnerCommand[];
5213
3788
  abstract invokeCommand(name: string, args?: string): string;
5214
- abstract answerPermission(id: string, decision: ApprovalDecision): boolean;
3789
+ abstract answerPermission(id: string, decision: ApprovalDecision): boolean | Promise<boolean>;
5215
3790
  abstract onTaskComplete(listener: (report: TaskCompleteArgs) => void): void;
5216
3791
  abstract onToolCheckpoint(handler: (question: string, signal: AbortSignal) => Promise<CheckpointAnswer>): void;
5217
3792
  }
@@ -5268,6 +3843,8 @@ declare class StructuredSession extends AbstractRunnerSession {
5268
3843
  private readonly text;
5269
3844
  private state;
5270
3845
  private turn;
3846
+ /** A terminal event ends the turn before the adapter releases its send. */
3847
+ private sending;
5271
3848
  private turnCount;
5272
3849
  private queue;
5273
3850
  private messageCount;
@@ -5349,7 +3926,7 @@ declare class StructuredSession extends AbstractRunnerSession {
5349
3926
  queued(): QueuedTaskMessage[];
5350
3927
  nativeSessionId(): string | null;
5351
3928
  sessionCost(): RunnerSessionCost | null;
5352
- answerPermission(id: string, decision: ApprovalDecision): boolean;
3929
+ answerPermission(id: string, decision: ApprovalDecision): boolean | Promise<boolean>;
5353
3930
  /** Every open request goes unanswered once the process that asked is gone. */
5354
3931
  private withdrawPermissions;
5355
3932
  interrupt(): Promise<void>;
@@ -5848,4 +4425,4 @@ declare const DEFAULT_MAX_PARALLEL = 5;
5848
4425
  */
5849
4426
  declare function parseMaxParallel(value: unknown): number | null;
5850
4427
 
5851
- export { ALL_PROVIDERS, AbstractRunner, AbstractRunnerSession, type AdoptSessionResponse, type Adr0013IsolationRun, type Adr0013PlanIsolation, type Adr0013TaskRecord, AgentAdapter, AgentEvent, AgentProcessDeps, AgentStartOptions, AiProvider, AlreadyExecutingError, ApplyTaskOpsResult, ApprovalAnswer, type ApprovalAnswerResponse, ApprovalDecision, ApprovalModeSetting, ApprovalPolicy, ApprovalRequest, type ApprovalRequestMessage, type ApprovalsResponse, type AskOptions, AssignmentScope, BaseAiService, BaseConfig, BaseFileSystem, BufferedTaskOutputSource, type ByPlannerBackend, CHAT_COMMANDS, CLAUDE_CODE_MANIFEST, CLI_PROVIDERS, CMD_EXE_MAX_COMMAND_LINE, CODEX_SANDBOX_DOCS, CODEX_SANDBOX_OFF_LINE, type CachedPage, type CancelPlanningResponse, type CancelResumeResponse, type CappedRows, CheckpointAnswer, type CheckpointListener, ClaudeCodeAdapter, CliAgentAiService, type CliAgentAiServiceDeps, CodexAdapter, CodexSandboxAvailability, type CommandDescriptor, CommandLineTooLongError, type CommandOutcome, type CommandsResponse, type ConfiguredPlanner, type Consent, ConsoleLogger, ContextCollector, type Continuability, ConversationBusyError, type ConversationCompactResponse, type ConversationCompaction, ConversationEditError, type ConversationFork, type ConversationForkResponse, ConversationMessage, type ConversationRequest, type ConversationRewind, type ConversationRewindResponse, type ConversationTurn, DAEMON_SUBPROTOCOL, DAEMON_TOKEN_SUBPROTOCOL_PREFIX, DEFAULT_MAX_PARALLEL, DEFAULT_WEB_READ_BUDGET, type DaemonErrorCode, type DecideOptions, type DirEntry, DiscoveredModel, DuckDuckGoSearchProvider, EmbeddedNewlineError, type EnvAdmission, EnvConfig, type ErrorBody, type ExecImpl, ExecutableNotFoundError, type ExecuteResponse, ExecutionActor, type ExecutionCommand, ExecutionCommandName, ExecutionCommands, type ExecutionHost, ExecutionRecord, FindSymbolOptions, type ForceSendResponse, GREP_PCRE_PROBE_ARGS, GeminiService, type GeneratePlanResponse, type GitExecFn, GlobOptions, GlobalRunSettings, type GrepInvocation, GrepOptions, type GrepRegex, GuardTarget, GuardTrip, HARNESS_PLANNER_REASON, HEADLESS_CLI_COMMANDS, type HasBinFn, HttpWebFetcher, type HttpWebFetcherOptions, type IAiService, IApproval, IConfig, IFileSystem, type ILogger, INotification, IRunner, IRunnerSession, type ISearchProvider, IWorktreeIsolation, type IsolationDiffResponse, IsolationLandedTask, type IsolationMergeResponse, IsolationMergeResult, IsolationTaskStatus, IsolationView, type KillTreeDeps, type LaunchDeps, type LaunchPlan, LegacyPlanState, type LiveOutputLookup, LiveTail, LiveTailOptions, type LoopGuard, type MappedTool, type MergeGateEntry, type MergeGateResponse, type MergeWordingHandoff, ModelResolver, type ModelResolverDeps, type ModelsResponse, NoPlanError, OPENCODE_MANIFEST, ORDEWELL_SETTABLE_ENV, OUTPUT_LINES_DEFAULT, OUTPUT_LINES_MAX, type OkResponse, OpenAiService, OpenCodeAdapter, OrchestratorObserver, OrchestratorOption, OrdewellMcpServer, PROVIDER_CREDENTIAL_ENV, PROVIDER_DETECT_PRIORITY, PROVIDER_LABEL, PROVIDER_PRIORITY, PROVIDER_SHORT_LABEL, type PendingApproval, PendingApprovals, type PendingApprovalsOptions, PlanEditError, PlanIsolation, type PlanRequest, type PlanResponse, PlanState, PlanStatus, PlanStore, Planner, type PlannerBackendEntry, PlannerBackendId, type PlannerChoice, type PlannerCommandSyntax, type PlannerModelCandidate, type PlannerModelChoice, type PlannerModelLookup, PlannerModelMemory, type PlannerModelRecall, type PlannerModelSource, type PlannerModelStore, type PlannerNotReady, PlannerNotReadyError, type PlannerReadinessConfig, type PlannerSelectionChange, type PlannerSelectionCurrent, type PlannerSelectionResult, type PlannerSelectionState, type PlannerSubmission, PlannerToolHandler, PlannerTurnDiscardedError, PlannerTurnStoppedError, type PlannerUsability, PlannerUsage, PlannerUsageLimitError, type PrdBlock, type PrdResponse, ProviderModelLists, type ProviderRegistration, QueuedTaskMessage, ReadFileOpts, type RemoveMessageResponse, type ResearchChat, ResearchLogEntry, ResearchShell, type ResearchTurn, type ResolveConflictResponse, type ResolvedChoice, type ResolvedPlanner, type RewindTarget, type RewindTargetsResponse, RunMergeView, type RunSnapshotFrame, RunView, RunnerApprovals, RunnerCommand, RunnerId, RunnerInstallation, RunnerManifest, RunnerModeInfo, RunnerRegistry, RunnerSessionCost, type RunnerSpawnOptions, type RunnersResponse, SETTINGS_ENV_ALLOWLIST, SETTINGS_ENV_REFUSED, STATE_DIR, STATE_DIR_IGNORE, STOPPED_TOOL_RESULT, SUPERVISOR_LEVELS, SYMBOL_LANGUAGES, type SaveSession, type SearchHit, type SearchOutcome, type SearchPage, type SearchTransport, Session, SessionBroadcaster, type SessionData, type SessionDeps, type SessionFrame, type SessionListResponse, SessionMessage, type SessionMeta, SessionNotFoundError, SessionNotice, type SessionPlanner, type SessionResponse, type SessionRuntimeSettings, type SettingsResponse, type SettingsUpdateResponse, SkillInfo, type SkillInvocation, SkillLoad, SkillsService, type SpawnSpec, StartAdmission, StdioAgentAdapter, type StopResponse, type StreamStamp, StructuredEvent, StructuredRunner, type StructuredRunnerDeps, StructuredSession, StructuredTurnEnd, SupervisorLevel, type SupervisorPolicy, SurfacePlan, SurfacePlanState, TASK_QUERY_ANSWER_MAX_CHARS, TASK_QUERY_ANSWER_OR_OPS, TASK_QUERY_FIELDS, TASK_QUERY_PROTOCOL, TASK_QUERY_REMINDER, TERMINAL_UI_COMMANDS, Task, TaskCompleteArgs, type TaskLogAttemptsResponse, TaskLogEvent, type TaskLogFile, type TaskLogLocation, TaskLogRecorder, type TaskLogRecorderDeps, type TaskLogResponse, TaskMessageFrom, type TaskMessageResponse, TaskModeAgentAdapter, TaskOp, TaskOrchestrator, TaskOutputSource, type TaskQuery, type TaskQueryCatalog, type TaskQueryField, TaskRunnerFlags, TaskStartOptions, type TaskStartResponse, TaskTokenScope, type TokenCarriers, type ToolCall, ToolOutcome, type ToolResult, UsageRecord, UserSettings, VerdictEngine, type VerdictListener, WINDOWS_MAX_COMMAND_LINE, type WebRead, type WebReadDecision, type WebResponse, WebResponseCache, type WebResponseCacheOptions, type WebTransport, type WorkspaceCheckDeps, WorkspaceNotAProjectError, WorkspaceNotFoundError, type WorkspaceProjectCheckDeps, type WorkspacesResponse, type WorktreeIsolationDeps, abbreviateHome, admitSettingsEnv, applyHeadLimit, assertWorkspaceExists, assertWorkspaceIsProject, augmentedPath, bearerHeaderValue, buildFallbackGrepArgs, buildGlobArgs, buildGrepArgs, canContinue, checkoutRule, clearAugmentedPathCache, clearDaemonToken, clearState, clipboardCopyCommand, codexSandboxNotice, configuredProviders, continuability, createAiService, createSession, createTaskAdapter, createWorktreeIsolation, daemonTokenPath, defaultEnabledRunners, defaultLogger, definitionPattern, deleteSession, describeMergeResult, describePlannerNotReady, detectInstalledPlanner, discoverGeminiModels, ensureDir, ensurePrivateDir, ensureStateDirIgnored, extractPrdBlock, extractPresentedToken, filterFallbackByAnchoredInclude, findInstalledPlanner, formatSearchOutput, getLatestSession, getProviderMeta, getStateDir, globalDataDir, grantScopeFor, grepRegexFromProbe, idSegment, includeGlobFor, installedPlannerAnswered, installedPlannerDetected, isApiProvider, isCliProvider, isExecutableResolved, isOpenAiProvider, isPlannerBackend, isRepoGroup, isVendorModelFor, killTree, languageForId, listSessions, listTaskLogAttempts, listTree, loadSession, loadSessionPlanState, loadState, looksLikeChallenge, mapAgentTool, mergeTarget, migrateOldConfigDir, migratePlanIsolation, mintDaemonToken, mintSessionId, normalizeAgentArgs, normalizeGeminiModel, openTaskLog, parseMaxParallel, parseSearchHits, parseTaskQueryJson, planDirectLaunch, plannerBackendEntries, plannerMessage, plannerNotReady, plannerNotReadyFor, plannerTranscript, prefixModelId, providerForRunner, readDaemonToken, readTaskLog, recordInstalledPlanner, recordedInstalledPlanner, referencePattern, removedPluginNotice, renderTaskQueryAnswer, reposWithWork, resetInstalledPlanner, resolveProviderFromPrefix, resolveSkillInvocation, resolveTaskRunnerFlags, resolveWithin, runnerForProvider, sanitizeSlug, savePrdMarkdown, saveSession, saveState, selectPlanner, sessionDataDir, sessionRuntimeSettings, sharedSearchProvider, sharedWebResponseCache, stateExists, stripAnsi, stripModelPrefix, supportsTaskMode, taskQuerySignature, textHasTaskQuery, tokenSubprotocols, tokensMatch, wellKnownBinDirs, windowsCommandLine, withPath, writePrivateFile };
4428
+ export { ALL_PROVIDERS, AbstractRunner, AbstractRunnerSession, type Adr0013IsolationRun, type Adr0013PlanIsolation, type Adr0013TaskRecord, AgentAdapter, AgentEvent, AgentProcessDeps, AgentStartOptions, AiProvider, AlreadyExecutingError, ApprovalAnswer, ApprovalDecision, ApprovalModeSetting, ApprovalPolicy, AssignmentScope, BaseAiService, BaseConfig, BaseFileSystem, BufferedTaskOutputSource, type ByPlannerBackend, CLAUDE_CODE_MANIFEST, CLI_PROVIDERS, CMD_EXE_MAX_COMMAND_LINE, CODEX_SANDBOX_DOCS, CODEX_SANDBOX_OFF_LINE, type CachedPage, type CappedRows, CheckpointAnswer, type CheckpointListener, ClaudeCodeAdapter, CodexAdapter, CodexSandboxAvailability, CommandLineTooLongError, type CommandOutcome, type CommandPayload, type ConfiguredPlanner, type Consent, ConsoleLogger, ContextCollector, type Continuability, ConversationCompaction, type ConversationFork, ConversationRequest, type ConversationRewind, ConversationTurn, DAEMON_SUBPROTOCOL, DAEMON_TOKEN_SUBPROTOCOL_PREFIX, DEFAULT_MAX_PARALLEL, type DirEntry, DiscoveredModel, DuckDuckGoSearchProvider, EmbeddedNewlineError, type EnvAdmission, EnvConfig, type ExecImpl, ExecutableNotFoundError, ExecutionActor, type ExecutionCommand, ExecutionCommandName, ExecutionCommands, type ExecutionHost, ExecutionRecord, FindSymbolOptions, GREP_PCRE_PROBE_ARGS, GeminiService, type GitExecFn, GlobOptions, GlobalRunSettings, type GrepInvocation, GrepOptions, type GrepRegex, GuardTarget, GuardTrip, HARNESS_PLANNER_REASON, type HasBinFn, HttpWebFetcher, type HttpWebFetcherOptions, IAiService, IApproval, IConfig, IFileSystem, type ILogger, INotification, IRunner, IRunnerSession, type ISearchProvider, IWorktreeIsolation, IsolationLandedTask, IsolationMergeResult, IsolationTaskStatus, IsolationView, type KillTreeDeps, type LaunchDeps, type LaunchPlan, LegacyPlanState, LiveTail, LiveTailOptions, type LoopGuard, type MappedTool, type MergeWordingHandoff, ModelResolver, type ModelResolverDeps, NoPlanError, OPENCODE_MANIFEST, ORDEWELL_SETTABLE_ENV, OpenAiService, OpenCodeAdapter, OrchestratorObserver, OrchestratorOption, OrdewellMcpServer, PROVIDER_CREDENTIAL_ENV, PROVIDER_DETECT_PRIORITY, PROVIDER_LABEL, PROVIDER_PRIORITY, PROVIDER_SHORT_LABEL, PendingApproval, PendingApprovals, PlanEditError, PlanIsolation, type PlanRequest, PlanState, PlanStore, Planner, type PlannerBackendEntry, PlannerBackendId, type PlannerChoice, PlannerModelCandidate, type PlannerModelLookup, PlannerModelRecall, type PlannerModelSource, PlannerModelStore, type PlannerSelectionChange, type PlannerSelectionCurrent, type PlannerSelectionResult, type PlannerSelectionState, PlannerToolHandler, type PlannerUsability, PlannerUsage, type PrdBlock, ProviderModelLists, type ProviderRegistration, QueuedTaskMessage, ReadFileOpts, type ResearchChat, ResearchLogEntry, ResearchShell, type ResearchTurn, type ResolvedChoice, type ResolvedPlanner, RewindTarget, RunMergeView, RunView, RunnerApprovals, RunnerCommand, RunnerId, RunnerInstallation, RunnerManifest, RunnerModeInfo, RunnerRegistry, RunnerSessionCost, type RunnerSpawnOptions, SETTINGS_ENV_ALLOWLIST, SETTINGS_ENV_REFUSED, STATE_DIR, STATE_DIR_IGNORE, STOPPED_TOOL_RESULT, SUPERVISOR_LEVELS, SYMBOL_LANGUAGES, type SaveSession, type SearchHit, type SearchOutcome, type SearchPage, type SearchTransport, Session, SessionBroadcaster, type SessionDeps, SessionMessage, SessionMeta, SessionNotFoundError, SessionNotice, type SessionPlanner, type SessionRuntimeSettings, SkillsService, type SpawnSpec, StartAdmission, StdioAgentAdapter, StructuredEvent, StructuredRunner, type StructuredRunnerDeps, StructuredSession, StructuredTurnEnd, SupervisorLevel, type SupervisorPolicy, Task, TaskCompleteArgs, TaskLogEvent, type TaskLogFile, type TaskLogLocation, TaskLogRecorder, type TaskLogRecorderDeps, TaskMessageFrom, TaskModeAgentAdapter, TaskOrchestrator, TaskOutputSource, TaskQueryCatalog, TaskRunnerFlags, TaskStartOptions, TaskTokenScope, type TokenCarriers, type ToolCall, ToolOutcome, type ToolResult, UsageRecord, UserSettings, VerdictEngine, type VerdictListener, WINDOWS_MAX_COMMAND_LINE, WebRead, WebReadDecision, type WebResponse, WebResponseCache, type WebResponseCacheOptions, type WebTransport, type WorkspaceCheckDeps, WorkspaceNotAProjectError, WorkspaceNotFoundError, type WorkspaceProjectCheckDeps, type WorktreeIsolationDeps, admitSettingsEnv, applyHeadLimit, assertWorkspaceExists, assertWorkspaceIsProject, augmentedPath, bearerHeaderValue, buildFallbackGrepArgs, buildGlobArgs, buildGrepArgs, canContinue, checkoutRule, clearAugmentedPathCache, clearDaemonToken, clearState, clipboardCopyCommand, codexSandboxNotice, configuredProviders, continuability, createSession, createTaskAdapter, createWorktreeIsolation, daemonTokenPath, defaultEnabledRunners, defaultLogger, definitionPattern, deleteSession, describeMergeResult, detectInstalledPlanner, discoverGeminiModels, ensureDir, ensurePrivateDir, ensureStateDirIgnored, extractPrdBlock, extractPresentedToken, filterFallbackByAnchoredInclude, findInstalledPlanner, formatSearchOutput, getLatestSession, getProviderMeta, getStateDir, globalDataDir, grantScopeFor, grepRegexFromProbe, idSegment, includeGlobFor, installedPlannerAnswered, installedPlannerDetected, isApiProvider, isCliProvider, isExecutableResolved, isOpenAiProvider, isPlannerBackend, isRepoGroup, isVendorModelFor, killTree, languageForId, listSessions, listTaskLogAttempts, listTree, loadSession, loadSessionPlanState, loadState, looksLikeChallenge, mapAgentTool, mergeTarget, migrateOldConfigDir, migratePlanIsolation, mintDaemonToken, mintSessionId, normalizeAgentArgs, normalizeGeminiModel, openTaskLog, parseMaxParallel, parseSearchHits, planDirectLaunch, plannerBackendEntries, prefixModelId, providerForRunner, readDaemonToken, readTaskLog, recordInstalledPlanner, recordedInstalledPlanner, referencePattern, removedPluginNotice, reposWithWork, resetInstalledPlanner, resolveProviderFromPrefix, resolveTaskRunnerFlags, resolveWithin, runnerForProvider, sanitizeSlug, savePrdMarkdown, saveSession, saveState, selectPlanner, sessionDataDir, sessionRuntimeSettings, sharedSearchProvider, sharedWebResponseCache, stateExists, stripAnsi, stripModelPrefix, supportsTaskMode, tokenSubprotocols, tokensMatch, wellKnownBinDirs, windowsCommandLine, withPath, writePrivateFile };