@ordewell/core 0.10.4 → 0.10.6

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 (49) hide show
  1. package/dist/{IRunner-D_UkkMbJ.d.ts → IFileSystem-C4_mpfze.d.ts} +159 -15
  2. package/dist/{IRunner-NtTHQgLR.d.mts → IFileSystem-DUgeWWQN.d.mts} +159 -15
  3. package/dist/{ModeResolver-wnTFsDnF.d.mts → ModeResolver-4qNjxcDs.d.mts} +1 -1
  4. package/dist/{ModeResolver-Dy3Ef6p-.d.ts → ModeResolver-DGK2rWHE.d.ts} +1 -1
  5. package/dist/{Task-DZs9eKgg.d.mts → Task-C0o4i3Nn.d.mts} +114 -13
  6. package/dist/{Task-DZs9eKgg.d.ts → Task-C0o4i3Nn.d.ts} +114 -13
  7. package/dist/{chunk-FH6M5QZG.mjs → chunk-DMG4ISUG.mjs} +1008 -49
  8. package/dist/chunk-DMG4ISUG.mjs.map +1 -0
  9. package/dist/{chunk-SM6BV6XS.mjs → chunk-EEE2GBLG.mjs} +116 -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-645FALZX.mjs → chunk-LTHKFBKM.mjs} +4 -4
  14. package/dist/chunk-LTHKFBKM.mjs.map +1 -0
  15. package/dist/{chunk-OYUSADFM.mjs → chunk-NJGID6BG.mjs} +3 -1
  16. package/dist/chunk-NJGID6BG.mjs.map +1 -0
  17. package/dist/index.d.mts +498 -1689
  18. package/dist/index.d.ts +498 -1689
  19. package/dist/index.js +4108 -2082
  20. package/dist/index.js.map +1 -1
  21. package/dist/index.mjs +3380 -1639
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/parsing.d.mts +2 -2
  24. package/dist/parsing.d.ts +2 -2
  25. package/dist/parsing.js +231 -75
  26. package/dist/parsing.js.map +1 -1
  27. package/dist/parsing.mjs +3 -3
  28. package/dist/plan-utils.d.mts +20 -4
  29. package/dist/plan-utils.d.ts +20 -4
  30. package/dist/plan-utils.js +335 -88
  31. package/dist/plan-utils.js.map +1 -1
  32. package/dist/plan-utils.mjs +43 -19
  33. package/dist/{plan-utils-CtKceSsQ.d.mts → runSettingsText-BgOmLxdw.d.mts} +2242 -224
  34. package/dist/{plan-utils-POIz5d8z.d.ts → runSettingsText-BgXvajj2.d.ts} +2242 -224
  35. package/dist/testing.d.mts +5 -3
  36. package/dist/testing.d.ts +5 -3
  37. package/dist/testing.js +7 -0
  38. package/dist/testing.js.map +1 -1
  39. package/dist/testing.mjs +7 -2
  40. package/dist/testing.mjs.map +1 -1
  41. package/package.json +1 -1
  42. package/dist/IFileSystem-_effm8-b.d.ts +0 -78
  43. package/dist/IFileSystem-vyqMdL-x.d.mts +0 -78
  44. package/dist/chunk-645FALZX.mjs.map +0 -1
  45. package/dist/chunk-A57ITQE7.mjs +0 -1378
  46. package/dist/chunk-A57ITQE7.mjs.map +0 -1
  47. package/dist/chunk-FH6M5QZG.mjs.map +0 -1
  48. package/dist/chunk-OYUSADFM.mjs.map +0 -1
  49. package/dist/chunk-SM6BV6XS.mjs.map +0 -1
package/dist/index.d.ts CHANGED
@@ -1,13 +1,11 @@
1
- import { af as Task, E as ExecutionActor, r as IsolationMergeResult, f as ExecutionRecord, g as GuardTrip, ae as SupervisorLevel, e as ExecutionCommandName, G as GuardTarget, a2 as RunnerId, d as DiscoveredModel, Z as ResearchProgress, Y as ResearchLogEntry, C as ConversationMessage, a7 as SkillInfo, a8 as SkillLoad, ac as SkillsService, L as LegacyPlanState, N as PlanStatus, n as IsolationLandedTask, I as IWorktreeIsolation, F as IsolationTaskStatus, J as PlanIsolation, ar as UsageRecord, a0 as ResearchToolType, Q as PlannerUsage, an as TaskSnapshot, K as PlanState, H as IsolationView, at as Verdict, am as TaskSkillSnapshot } from './Task-DZs9eKgg.js';
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, R as PrepareIntent, S as PreparedTask, T as PreservedWork, U as PreviousWork, V as RefusedRemoval, W as RepairEvidence, X as RepoGroupLayout, _ as ResearchStep, $ as ResearchStepOutcome, a1 as RunSettings, a3 as SetupFailure, a4 as ShadowedSkill, a5 as SkillAppliesTo, a6 as SkillCatalog, a9 as SkillLoadNotice, aa as SkillMetadata, ab as SkillSource, ai as TaskIsolation, aj as TaskIsolationState, ak as TaskModelAssignment, ao as TaskStatus, ap as TreeSnapshot, aq as UsageLine, as as UsageTotals, au as VerificationCheck, av as addAttemptUsage, aw as addModelUsage, ax as addPlannerUsage, ay as addUsage, az as addUsageTotals, aA as createEmptyPlan, aB as createSkillsService, aC as createTask, aD as endAttemptUsage, aE as flattenTasks, aF as flattenTasksWithParents, aG as isAwaitingReason, aH as isMeasured, aI as isSupervisorLevel, aJ as isUserMessage, aK as migratePlanState, aL as partedPromptUsage, aM as pausedOnLimit, aN as planUsage, aO as plannerContextFill, aP as rejoinPlanTree, aQ as startAttemptUsage, aR as usageLine, aS as waitsForApproval } from './Task-DZs9eKgg.js';
3
- import { ah as StartAdmission, w as LiveTailOptions, L as LiveTail, U as PlannerModes, at as TaskOp, u as IsolatedExecution, aa as SessionBroadcaster, b as ApplyTaskOpsResult, af as SkillLookup, ac as SessionNotice, q as EditCatalog, ab as SessionMessage, T as PlanStore, au as TaskOrchestrator, v as IsolationRunController, O as OrchestratorObserver, ar as TaskLogEvent, I as INotification, s as GlobalRunSettings, a1 as RunView, Z as RunMergeView, ax as TaskOutputSource, aO as UserSettings, al as SurfacePlan, am as SurfacePlanState } from './plan-utils-POIz5d8z.js';
4
- export { A as ACT_RISK, a as AUTORUN_RISK, c as ApprovalBlock, d as ApprovalStatus, e as AutoMergeTrigger, B as BlockingSubject, C as CHECKPOINT_TRUNCATE_LENGTH, f as CommandBlock, g as CommandStatus, h as CompactionBlock, i as ConversationInput, j as ConversationView, D as DiffRow, k as DiffRowKind, l as DiffStat, m as DisplayBlock, E as EMPTY_CONVERSATION, n as EMPTY_HOLD, o as EMPTY_RUN, p as EMPTY_TASK_LOG, r as ExecutionSummary, G as GatedConversation, H as HeldEdit, t as HeldEditMark, x as LocalEntry, M as MarkdownToken, y as MessageBlock, z as MessageRole, N as NOT_STARTED, F as NO_TURN, J as NewTaskFields, K as NotificationAction, P as OutputPreview, Q as PlanBlock, R as PlanMarkerStatus, S as PlanSnapshot, V as PromptHold, W as RUN_SETTINGS_DEFAULTS, Y as RunInput, _ as RunSettingsError, $ as RunTask, a0 as RunTaskStatus, a2 as RunnerLoadedBlock, a3 as STOPPED_BEFORE_START, a4 as SUPERVISOR_LEVEL_LABEL, a5 as SerializedConversationMessage, a6 as SerializedPendingTurn, a7 as SerializedPlan, a8 as SerializedTask, a9 as SerializedTaskStatus, ad as SettingsService, ae as SkillLoadBlock, ag as SkillToken, ai as SubagentBlock, aj as SubagentChild, ak as SubagentStatus, an as SurfaceTask, ao as TakenPrompt, ap as TaskAttemptSnapshot, aq as TaskControlError, as as TaskLogView, av as TaskOrchestratorDeps, aw as TaskOrchestratorOptions, ay as TaskRef, az as TaskRowAction, aA as TaskRowContext, aB as TaskRowIsolation, aC as TaskRowKind, aD as TaskRowPlacement, aE as TaskRowSubject, aF as TaskRowUsage, aG as TaskRowView, aH as TaskStatusKind, aI as ThinkingDisplayBlock, aJ as ToolBlock, aK as ToolHeadline, aL as ToolStatus, aM as TurnGate, aN as UsageBlock, aP as WAITS_FOR_APPROVAL_LABEL, aQ as aheadOfDraft, aR as applyTaskOps, aS as approvalLabel, aT as automergeSummary, aU as autorunSummary, aV as awaitingLabel, aW as blockedByLabel, aX as blockingFailures, aY as canMergeTasks, aZ as canSetDependencies, a_ as canSplitTask, a$ as capConflictFiles, b0 as classifyOutcome, b1 as coalesceTaskLog, b2 as dependencyCandidates, b3 as dependentsNotice, b4 as dependentsOf, b5 as diffRows, b6 as diffStat, b7 as diffSummary, b8 as digestTaskLog, b9 as drainNext, ba as executionSummary, bb as followTurn, bc as fromTranscript, bd as getSettingsPath, be as hasHiddenDetail, bf as holdPrompt, bg as isTaskRunning, bh as loadedSkillTokens, bi as markAction, bj as markRequestFor, bk as markdownHtml, bl as markdownTokens, bm as namedRepos, bn as newTaskFields, bo as opsEditable, bp as outputLines, bq as outputPreview, br as parseCount, bs as parseSwitch, bt as parseTaskOpsJson, bu as pastGateConfirmation, bv as reconcileApprovals, bw as reduceConversation, bx as reduceRun, by as reduceTaskLog, bz as replayTaskLog, bA as resolveTaskRef, bB as resumeLabel, bC as resumeTime, bD as runSettingsOf, bE as runnerToolSubject, bF as serializePlan, bG as serializeTask, bH as serializeTaskStatus, bI as skillTokens, bJ as skippedLabel, bK as stopTurn, bL as summarizeToolCall, bM as supervisorSummary, bN as surfacePlan, bO as surfacePlanState, bP as takeAt, bQ as taskLogForSurface, bR as taskOrderLabel, bS as taskRef, bT as taskRefOf, bU as taskRowActions, bV as taskRowView, bW as taskStartedNotice, bX as taskStatusKind, bY as textHasTaskOps, bZ as titledRefs, b_ as titledTaskRef, b$ as titledTaskRefOf, c0 as toTaskLogEvent, c1 as toolHeadline, c2 as trimToolOutput, c3 as truncateCheckpointSummary, c4 as unsendAll, c5 as unsendLatest } from './plan-utils-POIz5d8z.js';
5
- import { I as IApproval, y as IConfig, d as AgentProcessDeps, a as AgentAdapter, _ as OrdewellMcpServer, 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, aq as RunnerRegistry, Z as OrchestratorOption, aD as TaskModeAgentAdapter, e as AgentStartOptions, c as AgentEvent, ai as RunnerCommand, a7 as ProjectMcpServer, n as ApprovalPolicy, B as IRunnerSession, aA as TaskCompleteArgs, r as CheckpointAnswer, av as StructuredTurnEnd, au as StructuredEvent, aC as TaskMessageFrom, ab as QueuedTaskMessage, aI as TaskStartOptions, aJ as TaskTokenScope, am as RunnerManifest, aH as TaskRunnerFlags } from './IRunner-D_UkkMbJ.js';
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, ar as ShellDialect, as as SpawnFn, at as StandingDecision, aw as SubmitPlanArgs, ax as TASK_COMPLETE_TOOL, ay as TASK_COMPLETION_CONTRACT, az as TASK_TOOLS, aB as TaskDiffArgs, aE as TaskModeUnsupportedError, aF as TaskOutputArgs, aG as TaskQueryArgs, aK as TaskToolHandler, aL as approvalScopes, aM as classifyCommand, aN as clearResearchShellCache, aO as collectProviderCredentials, aP as commandAllowedBy, aQ as effectiveApprovalMode, aR as enabledRunners, aS as fetchAllProviderModels, aT as findRunnerCommand, aU as isGranted, aV as isRunnerApproval, aW as knownModelId, aX as mcpClientConfig, aY as ownerOnlyConfigFile, aZ as parseAllowlist, a_ as parseApprovalModeSetting, a$ as researchShellWarning, b0 as researchToolsPath, b1 as resolveModelShortcut, b2 as resolveProvider, b3 as resolveResearchShell, b4 as scopeAllowed, b5 as sharedMcpServer, b6 as toApprovalDecision, b7 as toOrchestratorOptions, b8 as toolAllowed } from './IRunner-D_UkkMbJ.js';
7
- import { T as ToolOutcome, I as IFileSystem, R as ReadFileOpts, b as GrepOptions, a as GlobOptions, F as FindSymbolOptions } from './IFileSystem-_effm8-b.js';
8
- export { G as GREP_DEFAULT_HEAD_LIMIT, c as GrepOutputMode, S as SEARCH_EXCLUSIONS } from './IFileSystem-_effm8-b.js';
9
- import { R as RunnerModeInfo } from './ModeResolver-Dy3Ef6p-.js';
10
- export { a as autonomyLevelLabel, p as parseAutonomyLevel, r as runnerModesFrom } from './ModeResolver-Dy3Ef6p-.js';
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.js';
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.js';
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-BgXvajj2.js';
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-BgXvajj2.js';
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-C4_mpfze.js';
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-C4_mpfze.js';
7
+ import { R as RunnerModeInfo } from './ModeResolver-DGK2rWHE.js';
8
+ export { a as autonomyLevelLabel, p as parseAutonomyLevel, r as runnerModesFrom } from './ModeResolver-DGK2rWHE.js';
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. */
@@ -125,6 +195,8 @@ interface ExecutionHost {
125
195
  planApproved(): boolean;
126
196
  /** The user approved this task: it was in the plan they executed, or they started it themselves. */
127
197
  taskApproved(taskId: string): boolean;
198
+ /** A task the scheduler leaves for the user to approve: added to the plan since they approved it (ADR-0035). */
199
+ waitsForApproval(taskId: string): boolean;
128
200
  /** A start the user consented to approves these tasks, before it acts. */
129
201
  approve(taskIds: readonly string[]): void;
130
202
  /** Full autonomy: starts follow plan approval without a question. */
@@ -146,17 +218,28 @@ interface ExecutionHost {
146
218
  maxParallel(): number;
147
219
  /** The run waits on the user to stash or skip isolation before anything starts. */
148
220
  runBlocked(): boolean;
149
- /** The tasks a task's dependencies still hold it back for: unmet dependencies, or the unfinished work behind a skipped one. */
150
- unmetDependencies(task: Readonly<Task>): string[];
151
- /** The ancestors a task would wait on at its merge gate if it started now, whatever its status (ADR-0020). */
152
- mergeGate(taskId: string): string[];
153
- /** The failed tasks a task not started waits on through its dependencies (ADR-0034). */
154
- 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[];
155
227
  /** The runner requests waiting on a task for an answer. */
156
228
  waitingRequests(taskId: string): readonly WaitingRequest[];
157
229
  /** A start is told who asked: whether a run it opens continues the last one depends on it (ADR-0029). */
158
230
  runPlan(actor: ExecutionActor): Promise<void>;
231
+ /**
232
+ * The user's Execute while a run is under way, once it approved every task:
233
+ * the run takes them in, each starting once its dependencies finish. Answers
234
+ * the line that says so, naming how many had waited for approval.
235
+ */
236
+ joinRun(approved: number): Promise<string>;
159
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>;
160
243
  stopTask(taskId: string): Promise<void>;
161
244
  stopRun(): void;
162
245
  retryTask(taskId: string, actor: ExecutionActor): Promise<void>;
@@ -166,8 +249,22 @@ interface ExecutionHost {
166
249
  /** Resume a task paused on a usage limit now; see {@link TaskOrchestrator.resumeNow}. */
167
250
  resumeNow(taskId: string, message: string | undefined, actor: ExecutionActor): Promise<void>;
168
251
  messageTask(taskId: string, text: string): string;
169
- denyRequest(requestId: string, note?: string): boolean;
170
- /** 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. */
171
268
  mergeAll(): Promise<IsolationMergeResult>;
172
269
  /** Keep the call in the timeline, worded as `text`. */
173
270
  record(record: ExecutionRecord, text: string): void;
@@ -200,12 +297,13 @@ interface ExecutionCommandsDeps {
200
297
  now?: () => string;
201
298
  }
202
299
  /**
203
- * The command seam (ADR-0029, #91): the one place an execution action from
204
- * anyone but a button reaches the scheduler. It resolves the task, checks the
205
- * call against the plan's state, who asked and the user's approval, asks the
206
- * user where consent needs a person, runs the Session operation the matching
207
- * button runs, and records the call in the timeline with who asked. A refusal
208
- * 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}).
209
307
  */
210
308
  declare class ExecutionCommands {
211
309
  private readonly deps;
@@ -261,8 +359,9 @@ declare class ExecutionCommands {
261
359
  private asksToApprove;
262
360
  /**
263
361
  * Who may do what without the mandate (#23): the user's own call needs no
264
- * consent; a planner turn may always end or steer work, and starts it only
265
- * 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.
266
365
  */
267
366
  private consent;
268
367
  /**
@@ -274,6 +373,19 @@ declare class ExecutionCommands {
274
373
  private prepareRunPlan;
275
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. */
276
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;
277
389
  private prepareContinue;
278
390
  /**
279
391
  * A pause is not a failure: the attempt stopped on its account's limit, so
@@ -284,1109 +396,49 @@ declare class ExecutionCommands {
284
396
  private prepareStop;
285
397
  private prepareStopRun;
286
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
+ */
287
406
  private prepareAnswer;
288
407
  private prepareMergeAll;
289
408
  /**
290
409
  * A skip is a decision not to wait for the task. Running it again undoes
291
410
  * that decision, so the supervisor leaves it to the user rather than going
292
- * round between the two.
293
- */
294
- private skippedRefusal;
295
- /**
296
- * A skip ends a task without a verdict and lands nothing, so it never
297
- * vouches for work. Anyone but the user names why, and the supervisor only
298
- * gives up on a task that already needs someone: failed, blocked, or
299
- * waiting for input.
300
- */
301
- private prepareSkip;
302
- /**
303
- * Why a start is not the user's to have made, or the task it asks them to
304
- * approve, or null: the user's own call is its own approval.
305
- */
306
- private unapproved;
307
- /**
308
- * The Guarded question, on the card the user answers other planner requests
309
- * on. It names every task a yes approves, since a yes is the user's
310
- * approval of them as Execute plan would be.
311
- */
312
- private question;
313
- /** What was asked, as "start task #4 "Title"". */
314
- private intent;
315
- private refuse;
316
- private keep;
317
- private all;
318
- private located;
319
- /** A reference read against the plan as the held edit leaves it, where the handles its batch gives come first. */
320
- private locateAsEdited;
321
- /** A reference as a caller writes one — see {@link resolveTaskRef}. */
322
- private locate;
323
- }
324
-
325
- /**
326
- * The planner's read channel: a `{"taskQuery":{...}}` reply asking Ordewell to
327
- * show it what the per-turn plan block deliberately leaves out.
328
- *
329
- * The plan block carries only short fields, so a planner asked to "add a line
330
- * to task 3's prompt" would be rewriting text it has never read. Inlining every
331
- * body on every turn was rejected — a twenty-task plan re-sends thousands of
332
- * tokens per message — so the planner asks instead, and pays for the detail
333
- * only on the turns it uses it.
334
- *
335
- * This module owns the wire shape and the rendering of the answer; the loop,
336
- * the budgets and the injection live in the Session, and the classification
337
- * lives in PlanRepair alongside the plan and task-ops envelopes.
338
- */
339
- /** The long fields a query may ask for. Everything else is already in the plan block. */
340
- declare const TASK_QUERY_FIELDS: readonly ["description", "prompt", "userSteps", "verdict", "outputSummary", "userStoriesCovered", "skills", "output"];
341
- type TaskQueryField = typeof TASK_QUERY_FIELDS[number];
342
- /** Lines a `output` read returns when the query sets no count. */
343
- declare const OUTPUT_LINES_DEFAULT = 80;
344
- /** Hard cap on `outputLines` — the tail read is bounded however the planner asks. */
345
- declare const OUTPUT_LINES_MAX = 400;
346
- /**
347
- * Character budget the rendered answer converges to. A running output tail is
348
- * the only part with no natural bound — a runner may print for minutes — so
349
- * the tail absorbs the clamp; everything else (task bodies, the catalog cap)
350
- * is bounded upstream.
351
- */
352
- declare const TASK_QUERY_ANSWER_MAX_CHARS = 20000;
353
- interface TaskQuery {
354
- /** Task references to read in full, as {@link resolveTaskRef} reads them. */
355
- tasks: string[];
356
- /** Narrows what comes back. Omitted means every field in {@link TASK_QUERY_FIELDS}. */
357
- fields?: TaskQueryField[];
358
- /** Also return the full runner/model catalog with labels, variants and mode descriptions. */
359
- catalog: boolean;
360
- /** Line count for `output` reads. Clamped at render to [1, {@link OUTPUT_LINES_MAX}]. */
361
- outputLines?: number;
362
- /** A previous answer's next offset: the output tail then covers only what came after it. */
363
- outputSince?: number;
364
- }
365
- declare function textHasTaskQuery(text: string): boolean;
366
- /** Parse a `{"taskQuery":...}` reply. Throws PlanParseError when the JSON is unusable. */
367
- declare function parseTaskQueryJson(text: string): TaskQuery;
368
- /** Stable identity of a query, so a turn can recognise the planner asking the same thing twice. */
369
- declare function taskQuerySignature(query: TaskQuery): string;
370
- /** What a full-catalog read is answered from. Models arrive already allowlist-filtered. */
371
- interface TaskQueryCatalog {
372
- runners: RunnerId[];
373
- models: Partial<Record<RunnerId, DiscoveredModel[]>>;
374
- modes: Record<RunnerId, RunnerModeInfo[]>;
375
- autonomousDefault: boolean;
376
- }
377
- /**
378
- * Appended when the planner has spent its read budget, or asked the identical
379
- * question twice. The detail still comes back — refusing it would leave the
380
- * planner editing text it has not read, which is the whole failure this
381
- * channel exists to prevent — but the turn is told to land.
382
- */
383
- declare const TASK_QUERY_ANSWER_OR_OPS: string;
384
- /**
385
- * The protocol as the planner is taught it, owned here beside the parser so the
386
- * two cannot drift. Both planner backends get these lines verbatim (ADR-0009):
387
- * a harness planner has no tool loop to reach, so the envelope is the channel.
388
- */
389
- declare const TASK_QUERY_PROTOCOL: string[];
390
- /** The one-line reminder the per-turn plan block carries, so the protocol has a single owner. */
391
- 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.";
392
- /**
393
- * Where an `output` read is answered from: the orchestrator's live capture of
394
- * the task's latest attempt. Injected so this module stays planner-side and
395
- * never imports the orchestrator.
396
- */
397
- type LiveOutputLookup = (taskId: string, opts: LiveTailOptions) => LiveTail | null;
398
- /**
399
- * Render the injected answer to one read. Never persisted — it is context for
400
- * the planner's next reply and nothing else — so it is built fresh from live
401
- * state on every query rather than cached. `liveOutput` backs the `output`
402
- * field; omitting it reads as "nothing captured".
403
- */
404
- declare function renderTaskQueryAnswer(query: TaskQuery, tasks: readonly Task[], catalog: TaskQueryCatalog, liveOutput?: LiveOutputLookup): string;
405
-
406
- /**
407
- * The SSRF guard for anything that leaves the machine on the planner's behalf.
408
- * An unguarded `http://169.254.169.254/` would hand a prompt-injected planner
409
- * the cloud metadata credentials, so every outbound host is vetted here: IP
410
- * literals by range, names by every address DNS answers with.
411
- */
412
- type ResolveAll = (host: string) => Promise<string[]>;
413
-
414
- /**
415
- * The one decision every planner web read goes through, whichever runner
416
- * plans and whichever path carries the bytes (ADR-0027). A fetch names the
417
- * URL it reads; a search names its query and the provider that receives it.
418
- */
419
- type WebRead = {
420
- kind: 'fetch';
421
- url: string;
422
- }
423
- /** `provider` keys the grant; `providerLabel`, when given, names it to the person asked. */
424
- | {
425
- kind: 'search';
426
- query: string;
427
- provider: string;
428
- providerLabel?: string;
429
- note?: string;
430
- };
431
- /** A refusal's `reason` is written for the planner: it says what to do next. */
432
- type WebReadDecision = {
433
- allowed: true;
434
- } | {
435
- allowed: false;
436
- reason: string;
437
- };
438
- interface DecideOptions {
439
- /**
440
- * Research subagents run in the background where nothing can prompt
441
- * (ADR-0008, T6). When false, a read that would ask is refused with the gap
442
- * to report, rather than raising a prompt nobody can answer.
443
- */
444
- prompting?: boolean;
445
- }
446
- interface WebReadPolicyOptions {
447
- approval: IApproval;
448
- /** The secrets Ordewell holds — provider keys, MCP tokens — read per request. */
449
- secrets?: () => Iterable<string>;
450
- /** Defaults to `process.env`; its credential-named values are secrets too. */
451
- env?: () => Record<string, string | undefined>;
452
- /** How many web reads one planner turn may make; read per request, so a settings change applies at once. */
453
- budget?: () => number;
454
- /** The planner turn in progress; a new one starts a new budget. */
455
- turn?: () => string | undefined;
456
- /** Override DNS resolution (every answer for a name) in tests. */
457
- resolveHost?: ResolveAll;
458
- }
459
- declare const DEFAULT_WEB_READ_BUDGET = 40;
460
- declare class WebReadPolicy {
461
- private readonly approval;
462
- private readonly resolveHost;
463
- private readonly secrets;
464
- private readonly env;
465
- private readonly budget;
466
- private readonly turn;
467
- private spent;
468
- constructor(opts: WebReadPolicyOptions);
469
- /**
470
- * Every check that needs no network runs first, then the budget and the
471
- * approval, and only then is a fetch's host resolved: a lookup is itself a
472
- * request, so `<data>.attacker.example` would leave through DNS even for a
473
- * read that is then excluded, over budget or declined.
474
- */
475
- decide(read: WebRead, opts?: DecideOptions): Promise<WebReadDecision>;
476
- private subagentRefusal;
477
- /**
478
- * Whether a search through `provider` would run without asking anyone, for
479
- * a runner whose own search raises no request and is set on or off when
480
- * its session starts (ADR-0027). A zero budget disables it; no query is
481
- * known yet, so secrets and individual reads cannot be checked or counted.
482
- */
483
- searchesUnasked(provider: string): boolean;
484
- /** The last check, the one that leaves the machine: every DNS answer for the host must be public. */
485
- private resolved;
486
- private refuse;
487
- private vetFetch;
488
- private vetSearch;
489
- private spend;
490
- private refund;
491
- private carriesSecret;
492
- }
493
-
494
- interface IWebFetcher {
495
- /**
496
- * The web-read policy's decision (ADR-0027), for a read this fetcher will
497
- * carry or one a runner carries natively, such as Claude's `WebFetch`.
498
- * `fetch` and `search` decide for themselves; never call both.
499
- */
500
- decide(read: WebRead): Promise<WebReadDecision>;
501
- /** Whether a runner's own search through `provider` would run unasked; see `WebReadPolicy.searchesUnasked`. */
502
- searchesUnasked?(provider: string): boolean;
503
- fetch(url: string): Promise<ToolOutcome>;
504
- /**
505
- * Optional web search. A fetcher without it makes the `web_search` tool
506
- * report itself unavailable rather than failing the turn — the same
507
- * degradation `fetch` already uses when no fetcher is wired at all.
508
- */
509
- search?(query: string): Promise<ToolOutcome>;
510
- /**
511
- * A variant of this fetcher for a research subagent: same policy, cache,
512
- * transport and budget, but a read that would ask a person is refused
513
- * instead (ADR-0027, ADR-0008 T6). Absent means subagents get no web tools
514
- * and the `fetch`/`web_search` tools report themselves unavailable.
515
- */
516
- forSubagent?(): IWebFetcher;
517
- }
518
-
519
- /**
520
- * A person's approvals of project MCP servers, kept across sessions
521
- * (ADR-0028). Keyed by repository root and scope, which names the server
522
- * and hashes its definition: a changed definition is a new question.
523
- */
524
- interface ProjectServerApprovals {
525
- approved(repoRoot: string, scope: string): boolean;
526
- remember(repoRoot: string, scope: string): void;
527
- }
528
-
529
- interface CliAgentAiServiceDeps extends Partial<AgentProcessDeps> {
530
- /** Overrides adapter construction. Tests supply a fake agent; production picks by runner id. */
531
- createAdapter?: (runner: string, deps: AgentProcessDeps) => AgentAdapter | null;
532
- /** Workspace root the agent explores. Defaults to the host process's cwd. */
533
- workspaceRoot?: () => string;
534
- /** Where a conversation's planner tools are served (ADR-0022). Absent: no conversation can start. */
535
- mcpServer?: OrdewellMcpServer;
536
- /** A person's approvals of project MCP servers, kept across sessions (ADR-0028). Defaults to Ordewell's data directory. */
537
- projectServerApprovals?: ProjectServerApprovals;
538
- }
539
- /**
540
- * A coding agent driven as Ordewell's planner (ADR-0009).
541
- *
542
- * The second transport behind `IAiService`, sitting beside `OpenAiService` and
543
- * `GeminiService`. It deliberately does **not** extend {@link BaseAiService}:
544
- * that class's body is Ordewell executing research tools on a model's behalf,
545
- * which is precisely the part a coding agent replaces. What it reuses instead
546
- * is everything above the transport — `settleReply` (the empty-reply nudge
547
- * and the turn's settling), the plan validator, the `ResearchProgress` events the
548
- * four surfaces already render. That is why this backend reaches VS Code, the
549
- * web UI, the CLI and the TUI without any of them learning a coding agent is
550
- * on the other end.
551
- */
552
- declare class CliAgentAiService implements IAiService {
553
- private config;
554
- private readonly runner;
555
- private readonly processDeps;
556
- private readonly makeAdapter;
557
- private readonly workspaceRoot;
558
- private environmentSecrets;
559
- /** The conversation's session, from its start until the next session boundary. */
560
- private session;
561
- private readonly mcpServer;
562
- private readonly projectServerApprovals;
563
- private activeAbort;
564
- constructor(config: IConfig, deps?: CliAgentAiServiceDeps);
565
- hasActiveConversation(): boolean;
566
- /**
567
- * False when the running agent process was spawned under a model/effort the
568
- * user has since changed in the picker. Unlike a vendor backend, the model
569
- * is a spawn-time argument to the agent CLI (`--model`), not a per-request
570
- * field — `continueConversation` sends the next turn to whatever process is
571
- * already running, so a plain config read here would silently keep planning
572
- * on the old model. No conversation yet is vacuously "current".
573
- */
574
- conversationMatchesConfig(): boolean;
575
- /** Always: a conversation whose planner cannot reach the Ordewell MCP server fails to start rather than falling back to the envelopes. */
576
- plannerToolsAttached(): boolean;
577
- /** The agent's own session id, a resumption hint only — Ordewell's transcript is authoritative (T4). */
578
- nativeSessionId(): string | null;
579
- heldSecrets(): readonly string[];
580
- restart(): void;
581
- reset(): void;
582
- startConversation(req: ConversationRequest): Promise<ConversationTurn>;
583
- continueConversation(userMessage: string, onProgress: (progress: ResearchProgress) => void, signal?: AbortSignal): Promise<ConversationTurn>;
584
- private openingMessage;
585
- /**
586
- * Drive one user message to a settled planner turn through
587
- * {@link settleReply}, the loop the API backend settles through too. The
588
- * tool rounds belong to the agent now, so one call is one agent turn —
589
- * continued while it left subagents running in the background.
590
- */
591
- private runConversation;
592
- /**
593
- * Run one agent turn: stream its events into `ResearchProgress`, collect the
594
- * reply text, and return the research steps produced. Tool calls are matched
595
- * to their results by the agent's own call id — matching by tool name puts
596
- * one file's body on another file's row the moment an agent runs two reads
597
- * at once, which all three of these do routinely.
598
- */
599
- /**
600
- * A turn, and then — while the agent ended it with subagents still in the
601
- * background — a turn asking it to wait for them: whatever they find would
602
- * otherwise be said into a closed turn, or, in a one-shot, into a session
603
- * already thrown away. Bounded by `waits`, shared across a reply's sends.
604
- */
605
- private runTurnAwaitingAgents;
606
- private runTurn;
607
- /**
608
- * The only way this service starts an agent, and it takes a planner start by
609
- * type: the read-only boundary (ADR-0008/0009) cannot be crossed into task
610
- * mode from here without changing this signature. A planner that cannot
611
- * reach its tools cannot submit a plan, so it is respawned once and never
612
- * left to plan without them (ADR-0025).
613
- */
614
- private startAdapter;
615
- private newAdapter;
616
- /** Whether the web-read policy lets the runner's own session-wide search run unasked now (ADR-0027). */
617
- private nativeSearch;
618
- /**
619
- * The live agent process, restarted from its own session id if it died
620
- * between turns. Resume is a hint: when it fails, the caller's next
621
- * `startConversation` reseeds from Ordewell's transcript, which is the same
622
- * degradation `restoreChat` already performs on every surface.
623
- *
624
- * Also restarted when the policy's decision on the runner's own search has
625
- * changed since the process was spawned: the running thread would otherwise
626
- * keep searching unasked after Full turned Guarded. Codex 0.162 applies
627
- * `thread/resume`'s `config.web_search` either way (checked live).
628
- */
629
- private ensureAdapter;
630
- /**
631
- * What the planner gets of the repository's config, its runner being
632
- * launched without it (ADR-0028): the instruction files, which are text, in
633
- * its system prompt, and the project MCP servers it may start. Decided once
634
- * per conversation, so a respawn starts the same servers without asking.
635
- */
636
- private repoConfig;
637
- private plannerModel;
638
- /**
639
- * A plan from one-shot agent sessions: only what a submit_plan call
640
- * accepted, never the reply text, so a plan the tool refused cannot come
641
- * back in as the JSON the agent echoed. A session that ends without an
642
- * accepted call is followed by one corrective session, then fails.
643
- */
644
- private submittedPlan;
645
- /**
646
- * A single agent session that answers one prompt and exits. Used by every
647
- * non-conversational entry point. A plan-shaped envelope in its text
648
- * streams to the plan display, as a vendor planner's one-shot does, and the
649
- * prose around it is not streamed at all, since no turn is open to show it in.
650
- */
651
- private oneShot;
652
- 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<{
653
- tasks: Task[];
654
- researchLog: ResearchLogEntry[];
655
- researchResults: string;
656
- }>;
657
- 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[]>;
658
- }
659
-
660
- /**
661
- * The planner tools a session offers its conversation (ADR-0022). A backend
662
- * that can inject the Ordewell MCP server into its planner issues a planner
663
- * token bound to `sessionId` and these handlers; API planners use the same task read handlers directly in their tool loop.
664
- */
665
- interface PlannerToolsOffer {
666
- sessionId: string;
667
- handler: PlannerToolHandler;
668
- }
669
- /**
670
- * Everything the conversation loop needs to start planning (ADR-0002).
671
- * The AI service keeps the tool-use message history internally across turns;
672
- * the surfaces only exchange user/assistant messages with it.
673
- */
674
- interface ConversationRequest {
675
- goal: string;
676
- runners: RunnerId[];
677
- modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>;
678
- fs: IFileSystem;
679
- onProgress: (progress: ResearchProgress) => void;
680
- fetcher?: IWebFetcher;
681
- runnerModes?: Record<RunnerId, RunnerModeInfo[]>;
682
- autonomousDefault?: boolean;
683
- /**
684
- * The planner model's context window when the catalog knows it. Threaded here
685
- * so a usage record can carry it (and the UI can show context fill); absent
686
- * when unknown, never guessed (#49).
687
- */
688
- contextWindow?: number;
689
- /** Where tasks will run (ADR-0013, ADR-0014): in worktrees, the prompt drops file-overlap ordering and describes a repo group. */
690
- isolatedExecution?: IsolatedExecution;
691
- /** A plan the planner submits starts at once (ADR-0030), so the opening prompt has no outline gate. */
692
- autorun?: boolean;
693
- signal?: AbortSignal;
694
- /**
695
- * Persisted dialogue to seed a resumed conversation (session reload). The
696
- * turns are injected into the message history verbatim — no LLM call happens
697
- * for them; the first call is the turn opened by `initialMessage`.
698
- */
699
- priorHistory?: ConversationMessage[];
700
- /**
701
- * The user message that opens this turn. Defaults to `goal` — set it when
702
- * resuming so the system prompt keeps the original goal while the turn
703
- * carries the user's new message.
704
- */
705
- initialMessage?: string;
706
- plannerTools?: PlannerToolsOffer;
707
- /**
708
- * The session's approval policy, which a harness planner's own tool
709
- * requests are decided against as the API planner's are (ADR-0026). Absent:
710
- * nobody can be asked, so they are denied.
711
- */
712
- approval?: IApproval;
713
- /**
714
- * The workspace's skill catalog. Every planner is shown the task skills it
715
- * may attach; only one with tools is shown the planner skills, which load
716
- * through `load_skill` alone (ADR-0024).
717
- */
718
- skills?: readonly SkillInfo[];
719
- }
720
- /**
721
- * One planner turn's outcome. The planner talks to the user (`message`),
722
- * commits a full plan (`plan`, a `{tasks:[...]}` JSON object), emits targeted
723
- * task edits (`task_ops`, a `{taskOps:[...]}` JSON object), or asks to read
724
- * task bodies and the full catalog before editing (`task_query`, a
725
- * `{taskQuery:{...}}` JSON object). The Session validates and applies task ops
726
- * atomically and answers queries out of its own state; the AI service only
727
- * parses them.
728
- */
729
- type ConversationTurn =
730
- /**
731
- * `aborted`: the call was stopped, and `text` is only what it had by then.
732
- * `failed`: the call ended without a reply (a dead agent, a usage limit),
733
- * and `text` says why.
734
- */
735
- {
736
- kind: 'message';
737
- text: string;
738
- researchLog: ResearchLogEntry[];
739
- aborted?: boolean;
740
- failed?: boolean;
741
- } | {
742
- kind: 'plan';
743
- tasks: Task[];
744
- text: string;
745
- researchLog: ResearchLogEntry[];
746
- } | {
747
- kind: 'task_ops';
748
- ops: TaskOp[];
749
- text: string;
750
- researchLog: ResearchLogEntry[];
751
- } | {
752
- kind: 'task_query';
753
- query: TaskQuery;
754
- text: string;
755
- researchLog: ResearchLogEntry[];
756
- };
757
- interface IAiService {
758
- /** Credential values from the harness planner's effective environment, for the outbound secret check. */
759
- heldSecrets?(): readonly string[];
760
- /**
761
- * Begin the planner conversation: collect workspace context, run the
762
- * research/tool loop, and return the first planner turn. The service
763
- * retains the full API message history for subsequent
764
- * {@link continueConversation} calls.
765
- */
766
- startConversation(req: ConversationRequest): Promise<ConversationTurn>;
767
- /**
768
- * Feed the user's reply into the active conversation and return the next
769
- * planner turn. Throws if no conversation is active.
770
- */
771
- continueConversation(userMessage: string, onProgress: (progress: ResearchProgress) => void, signal?: AbortSignal): Promise<ConversationTurn>;
772
- /**
773
- * Whether a planner conversation is currently held in memory. A branching
774
- * predicate only — never a test for whether there is anything to release.
775
- * The harness backend holds an OS process that outlives its conversation, so
776
- * guarding {@link reset} with this leaks an agent process; call `reset`
777
- * unconditionally instead.
778
- */
779
- hasActiveConversation(): boolean;
780
- /**
781
- * Optional: end the planner's live process but keep its own session id, so
782
- * the next turn starts a fresh one resumed from it (ADR-0009). What a
783
- * supervisor does to a planner that crashed or hung (ADR-0029). Absent where
784
- * there is no process to end: the next call is a fresh request anyway.
785
- */
786
- restart?(): void;
787
- /**
788
- * Whether the active conversation (if any) still matches the planner
789
- * model/effort currently configured. Optional, and true when absent: a
790
- * vendor backend re-reads its model from config on every turn, so there is
791
- * nothing to drift. A harness planner (ADR-0009) is the exception — its
792
- * model is baked into the agent process at spawn — so only
793
- * {@link CliAgentAiService} answers this for real. False tells the caller
794
- * the same thing an absent conversation would: don't call
795
- * `continueConversation`, restart instead so the new model takes effect.
796
- */
797
- conversationMatchesConfig?(): boolean;
798
- /**
799
- * Whether the planner reads the catalog and submits plans through Ordewell's
800
- * MCP tools (ADR-0022). Optional, and false when absent: only a harness
801
- * planner the server attached to has them, and every other planner keeps
802
- * the per-turn catalog and the envelopes.
803
- */
804
- plannerToolsAttached?(): boolean;
805
- /**
806
- * Optional: prune bulky raw tool output out of the live conversation, in
807
- * place, keeping subagent digests. Returns the characters removed. Absent
808
- * where the backend holds no tool transcript to prune (a harness planner's
809
- * agent owns its own context).
810
- */
811
- pruneContext?(): number;
812
- /**
813
- * One-shot research + plan for non-conversational surfaces (CLI `plan --goal`,
814
- * web REST). Never asks questions — it plans with what it can find.
815
- */
816
- 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<{
817
- tasks: Task[];
818
- researchLog: ResearchLogEntry[];
819
- researchResults: string;
820
- }>;
821
- 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[]>;
822
- /**
823
- * Release everything this service holds: the conversation, any in-flight
824
- * turn, and — on the harness backend — the agent process itself. Idempotent
825
- * and cheap in every implementation, so callers never gate it.
826
- */
827
- reset(): void;
828
- }
829
- /**
830
- * The single branch point between planner transports. Two of them talk HTTP to
831
- * an LLM vendor; the third (ADR-0009) drives a coding agent already installed
832
- * on the machine. Everything downstream — the plan contract, the research-step
833
- * stream, the four surfaces — is shared.
834
- */
835
- declare function createAiService(config: IConfig, deps?: CliAgentAiServiceDeps): IAiService;
836
-
837
- /** The dialogue record a fork carries — what {@link PlannerConversation.clone} returns. */
838
- interface ForkedDialogue {
839
- conversationHistory: ConversationMessage[];
840
- researchLog: ResearchLogEntry[];
841
- }
842
-
843
- /** A user's message as sent, and the skills its `/name` tokens load. */
844
- interface SkillInvocation {
845
- text: string;
846
- skills: SkillLoad[];
847
- }
848
- /**
849
- * A path with the home directory written `~`, for a notice or listing. On
850
- * Windows the drive letter's case is not significant and the separators are
851
- * rendered `/`, so the same skill reads alike wherever the session reloads.
852
- */
853
- declare function abbreviateHome(file: string, home?: string, platform?: NodeJS.Platform): string;
854
- /**
855
- * Resolve the `/skill-name` tokens in a message to snapshots of the skills they
856
- * name. Pure and exported so surfaces can resolve without a full Session. The text itself is never rewritten.
857
- *
858
- * Every distinct skill named loads, once, in the order first named. A token
859
- * naming no skill is plain text, wherever it stands — a whole message of
860
- * `/unknown` included: embedded in a sentence it is as likely to be incidental
861
- * text (a path, an example command) as a typo'd invocation. So is one naming
862
- * a skill with `user-invocable: false`, which only the model may invoke.
863
- */
864
- declare function resolveSkillInvocation(text: string, skillsService: Pick<SkillsService, 'findSkill'>, home?: string): SkillInvocation;
865
- /**
866
- * What the planner receives for a user message: the bodies of the skills it
867
- * loaded, then the message as the user wrote it. The one composition for a
868
- * live send and for a replay of the transcript, so a resumed conversation
869
- * hands the planner what the live one did.
870
- */
871
- declare function plannerMessage(text: string, loads: readonly SkillLoad[]): string;
872
- /**
873
- * The transcript as the planner is to see it on a replay: each user message
874
- * carries the skills loaded after it, composed by {@link plannerMessage}, and
875
- * the load entries themselves are folded away. Sessions saved before skill
876
- * loads existed hold the expanded text in the message and replay unchanged.
877
- */
878
- declare function plannerTranscript(history: readonly ConversationMessage[]): ConversationMessage[];
879
-
880
- /**
881
- * The planner turn (see CONTEXT.md): the one piece of planner work that holds
882
- * the conversation, from its claim to its settled outcome. Its signal is what a
883
- * stop aborts.
884
- */
885
- declare class PlannerTurn {
886
- private readonly controller;
887
- private dropped;
888
- private forced;
889
- get signal(): AbortSignal;
890
- /** The plan it was answering was dropped under it, so nothing it settles may land. */
891
- get abandoned(): boolean;
892
- /** A force send stopped it, not the user: the forced turn follows. A turn already stopped stays the user's stop. */
893
- get forceSent(): boolean;
894
- stop(by?: 'force_send'): void;
895
- abandon(): void;
896
- }
897
- /** What a planner turn may change: the whole plan, or tasks through edits. */
898
- type PlanChange = 'plan' | 'edit';
899
- /**
900
- * A wake-up turn (ADR-0029): a supervisor's turn on wake-up digests alone, no
901
- * user message in it. What it may do is its level's, decided by the host.
902
- */
903
- interface WakeUpTurn {
904
- /** Reads the turn may make, in place of a user message's caps. */
905
- readBudget: number;
906
- /**
907
- * Why a whole plan, or a task edit, is refused in this turn, worded for the
908
- * model; undefined when its level allows it. Asked when the change is made
909
- * and again as it lands, so a level lowered during the turn applies at once.
910
- */
911
- refused(change: PlanChange): string | undefined;
912
- /** 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. */
913
- brief?: string;
914
- }
915
- /** 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. */
916
- type ToolRead<T> = {
917
- status: 'refused';
918
- } | {
919
- status: 'answered';
920
- value: T;
921
- landNow: boolean;
922
- };
923
- /** A plan or task edit handed to the open turn over a channel other than its reply (ADR-0022). */
924
- type PlannerSubmission = {
925
- kind: 'plan';
926
- tasks: Task[];
927
- } | {
928
- kind: 'task_ops';
929
- ops: TaskOp[];
930
- };
931
- /** Everything (re)opening a conversation needs besides the dialogue itself. */
932
- type ConversationOpening = Omit<ConversationRequest, 'goal' | 'onProgress' | 'signal' | 'priorHistory' | 'initialMessage'>;
933
- /**
934
- * What the conversation needs from the session that hosts it. The session keeps
935
- * plan state, persistence and scheduling; the conversation reaches them only
936
- * through here, so a fake host is enough to drive it.
937
- */
938
- interface PlannerConversationHost {
939
- /** The plan the transcript lives on (`conversationHistory`, `researchLog`). */
940
- plan(): LegacyPlanState | null;
941
- goal(): string;
942
- aiService(): IAiService;
943
- onProgress(progress: ResearchProgress): void;
944
- /** Fresh discovery, allowlist-filtered the way the system prompt shows it. */
945
- opening(runners: RunnerId[]): Promise<ConversationOpening>;
946
- /** What the per-turn catalog block and every read draw from, as of now. */
947
- catalog(): TaskQueryCatalog;
948
- /** Whether autorun is on now (ADR-0030); the planner is told in the turn after it changes. */
949
- autorun(): boolean;
950
- /** Run updates no turn has carried yet, taken by the turn that asks: the next turn is told, whoever opens it. */
951
- takeRunUpdates(): string[];
952
- tasks(): readonly Task[];
953
- /**
954
- * The orchestrator's live capture for a task's latest attempt, backing the
955
- * `output` field of a read. Injected the same way as the catalog so the
956
- * conversation never reaches into execution state directly.
957
- */
958
- liveOutput: LiveOutputLookup;
959
- isExecuting(): boolean;
960
- /** The session's single mutation ritual: op → persist → notify (default: the plan). */
961
- mutate(op: () => boolean, notify?: () => void): LegacyPlanState | null;
962
- broadcast: SessionBroadcaster;
963
- /** `turnId`: the planner turn whose commit this is, when one is. */
964
- broadcastPlan(turnId?: string): void;
965
- /** Validate a batch against live state. Pure — nothing is applied. */
966
- validateOps(ops: TaskOp[]): ApplyTaskOpsResult;
967
- /** The workspace's skill catalog under its repo group's layout, which the skills a plan or edit attaches are checked against. */
968
- taskSkills(): SkillLookup;
969
- /** Tell the user something the transcript does not carry. */
970
- notice(level: SessionNotice['level'], message: string): void;
971
- /** Load planner-produced tasks: an edit keeps run state, a commit starts over. Returns how many landed. */
972
- adoptTasks(tasks: readonly Task[], how: 'edit' | 'commit'): number;
973
- capturePrd(text: string): void;
974
- /** The planner turn was stopped or abandoned: whatever it is waiting on (a parked approval) will never be answered. */
975
- turnAborted(): void;
976
- /** A planner turn let the conversation go, so the next one may take it. */
977
- turnReleased?(): void;
978
- }
979
- /**
980
- * A conversation edit (rewind, fork) the conversation refused because the
981
- * request itself is wrong — no such message, nothing to fork. Transport-agnostic
982
- * like `PlanEditError`: a route maps it to a status, core carries none.
983
- */
984
- declare class ConversationEditError extends Error {
985
- constructor(message: string);
986
- }
987
- /**
988
- * Refused because a planner turn is in flight. Distinct from a bad request:
989
- * the same call succeeds once the turn settles, and the turn would otherwise
990
- * append its reply to a transcript that no longer holds the message it
991
- * answers.
992
- */
993
- declare class ConversationBusyError extends ConversationEditError {
994
- constructor(operation: string);
995
- }
996
- /**
997
- * A planner turn that settled after the plan it was answering was dropped (a
998
- * new plan, another session adopted, the session closed), or after its call
999
- * was cut off by something other than its own stop. Nothing it produced was
1000
- * committed: landing it would write one session's reply into another's
1001
- * transcript.
1002
- */
1003
- declare class PlannerTurnDiscardedError extends Error {
1004
- constructor(options?: ErrorOptions);
1005
- }
1006
- /**
1007
- * The user stopped the turn and the backend reported that as a thrown error
1008
- * rather than a settled reply. Typed so a surface can stay quiet for it without
1009
- * reading the message: some SDKs' abort errors are plain `Error`s by name, and
1010
- * a real failure can mention "aborted" too.
1011
- */
1012
- declare class PlannerTurnStoppedError extends Error {
1013
- constructor(options?: ErrorOptions);
1014
- }
1015
- /** A user message a rewind can fork the conversation from just before. */
1016
- interface RewindTarget {
1017
- /** Position in the transcript — what {@link PlannerConversation.cloneBefore} takes. */
1018
- index: number;
1019
- preview: string;
1020
- /** The whole message — the preview cuts it to one line, and a surface confirming the rewind quotes it. */
1021
- content: string;
1022
- timestamp: string;
1023
- }
1024
- /** What a rewind forks from, and the message it lands just before. */
1025
- interface RewoundDialogue {
1026
- dialogue: ForkedDialogue;
1027
- rewoundMessage: string;
1028
- }
1029
- /** What a compaction left behind. */
1030
- interface ConversationCompaction {
1031
- summary: string;
1032
- /** Transcript entries kept verbatim after the summary entry. */
1033
- keptMessages: number;
1034
- }
1035
- /** A point a failed turn returns the dialogue to. Opaque outside this module. */
1036
- interface TranscriptSnapshot {
1037
- readonly plan: LegacyPlanState;
1038
- readonly history: ConversationMessage[] | undefined;
1039
- readonly researchLog: ResearchLogEntry[] | undefined;
1040
- readonly persisted: number;
1041
- readonly savedInBackground: number;
1042
- }
1043
- interface ReplyOptions {
1044
- signal?: AbortSignal;
1045
- /**
1046
- * Run inside the mutation that lands the turn's outcome — the edit, the
1047
- * plan or the reply — so what the caller drops with it (the inputs the turn
1048
- * answered) leaves disk in the same save.
1049
- */
1050
- onCommit?: () => void;
1051
- /** Wake-up digests delivered with this turn (ADR-0029), oldest first, ahead of the user's message if there is one. */
1052
- wakeUps?: readonly string[];
1053
- /** Set when the turn is a wake-up's alone. */
1054
- wakeUp?: WakeUpTurn;
1055
- /** The user's message was force sent: the turn replaces the one it stopped (ADR-0023). */
1056
- forced?: boolean;
1057
- }
1058
- /**
1059
- * The planner conversation (ADR-0002), end to end: the persisted dialogue
1060
- * record, the live model context behind it, and every turn from the user's
1061
- * message to a settled, persisted outcome.
1062
- *
1063
- * It is the only writer of `conversationHistory` and of the planner's
1064
- * `researchLog`. The writes land on the host's plan object — which is what the
1065
- * session persists and broadcasts — but only ever inside the host's mutation
1066
- * ritual or ahead of a turn that will either reach it or be rolled back.
1067
- *
1068
- * The live model context (a vendor service's message list, a harness planner's
1069
- * process and native session id) is disposable: {@link reset} drops it and the
1070
- * next turn is replayed from the transcript. That is what lets the transcript
1071
- * be replaced by a summary, or copied into a fork, without the model's memory
1072
- * drifting from it.
1073
- */
1074
- declare class PlannerConversation {
1075
- private readonly host;
1076
- /** Bumped on every persist, so a rollback can tell whether its writes already reached disk. */
1077
- private persisted;
1078
- /** Saves an execution event made while a turn may be in flight; see {@link restore}. */
1079
- private savedInBackground;
1080
- /** What the planner was last told of autorun: by its opening prompt, or a turn's block since. */
1081
- private toldAutorun;
1082
- private live;
1083
- private openTurn;
1084
- constructor(host: PlannerConversationHost);
1085
- get transcript(): readonly ConversationMessage[];
1086
- /** Whether a planner turn holds the conversation. */
1087
- get isTurnInFlight(): boolean;
1088
- /** Stop the planner turn in flight; it settles as stopped. False when none is. */
1089
- stopTurn(by?: 'force_send'): boolean;
1090
- /**
1091
- * Drop the planner turn in flight together with the plan it was answering:
1092
- * it is stopped, whatever it settles is discarded, and the conversation is
1093
- * free at once rather than when the abandoned turn finishes unwinding.
1094
- */
1095
- abandonTurn(): void;
1096
- /**
1097
- * Free the conversation from a turn that did not end when stopped — a hung
1098
- * wake-up (ADR-0029). Unlike {@link abandonTurn} the plan stays, so what the
1099
- * turn wrote is taken back as its failure would have, and its end is
1100
- * announced: the turn itself may never get there.
1101
- */
1102
- abandonHungTurn(): void;
1103
- /**
1104
- * Run planner work that is not a conversation turn — one-shot plan
1105
- * generation — as the planner turn, so it is stopped, refused and superseded
1106
- * like one. It starts a fresh plan, so it supersedes the turn in flight.
1107
- */
1108
- hold<T>(signal: AbortSignal | undefined, body: (turn: PlannerTurn) => Promise<T>): Promise<T>;
1109
- /**
1110
- * Take the conversation for a new planner turn. A caller's own signal stops
1111
- * the turn too; the relay is detached on release because a caller may reuse
1112
- * that signal, and a stale listener would stop the next turn with it.
1113
- */
1114
- private claim;
1115
- /** The user turn being answered, for what the host raises during it — an approval the turn's research asks for. */
1116
- get currentTurnId(): string | undefined;
1117
- /** The open turn's wake-up rules, when the supervisor is answering it rather than the user's message. */
1118
- get currentWakeUp(): WakeUpTurn | undefined;
1119
- recordPlannerSkill(skill: SkillLoad): boolean;
1120
- /**
1121
- * Hand the open turn a plan (already validated) or a task edit. The turn
1122
- * settles with it exactly as with the same plan or ops parsed out of its
1123
- * reply, and it wins over any envelope that reply carries. A later
1124
- * submission replaces an earlier one. False when no user turn is open.
1125
- */
1126
- submit(submission: PlannerSubmission): boolean;
1127
- /**
1128
- * Tell surfaces what the turn's edit would leave, from the same applier that
1129
- * lands it, so an edit that would not apply shows nothing. It stays shown
1130
- * from the moment it is handed in until it lands, is refused, or the turn
1131
- * ends: the backend's reply claims it, but it has not landed by then.
1132
- */
1133
- private showHeld;
1134
- private clearHeld;
1135
- /**
1136
- * The edit the open turn already holds, for a further edit in the same reply
1137
- * to join rather than replace. Empty when it holds nothing; null when it holds
1138
- * a whole plan, which an edit cannot be layered on.
1139
- */
1140
- pendingOps(): TaskOp[] | null;
1141
- /**
1142
- * A read the planner makes through a tool. It spends the same per-turn budget
1143
- * as an envelope read, so a planner cannot read more by switching channels:
1144
- * past the soft limit, or on a repeated question, the answer still comes but
1145
- * is told to land the turn; at the hard limit it is refused. `signature`
1146
- * names the question, for the repeat check.
1147
- */
1148
- read<T>(signature: string, answer: () => Promise<T>): Promise<ToolRead<T>>;
1149
- /** Whether the model still holds this conversation in memory. */
1150
- get isActive(): boolean;
1151
- append(role: ConversationMessage['role'], content: string, opts?: {
1152
- timestamp?: string;
1153
- kind?: ConversationMessage['kind'];
1154
- }): void;
1155
- private appendEntries;
1156
- /** Swap the whole transcript. The live context no longer matches it, so pair with {@link reset}. */
1157
- replace(messages: readonly ConversationMessage[]): void;
1158
- snapshot(): TranscriptSnapshot | null;
1159
- /**
1160
- * Put the dialogue back where {@link snapshot} found it — unless anything
1161
- * was persisted since, because then memory already matches disk and every
1162
- * surface, and undoing it would erase work the user has seen land. Also a
1163
- * no-op once a different plan has been adopted.
1164
- *
1165
- * A background save (a task settling mid-turn) wrote the turn's writes to
1166
- * disk without landing anything the user saw, so it does not stop the undo;
1167
- * the undo is saved in turn, or a reload would bring the writes back.
1168
- */
1169
- restore(snapshot: TranscriptSnapshot): boolean;
1170
- /**
1171
- * The host calls this after every persist. `background` is a save no call
1172
- * of the user's made — an execution event landing mid-turn.
1173
- */
1174
- markPersisted(opts?: {
1175
- background?: boolean;
1176
- }): void;
1177
- /**
1178
- * Drop the live model context. Idempotent; the harness backend holds an OS
1179
- * process here, so callers never gate it on {@link isActive}.
1180
- */
1181
- reset(): void;
1182
- /**
1183
- * The user messages a rewind may land before. The opening message is the
1184
- * goal: cutting it leaves a conversation about nothing, which is a new
1185
- * session, not a rewind. After a compaction the summary entry, always
1186
- * first, plays the goal's part: what it replaced is gone, so a rewind stops
1187
- * at it.
1188
- */
1189
- rewindTargets(): RewindTarget[];
1190
- /**
1191
- * Replace the transcript with a summary of it, keeping the last two
1192
- * exchanges as they were, and drop the live context so the next message
1193
- * replays from the shorter record.
1194
- *
1195
- * The summary is one hidden turn through whichever planner is configured —
1196
- * on the live context when it still matches, replayed from the transcript
1197
- * when not — so the compaction is the same for a vendor API and a harness
1198
- * agent. Whatever the turn emits besides the summary is discarded, ops
1199
- * included: it condenses the conversation, never the plan. Nothing is
1200
- * written until the summary is in hand, so a failed or stopped turn leaves
1201
- * the transcript exactly as it was.
1202
- */
1203
- compact(signal?: AbortSignal): Promise<ConversationCompaction>;
1204
- private summarize;
1205
- /**
1206
- * A copy of the dialogue record for a forked session to carry. Refused
1207
- * mid-turn: the copy would hold the user's message without the reply to it.
1208
- * The live context is not part of it — the fork replays from this record on
1209
- * its first turn, like any adopted session.
1210
- */
1211
- clone(): ForkedDialogue;
1212
- /**
1213
- * A copy of the dialogue as it stood just before the user message at
1214
- * `index` (a position from {@link rewindTargets}) — what a rewind forks
1215
- * from — plus that message's full text, for a surface to offer back. This
1216
- * conversation is not touched. The research trace is cut at the same point
1217
- * by time: its entries carry no link to the message that caused them.
1218
- */
1219
- cloneBefore(index: number): RewoundDialogue;
1220
- /**
1221
- * What the skill check of a landed plan or edit warned about, kept in the
1222
- * transcript so every surface shows it, on reload too, and not only the
1223
- * planner that may have read it in a tool result. Call inside the host's
1224
- * mutation ritual, so the broadcast plan carries it.
1225
- */
1226
- recordSkillWarnings(warnings: readonly string[], timestamp?: string): void;
1227
- /**
1228
- * A call at the command seam (ADR-0029), kept where it happened in the
1229
- * dialogue: a reload shows it, and a planner replayed from the transcript
1230
- * knows what was started or stopped, and by whom. Call inside the host's
1231
- * mutation ritual.
1232
- */
1233
- recordExecution(record: ExecutionRecord, text: string): void;
1234
- /**
1235
- * Open the conversation on the host's fresh plan: the goal is its first
1236
- * message. `prepare` (discovery) runs inside the turn, so a stop reaches it.
1237
- */
1238
- start(goal: SkillInvocation, prepare: () => Promise<ConversationOpening>, signal?: AbortSignal): Promise<LegacyPlanState>;
1239
- /**
1240
- * Every later user message, from the transcript append to a persisted
1241
- * outcome. A turn that throws before anything was persisted takes its own
1242
- * writes back out, so session memory never drifts from disk and the UI.
1243
- */
1244
- reply(message: string | SkillInvocation, options?: ReplyOptions): Promise<LegacyPlanState>;
1245
- private replyTurn;
1246
- /**
1247
- * A wake-up turn lands only a real answer. Stopped — by the user, or by its
1248
- * timeout — it lands nothing, not the fragment a backend hands back; a
1249
- * failure is thrown rather than shown, for the supervisor to retry
1250
- * (ADR-0029).
1251
- */
1252
- private assertWakeUpAnswered;
1253
- private assertIdle;
1254
- /**
1255
- * Refuse to land a turn that no longer belongs here: it was abandoned, its
1256
- * plan is no longer the host's, or its call was cut off by something other
1257
- * than its own stop (`IAiService.reset` under a plan change) — what such a
1258
- * call hands back is a fragment of an answer to a dialogue that is gone.
1259
- */
1260
- private assertCurrent;
1261
- /**
1262
- * {@link assertCurrent} on the far side of an await that precedes a plan
1263
- * mutation: the skill check waits on git, and a stop, rewind or session
1264
- * switch in that window drops the edit instead of landing it on whatever
1265
- * plan is current by then. A stop counts here because nothing has landed
1266
- * yet — the user asked for the turn to end before it changed the plan.
1267
- */
1268
- private assertStillCurrent;
1269
- /**
1270
- * A live conversation is only safe to continue in-place when it also
1271
- * matches the model/effort configured right now — a harness planner's
1272
- * running process was spawned with the old one baked in and cannot pick up
1273
- * a switch (ADR-0009). Otherwise the turn resumes from the transcript.
1274
- */
1275
- private canContinueLive;
1276
- /**
1277
- * Bracket one user turn with its start and end, under an id minted here:
1278
- * the turn is where the stream a surface draws begins and ends, and only the
1279
- * conversation sees all of it — every backend call, read and retry.
1280
- */
1281
- private userTurn;
1282
- private requirePlan;
1283
- /** The message as typed, then one entry per skill it loaded, sharing its timestamp so a reload keeps them together. */
1284
- private userEntries;
1285
- private recordUser;
1286
- /**
1287
- * Write the turn's held inputs inside the mutation that lands the outcome —
1288
- * call at its top, before the assistant entries, so the transcript reads in
1289
- * the order it happened and the inputs leave the plan in the save that
1290
- * carried their answer (ADR-0029). They go back where the turn opened, ahead
1291
- * of whatever it wrote as it ran (a planner skill load, a command at the
1292
- * seam), not at the end.
1293
- */
1294
- private commitInput;
1295
- private recordResearch;
1296
- /**
1297
- * Reopen the model context from the transcript — because the in-memory one
1298
- * is gone (session reload, extension restart), or because it is stale
1299
- * against the planner config now in effect. No LLM call happens for the
1300
- * replayed turns; the first call is the one the user's message opens.
1301
- */
1302
- private resume;
1303
- /**
1304
- * Answer every read the planner emits until it says something else.
1305
- *
1306
- * The channel is a text envelope rather than a registered tool because the
1307
- * protocol has to be identical on both planner backends (ADR-0009): Ordewell
1308
- * owns a tool loop only in the API case, and a harness planner running as a
1309
- * coding-agent subprocess can only be reached this way.
1310
- *
1311
- * Draining here — outside {@link repairLoop} — is what keeps reads free of
1312
- * the repair budget. A planner that looks a task up and *then* fumbles its
1313
- * ops JSON still gets its two corrective retries; charging it for the read
1314
- * would cost it the chance to fix the edit.
1315
- */
1316
- private drainTaskQueries;
1317
- /**
1318
- * The backend's answer, with what was handed in while it ran standing in
1319
- * for whatever its reply carried. Taken once: a corrective re-send after a
1320
- * rejected edit must not settle on the same submission again. A stopped
1321
- * turn commits nothing, the same as a stopped reply with a plan in it.
411
+ * round between the two.
1322
412
  */
1323
- private claimSubmission;
413
+ private skippedRefusal;
1324
414
  /**
1325
- * Render one read out of live state. Never persisted to the transcript: the
1326
- * detail is context for the planner's next reply, and re-sending it on every
1327
- * later turn is exactly the cost this channel exists to avoid.
415
+ * A skip ends a task without a verdict and lands nothing, so it never
416
+ * vouches for work. Anyone but the user names why, and the supervisor only
417
+ * gives up on a task that already needs someone: failed, blocked, or
418
+ * waiting for input.
1328
419
  */
1329
- private taskQueryAnswer;
420
+ private prepareSkip;
1330
421
  /**
1331
- * Drive a planner turn, its reads already drained, to a persisted, broadcast
1332
- * outcome. Task edits apply atomically; validation failures are fed back to
1333
- * the model for up to 2 silent retries, then surfaced as a message with the
1334
- * plan untouched. The first turn and every later turn route through here —
1335
- * one path, not two.
1336
- */
1337
- private settle;
1338
- /** A name not found at submit only warns: a task the attaching one depends on may create it. */
1339
- private warnSkills;
1340
- /** Validate + commit a task_ops turn atomically. Returns the errors on rejection (plan untouched). */
1341
- private applyTaskOps;
1342
- /** The edit was refused as it landed: what it showed is not coming. */
1343
- private refusedHeld;
1344
- /** Commit a settled (non-task_ops) turn through the host's mutation ritual. */
1345
- private commit;
1346
- /**
1347
- * The autorun rule (ADR-0030) when it changed since the planner was last
1348
- * told — in the opening prompt or an earlier turn — so a switch mid-
1349
- * conversation reaches its next turn without repeating in every one.
1350
- */
1351
- private autorunBlock;
1352
- private runUpdatesBlock;
1353
- /**
1354
- * The catalog the planner may actually draw from — model ids and task-mode
1355
- * ids, per runner in the plan — emitted on EVERY turn, before any plan exists
1356
- * or after. The system prompt shows this once at conversation start; a long
1357
- * clarifying conversation outlives that single showing and the planner
1358
- * starts misquoting it, so this re-states it per turn instead.
1359
- *
1360
- * The host's catalog is allowlist-filtered, so a restricted allowlist stays a
1361
- * hard bound on every turn, and reads the plan's runners live, so a runner
1362
- * admitted mid-session by a retarget is shown like every other. A planner
1363
- * with Ordewell's tools gets a reminder to read the catalog instead.
422
+ * Why a start is not the user's to have made, or the task it asks them to
423
+ * approve, or null: the user's own call is its own approval.
1364
424
  */
1365
- private catalogBlock;
425
+ private unapproved;
1366
426
  /**
1367
- * The "you are here" block for post-plan chat: current tasks with stable
1368
- * references, plus the read and edit protocols. Injected per turn (never
1369
- * persisted) so the model always sees live statuses — including which tasks
1370
- * are locked by a running execution.
427
+ * The Guarded question, on the card the user answers other planner requests
428
+ * on. It names every task a yes approves, since a yes is the user's
429
+ * approval of them as Execute plan would be.
1371
430
  */
1372
- private planContextBlock;
1373
- /** One line per task, subtasks indented under their parent, with stable references — null until the plan has tasks. */
1374
- private currentPlanLines;
1375
- }
1376
-
1377
- interface SessionMeta {
1378
- id: string;
1379
- goal: string;
1380
- runners: RunnerId[];
1381
- taskCount: number;
1382
- status: PlanStatus;
1383
- createdAt: string;
1384
- updatedAt: string;
1385
- }
1386
- interface SessionData {
1387
- meta: SessionMeta;
1388
- /** The full plan state — including conversationHistory, prdMarkdown, and queuedMessages. */
1389
- plan: LegacyPlanState;
431
+ private question;
432
+ /** What was asked, as "start task #4 "Title"". */
433
+ private intent;
434
+ private refuse;
435
+ private keep;
436
+ private all;
437
+ private located;
438
+ /** A reference read against the plan as the held edit leaves it, where the handles its batch gives come first. */
439
+ private locateAsEdited;
440
+ /** A reference as a caller writes one — see {@link resolveTaskRef}; the user's as they type one ({@link resolveUserTaskRef}). */
441
+ private locate;
1390
442
  }
1391
443
 
1392
444
  /**
@@ -1701,78 +753,6 @@ declare class HttpWebFetcher implements IWebFetcher {
1701
753
  private hop;
1702
754
  }
1703
755
 
1704
- /**
1705
- * The bridge between "core needs an answer" and "a human somewhere is looking
1706
- * at a UI". Core cannot prompt: the human may be at a browser, a TUI, a CLI
1707
- * stream, or a VS Code webview, and on the web server they are on the far end
1708
- * of a socket. So the Session parks a promise here, announces the request
1709
- * through the normal broadcast seam, and every surface answers through the
1710
- * same `resolve(id, granted)` call.
1711
- *
1712
- * Timeouts are load-bearing rather than defensive: a planner turn that blocks
1713
- * forever on an unanswered prompt would hang the whole research loop with no
1714
- * visible cause. On expiry the request resolves to denied and the model gets a
1715
- * normal, actionable tool result. A task runner's request is the exception
1716
- * (ADR-0018, A1): the runner is parked on it rather than a research loop, and
1717
- * it waits for a person — or the supervisor — however long that takes.
1718
- */
1719
- interface PendingApproval {
1720
- id: string;
1721
- request: ApprovalRequest;
1722
- createdAt: string;
1723
- }
1724
- interface PendingApprovalsOptions {
1725
- /** Denies and resolves after this long with no answer. Default 5 minutes. */
1726
- timeoutMs?: number;
1727
- /** Announce a new request to the surfaces. */
1728
- onRequest?: (pending: PendingApproval) => void;
1729
- /** Announce that a request is no longer actionable (answered or expired). */
1730
- onSettled?: (id: string, granted: boolean, settled: {
1731
- request: ApprovalRequest;
1732
- decision: ApprovalDecision;
1733
- }) => void;
1734
- }
1735
- interface AskOptions {
1736
- /**
1737
- * An id the caller already announced the request under. One still in use is
1738
- * refused — denied at once — rather than overwriting the request that has it.
1739
- */
1740
- id?: string;
1741
- /** Wait for an answer however long it takes. */
1742
- noTimeout?: boolean;
1743
- /**
1744
- * Given the answer as the request settles, before anything awaiting the
1745
- * promise runs: a caller about to tear down what asked (a runner being
1746
- * stopped) must deliver the answer before it goes.
1747
- */
1748
- onDecision?: (decision: ApprovalDecision) => void;
1749
- }
1750
- declare class PendingApprovals {
1751
- private readonly opts;
1752
- private readonly entries;
1753
- private readonly listeners;
1754
- constructor(opts?: PendingApprovalsOptions);
1755
- /** Hear each new request once it is announced — the supervisor wakes on a runner's (ADR-0029). */
1756
- listen(listener: (pending: PendingApproval) => void): () => void;
1757
- /** Park a request and return the promise the approval policy awaits. */
1758
- ask(request: ApprovalRequest, options?: AskOptions): Promise<boolean>;
1759
- /** Park a request whose answer is more than yes or no. */
1760
- decide(request: ApprovalRequest, options?: AskOptions): Promise<ApprovalDecision>;
1761
- /**
1762
- * Answer one request. Returns false when the id is unknown or already
1763
- * settled. "Allow for this task" on a request that did not offer it is a
1764
- * plain allow: no grant is made that the requester never proposed.
1765
- */
1766
- resolve(id: string, answer: ApprovalAnswer): boolean;
1767
- /** Everything still awaiting an answer — replayed to a surface that connects late. */
1768
- outstanding(): PendingApproval[];
1769
- /**
1770
- * Deny everything in flight, or only the requests `which` picks. Called on
1771
- * abort and on session reset.
1772
- */
1773
- clear(which?: (request: ApprovalRequest) => boolean, note?: string): void;
1774
- }
1775
-
1776
756
  /**
1777
757
  * Carries a structured task's tool requests to the session's one approval
1778
758
  * seam (ADR-0018, A1), so a runner's request is answered through
@@ -2157,6 +1137,35 @@ interface Adr0013PlanIsolation {
2157
1137
  */
2158
1138
  declare function migratePlanIsolation(state: PlanIsolation | Adr0013PlanIsolation): PlanIsolation;
2159
1139
 
1140
+ /** The part of a run's handoff a Merge confirmation is worded from; structural, so each surface passes the handoff it holds. */
1141
+ interface MergeWordingHandoff {
1142
+ repos: readonly {
1143
+ path: string;
1144
+ baseBranch?: string;
1145
+ landed: readonly unknown[];
1146
+ }[];
1147
+ }
1148
+ /**
1149
+ * Whether the run spans more than a lone repo at the workspace root. A group of
1150
+ * one at `.` is worded and drawn exactly as it was before repo groups; anything
1151
+ * else names its repos.
1152
+ */
1153
+ declare function isRepoGroup(handoff: {
1154
+ repos: readonly {
1155
+ path: string;
1156
+ }[];
1157
+ }): boolean;
1158
+ /** The repos Merge all will merge: those with work on their integration branch, else all of them. */
1159
+ declare function reposWithWork(handoff: MergeWordingHandoff): string[];
1160
+ /**
1161
+ * What a Merge confirmation says the branch lands on: the branch the run
1162
+ * started on, which Merge all insists is checked out. A repo that started
1163
+ * detached has none, and keeps the old wording.
1164
+ */
1165
+ declare function mergeTarget(handoff: MergeWordingHandoff): string;
1166
+ /** The refusal Merge all makes when a repo is on another branch, for a confirmation to warn of; empty when no repo named a branch. */
1167
+ declare function checkoutRule(handoff: MergeWordingHandoff): string;
1168
+
2160
1169
  interface ILogger {
2161
1170
  warn(scope: string, message: string, err?: unknown): void;
2162
1171
  }
@@ -2187,6 +1196,8 @@ interface ResearchTurn {
2187
1196
  finishReason?: string;
2188
1197
  /** Exact prompt tokens this turn consumed, when the provider reports usage — drives proactive compaction. */
2189
1198
  promptTokens?: number;
1199
+ /** The segment `text` streamed under, where the provider streams it. */
1200
+ segmentId?: string;
2190
1201
  }
2191
1202
  interface ResearchChat {
2192
1203
  sendMessage(text: string, signal?: AbortSignal): Promise<ResearchTurn>;
@@ -2285,8 +1296,12 @@ declare class GeminiService extends BaseAiService implements IAiService {
2285
1296
  private genAI;
2286
1297
  private genAIKey;
2287
1298
  private model;
1299
+ /** The id `model` was made for; the SDK's own `model.model` is its `models/…` resource path. */
1300
+ private modelId;
2288
1301
  constructor(config: IConfig);
2289
1302
  private init;
1303
+ /** The planner model picked for this session, as the API names it. */
1304
+ private plannerModel;
2290
1305
  private getPlanningModel;
2291
1306
  ensureInit(): void;
2292
1307
  reset(): void;
@@ -2441,13 +1456,15 @@ declare class ModelResolver {
2441
1456
  getCachedRunnerModels(runner: string): DiscoveredModel[];
2442
1457
  getCachedPickerOptions(): OrchestratorOption[];
2443
1458
  /**
2444
- * The planner model's context window, from whatever catalog is already
2445
- * cached (#49): a vendor model comes from the picker catalog, a harness
2446
- * planner's model from the runner's own discovered models. Reads only —
2447
- * never triggers a fetch or discovery of its own, so an unknown window stays
2448
- * unknown and context fill is omitted rather than guessed.
1459
+ * A model's context window from whatever catalog is already cached (#49),
1460
+ * for a usage record whose reporter stated none: a vendor's model from the
1461
+ * picker catalog (its own `/models` endpoint), a runner's model from that
1462
+ * runner's own discovered listing. `source` is the record's reporter, and
1463
+ * `model` the id it named. Reads only — never triggers a fetch or discovery
1464
+ * of its own, so an unknown window stays unknown and context fill is
1465
+ * omitted rather than guessed.
2449
1466
  */
2450
- contextWindowFor(modelId: string): number | undefined;
1467
+ contextWindowFor(source: string, model: string): number | undefined;
2451
1468
  invalidate(): void;
2452
1469
  }
2453
1470
 
@@ -2815,16 +1832,17 @@ declare class ClaudeCodeAdapter extends StdioAgentAdapter implements TaskModeAge
2815
1832
  /** An interrupt was sent during the current turn, so an aborted result is that interrupt, not a failure. */
2816
1833
  private interruptRequested;
2817
1834
  private readonly permissions;
2818
- /** Whether this turn has already emitted reply text — see {@link handleLine}. */
2819
- private turnHasText;
2820
- /** The text block streaming now follows earlier reply text, so its first delta opens the paragraph. */
2821
- private pendingBreak;
2822
- /** 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. */
2823
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;
2824
1842
  /**
2825
- * The session's `total_cost_usd` as last reported. Undefined after a resume:
2826
- * the CLI restores the resumed session's running total, and what Ordewell
2827
- * already counted of it is not ours to know here.
1843
+ * The session's `total_cost_usd` as last counted. A resume starts from what
1844
+ * was counted of that session before, and is undefined without it: the CLI
1845
+ * restores the session's running total, which holds turns counted earlier.
2828
1846
  */
2829
1847
  private reportedCostUsd;
2830
1848
  private reportedModelCostUsd;
@@ -2865,7 +1883,23 @@ declare class ClaudeCodeAdapter extends StdioAgentAdapter implements TaskModeAge
2865
1883
  private terminalOnly;
2866
1884
  /** Whether this process's `runner_loaded` went out: its first `init` sends it. */
2867
1885
  private loadedReported;
1886
+ /** The models `initialize` listed, which turn the ids the CLI reports back into the ones a plan holds. */
1887
+ private listedModels;
1888
+ /** The id the task's main thread last reported answering on. */
1889
+ private mainModel;
1890
+ /** What the plan should say the task runs: its spawn's model, then each switch followed. */
1891
+ private followedModel;
1892
+ /**
1893
+ * Model commands sent through the chat whose switch has not been seen yet,
1894
+ * oldest first, each with the turns still to end before its own has. Several
1895
+ * can be pending: a command's switch is first reported by the next turn's
1896
+ * `init`, which can be another `/model`'s.
1897
+ */
1898
+ private modelCommands;
2868
1899
  protected spawnSpec(opts: AgentStartOptions, workspace: Record<string, string>): SpawnSpec;
1900
+ sessionCost(): RunnerSessionCost | null;
1901
+ /** A count kept for another session says nothing of this one's. */
1902
+ private countFrom;
2869
1903
  savedOutput(abs: string): boolean;
2870
1904
  /**
2871
1905
  * A task's run: the manifest decides what its mode and effort mean
@@ -2937,6 +1971,12 @@ declare class ClaudeCodeAdapter extends StdioAgentAdapter implements TaskModeAge
2937
1971
  dispose(): void;
2938
1972
  private clearFollowOnTimer;
2939
1973
  protected turnPayload(message: string): string;
1974
+ /**
1975
+ * The CLI runs a message that starts with a slash command as that command,
1976
+ * whether the surface sent it as one or as plain text, so the message
1977
+ * itself says a model switch was asked for: every way into the chat counts.
1978
+ */
1979
+ private noteModelCommand;
2940
1980
  private openTurnState;
2941
1981
  /**
2942
1982
  * The echo of a steered message: the model has it. After a closed turn it
@@ -2945,6 +1985,29 @@ declare class ClaudeCodeAdapter extends StdioAgentAdapter implements TaskModeAge
2945
1985
  */
2946
1986
  private steerDelivered;
2947
1987
  protected handleLine(line: string, emit: (event: AgentEvent) => void): void;
1988
+ /**
1989
+ * A task's main thread reported the model it answers on. A change is the
1990
+ * user's own switch when a model command went out before it: Ordewell never
1991
+ * passes `--fallback-model`, so nothing else should move it. A change with
1992
+ * no command before it is reported as drift, and the plan does not follow
1993
+ * it. A command whose turn ended with the model unchanged switched nothing,
1994
+ * and is let go.
1995
+ *
1996
+ * Effort is never reported (recorded on 2.1.302, `task-effort-command`):
1997
+ * `init` names the model alone, its `per_turn_effort_active` says effort is
1998
+ * on, not which; `/effort low` only prints that it set it; and `/model`
1999
+ * takes no effort (`/model haiku high` is "not found"). So a switch carries
2000
+ * no effort and the plan's stays as planned.
2001
+ */
2002
+ private followModel;
2003
+ /** Whether the model value `asked` runs as the id `resolved`, by what `initialize` lists. */
2004
+ private runsAs;
2005
+ /**
2006
+ * The value `initialize` lists for the id a model runs as. Several can share
2007
+ * one (`sonnet`, `default`); the one asked for wins, and `default` only as a
2008
+ * last resort, since it names whatever the CLI's default becomes.
2009
+ */
2010
+ private listedModel;
2948
2011
  /**
2949
2012
  * `--permission-mode auto` on a model or account without auto mode is not
2950
2013
  * refused: the CLI says nothing and starts in `default`, then asks about every
@@ -2974,6 +2037,13 @@ declare class ClaudeCodeAdapter extends StdioAgentAdapter implements TaskModeAge
2974
2037
  * complete usage instead.
2975
2038
  */
2976
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;
2977
2047
  /**
2978
2048
  * Partial output of the planner's own message. The complete `assistant` line
2979
2049
  * for each block follows its deltas and is authoritative for that block (see
@@ -2984,7 +2054,8 @@ declare class ClaudeCodeAdapter extends StdioAgentAdapter implements TaskModeAge
2984
2054
  * What only the result line knows: the cost, which covers every call the
2985
2055
  * session made — subagents included, since none of their lines carries one —
2986
2056
  * and the planner model's window. `total_cost_usd` is a running total, so a
2987
- * turn reports its growth. The first turn after a resume only sets the
2057
+ * turn reports its growth. After a resume it grows from what was counted of
2058
+ * the session before; with no count kept, the first turn only sets the
2988
2059
  * baseline: its total includes turns counted before, and a turn's own share
2989
2060
  * cannot be told apart from them.
2990
2061
  */
@@ -3017,12 +2088,17 @@ declare class CodexAdapter extends StdioAgentAdapter implements TaskModeAgentAda
3017
2088
  private threadModel;
3018
2089
  private readonly rpc;
3019
2090
  private startOpts;
3020
- /** Whether this turn has already emitted prose — see the `agentMessage` case. */
3021
- private turnHasText;
2091
+ /** The planner thread's runs of reply text this turn, each under its `agentMessage` item's id. */
2092
+ private replyRuns;
3022
2093
  /** The turn in flight. `turn/interrupt` and `turn/steer` cannot be sent before it has an id. */
3023
2094
  private turn;
3024
2095
  /** The last turn this adapter ended — so its second end signal, landing late, ends nothing. */
3025
2096
  private endedTurnId;
2097
+ /**
2098
+ * The account's limit windows as Codex last reported them. Only read when a
2099
+ * turn fails on the usage limit; an update on its own acts on nothing.
2100
+ */
2101
+ private rateLimits;
3026
2102
  /** An interrupt was asked for during this turn, so however it ends, it was cut short. */
3027
2103
  private interruptRequested;
3028
2104
  /**
@@ -3116,6 +2192,7 @@ declare class CodexAdapter extends StdioAgentAdapter implements TaskModeAgentAda
3116
2192
  /** `turn/start`'s answer. One for a turn that has since closed or been replaced names and closes nothing. */
3117
2193
  private turnAnswered;
3118
2194
  protected handleLine(line: string, emit: (event: AgentEvent) => void): void;
2195
+ private turnFailed;
3119
2196
  private turnStarted;
3120
2197
  /**
3121
2198
  * Ahead of the event that ends the turn: a steer Codex accepted and the turn
@@ -3339,9 +2416,10 @@ declare class OpenCodeAdapter implements TaskModeAgentAdapter {
3339
2416
  private dropUnreadSteers;
3340
2417
  /**
3341
2418
  * Whatever of the turn's own messages the stream missed. Each part is
3342
- * emitted once, so a turn the stream saw whole adds nothing here; one that
3343
- * lost its last text part still reaches the plain-text channel, which a
3344
- * 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.
3345
2423
  */
3346
2424
  private readBack;
3347
2425
  /**
@@ -3363,7 +2441,7 @@ declare class OpenCodeAdapter implements TaskModeAgentAdapter {
3363
2441
  */
3364
2442
  interrupt(timeoutMs: number): Promise<boolean>;
3365
2443
  onProcessExit(listener: (code: number, lastWords?: string) => void): void;
3366
- answerPermission(id: string, decision: ApprovalDecision): boolean;
2444
+ answerPermission(id: string, decision: ApprovalDecision): boolean | Promise<boolean>;
3367
2445
  /**
3368
2446
  * OpenCode 2.x replaced the 1.x HTTP surface rather than extending it, and
3369
2447
  * only 2.x answers `/api/info` with its version. A 1.x server — or one that
@@ -3466,17 +2544,48 @@ declare function mapAgentTool(name: string): MappedTool;
3466
2544
  */
3467
2545
  declare function normalizeAgentArgs(tool: ResearchToolType, args: Record<string, unknown>): Record<string, unknown>;
3468
2546
 
3469
- /** What a runner offers a task: the models discovered for it and the modes its manifest declares. */
3470
- interface RunnerCatalog {
3471
- models: DiscoveredModel[];
3472
- modes: RunnerModeInfo[];
3473
- /**
3474
- * The mode a task lands on when it carries none this runner offers: the
3475
- * manifest's `autonomous`/`safe`-tagged mode under the user's toggle
3476
- * (ADR-0001). Absent, `modes[0]` stands in — which for Codex is its
3477
- * workspace-write sandbox, not the full access an unattended run needs.
3478
- */
3479
- 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;
3480
2589
  }
3481
2590
 
3482
2591
  type ModelsByRunner = Partial<Record<RunnerId, DiscoveredModel[]>>;
@@ -3491,6 +2600,8 @@ interface SessionCatalogDeps {
3491
2600
  };
3492
2601
  /** The runners of the plan being edited; empty while the session has none. */
3493
2602
  planRunners: () => RunnerId[];
2603
+ /** Whether a task's runner is up: an attempt claimed, running, quiet or waiting on someone. */
2604
+ liveRunner?: (taskId: string) => boolean;
3494
2605
  }
3495
2606
  /**
3496
2607
  * What a session may assign: the enabled runners, their modes, the models
@@ -3508,14 +2619,16 @@ declare class SessionCatalog {
3508
2619
  constructor(deps: SessionCatalogDeps);
3509
2620
  /** The runners enabled right now. */
3510
2621
  enabledRunners(): RunnerId[];
2622
+ /** The runners `writer` may put a task on now ({@link assignableRunners}); undefined means any. */
2623
+ runnersFor(writer: AssignmentWriter): RunnerId[] | undefined;
3511
2624
  /**
3512
2625
  * The allowlist in force right now. Unset means no restriction — falling
3513
2626
  * back to the one planning started under would keep a restriction the user
3514
2627
  * has since cleared.
3515
2628
  */
3516
2629
  allowlist(): Record<string, string[]>;
3517
- /** The models the user allows on one runner; unset means no restriction. */
3518
- allowlistFor(runner: RunnerId): string[] | undefined;
2630
+ /** The enabled runners and allowlist together, for whoever compares an assignment with them. */
2631
+ scope(): AssignmentScope;
3519
2632
  /**
3520
2633
  * The discovered catalog as the resolver holds it now, per runner, with this
3521
2634
  * session's own discovery as the fallback where the resolver has nothing
@@ -3544,18 +2657,29 @@ declare class SessionCatalog {
3544
2657
  * asked for.
3545
2658
  */
3546
2659
  live(): Promise<TaskQueryCatalog>;
2660
+ /**
2661
+ * What {@link live} answers, from what is cached and without discovering:
2662
+ * the enabled runners, however the plan was started. The per-turn block
2663
+ * reads this, so it can never offer a different set than `list_runners`.
2664
+ */
2665
+ current(): TaskQueryCatalog;
3547
2666
  queryCatalog(runners: RunnerId[]): TaskQueryCatalog;
3548
2667
  /**
3549
- * The catalog a model/task-mode edit is checked against — the same
3550
- * discovered models and manifest modes the per-turn catalog block shows the
3551
- * planner, so a refusal here can never name something invalid that the
3552
- * planner was never told about. The catalog is not filtered by the
3553
- * allowlist here — `checkModelAndModeValidity` narrows by allowlist itself,
3554
- * 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.
3555
2681
  */
3556
- edit(runners?: RunnerId[]): EditCatalog;
3557
- /** What a runner offers, as `runnerAssignment` needs it. Spawns the runner's CLI to list models. */
3558
- runner(runner: RunnerId): Promise<RunnerCatalog>;
2682
+ runner(runner: RunnerId): Promise<RunnerCatalog | null>;
3559
2683
  /**
3560
2684
  * The one place planning discovers what it may draw from: the runners'
3561
2685
  * models and their modes. `filteredModels` is the allowlisted view a prompt
@@ -3569,6 +2693,13 @@ declare class SessionCatalog {
3569
2693
  }>;
3570
2694
  }
3571
2695
 
2696
+ /**
2697
+ * A model's context window from a catalog Ordewell already holds — a runner's
2698
+ * own model listing or a provider's own models endpoint, never a table kept in
2699
+ * Ordewell. `source` is the record's reporter: a runner id or a vendor id.
2700
+ */
2701
+ type CatalogWindow = (source: string, model: string) => number | undefined;
2702
+
3572
2703
  /** The `planner_usage` member of the session broadcast union. */
3573
2704
  type PlannerUsageMessage = Extract<SessionMessage, {
3574
2705
  type: 'planner_usage';
@@ -3583,7 +2714,11 @@ type PlannerUsageMessage = Extract<SessionMessage, {
3583
2714
  * defined — this module just holds the state and the broadcast shape.
3584
2715
  */
3585
2716
  declare class PlannerUsageLedger {
2717
+ private readonly catalogWindow;
3586
2718
  private usage;
2719
+ private windows;
2720
+ /** `catalogWindow` serves the window of a planner model whose backend reports none. */
2721
+ constructor(catalogWindow?: CatalogWindow);
3587
2722
  /** Fold one model call's record into the totals; returns the running ledger. */
3588
2723
  record(record: UsageRecord): PlannerUsage;
3589
2724
  /** Adopt a persisted ledger (a reopened session) or start from zero. */
@@ -3621,14 +2756,17 @@ interface SessionEventRelayDeps {
3621
2756
  * observers. A wait on a usage limit, and a task gone idle, reach surfaces as
3622
2757
  * the task's status; `onUsageLimitWait` and `onTaskIdle` themselves are for
3623
2758
  * the supervisor to wake on. The end-of-run handoff is marked by supervision
3624
- * and remembered for a surface joining late, so Session announces it too.
2759
+ * and remembered for a surface joining late, so Session announces it too. A
2760
+ * model switch is a plan edit, which Session's editor announces as one.
3625
2761
  */
3626
- type RelayObserver = Required<Omit<OrchestratorObserver, 'onTaskSettled' | 'onRunStarted' | 'onAutoMerge' | 'onUsageLimitWait' | 'onTaskIdle' | 'onIsolationHandoff'>>;
2762
+ type RelayObserver = Required<Omit<OrchestratorObserver, 'onTaskSettled' | 'onRunStarted' | 'onAutoMerge' | 'onUsageLimitWait' | 'onModelSwitched' | 'onTaskIdle' | 'onIsolationHandoff' | 'onRunClosed'>>;
3627
2763
  /**
3628
2764
  * Turns orchestrator and planner events into {@link SessionMessage}
3629
2765
  * broadcasts — the one place a surface's view of a Session is produced. It
3630
- * never persists: where an event also owes a save, the Session does that
3631
- * 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.
3632
2770
  */
3633
2771
  declare class SessionEventRelay {
3634
2772
  private readonly broadcast;
@@ -3640,12 +2778,6 @@ declare class SessionEventRelay {
3640
2778
  private readonly awaitingApproval;
3641
2779
  private readonly checkpointQuestion;
3642
2780
  private readonly waitsForApproval;
3643
- /**
3644
- * The in-flight turn's subagent activity, grouped one run per subagent so a
3645
- * replay nests each step under its own brief/result. Folded into the plan's
3646
- * researchLog at persist — see {@link flushSubagentRuns}.
3647
- */
3648
- private pendingSubagents;
3649
2781
  private statusHeld;
3650
2782
  private statusOwed;
3651
2783
  /** The planner's requests announced and not yet settled, as they were announced: a surface joining now is owed them. */
@@ -3678,20 +2810,6 @@ declare class SessionEventRelay {
3678
2810
  /** `turnId`: the planner turn whose commit this is, when one is. */
3679
2811
  planGenerated(plan: LegacyPlanState | null, goal: string, turnId?: string): void;
3680
2812
  progress(progress: ResearchProgress): void;
3681
- /** The run for `subagentId`, created on first sighting so a step arriving
3682
- * before (or without) its started event still gets a home. */
3683
- private subagentRun;
3684
- /**
3685
- * Fold the turn's subagent runs into the plan's researchLog as one contiguous
3686
- * group per subagent — its entry then its steps, in the order they started —
3687
- * so a replay nests each step under its own subagent however the live stream
3688
- * interleaved. A harness planner already logs its child steps through the
3689
- * turn's researchLog; they are pulled out of that position and re-grouped
3690
- * rather than duplicated.
3691
- */
3692
- flushSubagentRuns(plan: LegacyPlanState): void;
3693
- /** Forget a turn's subagent runs that will never be persisted. */
3694
- dropSubagentRuns(): void;
3695
2813
  }
3696
2814
 
3697
2815
  /** Which session's logs, in which workspace. */
@@ -3707,11 +2825,12 @@ interface TaskLogFile {
3707
2825
  /** The attempts a task has a log for, oldest first. */
3708
2826
  declare function listTaskLogAttempts(location: TaskLogLocation, taskId: string): number[];
3709
2827
  /**
3710
- * Start the next attempt's file. Numbered from what is on disk rather than
3711
- * from the orchestrator's count, which restarts at one whenever a plan is
3712
- * loaded and would overwrite the attempts before it.
2828
+ * Start an attempt's file, under the attempt's own number so its log and its
2829
+ * usage ledger name the same attempt. Without one, or when that file already
2830
+ * exists, it takes the next number after what is on disk: nothing saved is
2831
+ * ever overwritten.
3713
2832
  */
3714
- declare function openTaskLog(location: TaskLogLocation, taskId: string): TaskLogFile;
2833
+ declare function openTaskLog(location: TaskLogLocation, taskId: string, attempt?: number): TaskLogFile;
3715
2834
  /**
3716
2835
  * One attempt's events, in order; empty when it has no log. A line that does
3717
2836
  * not parse is skipped — the last one, most likely, cut off by a crash
@@ -3719,6 +2838,42 @@ declare function openTaskLog(location: TaskLogLocation, taskId: string): TaskLog
3719
2838
  */
3720
2839
  declare function readTaskLog(location: TaskLogLocation, taskId: string, attempt: number): TaskLogEvent[];
3721
2840
 
2841
+ interface TaskLogRecorderDeps {
2842
+ broadcast: SessionBroadcaster;
2843
+ /** Where the session's logs go, read as each attempt starts: a session's id can change between plans. */
2844
+ location: () => TaskLogLocation;
2845
+ /** Opens an attempt's file, under the attempt's number when the spawn named one; tests pass one that never touches the disk. */
2846
+ open?: (location: TaskLogLocation, taskId: string, attempt?: number) => TaskLogFile;
2847
+ flushMs?: number;
2848
+ logger?: ILogger;
2849
+ }
2850
+ /**
2851
+ * Keeps each structured task's log (ADR-0018, P1): every event of an attempt
2852
+ * is appended to that attempt's file and broadcast as `task_log`, in the same
2853
+ * batches, so what a surface saw live and what it reloads are one sequence.
2854
+ *
2855
+ * It sits around the runner rather than inside the orchestrator, which stays
2856
+ * unaware of logs.
2857
+ */
2858
+ declare class TaskLogRecorder {
2859
+ private readonly deps;
2860
+ private readonly open;
2861
+ private readonly flushMs;
2862
+ private readonly logger;
2863
+ /** Each task's latest attempt, by the session it records: its log takes a {@link note} until the next attempt starts. */
2864
+ private readonly latest;
2865
+ constructor(deps: TaskLogRecorderDeps);
2866
+ wrap(runner: IRunner): IRunner;
2867
+ /**
2868
+ * An entry the session did not emit, written into the log of the attempt
2869
+ * that runs `sessionId` and sent at once: what Ordewell made of one of its
2870
+ * events once that settled. Dropped once the task's next attempt started.
2871
+ */
2872
+ note(sessionId: string, taskId: string, entry: TaskLogEvent): void;
2873
+ /** Start a new attempt's log for `session`, opening with the skills it was given, and keep it until the session exits. */
2874
+ record(taskId: string, session: IRunnerSession, skills?: readonly TaskSkillSnapshot[], attemptNumber?: number): void;
2875
+ }
2876
+
3722
2877
  /**
3723
2878
  * Options for plan generation. Progress is not overridable: every planner
3724
2879
  * progress event is translated to a SessionMessage inside the Session and
@@ -3803,7 +2958,7 @@ interface SessionDeps {
3803
2958
  /** Persistence seam. Defaults to the saved-session store under the workspace. */
3804
2959
  saveSession?: SaveSession;
3805
2960
  /** Where a structured task's log is saved (ADR-0018, P1). Defaults to a file per attempt beside the session's. */
3806
- openTaskLog?: (location: TaskLogLocation, taskId: string) => TaskLogFile;
2961
+ openTaskLog?: (location: TaskLogLocation, taskId: string, attempt?: number) => TaskLogFile;
3807
2962
  /** The Ordewell MCP server a harness planner's tools are served from (ADR-0022). Defaults to the process's one. */
3808
2963
  mcpServer?: OrdewellMcpServer;
3809
2964
  }
@@ -3825,7 +2980,7 @@ interface SessionParts {
3825
2980
  modelResolver: ModelResolver;
3826
2981
  settings: () => SessionRuntimeSettings;
3827
2982
  hostSessionId?: string;
3828
- aiService: () => IAiService;
2983
+ backend: PlannerBackend;
3829
2984
  planner: SessionPlanner;
3830
2985
  store: PlanStore;
3831
2986
  orchestrator: TaskOrchestrator;
@@ -3835,6 +2990,8 @@ interface SessionParts {
3835
2990
  usage: PlannerUsageLedger;
3836
2991
  approvals: PendingApprovals;
3837
2992
  approvalPolicy: ApprovalPolicy;
2993
+ /** Where the Session writes what it made of a task's event into that attempt's log. */
2994
+ taskLogs: Pick<TaskLogRecorder, 'note'>;
3838
2995
  fetcher: IWebFetcher;
3839
2996
  skillsService: Pick<SkillsService, 'findSkill' | 'listSkills' | 'searchedDirs' | 'forRoot'>;
3840
2997
  saveSession: SaveSession;
@@ -3854,7 +3011,7 @@ interface SessionParts {
3854
3011
  * (setModels, setRunnerList, …) stay on the host; Session never emits them.
3855
3012
  */
3856
3013
  declare class Session {
3857
- private readonly aiService;
3014
+ private readonly backend;
3858
3015
  private readonly usage;
3859
3016
  private readonly events;
3860
3017
  private readonly planner;
@@ -3874,6 +3031,8 @@ declare class Session {
3874
3031
  * supervisor has first.
3875
3032
  */
3876
3033
  private sentHandoff;
3034
+ /** The run was forgotten and no surface has been told yet; see {@link announceClosed}. */
3035
+ private closedUnannounced;
3877
3036
  /** The edit the open planner turn holds, as last announced: a surface that joins mid-turn is shown it. */
3878
3037
  private sentHeld;
3879
3038
  private readonly notifications;
@@ -3882,6 +3041,7 @@ declare class Session {
3882
3041
  private readonly fsAdapter;
3883
3042
  private readonly approvals;
3884
3043
  private readonly approvalPolicy;
3044
+ private readonly taskLogs;
3885
3045
  private readonly fetcher;
3886
3046
  private readonly settingsFn;
3887
3047
  private readonly save;
@@ -3909,8 +3069,6 @@ declare class Session {
3909
3069
  private turnEffects;
3910
3070
  /** Every turn's effects with starts still owed: a turn's may still be running its starts when the next opens. */
3911
3071
  private readonly owingEffects;
3912
- /** What the planner's next turn is told before anything else; see {@link PlannerConversationHost.takeRunUpdates}. */
3913
- private readonly untoldRunUpdates;
3914
3072
  private unlistenApprovals;
3915
3073
  private readonly plannerTools;
3916
3074
  constructor(parts: SessionParts);
@@ -3955,9 +3113,9 @@ declare class Session {
3955
3113
  /** The open planner turn is the supervisor's, acting at Act: its edits and added tasks are its own (ADR-0029). */
3956
3114
  private supervisorEditing;
3957
3115
  /**
3958
- * The runners a planner edit may put a task on: the plan's own, and every
3959
- * runner enabled now. An edit is checked as it is made and again as it lands
3960
- * 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.
3961
3119
  */
3962
3120
  private editRunners;
3963
3121
  /**
@@ -3997,7 +3155,6 @@ declare class Session {
3997
3155
  * task under the same id (ADR-0029).
3998
3156
  */
3999
3157
  private keepApprovals;
4000
- private approveTask;
4001
3158
  /**
4002
3159
  * Answer an outstanding approval. Every surface funnels here — the web
4003
3160
  * server's HTTP route, the VS Code webview, the TUI prompt — so the decision
@@ -4038,9 +3195,9 @@ declare class Session {
4038
3195
  */
4039
3196
  private syncPlanTasks;
4040
3197
  /**
4041
- * Persists PlanStore state to disk. PlanStore is the single authority.
4042
- * `background`: an execution event's save, which may land in the middle of a
4043
- * 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.
4044
3201
  */
4045
3202
  private persist;
4046
3203
  /** A new plan on a long-lived Session gets its own persisted identity (unless the host fixed one). */
@@ -4049,8 +3206,8 @@ declare class Session {
4049
3206
  * The plan scope ends: everything that belongs to the plan this Session
4050
3207
  * holds goes, so nothing of it reaches the next one — a new plan, another
4051
3208
  * session adopted, or the Session closed. Its run is stopped, the planner
4052
- * turn in flight is abandoned with its uncommitted subagent steps, what that
4053
- * 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,
4054
3211
  * execution log and planner prompts are cleared. A new plan on the same
4055
3212
  * Session keeps the scopes the user granted (`keepGrants`); a session
4056
3213
  * boundary does not (ADR-0008).
@@ -4081,6 +3238,8 @@ declare class Session {
4081
3238
  * boundary rewrites `in_progress` to `pending` — nothing is running when a
4082
3239
  * session comes off a file — so a surface that re-reads the plan mid-run must
4083
3240
  * come here instead, or every task it is watching reads as never started.
3241
+ * An archived snapshot carries the task as it is now: a retry or continue
3242
+ * keeps the archive while the live task runs on and spends more.
4084
3243
  */
4085
3244
  get currentPlanState(): PlanState | null;
4086
3245
  get currentGoal(): string;
@@ -4192,12 +3351,6 @@ declare class Session {
4192
3351
  */
4193
3352
  compactConversation(signal?: AbortSignal): Promise<ConversationCompaction>;
4194
3353
  rewindTargets(): RewindTarget[];
4195
- /**
4196
- * Hand the planner turn in flight a validated plan or a task edit; the turn
4197
- * commits it as it would the same JSON in the planner's reply. False when
4198
- * no turn is open.
4199
- */
4200
- submitToTurn(submission: PlannerSubmission): boolean;
4201
3354
  /** A plan the planner submitted through its tool, already checked against the live catalog. */
4202
3355
  private submitPlanFromTool;
4203
3356
  /**
@@ -4254,13 +3407,16 @@ declare class Session {
4254
3407
  * level at each call. Other sessions' runs keep theirs until they end.
4255
3408
  */
4256
3409
  applyRunSettings(changes: Partial<GlobalRunSettings>): void;
4257
- executePlan(): Promise<void>;
4258
3410
  private idlePlan;
4259
3411
  private runWholePlan;
4260
- approveReview(): Promise<LegacyPlanState>;
4261
- forceStartTask(taskId: string): Promise<void>;
3412
+ /**
3413
+ * Execute while a run is under way (#95): the seam has approved every task,
3414
+ * as executing does, and the run takes them in. A run the scheduler was not
3415
+ * driving, one task started on its own, becomes the plan's run, as Execute
3416
+ * would make it.
3417
+ */
3418
+ private joinRun;
4262
3419
  private startOneTask;
4263
- runTask(taskId: string): Promise<void>;
4264
3420
  /**
4265
3421
  * A stale id — a task removed since the surface last drew it — is refused.
4266
3422
  * Reloading the plan to find it never could, and reset the verifier under
@@ -4269,26 +3425,28 @@ declare class Session {
4269
3425
  private requireTask;
4270
3426
  /** Start whatever the scheduler can now fit — after the parallel limit was raised mid-run, say. */
4271
3427
  reschedule(): Promise<void>;
4272
- retryTask(taskId: string): Promise<void>;
4273
3428
  private restartTask;
3429
+ private resumeTask;
4274
3430
  /**
4275
- * Continue a finished structured task in its saved runner session with the
4276
- * user's message (ADR-0018, K1); throws `TaskControlError` for a task that
4277
- * cannot be continued.
3431
+ * Keep what waits on the planner's usage limit from being sent at the reset:
3432
+ * it goes with the user's next message instead. False when nothing waits on
3433
+ * a reset.
4278
3434
  */
4279
- continueTask(taskId: string, message: string): Promise<void>;
4280
- private resumeTask;
4281
- cancelTask(taskId: string): Promise<void>;
4282
- markTaskComplete(taskId: string): Promise<void>;
4283
- markTaskIncomplete(taskId: string): Promise<void>;
4284
- /** Resume a task paused on a usage limit now, once the user says it has reset; see {@link TaskOrchestrator.resumeNow}. */
4285
- resumeNow(taskId: string): Promise<void>;
3435
+ cancelPlannerResend(): boolean;
4286
3436
  /** Keep a task paused on a usage limit from resuming on its own; see {@link TaskOrchestrator.cancelScheduledResume}. */
4287
- cancelScheduledResume(taskId: string): boolean;
3437
+ private cancelScheduledResume;
3438
+ /**
3439
+ * The plan follows a model switch the user made in the runner (#106), and
3440
+ * the task's log says what the plan holds once that settled: a write-back
3441
+ * not made — no model command asked for the change, or the edit failed — is
3442
+ * stated, never shown as made. Once the switch's attempt has ended, the plan
3443
+ * may be another one, so nothing of it is said.
3444
+ */
3445
+ private followRunnerModel;
4288
3446
  /** Said through both channels: a host shows one or the other, never both (the daemon's toasts are no-ops). */
4289
3447
  private notice;
4290
- approveCheckpoint(taskId: string): void;
4291
- rejectCheckpoint(taskId: string, reason?: string): void;
3448
+ private approveCheckpoint;
3449
+ private rejectCheckpoint;
4292
3450
  /** Whether an answer would reach the task: it waits at a checkpoint and a runner is left to hear it. */
4293
3451
  awaitsCheckpoint(taskId: string): boolean;
4294
3452
  /**
@@ -4296,20 +3454,21 @@ declare class Session {
4296
3454
  * `TaskControlError` for one that cannot take it. A task waiting for input
4297
3455
  * is back in progress, and saved so before any surface is told.
4298
3456
  */
4299
- sendTaskMessage(taskId: string, text: string): string;
3457
+ private sendTaskMessage;
4300
3458
  /**
4301
3459
  * Force send (ADR-0023, F1): interrupt the task's running turn and deliver
4302
3460
  * this message next. Throws `TaskControlError` where a plain message would.
4303
3461
  */
4304
- forceSendTaskMessage(taskId: string, text: string): string;
3462
+ private forceSendTaskMessage;
4305
3463
  /**
4306
- * The task runner's own command `/name args` (#66), as a turn of its own.
4307
- * 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.
4308
3468
  */
4309
3469
  invokeTaskCommand(taskId: string, name: string, args?: string): string;
4310
- forceSendQueuedTaskMessage(taskId: string, id: string): boolean;
4311
- removeQueuedTaskMessage(taskId: string, id: string): boolean;
4312
- interruptTask(taskId: string): Promise<void>;
3470
+ private forceSendQueuedTaskMessage;
3471
+ private removeQueuedTaskMessage;
4313
3472
  /** Replay a run a dirty tree blocked, after stashing the tracked changes. */
4314
3473
  continueWithStash(): Promise<void>;
4315
3474
  /** Replay a run a dirty tree blocked, in the workspace root, for this run only. */
@@ -4335,13 +3494,20 @@ declare class Session {
4335
3494
  /**
4336
3495
  * "Merge all": each repo's integration branch into whatever the user has
4337
3496
  * checked out there — every repo, or none if any cannot take it. The one
4338
- * irreversible step of isolated execution. Automatic gate merging and the
4339
- * supervisor's merge_all share this path. During a run it merges what has
4340
- * landed so far,
4341
- * which is what opens a merge gate (ADR-0020); it waits for no ops task,
4342
- * 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.
3502
+ */
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).
4343
3509
  */
4344
- mergeRun(): Promise<IsolationMergeResult>;
3510
+ private announceClosed;
4345
3511
  /** Remove the run's worktrees and task branches; keep its integration branch to review or merge. */
4346
3512
  cleanupRun(): Promise<void>;
4347
3513
  /**
@@ -4368,10 +3534,7 @@ declare class Session {
4368
3534
  * a hold would stall every task's status for as long, and an
4369
3535
  * `execution_complete` the control triggers would overtake the status held
4370
3536
  * back — which a surface treats as the end of the stream. Instead each status
4371
- * announced while one runs is saved first (see {@link observer}), as the
4372
- * background save of an execution event: it may land mid planner turn. So
4373
- * is the last one where the control is itself execution, not the user's
4374
- * word on the plan (`background`).
3537
+ * announced while one runs is saved first (see {@link observer}).
4375
3538
  */
4376
3539
  private withSave;
4377
3540
  private saveForControl;
@@ -4417,7 +3580,12 @@ declare class Session {
4417
3580
  persist?: boolean;
4418
3581
  }): void;
4419
3582
  destroy(): void;
4420
- 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;
4421
3589
  }
4422
3590
 
4423
3591
  /**
@@ -4433,341 +3601,6 @@ declare class PlanEditError extends Error {
4433
3601
  constructor(message: string);
4434
3602
  }
4435
3603
 
4436
- /** What the preflight reads: the planner choice and whatever it needs to be reachable. */
4437
- type PlannerReadinessConfig = Pick<IConfig, 'aiProvider' | 'planningModel' | 'openaiCompatibleBaseUrl' | 'getProviderApiKey'>;
4438
- /**
4439
- * Why the selected planner cannot plan yet, as data. Each surface words it with
4440
- * its own command spelling (`describePlannerNotReady`), so a daemon, a chat and
4441
- * a shell script can all say the same thing without sharing a syntax.
4442
- */
4443
- type PlannerNotReady = {
4444
- code: 'no_key';
4445
- provider: AiProvider;
4446
- label: string;
4447
- keyEnvVar: string;
4448
- } | {
4449
- code: 'no_base_url';
4450
- provider: AiProvider;
4451
- label: string;
4452
- baseUrlEnvVar: string;
4453
- } | {
4454
- code: 'no_model';
4455
- provider: AiProvider;
4456
- label: string;
4457
- };
4458
- /**
4459
- * What the selected planner still needs before it can plan, or null when it is
4460
- * ready. There are two ways in (ADR-0009), and only one of them involves a
4461
- * credential: a harness planner drives a coding agent already installed on the
4462
- * machine, authenticated by the user's own subscription, so demanding an API
4463
- * key of it turns a working setup into a wall. Its model is optional too: an
4464
- * unset id means "the agent's own default".
4465
- */
4466
- declare function plannerNotReady(config: PlannerReadinessConfig): PlannerNotReady | null;
4467
- /**
4468
- * `plannerNotReady` judged on a host's own config — the one way every host
4469
- * builds the preflight's input, so none can hand it a key the selected
4470
- * planner's transport will not read (a stored OpenRouter key passing for an
4471
- * OpenAI planner, which then failed inside the transport). Every planner turn
4472
- * judges through this too, so none can let through what another refuses.
4473
- */
4474
- declare function plannerNotReadyFor(config: Pick<IConfig, 'aiProvider' | 'orchestratorModel' | 'planningModel' | 'openaiCompatibleBaseUrl' | 'getProviderApiKey'>): PlannerNotReady | null;
4475
- /** How one surface spells the commands a not-ready message points at. */
4476
- interface PlannerCommandSyntax {
4477
- pickPlanner: string;
4478
- setKey(provider: AiProvider): string;
4479
- setModel: string;
4480
- }
4481
- declare const TERMINAL_UI_COMMANDS: PlannerCommandSyntax;
4482
- /** The VS Code chat shares the terminal's slash commands, except that its `/model` needs `set`. */
4483
- declare const CHAT_COMMANDS: PlannerCommandSyntax;
4484
- declare const HEADLESS_CLI_COMMANDS: PlannerCommandSyntax;
4485
- declare function describePlannerNotReady(reason: PlannerNotReady, commands: PlannerCommandSyntax): string;
4486
- /**
4487
- * A planner turn refused before it began. Carries the reason as data so a
4488
- * client that spells commands differently (the headless CLI) can re-word it;
4489
- * the message is the terminal UI's spelling, which is what most clients show.
4490
- */
4491
- declare class PlannerNotReadyError extends Error {
4492
- readonly reason: PlannerNotReady;
4493
- constructor(reason: PlannerNotReady);
4494
- }
4495
-
4496
- /** What the user last picked for one planner backend. */
4497
- interface PlannerModelChoice {
4498
- model: string;
4499
- effort?: string;
4500
- }
4501
- /**
4502
- * A candidate model as this planner's catalog lists it. Deliberately plain
4503
- * data: a harness planner's catalog is its runner's own model list and a
4504
- * vendor's is a picker list (ADR-0009), and the rule below is the same for
4505
- * both — so the caller normalizes, and this module never reaches for a
4506
- * ModelResolver, a config or the network.
4507
- */
4508
- interface PlannerModelCandidate {
4509
- id: string;
4510
- variants?: {
4511
- id: string;
4512
- }[];
4513
- }
4514
- interface PlannerModelRecall {
4515
- model: string;
4516
- effort: string;
4517
- source: 'remembered' | 'catalog-default' | 'none';
4518
- }
4519
- /** The slice of `SettingsService` this needs, so tests can pass a plain object. */
4520
- interface PlannerModelStore {
4521
- getPlannerModel(provider: string): PlannerModelChoice | undefined;
4522
- setPlannerModel(provider: string, entry: PlannerModelChoice | undefined): void;
4523
- }
4524
- /**
4525
- * Remembers, per planner backend, the model the user last chose for it.
4526
- *
4527
- * Switching the planner has to clear the model — an id is scoped to the agent
4528
- * that listed it, so an OpenRouter slug handed to Claude Code points it at
4529
- * something it cannot spawn. That clear is correct and stays; what was missing
4530
- * is that switching *back* then forced a re-pick of a model the user had
4531
- * already chosen. The rule itself is `selectPlanner`'s; this is the storage
4532
- * the surfaces that keep their memory in the settings file hand it.
4533
- */
4534
- declare class PlannerModelMemory {
4535
- private store;
4536
- constructor(store: PlannerModelStore);
4537
- /** Record what the user picked for `provider`; a blank model forgets it instead. */
4538
- remember(provider: string, model: string, effort?: string): void;
4539
- getPlannerModel(provider: string): PlannerModelChoice | undefined;
4540
- /**
4541
- * What the model and effort should become when `provider` takes over the
4542
- * planner, given the models it actually serves. The `source` is what the
4543
- * three surfaces word their notice from — restored, defaulted, or nothing.
4544
- */
4545
- recall(provider: string, catalog: PlannerModelCandidate[]): PlannerModelRecall;
4546
- }
4547
-
4548
- /**
4549
- * The daemon contract: every HTTP body the local daemon sends, in one place.
4550
- *
4551
- * The daemon's routes build their bodies against these types and the CLI's
4552
- * `ApiClient` reads them back, so a field added on one side is a compile error
4553
- * on the other. Before this there were three hand-written copies — the routes'
4554
- * inline `c.json`, the client's per-method generics, and the TUI's slice of the
4555
- * client — and they had drifted (the client declared a `headless` field the
4556
- * daemon never sent, and typed `GET /sessions/:id`'s plan as the wrong shape).
4557
- *
4558
- * Types only. Core is the one package both the daemon and its clients already
4559
- * depend on; importing from the daemon would pull its runtime into the CLI.
4560
- */
4561
- /**
4562
- * Why a request was refused, as a stable code a client switches on instead of
4563
- * reading the message. Absent on a refusal nobody needs to tell apart — a
4564
- * missing field, say — where the message is all there is.
4565
- */
4566
- type DaemonErrorCode = 'session_not_found' | 'task_not_found' | 'no_plan' | 'already_executing' | 'workspace_not_found' | 'workspace_not_a_project' | 'conversation_busy'
4567
- /** The user stopped the planner turn: the turn ended as asked, so a client stays quiet. */
4568
- | 'planner_turn_stopped'
4569
- /** The planner turn settled after its plan was dropped; nothing it produced was kept. */
4570
- | 'planner_turn_discarded'
4571
- /** The selected planner cannot plan yet (no key, no base URL, no model); `plannerNotReady` says which. */
4572
- | 'planner_not_ready'
4573
- /** The request asked for something the plan, task or conversation does not allow. */
4574
- | 'refused' | 'checkpoint_not_waiting' | 'approval_not_outstanding';
4575
- /** The body of every non-2xx answer a route gives. */
4576
- interface ErrorBody {
4577
- error: string;
4578
- code?: DaemonErrorCode;
4579
- /** Set with `workspace_not_a_project`: the directory the daemon refused, so a client can offer to initialize it. */
4580
- workspace?: string;
4581
- /** Set with `planner_not_ready`: the reason as data, so a client can word it in its own commands. */
4582
- plannerNotReady?: PlannerNotReady;
4583
- }
4584
- interface OkResponse {
4585
- ok: boolean;
4586
- }
4587
- /**
4588
- * A plan as the planner routes return it: the session's plan state, not the
4589
- * `SerializedPlan` a websocket `plan_generated` carries. The two differ —
4590
- * this one holds raw tasks and the dialogue's bookkeeping — and a client that
4591
- * reads either reads only the fields it needs. Skill bodies stay in the
4592
- * session: loads and attempt snapshots arrive as notices.
4593
- */
4594
- interface PlanResponse {
4595
- plan: SurfacePlan;
4596
- }
4597
- /** `POST /plans/:id/resolve-conflict`: `null` when the session had no conflict to hand a resolver task. */
4598
- interface ResolveConflictResponse {
4599
- plan: SurfacePlan | null;
4600
- }
4601
- /** What `GET /api/models` returns, and what the pool's provider discovery produces. */
4602
- interface ModelsResponse {
4603
- models: DiscoveredModel[];
4604
- modelsByRunner: Record<string, DiscoveredModel[]>;
4605
- /** Each runner's manifest modes, so a surface can offer a per-task mode picker. */
4606
- modesByRunner: Record<string, RunnerModeInfo[]>;
4607
- orchestratorModel: string;
4608
- providers: AiProvider[];
4609
- /** Full cross-provider catalog for the orchestrator (planner) model picker. */
4610
- orchestratorModels: OrchestratorOption[];
4611
- /** Per-provider catalog-fetch failures, keyed by provider id. */
4612
- providerErrors: Record<string, string>;
4613
- }
4614
- /** One-shot planning answers in the phase-tagged shape a saved session is read back in. */
4615
- interface GeneratePlanResponse extends Pick<ModelsResponse, 'models' | 'modelsByRunner'> {
4616
- plan: SurfacePlanState;
4617
- /** The host notes the plan's transcript records, a skill check's warnings among them, which the phase-tagged plan does not carry. */
4618
- notes?: string[];
4619
- }
4620
- interface ExecuteResponse {
4621
- status: 'running';
4622
- }
4623
- interface StopResponse {
4624
- status: 'stopped';
4625
- }
4626
- interface CancelPlanningResponse {
4627
- cancelled: boolean;
4628
- }
4629
- /** `POST /api/plans/:id/tasks/:taskId/resume/cancel` — false when no resume was scheduled for the task. */
4630
- interface CancelResumeResponse {
4631
- cancelled: boolean;
4632
- }
4633
- interface TaskMessageResponse {
4634
- id: string;
4635
- }
4636
- interface ForceSendResponse {
4637
- sent: boolean;
4638
- }
4639
- interface RemoveMessageResponse {
4640
- removed: boolean;
4641
- }
4642
- interface MergeGateEntry {
4643
- id: string;
4644
- order: number;
4645
- title: string;
4646
- }
4647
- interface MergeGateResponse {
4648
- mergeGate: MergeGateEntry[];
4649
- }
4650
- interface IsolationDiffResponse {
4651
- diff: string;
4652
- }
4653
- /** A merge that conflicts or is blocked is an outcome, not a refusal, so it answers 200 with the result. */
4654
- type IsolationMergeResponse = IsolationMergeResult;
4655
- /** A fork the daemon has already adopted. */
4656
- interface ConversationForkResponse {
4657
- sessionId: string;
4658
- goal: string;
4659
- plan: SurfacePlan;
4660
- }
4661
- /** A fork made by a rewind, with the full text of the message it was made just before. */
4662
- interface ConversationRewindResponse extends ConversationForkResponse {
4663
- rewoundMessage: string;
4664
- }
4665
- interface RewindTargetsResponse {
4666
- targets: RewindTarget[];
4667
- }
4668
- interface ConversationCompactResponse extends ConversationCompaction, PlanResponse {
4669
- }
4670
- interface PrdResponse {
4671
- prdMarkdown: string;
4672
- }
4673
- interface RunnerEntry {
4674
- id: string;
4675
- name: string;
4676
- enabled: boolean;
4677
- }
4678
- interface RunnersResponse {
4679
- runners: RunnerEntry[];
4680
- orchestratorModel: string;
4681
- }
4682
- /** Sessions: a saved one comes off its file, a live one from the pool's store. */
4683
- interface SessionResponse {
4684
- meta: SessionMeta;
4685
- plan: SurfacePlanState;
4686
- }
4687
- interface AdoptSessionResponse extends OkResponse {
4688
- plan: SurfacePlan;
4689
- goal: string;
4690
- }
4691
- interface TaskLogAttemptsResponse {
4692
- attempts: number[];
4693
- }
4694
- interface TaskLogResponse {
4695
- attempt: number;
4696
- events: TaskLogEvent[];
4697
- }
4698
- type SessionListResponse = SessionMeta[];
4699
- interface SettingsResponse {
4700
- orchestratorModel: string;
4701
- /** Who plans (ADR-0009) — a vendor id, or a runner serving as a harness planner. */
4702
- aiProvider: PlannerBackendId;
4703
- plannerThinkingEffort: string;
4704
- maxParallel: number;
4705
- modelAllowlist: UserSettings['modelAllowlist'];
4706
- plannerModels: UserSettings['plannerModels'];
4707
- /** The user's run settings (ADR-0030), each at its default until set. */
4708
- run: GlobalRunSettings;
4709
- }
4710
- /** What `PATCH /api/settings` answers: the settings as they now stand, plus what the write itself decided. */
4711
- interface SettingsUpdateResponse extends SettingsResponse {
4712
- /**
4713
- * Which model a planner switch landed on and why — remembered for that
4714
- * backend, its catalog default, or none. Present only when the write changed
4715
- * the planner; absent for every other write, where nothing was recalled.
4716
- */
4717
- switchRecall?: PlannerModelRecall;
4718
- /** Env keys the daemon refused to apply, so a client does not persist what the daemon would not. */
4719
- rejectedEnvKeys?: string[];
4720
- }
4721
- interface CommandDescriptor {
4722
- name: string;
4723
- description: string;
4724
- }
4725
- interface CommandsResponse {
4726
- commands: CommandDescriptor[];
4727
- }
4728
- interface WorkspacesResponse {
4729
- workspaces: string[];
4730
- }
4731
- interface ApprovalsResponse {
4732
- pending: PendingApproval[];
4733
- approvedScopes: string[];
4734
- }
4735
- interface ApprovalAnswerResponse extends OkResponse {
4736
- granted: boolean;
4737
- decision: ApprovalDecision['decision'];
4738
- }
4739
- /**
4740
- * Where a frame stands in its session's broadcast stream. A client holding
4741
- * several sockets on one session gets every broadcast on each, and a frame's
4742
- * content is no identity: two clears of a held edit are the same JSON. `seq`
4743
- * rises by one per broadcast of the session for as long as the daemon runs,
4744
- * whichever Session object holds the id; `stream` names the daemon process,
4745
- * so a restarted daemon's numbering is never compared with the last one's.
4746
- */
4747
- interface StreamStamp {
4748
- stream: string;
4749
- seq: number;
4750
- }
4751
- /** A broadcast as the session's socket carries it. Unstamped from a daemon older than the stamp. */
4752
- type SessionFrame = (SessionMessage | SessionNotice) & Partial<StreamStamp>;
4753
- type ApprovalRequestMessage = Extract<SessionMessage, {
4754
- type: 'approval_request';
4755
- }>;
4756
- /**
4757
- * What a session's socket sends right after `connected`: its run as it stands,
4758
- * so a client that joins mid-run has the run view before the next update —
4759
- * the held edit's preview included — and the planner's approval requests
4760
- * still waiting, which no later frame would announce again. `seq` is the last
4761
- * broadcast it accounts for. Every frame after it is a {@link SessionFrame}.
4762
- */
4763
- interface RunSnapshotFrame extends Partial<StreamStamp> {
4764
- type: 'run_snapshot';
4765
- sessionId: string;
4766
- view: RunView;
4767
- /** Absent from a daemon older than the field: nothing is known, not "none waiting". */
4768
- approvals?: ApprovalRequestMessage[];
4769
- }
4770
-
4771
3604
  /**
4772
3605
  * Why a call on a session could not be made. Each is a class rather than a
4773
3606
  * message so the daemon can map it to a status and a stable code in one place,
@@ -4953,7 +3786,7 @@ declare abstract class AbstractRunnerSession implements IRunnerSession {
4953
3786
  abstract nativeSessionId(): string | null;
4954
3787
  abstract commands(): RunnerCommand[];
4955
3788
  abstract invokeCommand(name: string, args?: string): string;
4956
- abstract answerPermission(id: string, decision: ApprovalDecision): boolean;
3789
+ abstract answerPermission(id: string, decision: ApprovalDecision): boolean | Promise<boolean>;
4957
3790
  abstract onTaskComplete(listener: (report: TaskCompleteArgs) => void): void;
4958
3791
  abstract onToolCheckpoint(handler: (question: string, signal: AbortSignal) => Promise<CheckpointAnswer>): void;
4959
3792
  }
@@ -5010,11 +3843,14 @@ declare class StructuredSession extends AbstractRunnerSession {
5010
3843
  private readonly text;
5011
3844
  private state;
5012
3845
  private turn;
3846
+ /** A terminal event ends the turn before the adapter releases its send. */
3847
+ private sending;
5013
3848
  private turnCount;
5014
3849
  private queue;
5015
3850
  private messageCount;
5016
3851
  private interrupting;
5017
3852
  private lastSessionId;
3853
+ private lastSessionCost;
5018
3854
  private readonly structuredEmitter;
5019
3855
  private permissionCount;
5020
3856
  /**
@@ -5089,7 +3925,8 @@ declare class StructuredSession extends AbstractRunnerSession {
5089
3925
  removeQueued(id: string): boolean;
5090
3926
  queued(): QueuedTaskMessage[];
5091
3927
  nativeSessionId(): string | null;
5092
- answerPermission(id: string, decision: ApprovalDecision): boolean;
3928
+ sessionCost(): RunnerSessionCost | null;
3929
+ answerPermission(id: string, decision: ApprovalDecision): boolean | Promise<boolean>;
5093
3930
  /** Every open request goes unanswered once the process that asked is gone. */
5094
3931
  private withdrawPermissions;
5095
3932
  interrupt(): Promise<void>;
@@ -5173,34 +4010,6 @@ type Continuability = {
5173
4010
  declare function continuability(task: Task): Continuability;
5174
4011
  declare function canContinue(task: Task): boolean;
5175
4012
 
5176
- interface TaskLogRecorderDeps {
5177
- broadcast: SessionBroadcaster;
5178
- /** Where the session's logs go, read as each attempt starts: a session's id can change between plans. */
5179
- location: () => TaskLogLocation;
5180
- /** Opens an attempt's file; tests pass one that never touches the disk. */
5181
- open?: (location: TaskLogLocation, taskId: string) => TaskLogFile;
5182
- flushMs?: number;
5183
- logger?: ILogger;
5184
- }
5185
- /**
5186
- * Keeps each structured task's log (ADR-0018, P1): every event of an attempt
5187
- * is appended to that attempt's file and broadcast as `task_log`, in the same
5188
- * batches, so what a surface saw live and what it reloads are one sequence.
5189
- *
5190
- * It sits around the runner rather than inside the orchestrator, which stays
5191
- * unaware of logs.
5192
- */
5193
- declare class TaskLogRecorder {
5194
- private readonly deps;
5195
- private readonly open;
5196
- private readonly flushMs;
5197
- private readonly logger;
5198
- constructor(deps: TaskLogRecorderDeps);
5199
- wrap(runner: IRunner): IRunner;
5200
- /** Start a new attempt's log for `session`, opening with the skills it was given, and keep it until the session exits. */
5201
- record(taskId: string, session: IRunnerSession, skills?: readonly TaskSkillSnapshot[]): void;
5202
- }
5203
-
5204
4013
  declare function stripAnsi(text: string): string;
5205
4014
 
5206
4015
  /**
@@ -5616,4 +4425,4 @@ declare const DEFAULT_MAX_PARALLEL = 5;
5616
4425
  */
5617
4426
  declare function parseMaxParallel(value: unknown): number | null;
5618
4427
 
5619
- 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, 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, 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, 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, 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, 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, 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, isVendorModelFor, killTree, languageForId, listSessions, listTaskLogAttempts, listTree, loadSession, loadSessionPlanState, loadState, looksLikeChallenge, mapAgentTool, 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, 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 };