@ordewell/core 0.9.0 → 0.9.1

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.
package/dist/index.d.mts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { W as RunnerId, E as PlanStatus, L as LegacyPlanState, n as IsolationMergeResult, j as IsolationLandedTask, I as IWorktreeIsolation, w as IsolationTaskStatus, P as PlanIsolation, a3 as SkillsService, _ as SkillInfo, N as RepoGroupLayout, c as DiscoveredModel, a9 as Task, S as ResearchProgress, O as ResearchLogEntry, C as ConversationMessage, ao as UsageRecord, ah as TaskSnapshot, a as AwaitingReason, g as IsolationHandoff, s as IsolationRun, t as IsolationTaskRecord, aa as TaskIsolation, x as IsolationView, o as IsolationOutcome, K as RepairEvidence, am as TreeSnapshot, av as Verdict, ag as TaskSkillSnapshot, $ as SkillLoad, Q as QueuedMessage, A as ActiveTaskSession, V as ResearchToolType, G as PlannerUsage, z as PlanState } from './Task-1Q19y7sZ.mjs';
2
2
  export { B as BUILTIN_SKILL_NAMES, D as DEFAULT_RUNNERS, b as DiscoveredMode, d as IntegrationDisposal, e as InvalidSkill, f as IsolationAvailability, h as IsolationHandoffRepo, i as IsolationInactiveReason, k as IsolationLanding, l as IsolationMergeBlock, m as IsolationMergeBlockReason, p as IsolationPruneResult, q as IsolationRemoval, r as IsolationRepo, u as IsolationTaskRef, v as IsolationTaskRepo, M as Message, y as PlanModificationWarnings, F as PlannerTaskView, H as PreparedTask, J as PreservedWork, R as RefusedRemoval, T as ResearchStep, U as ResearchStepOutcome, X as ShadowedSkill, Y as SkillAppliesTo, Z as SkillCatalog, a0 as SkillLoadNotice, a1 as SkillMetadata, a2 as SkillSource, a4 as StreamEvent, a5 as StreamStepEvent, a6 as StreamThinkingEvent, a7 as SubagentLogEntry, a8 as SubagentOutcome, ab as TaskIsolationState, ac as TaskMode, ad as TaskModelAssignment, ae as TaskOutputSummary, af as TaskSkillNotice, ai as TaskStatus, aj as TaskType, ak as TaskWithParent, al as ThinkingBlock, an as UsageLine, ap as UsageTotals, aq as UserPromptEntry, ar as UserStep, as as ValidationCheck, at as ValidationContext, au as ValidationResult, aw as VerificationCheck, ax as addPlannerUsage, ay as addTaskToPlan, az as addUsage, aA as createEmptyPlan, aB as createSkillsService, aC as createTask, aD as emptyWarnings, aE as flattenTasks, aF as flattenTasksWithParents, aG as inheritedOps, aH as inheritedSkills, aI as isAwaitingReason, aJ as isMeasured, aK as isSkillName, aL as isUserMessage, aM as keepExecutionState, aN as migrateLegacyPlan, aO as migrateLoadedTasks, aP as migratePlanState, aQ as migrateTask, aR as opsFlag, aS as partedPromptUsage, aT as plannerContextFill, aU as plannerTaskView, aV as removeTaskFromPlan, aW as renumberTasks, aX as skillNames, aY as updateTaskInPlan, aZ as usageLine, a_ as validateModifiedPlan, a$ as warningsText } from './Task-1Q19y7sZ.mjs';
3
- import { R as TaskOp, M as MergeGateView, j as EditCatalog, u as SessionBroadcaster, A as ApplyTaskOpsResult, w as SessionNotice, v as SessionMessage, K as TaskLogEvent, H as SurfacePlan, I as SurfacePlanState } from './plan-utils-P3PIEwNa.mjs';
4
- export { a as ApprovalBlock, b as ApprovalStatus, C as CHECKPOINT_TRUNCATE_LENGTH, c as ConversationInput, d as ConversationView, D as DiffRow, e as DiffRowKind, f as DiffStat, g as DisplayBlock, E as EMPTY_CONVERSATION, h as EMPTY_HOLD, i as EMPTY_TASK_LOG, G as GatedConversation, L as LocalEntry, k as MessageBlock, l as MessageRole, N as NOT_STARTED, m as NO_TURN, n as NewTaskFields, O as OutputPreview, P as PlanBlock, o as PlanMarkerStatus, p as PromptHold, S as SerializedConversationMessage, q as SerializedPlan, r as SerializedQueuedMessage, s as SerializedTask, t as SerializedTaskStatus, x as SkillLoadBlock, y as SkillToken, z as SubagentBlock, B as SubagentChild, F as SubagentStatus, J as SurfaceTask, T as TakenPrompt, Q as TaskLogView, U as TaskRef, V as TaskRowAction, W as TaskRowContext, X as TaskRowIsolation, Y as TaskRowKind, Z as TaskRowPlacement, _ as TaskRowSubject, $ as TaskRowView, a0 as TaskStatusKind, a1 as ThinkingDisplayBlock, a2 as ToolBlock, a3 as ToolHeadline, a4 as ToolStatus, a5 as TurnGate, a6 as UsageBlock, a7 as aheadOfDraft, a8 as applyTaskOps, a9 as approvalLabel, aa as awaitingLabel, ab as canMergeTasks, ac as canSetDependencies, ad as canSplitTask, ae as capConflictFiles, af as classifyOutcome, ag as coalesceTaskLog, ah as dependencyCandidates, ai as dependentsNotice, aj as dependentsOf, ak as diffRows, al as diffStat, am as diffSummary, an as digestTaskLog, ao as drainNext, ap as executionSummary, aq as followTurn, ar as fromTranscript, as as hasHiddenDetail, at as holdPrompt, au as isTaskRunning, av as loadedSkillTokens, aw as markAction, ax as markRequestFor, ay as namedRepos, az as newTaskFields, aA as opsEditable, aB as outputLines, aC as outputPreview, aD as parseTaskOpsJson, aE as pastGateConfirmation, aF as reduceConversation, aG as reduceTaskLog, aH as replayTaskLog, aI as runnerToolSubject, aJ as serializePlan, aK as serializeTask, aL as serializeTaskStatus, aM as skillTokens, aN as stopTurn, aO as summarizeToolCall, aP as surfacePlan, aQ as surfacePlanState, aR as taskLogForSurface, aS as taskRowActions, aT as taskRowView, aU as taskStartedNotice, aV as taskStatusKind, aW as textHasTaskOps, aX as titledRefs, aY as toTaskLogEvent, aZ as toolHeadline, a_ as trimToolOutput, a$ as truncateCheckpointSummary, b0 as unsendAll, b1 as unsendLatest } from './plan-utils-P3PIEwNa.mjs';
5
- import { I as IFileSystem, R as ReadFileOpts, T as ToolOutcome, b as GrepOptions, a as GlobOptions, F as FindSymbolOptions } from './IFileSystem-_m7HdoxJ.mjs';
6
- export { G as GREP_DEFAULT_HEAD_LIMIT, c as GrepOutputMode, S as SEARCH_EXCLUSIONS } from './IFileSystem-_m7HdoxJ.mjs';
7
- import { V as PlannerAllowlist, I as IApproval, n as ApprovalRequest, h as ApprovalDecision, g as ApprovalAnswer, v as IRunner, u as IConfig, e as AiProvider, _ as ProviderModelLists, k as ApprovalModeSetting, w as IRunnerSession, c as AgentProcessDeps, A as AgentAdapter, S as OrdewellMcpServer, Y as PlannerToolHandler, a7 as RunnerRegistry, a0 as QueuedTaskMessage, R as OrchestratorOption, af as TaskModeAgentAdapter, d as AgentStartOptions, b as AgentEvent, l as ApprovalPolicy, ae as TaskCompleteArgs, p as CheckpointAnswer, aa as StructuredTurnEnd, a9 as StructuredEvent, ak as TaskStartOptions, al as TaskTokenScope, a4 as RunnerManifest, aj as TaskRunnerFlags } from './IRunner-CxiymkP_.mjs';
8
- export { a as AgentAdapterFactory, f as AllProviderModels, i as ApprovalKind, j as ApprovalMode, m as ApprovalPolicyOptions, o as ApprovalSource, C as CatalogModel, q as CheckpointArgs, r as CommandWords, D as DEFAULT_PLANNER_ALLOWLIST, s as DENY_ALL, t as DiscoveryCommand, E as EditPlanArgs, F as FetchAllProviderModelsOptions, L as LineBuffer, x as ListModelsArgs, y as ListRunnersArgs, z as LoadSkillArgs, M as McpClientConfig, B as McpCredential, G as McpTool, H as McpToolContext, J as McpToolReply, K as ModelCatalog, N as ModelShortcut, O as ORCHESTRATOR_SHORTCUTS, P as ORDEWELL_MCP_PATH, Q as ORDEWELL_MCP_SERVER_NAME, T as OwnerOnlyFile, U as PLANNER_TOOLS, W as PlannerStartOptions, X as PlannerTokenScope, Z as ProviderCredentialSource, $ as ProviderModelsResult, a1 as RunCommandArgs, a2 as RunnerEntry, a3 as RunnerFeatures, a5 as RunnerMode, a6 as RunnerModelDiscovery, a8 as SpawnFn, ab as SubmitPlanArgs, ac as TASK_COMPLETE_TOOL, ad as TASK_TOOLS, ag as TaskModeUnsupportedError, ah as TaskOutputArgs, ai as TaskQueryArgs, am as TaskToolHandler, an as approvalScopes, ao as collectProviderCredentials, ap as commandAllowedBy, aq as effectiveApprovalMode, ar as enabledRunners, as as fetchAllProviderModels, at as isGranted, au as isRunnerApproval, av as knownModelId, aw as mcpClientConfig, ax as ownerOnlyConfigFile, ay as parseAllowlist, az as parseApprovalModeSetting, aA as resolveModelShortcut, aB as resolveProvider, aC as scopeAllowed, aD as sharedMcpServer, aE as toApprovalDecision, aF as toOrchestratorOptions, aG as toolAllowed } from './IRunner-CxiymkP_.mjs';
3
+ import { R as TaskOp, M as MergeGateView, j as EditCatalog, u as SessionBroadcaster, A as ApplyTaskOpsResult, w as SessionNotice, v as SessionMessage, K as TaskLogEvent, H as SurfacePlan, I as SurfacePlanState } from './plan-utils-BmUp_Lko.mjs';
4
+ export { a as ApprovalBlock, b as ApprovalStatus, C as CHECKPOINT_TRUNCATE_LENGTH, c as ConversationInput, d as ConversationView, D as DiffRow, e as DiffRowKind, f as DiffStat, g as DisplayBlock, E as EMPTY_CONVERSATION, h as EMPTY_HOLD, i as EMPTY_TASK_LOG, G as GatedConversation, L as LocalEntry, k as MessageBlock, l as MessageRole, N as NOT_STARTED, m as NO_TURN, n as NewTaskFields, O as OutputPreview, P as PlanBlock, o as PlanMarkerStatus, p as PromptHold, S as SerializedConversationMessage, q as SerializedPlan, r as SerializedQueuedMessage, s as SerializedTask, t as SerializedTaskStatus, x as SkillLoadBlock, y as SkillToken, z as SubagentBlock, B as SubagentChild, F as SubagentStatus, J as SurfaceTask, T as TakenPrompt, Q as TaskLogView, U as TaskRef, V as TaskRowAction, W as TaskRowContext, X as TaskRowIsolation, Y as TaskRowKind, Z as TaskRowPlacement, _ as TaskRowSubject, $ as TaskRowView, a0 as TaskStatusKind, a1 as ThinkingDisplayBlock, a2 as ToolBlock, a3 as ToolHeadline, a4 as ToolStatus, a5 as TurnGate, a6 as UsageBlock, a7 as aheadOfDraft, a8 as applyTaskOps, a9 as approvalLabel, aa as awaitingLabel, ab as canMergeTasks, ac as canSetDependencies, ad as canSplitTask, ae as capConflictFiles, af as classifyOutcome, ag as coalesceTaskLog, ah as dependencyCandidates, ai as dependentsNotice, aj as dependentsOf, ak as diffRows, al as diffStat, am as diffSummary, an as digestTaskLog, ao as drainNext, ap as executionSummary, aq as followTurn, ar as fromTranscript, as as hasHiddenDetail, at as holdPrompt, au as isTaskRunning, av as loadedSkillTokens, aw as markAction, ax as markRequestFor, ay as namedRepos, az as newTaskFields, aA as opsEditable, aB as outputLines, aC as outputPreview, aD as parseTaskOpsJson, aE as pastGateConfirmation, aF as reduceConversation, aG as reduceTaskLog, aH as replayTaskLog, aI as runnerToolSubject, aJ as serializePlan, aK as serializeTask, aL as serializeTaskStatus, aM as skillTokens, aN as stopTurn, aO as summarizeToolCall, aP as surfacePlan, aQ as surfacePlanState, aR as taskLogForSurface, aS as taskRowActions, aT as taskRowView, aU as taskStartedNotice, aV as taskStatusKind, aW as textHasTaskOps, aX as titledRefs, aY as toTaskLogEvent, aZ as toolHeadline, a_ as trimToolOutput, a$ as truncateCheckpointSummary, b0 as unsendAll, b1 as unsendLatest } from './plan-utils-BmUp_Lko.mjs';
5
+ import { I as IFileSystem, R as ReadFileOpts, T as ToolOutcome, b as GrepOptions, a as GlobOptions, F as FindSymbolOptions } from './IFileSystem-Cy-TAWvu.mjs';
6
+ export { G as GREP_DEFAULT_HEAD_LIMIT, c as GrepOutputMode, S as SEARCH_EXCLUSIONS } from './IFileSystem-Cy-TAWvu.mjs';
7
+ import { a7 as ResearchShell, I as IApproval, o as ApprovalRequest, i as ApprovalDecision, h as ApprovalAnswer, y as IRunner, x as IConfig, f as AiProvider, a3 as ProviderModelLists, l as ApprovalModeSetting, z as IRunnerSession, d as AgentProcessDeps, a as AgentAdapter, W as OrdewellMcpServer, a0 as PlannerToolHandler, af as RunnerRegistry, a5 as QueuedTaskMessage, V as OrchestratorOption, ap as TaskModeAgentAdapter, e as AgentStartOptions, c as AgentEvent, a1 as ProjectMcpServer, m as ApprovalPolicy, ao as TaskCompleteArgs, q as CheckpointAnswer, ak as StructuredTurnEnd, aj as StructuredEvent, au as TaskStartOptions, av as TaskTokenScope, ac as RunnerManifest, at as TaskRunnerFlags } from './IRunner-D6YfbmHn.mjs';
8
+ export { A as AUTO_COMMANDS, b as AgentAdapterFactory, g as AllProviderModels, j as ApprovalKind, k as ApprovalMode, n as ApprovalPolicyOptions, p as ApprovalSource, C as CatalogModel, r as CheckpointArgs, s as CommandClassification, t as CommandTier, u as CommandWords, D as DEFAULT_PLANNER_ALLOWLIST, v as DENY_ALL, w as DiscoveryCommand, E as EditPlanArgs, F as FetchAllProviderModelsOptions, G as GIT_READONLY_SUBCOMMANDS, L as LineBuffer, B as ListModelsArgs, H as ListRunnersArgs, J as LoadSkillArgs, M as McpClientConfig, K as McpCredential, N as McpTool, O as McpToolContext, P as McpToolReply, Q as ModelCatalog, R as ModelShortcut, S as ORCHESTRATOR_SHORTCUTS, T as ORDEWELL_MCP_PATH, U as ORDEWELL_MCP_SERVER_NAME, X as OwnerOnlyFile, Y as PLANNER_TOOLS, Z as PlannerAllowlist, _ as PlannerStartOptions, $ as PlannerTokenScope, a2 as ProviderCredentialSource, a4 as ProviderModelsResult, a6 as REFUSED_COMMANDS, a8 as ResearchShellDeps, a9 as RunCommandArgs, aa as RunnerEntry, ab as RunnerFeatures, ad as RunnerMode, ae as RunnerModelDiscovery, ag as ShellDialect, ah as SpawnFn, ai as StandingDecision, al as SubmitPlanArgs, am as TASK_COMPLETE_TOOL, an as TASK_TOOLS, aq as TaskModeUnsupportedError, ar as TaskOutputArgs, as as TaskQueryArgs, aw as TaskToolHandler, ax as approvalScopes, ay as classifyCommand, az as clearResearchShellCache, aA as collectProviderCredentials, aB as commandAllowedBy, aC as effectiveApprovalMode, aD as enabledRunners, aE as fetchAllProviderModels, aF as isGranted, aG as isRunnerApproval, aH as knownModelId, aI as mcpClientConfig, aJ as ownerOnlyConfigFile, aK as parseAllowlist, aL as parseApprovalModeSetting, aM as researchShellWarning, aN as researchToolsPath, aO as resolveModelShortcut, aP as resolveProvider, aQ as resolveResearchShell, aR as scopeAllowed, aS as sharedMcpServer, aT as toApprovalDecision, aU as toOrchestratorOptions, aV as toolAllowed } from './IRunner-D6YfbmHn.mjs';
9
9
  import { R as RunnerModeInfo } from './ModeResolver-DE-o2lRF.mjs';
10
10
  export { M as ManifestLookup, a as autonomyLevelLabel, b as buildModeGuide, f as filteredBuildModes, p as parseAutonomyLevel, r as resolveDefaultMode, c as resolveTaskMode, d as runnerModesFrom } from './ModeResolver-DE-o2lRF.mjs';
11
11
  import { ChildProcess } from 'child_process';
@@ -31,192 +31,12 @@ interface SessionData {
31
31
  }
32
32
 
33
33
  /**
34
- * Tiered classification for the planner's `bash` tool.
35
- *
36
- * The planner is a read-only researcher: it never edits files, and the runners
37
- * do the real work. But research legitimately means running things — querying a
38
- * cloud control plane (`az`, `gh`), reproducing a failure (`npm test`), or
39
- * shaping output (`jq`). The old allowlist refused all of that, so the model's
40
- * only escape was to guess.
41
- *
42
- * Three tiers replace the flat allowlist:
43
- *
44
- * auto read-only inspection — runs with no prompt (the historical list)
45
- * ask anything else that is not obviously destructive — one approval,
46
- * remembered for the rest of the session at `scope` granularity
47
- * refuse writes, privilege escalation, and anything that would smuggle
48
- * arbitrary code past this classifier — never runs, never prompts
49
- *
50
- * `refuse` is deliberately not promptable. A planner that can `rm` is a planner
51
- * that can silently break the workspace it was asked to reason about, and the
52
- * architecture already says mutation belongs to the runners.
53
- *
54
- * Classification walks every segment of the command line (pipes, `&&`, `;`, and
55
- * command substitution) rather than matching substrings against the raw string.
56
- * The old substring denylist both over-matched (`ls docs/removed` tripped `rm`)
57
- * and under-matched (`$(rm -rf /)` was invisible once chaining was allowed).
58
- *
59
- * A segment is classified by the command that will actually execute, not by the
60
- * name at the front of it: wrappers are unwrapped first, recursively, so
61
- * `timeout 10 env nice rm -rf x` is an `rm`. See {@link WRAPPER_FAMILY}. A
62
- * runner that feeds its command arguments nobody can see is unwrapped the same
63
- * way but never runs unprompted ({@link XARGS_TARGETS}), and one that hands its
64
- * command to a shell is refused ({@link REFUSED_RUNNERS}).
65
- *
66
- * A permitted binary is permitted with the flags it is known to be read-only
67
- * with, not with any flag at all: several of them will run a helper program or
68
- * write a file when asked to, so `auto` is a per-binary flag allowlist and an
69
- * unrecognised flag is refused. See {@link FLAG_POLICY}.
70
- *
71
- * The lexer is dialect-aware, because the interpreter that will actually run
72
- * the command decides what the tokens are, and getting that wrong is not a
73
- * cosmetic error here — it is the difference between classifying what runs and
74
- * classifying something else. See {@link Dialect}.
34
+ * The SSRF guard for anything that leaves the machine on the planner's behalf.
35
+ * An unguarded `http://169.254.169.254/` would hand a prompt-injected planner
36
+ * the cloud metadata credentials, so every outbound host is vetted here: IP
37
+ * literals by range, names by every address DNS answers with.
75
38
  */
76
-
77
- /**
78
- * `env` is deliberately absent. Given a command it is a wrapper, classified by
79
- * what it actually runs (see {@link WRAPPER_FAMILY}); given none it prints the
80
- * whole process environment — provider credentials included — into the research
81
- * log, which is a disclosure the developer should get to see coming.
82
- *
83
- * Membership here is necessary but not sufficient: every name in this list also
84
- * declares the flags it is read-only with in {@link FLAG_POLICY}.
85
- */
86
- declare const AUTO_COMMANDS: string[];
87
- /**
88
- * `ls-remote` is deliberately absent, and it is the one entry here that is
89
- * read-only against the repository. It reaches the network: it contacts
90
- * whatever host the named remote or URL resolves to, which routes around the
91
- * web fetcher's per-origin approval and its request-forgery guard. So it
92
- * prompts rather than running silently.
93
- *
94
- * Prompting only became the right answer once {@link scopeFor} widened. Under
95
- * the old scope it would have been remembered as `git ls-remote`, so one
96
- * approval of the workspace's own remote would have authorised a listing
97
- * against any host at all — which is the same hole with an approval dialog in
98
- * front of it. The scope now carries the destination.
99
- */
100
- declare const GIT_READONLY_SUBCOMMANDS: string[];
101
- /**
102
- * Never runs, with or without approval.
103
- *
104
- * The `cmd.exe` builtins at the end matter as much as the POSIX names above
105
- * them. `del`, `rd`, `move`, and friends mutate exactly what `rm` and `mv` do,
106
- * and on Windows they are what a model reaches for — so without them the whole
107
- * refusal tier was bypassable on that platform by writing the command the way
108
- * the platform spells it. Listed unconditionally rather than per-platform: a
109
- * POSIX box has no `del` to refuse, so the extra names cost nothing there.
110
- */
111
- declare const REFUSED_COMMANDS: string[];
112
- /**
113
- * Options threaded through classification.
114
- *
115
- * `dialect` is keyed to the interpreter rather than the OS on purpose: a Windows
116
- * host that has Git Bash runs the planner's commands in a POSIX shell (see
117
- * {@link resolveResearchShell}), and classifying those under cmd.exe rules
118
- * would be the same mismatch in the other direction. Callers pass the dialect of
119
- * the shell they are actually going to use; omitting it falls back to the host
120
- * default.
121
- */
122
- interface CommandPolicyOptions {
123
- dialect?: ShellDialect;
124
- /**
125
- * `CDPATH` is set in the environment the command will run in. A relative
126
- * `cd` may then land in a directory the command line never names, so `cd`
127
- * stops being navigation the classifier can follow.
128
- */
129
- cdpathSet?: boolean;
130
- /** The planner's standing approvals (ADR-0026); an `ask` command they cover carries {@link CommandClassification.allowedBy}. */
131
- allow?: PlannerAllowlist;
132
- }
133
- type CommandTier = 'auto' | 'ask' | 'refuse';
134
- interface CommandClassification {
135
- tier: CommandTier;
136
- /** What a grant covers, for `ask`. Derived from the non-auto segments only. */
137
- scope: string;
138
- /** Populated for `refuse`: why, in a sentence the model can act on. */
139
- reason?: string;
140
- /** For `ask`: the allowlist rule that pre-approves every part of the command that needed asking. */
141
- allowedBy?: string;
142
- }
143
- /**
144
- * Classify one command line. Output redirection is refused outright unless the
145
- * target provably writes nothing — `/dev/null`, or an fd duplication like
146
- * `2>&1` — since a planner that writes files has stopped being a planner.
147
- */
148
- declare function classifyCommand(command: string, opts?: CommandPolicyOptions): CommandClassification;
149
-
150
- /**
151
- * Which interpreter runs the planner's `bash` tool, and in which language.
152
- *
153
- * `AUTO_COMMANDS` — the read-only tier that runs with no prompt — is `ls`, `cat`,
154
- * `wc`, `head`, `tail`, `du`, `df`, `file`, `sort`, `uniq`, `basename`, `find`,
155
- * `grep`. That set is POSIX, and passing it to `shell: true` on Windows means
156
- * cmd.exe, where most of those names do not exist and `find` is an unrelated
157
- * program that returns plausible wrong output instead of an error. The planner's
158
- * whole research surface degraded to guessing, quietly.
159
- *
160
- * Rather than write a Windows dialect of every research command — a second
161
- * behavior to keep in step forever — this finds a POSIX shell to run the
162
- * existing ones in. Git for Windows ships a full one, and git is already a
163
- * prerequisite for everything Ordewell does, so in practice it is there. Failing
164
- * that, cmd.exe is used and the classifier is told so, because the one thing
165
- * that must never happen is classifying a command in one language and running it
166
- * in another: `commandPolicy` reads {@link ResearchShell.dialect} for exactly
167
- * that reason.
168
- *
169
- * POSIX resolves to `{ file: null }`, meaning "use `shell: true`" — byte for
170
- * byte what the adapters did before this module existed.
171
- *
172
- * Windows paths are built with `path.win32` explicitly, not the host-flavoured
173
- * `path`, so the probe is the same on a Windows host as it is under a Linux
174
- * test runner.
175
- */
176
- type ShellDialect = 'posix' | 'cmd';
177
- interface ResearchShell {
178
- /**
179
- * Executable that takes a command string, or null to use the host default
180
- * via `shell: true`.
181
- */
182
- file: string | null;
183
- /** Arguments preceding the command string. */
184
- args: string[];
185
- /** The language the command string will be interpreted in. */
186
- dialect: ShellDialect;
187
- /**
188
- * Directory holding this shell's POSIX utilities, when it brings its own.
189
- * Prepended to PATH for the search subprocesses (`grep`, `tree`) that
190
- * `execFile` starts without a shell, so they resolve too.
191
- */
192
- utilsDir: string | null;
193
- }
194
- interface ResearchShellDeps {
195
- platform?: NodeJS.Platform;
196
- exists?: (candidate: string) => boolean;
197
- env?: NodeJS.ProcessEnv;
198
- }
199
- /**
200
- * The shell the planner's `bash` tool should use on this host. Resolved once per
201
- * process — the answer cannot change while Ordewell runs, and the probe costs a
202
- * handful of `stat` calls.
203
- */
204
- declare function resolveResearchShell(deps?: ResearchShellDeps): ResearchShell;
205
- /** Test seam: drop the per-process cache so the next call re-probes. */
206
- declare function clearResearchShellCache(): void;
207
- /**
208
- * PATH for the search subprocesses the adapters start without a shell, with the
209
- * research shell's own utilities in front. Without this, `grep` — the fallback
210
- * when ripgrep is absent — is unresolvable on a Windows box even when a
211
- * perfectly good `grep.exe` sits in the Git tree beside the shell.
212
- */
213
- declare function researchToolsPath(shell: ResearchShell, basePath: string | undefined): string;
214
- /**
215
- * A message explaining a degraded research surface, or null when there is
216
- * nothing to explain. Surfaced by the adapters on the first refused command so
217
- * the limitation is visible rather than inferred from bad answers.
218
- */
219
- declare function researchShellWarning(shell: ResearchShell): string | null;
39
+ type ResolveAll = (host: string) => Promise<string[]>;
220
40
 
221
41
  /**
222
42
  * The policy half of the planner's filesystem: path confinement, the tiered
@@ -231,7 +51,10 @@ declare function researchShellWarning(shell: ResearchShell): string | null;
231
51
  * record.
232
52
  */
233
53
  declare abstract class BaseFileSystem implements IFileSystem {
54
+ private readonly resolveWebHost;
234
55
  private approval;
56
+ private outboundSecrets;
57
+ constructor(resolveWebHost?: ResolveAll);
235
58
  /**
236
59
  * The interpreter `execBashImpl` will hand the command to. Owned here rather
237
60
  * than per-adapter because {@link classifyCommand} has to be told the same
@@ -241,6 +64,7 @@ declare abstract class BaseFileSystem implements IFileSystem {
241
64
  protected readonly researchShell: ResearchShell;
242
65
  /** Surfaces inject the human channel here; without it, external access is denied. */
243
66
  setApproval(approval: IApproval): void;
67
+ setOutboundSecrets(secrets: () => Iterable<string>): void;
244
68
  abstract getWorkspaceRoot(): string;
245
69
  protected abstract readFileImpl(absPath: string, opts?: ReadFileOpts): Promise<ToolOutcome>;
246
70
  protected abstract globImpl(pattern: string, absRoot: string, headLimit: number): Promise<ToolOutcome>;
@@ -292,8 +116,95 @@ declare abstract class BaseFileSystem implements IFileSystem {
292
116
  bash(command: string, signal?: AbortSignal): Promise<ToolOutcome>;
293
117
  }
294
118
 
119
+ /**
120
+ * The one decision every planner web read goes through, whichever runner
121
+ * plans and whichever path carries the bytes (ADR-0027). A fetch names the
122
+ * URL it reads; a search names its query and the provider that receives it.
123
+ */
124
+ type WebRead = {
125
+ kind: 'fetch';
126
+ url: string;
127
+ }
128
+ /** `provider` keys the grant; `providerLabel`, when given, names it to the person asked. */
129
+ | {
130
+ kind: 'search';
131
+ query: string;
132
+ provider: string;
133
+ providerLabel?: string;
134
+ note?: string;
135
+ };
136
+ /** A refusal's `reason` is written for the planner: it says what to do next. */
137
+ type WebReadDecision = {
138
+ allowed: true;
139
+ } | {
140
+ allowed: false;
141
+ reason: string;
142
+ };
143
+ interface DecideOptions {
144
+ /**
145
+ * Research subagents run in the background where nothing can prompt
146
+ * (ADR-0008, T6). When false, a read that would ask is refused with the gap
147
+ * to report, rather than raising a prompt nobody can answer.
148
+ */
149
+ prompting?: boolean;
150
+ }
151
+ interface WebReadPolicyOptions {
152
+ approval: IApproval;
153
+ /** The secrets Ordewell holds — provider keys, MCP tokens — read per request. */
154
+ secrets?: () => Iterable<string>;
155
+ /** Defaults to `process.env`; its credential-named values are secrets too. */
156
+ env?: () => Record<string, string | undefined>;
157
+ /** How many web reads one planner turn may make; read per request, so a settings change applies at once. */
158
+ budget?: () => number;
159
+ /** The planner turn in progress; a new one starts a new budget. */
160
+ turn?: () => string | undefined;
161
+ /** Override DNS resolution (every answer for a name) in tests. */
162
+ resolveHost?: ResolveAll;
163
+ }
164
+ declare const DEFAULT_WEB_READ_BUDGET = 40;
165
+ declare class WebReadPolicy {
166
+ private readonly approval;
167
+ private readonly resolveHost;
168
+ private readonly secrets;
169
+ private readonly env;
170
+ private readonly budget;
171
+ private readonly turn;
172
+ private spent;
173
+ constructor(opts: WebReadPolicyOptions);
174
+ /**
175
+ * Every check that needs no network runs first, then the budget and the
176
+ * approval, and only then is a fetch's host resolved: a lookup is itself a
177
+ * request, so `<data>.attacker.example` would leave through DNS even for a
178
+ * read that is then excluded, over budget or declined.
179
+ */
180
+ decide(read: WebRead, opts?: DecideOptions): Promise<WebReadDecision>;
181
+ private subagentRefusal;
182
+ /**
183
+ * Whether a search through `provider` would run without asking anyone, for
184
+ * a runner whose own search raises no request and is set on or off when
185
+ * its session starts (ADR-0027). A zero budget disables it; no query is
186
+ * known yet, so secrets and individual reads cannot be checked or counted.
187
+ */
188
+ searchesUnasked(provider: string): boolean;
189
+ /** The last check, the one that leaves the machine: every DNS answer for the host must be public. */
190
+ private resolved;
191
+ private refuse;
192
+ private vetFetch;
193
+ private vetSearch;
194
+ private spend;
195
+ private refund;
196
+ private carriesSecret;
197
+ }
198
+
295
199
  interface IWebFetcher {
296
- confirm(url: string): Promise<boolean>;
200
+ /**
201
+ * The web-read policy's decision (ADR-0027), for a read this fetcher will
202
+ * carry or one a runner carries natively, such as Claude's `WebFetch`.
203
+ * `fetch` and `search` decide for themselves; never call both.
204
+ */
205
+ decide(read: WebRead): Promise<WebReadDecision>;
206
+ /** Whether a runner's own search through `provider` would run unasked; see `WebReadPolicy.searchesUnasked`. */
207
+ searchesUnasked?(provider: string): boolean;
297
208
  fetch(url: string): Promise<ToolOutcome>;
298
209
  /**
299
210
  * Optional web search. A fetcher without it makes the `web_search` tool
@@ -301,6 +212,13 @@ interface IWebFetcher {
301
212
  * degradation `fetch` already uses when no fetcher is wired at all.
302
213
  */
303
214
  search?(query: string): Promise<ToolOutcome>;
215
+ /**
216
+ * A variant of this fetcher for a research subagent: same policy, cache,
217
+ * transport and budget, but a read that would ask a person is refused
218
+ * instead (ADR-0027, ADR-0008 T6). Absent means subagents get no web tools
219
+ * and the `fetch`/`web_search` tools report themselves unavailable.
220
+ */
221
+ forSubagent?(): IWebFetcher;
304
222
  }
305
223
 
306
224
  /**
@@ -331,6 +249,195 @@ declare function resolveWithin(root: string, target: string, from?: string): Res
331
249
  /** The directory a grant covers for `target` — its parent for files, itself for directories. */
332
250
  declare function grantScopeFor(abs: string, kind: 'file' | 'directory'): string;
333
251
 
252
+ /**
253
+ * A web-search backend behind a narrow seam (ADR-0027). The fetcher carries
254
+ * the bytes through its vetted transport; a provider owns only the endpoint,
255
+ * how to read its results, and how it reacts when it pushes back. `id` flows
256
+ * into the web-read policy's `search` decision, so an approval names the third
257
+ * party that receives the query.
258
+ *
259
+ * One provider exists today (DuckDuckGo's lite endpoint, zero-configuration).
260
+ * The seam exists so adding another is one class, not a refactor.
261
+ */
262
+ /** One search result: a title, its URL, and an optional snippet. */
263
+ interface SearchHit {
264
+ title: string;
265
+ url: string;
266
+ snippet?: string;
267
+ }
268
+ /**
269
+ * The fetcher's vetted page fetch, as a provider sees it. `status` is carried
270
+ * so a provider can tell 429/403 ("pushed back") from a plain error, and the
271
+ * body so it can tell a results page from a challenge.
272
+ */
273
+ type SearchPage = {
274
+ ok: true;
275
+ status: number;
276
+ body: string;
277
+ } | {
278
+ ok: false;
279
+ status?: number;
280
+ error: string;
281
+ };
282
+ type SearchTransport = (url: string) => Promise<SearchPage>;
283
+ /**
284
+ * What one lookup produced. `blocked` means the provider pushed back and
285
+ * marked itself unavailable; `none` is a plain 200 with nothing parseable,
286
+ * which is not a block; `error` is anything else that went wrong.
287
+ */
288
+ type SearchOutcome = {
289
+ kind: 'hits';
290
+ hits: SearchHit[];
291
+ } | {
292
+ kind: 'none';
293
+ } | {
294
+ kind: 'blocked';
295
+ reason: string;
296
+ } | {
297
+ kind: 'error';
298
+ error: string;
299
+ };
300
+ interface ISearchProvider {
301
+ /** The id that names this backend in the web-read policy's `search` decision. */
302
+ readonly id: string;
303
+ /** The human name shown wherever the query's destination is named. */
304
+ readonly name: string;
305
+ /** Whether a recent push-back has left the provider in cooldown; no request may go out. */
306
+ coolingDown(): boolean;
307
+ /** Run one query through `get`, which fetches through the fetcher's pinned transport. */
308
+ search(query: string, get: SearchTransport): Promise<SearchOutcome>;
309
+ }
310
+
311
+ declare function looksLikeChallenge(html: string): boolean;
312
+ /**
313
+ * Pull result links out of the lite endpoint's markup. A result's snippet is
314
+ * looked for only between its link and the next one: a result with no snippet
315
+ * must not borrow its neighbour's.
316
+ */
317
+ declare function parseSearchHits(html: string): SearchHit[];
318
+ /**
319
+ * The provider's shared instance: one process-wide provider means the cooldown
320
+ * a push-back starts is seen by every session, not just the one that hit it.
321
+ * `now` is injectable so tests drive the cooldown without waiting.
322
+ */
323
+ declare class DuckDuckGoSearchProvider implements ISearchProvider {
324
+ readonly id = "duckduckgo";
325
+ readonly name = "DuckDuckGo";
326
+ private failures;
327
+ private cooldownUntil;
328
+ private readonly now;
329
+ constructor(now?: () => number);
330
+ coolingDown(): boolean;
331
+ search(query: string, get: SearchTransport): Promise<SearchOutcome>;
332
+ private block;
333
+ }
334
+ /** The one provider every session's fetcher shares, so a cooldown is process-wide. */
335
+ declare const sharedSearchProvider: DuckDuckGoSearchProvider;
336
+
337
+ interface CachedPage {
338
+ body: string;
339
+ isHtml: boolean;
340
+ }
341
+ interface WebResponseCacheOptions {
342
+ ttlMs?: number;
343
+ maxEntries?: number;
344
+ maxBytes?: number;
345
+ now?: () => number;
346
+ }
347
+ declare class WebResponseCache {
348
+ private readonly entries;
349
+ private readonly ttlMs;
350
+ private readonly maxEntries;
351
+ private readonly maxBytes;
352
+ private readonly now;
353
+ private bytes;
354
+ constructor(opts?: WebResponseCacheOptions);
355
+ getFetch(url: string): CachedPage | undefined;
356
+ /** `maxAgeMs` is the response's own freshness; it shortens the TTL, never extends it. */
357
+ setFetch(url: string, page: CachedPage, maxAgeMs?: number): void;
358
+ getSearch(providerAndQuery: string): SearchHit[] | undefined;
359
+ setSearch(providerAndQuery: string, hits: SearchHit[]): void;
360
+ private get;
361
+ private set;
362
+ private delete;
363
+ }
364
+ /** The one cache every session's fetcher shares, so a page fetched once is reused across planners. */
365
+ declare const sharedWebResponseCache: WebResponseCache;
366
+
367
+ /**
368
+ * The concrete `fetch`/`web_search` implementation, in core so the web server
369
+ * and the VS Code extension share one behavior.
370
+ *
371
+ * Whether a read happens is the web-read policy's to decide (ADR-0027); this
372
+ * carries it. A redirect to another origin is put to the policy again here,
373
+ * since it saw only the first URL. Search goes through a pluggable provider
374
+ * behind the same transport; results and pages are cached in an injected,
375
+ * process-wide cache that saves the network but never the decision.
376
+ */
377
+ /** The slice of a `fetch` Response the fetcher reads; the platform `fetch` satisfies it. */
378
+ interface WebResponse {
379
+ ok: boolean;
380
+ status: number;
381
+ headers: {
382
+ get(name: string): string | null;
383
+ };
384
+ body: unknown;
385
+ text(): Promise<string>;
386
+ }
387
+ type WebTransport = (url: string, init: {
388
+ signal: AbortSignal;
389
+ headers: Record<string, string>;
390
+ redirect: 'manual';
391
+ }) => Promise<WebResponse>;
392
+ interface HttpWebFetcherOptions {
393
+ policy: WebReadDecider;
394
+ /**
395
+ * Injected in tests; defaults to a node:http(s) transport whose sockets
396
+ * connect only to addresses `resolveHost` answered and the guard vetted.
397
+ */
398
+ fetchImpl?: WebTransport;
399
+ maxChars?: number;
400
+ timeoutMs?: number;
401
+ /** Override DNS resolution (every answer for a name) in tests. */
402
+ resolveHost?: ResolveAll;
403
+ /** Shared across sessions in production; a fresh one per instance is the test default. */
404
+ cache?: WebResponseCache;
405
+ /** Shared across sessions so the search cooldown is process-wide. */
406
+ searchProvider?: ISearchProvider;
407
+ /** False for a research subagent, which must never prompt (see {@link forSubagent}). */
408
+ prompting?: boolean;
409
+ }
410
+ type WebReadDecider = Pick<WebReadPolicy, 'decide'> & Partial<Pick<WebReadPolicy, 'searchesUnasked'>>;
411
+ declare class HttpWebFetcher implements IWebFetcher {
412
+ private readonly policy;
413
+ private readonly fetchImpl;
414
+ private readonly maxChars;
415
+ private readonly timeoutMs;
416
+ private readonly resolveHost;
417
+ private readonly cache;
418
+ private readonly searchProvider;
419
+ private readonly prompting;
420
+ constructor(opts: HttpWebFetcherOptions);
421
+ decide(read: WebRead): Promise<WebReadDecision>;
422
+ private get decideOptions();
423
+ forSubagent(): IWebFetcher;
424
+ searchesUnasked(provider: string): boolean;
425
+ fetch(url: string): Promise<ToolOutcome>;
426
+ search(query: string): Promise<ToolOutcome>;
427
+ private get;
428
+ private render;
429
+ private getSearchPage;
430
+ private request;
431
+ /**
432
+ * Why a redirect must not be followed, or `undefined` if it may. The policy
433
+ * decided only the first URL, so a hop to another origin is a new read with
434
+ * the same decision; otherwise a redirect would carry the fetch past an
435
+ * exclusion, a Guarded grant or the budget.
436
+ */
437
+ private refuseRedirect;
438
+ private hop;
439
+ }
440
+
334
441
  /**
335
442
  * The bridge between "core needs an answer" and "a human somewhere is looking
336
443
  * at a UI". Core cannot prompt: the human may be at a browser, a TUI, a CLI
@@ -608,6 +715,7 @@ declare abstract class BaseConfig implements IConfig {
608
715
  get worktreeLinks(): string[];
609
716
  get conflictRepairAttempts(): number;
610
717
  get approvalMode(): ApprovalModeSetting;
718
+ get webReadBudget(): number;
611
719
  get approvalDefaults(): boolean;
612
720
  get approvalPreApproved(): string[];
613
721
  protected static detectProvider(fallback: AiProvider): AiProvider;
@@ -861,6 +969,16 @@ type LiveOutputLookup = (taskId: string, opts: LiveTailOptions) => LiveTail | nu
861
969
  */
862
970
  declare function renderTaskQueryAnswer(query: TaskQuery, tasks: readonly Task[], catalog: TaskQueryCatalog, liveOutput?: LiveOutputLookup): string;
863
971
 
972
+ /**
973
+ * A person's approvals of project MCP servers, kept across sessions
974
+ * (ADR-0028). Keyed by repository root and scope, which names the server
975
+ * and hashes its definition: a changed definition is a new question.
976
+ */
977
+ interface ProjectServerApprovals {
978
+ approved(repoRoot: string, scope: string): boolean;
979
+ remember(repoRoot: string, scope: string): void;
980
+ }
981
+
864
982
  interface CliAgentAiServiceDeps extends Partial<AgentProcessDeps> {
865
983
  /** Overrides adapter construction. Tests supply a fake agent; production picks by runner id. */
866
984
  createAdapter?: (runner: string, deps: AgentProcessDeps) => AgentAdapter | null;
@@ -868,6 +986,8 @@ interface CliAgentAiServiceDeps extends Partial<AgentProcessDeps> {
868
986
  workspaceRoot?: () => string;
869
987
  /** Where a conversation's planner tools are served (ADR-0022). Absent: no conversation can start. */
870
988
  mcpServer?: OrdewellMcpServer;
989
+ /** A person's approvals of project MCP servers, kept across sessions (ADR-0028). Defaults to Ordewell's data directory. */
990
+ projectServerApprovals?: ProjectServerApprovals;
871
991
  }
872
992
  /**
873
993
  * A coding agent driven as Ordewell's planner (ADR-0009).
@@ -888,6 +1008,7 @@ declare class CliAgentAiService implements IAiService {
888
1008
  private readonly processDeps;
889
1009
  private readonly makeAdapter;
890
1010
  private readonly workspaceRoot;
1011
+ private environmentSecrets;
891
1012
  private adapter;
892
1013
  /**
893
1014
  * The agent's own session id, kept across a process death so the next turn
@@ -897,9 +1018,18 @@ declare class CliAgentAiService implements IAiService {
897
1018
  private lastNativeSessionId;
898
1019
  /** `gate` is what the agent's own tool requests are decided against; absent, only Ordewell's tools are allowed (ADR-0026). */
899
1020
  private conversation;
1021
+ /**
1022
+ * What the runner's own session-wide search is decided against
1023
+ * (ADR-0027), asked again before every turn: the session's mode, its
1024
+ * exclusions or its budget can change between turns. Absent, it is off.
1025
+ */
1026
+ private searchesUnasked;
900
1027
  private readonly mcpServer;
1028
+ private readonly projectServerApprovals;
901
1029
  /** The planner token the running process was spawned with; revoked with the process (ADR-0022, A3). */
902
1030
  private plannerToken;
1031
+ /** The {@link PlannerStartOptions.nativeSearch} the running process was spawned with. */
1032
+ private adapterNativeSearch;
903
1033
  private activeAbort;
904
1034
  /**
905
1035
  * Every subagent this conversation has reported starting, and finishing.
@@ -922,6 +1052,7 @@ declare class CliAgentAiService implements IAiService {
922
1052
  plannerToolsAttached(): boolean;
923
1053
  /** The agent's own session id, a resumption hint only — Ordewell's transcript is authoritative (T4). */
924
1054
  nativeSessionId(): string | null;
1055
+ heldSecrets(): readonly string[];
925
1056
  reset(): void;
926
1057
  startConversation(req: ConversationRequest): Promise<ConversationTurn>;
927
1058
  continueConversation(userMessage: string, onProgress: (progress: ResearchProgress) => void, signal?: AbortSignal): Promise<ConversationTurn>;
@@ -952,13 +1083,27 @@ declare class CliAgentAiService implements IAiService {
952
1083
  private startWithTools;
953
1084
  /** Kill the planner process, and with it the token it was spawned with. */
954
1085
  private disposeAdapter;
1086
+ /** Whether the web-read policy lets the runner's own session-wide search run unasked now (ADR-0027). */
1087
+ private nativeSearch;
955
1088
  /**
956
1089
  * The live agent process, restarted from its own session id if it died
957
1090
  * between turns. Resume is a hint: when it fails, the caller's next
958
1091
  * `startConversation` reseeds from Ordewell's transcript, which is the same
959
1092
  * degradation `restoreChat` already performs on every surface.
1093
+ *
1094
+ * Also restarted when the policy's decision on the runner's own search has
1095
+ * changed since the process was spawned: the running thread would otherwise
1096
+ * keep searching unasked after Full turned Guarded. Codex 0.162 applies
1097
+ * `thread/resume`'s `config.web_search` either way (checked live).
960
1098
  */
961
1099
  private ensureAdapter;
1100
+ /**
1101
+ * What the planner gets of the repository's config, its runner being
1102
+ * launched without it (ADR-0028): the instruction files, which are text, in
1103
+ * its system prompt, and the project MCP servers it may start. Decided once
1104
+ * per conversation, so a respawn starts the same servers without asking.
1105
+ */
1106
+ private repoConfig;
962
1107
  private plannerModel;
963
1108
  /**
964
1109
  * A plan from one-shot agent sessions: only what a submit_plan call
@@ -1077,6 +1222,8 @@ type ConversationTurn =
1077
1222
  researchLog: ResearchLogEntry[];
1078
1223
  };
1079
1224
  interface IAiService {
1225
+ /** Credential values from the harness planner's effective environment, for the outbound secret check. */
1226
+ heldSecrets?(): readonly string[];
1080
1227
  /**
1081
1228
  * Begin the planner conversation: collect workspace context, run the
1082
1229
  * research/tool loop, and return the first planner turn. The service
@@ -2654,7 +2801,8 @@ declare const ORDEWELL_SETTABLE_ENV: readonly string[];
2654
2801
  * a known escalation from "can change a setting" to "can run code as the user":
2655
2802
  *
2656
2803
  * - The approval-mode and pre-approved-scope variables switch the consent gate
2657
- * off, so every later command runs unprompted.
2804
+ * off, so every later command runs unprompted; the web-read budget bounds
2805
+ * how much a planner may send out unasked in Full.
2658
2806
  * - The settings-path override is honoured by the settings service, which
2659
2807
  * creates directories recursively when persisting — admitting it turns the
2660
2808
  * same write into an arbitrary-path file write.
@@ -2743,8 +2891,11 @@ declare abstract class StdioAgentAdapter implements AgentAdapter {
2743
2891
  /** What this process is to the user — a planner or a task's runner — for the words a failure is reported in. */
2744
2892
  protected role: 'planner' | 'task';
2745
2893
  constructor(deps: AgentProcessDeps);
2746
- /** The command line that starts this agent in its read-only mode. */
2747
- protected abstract spawnSpec(opts: AgentStartOptions): SpawnSpec;
2894
+ /**
2895
+ * The command line that starts this agent in its read-only mode, given the
2896
+ * workspace's own variables (ADR-0016) the process will run under.
2897
+ */
2898
+ protected abstract spawnSpec(opts: AgentStartOptions, workspace: Record<string, string>): SpawnSpec;
2748
2899
  /** The bytes written to stdin to open one user turn. Must end with a newline. */
2749
2900
  protected abstract turnPayload(message: string): string;
2750
2901
  /**
@@ -2829,7 +2980,7 @@ declare class ClaudeCodeAdapter extends StdioAgentAdapter implements TaskModeAge
2829
2980
  private readonly steers;
2830
2981
  /** The last turn's `result` ended it, so a steer's echo now opens a turn the CLI started for that message. */
2831
2982
  private turnClosed;
2832
- protected spawnSpec(opts: AgentStartOptions): SpawnSpec;
2983
+ protected spawnSpec(opts: AgentStartOptions, workspace: Record<string, string>): SpawnSpec;
2833
2984
  /**
2834
2985
  * A task's run: the manifest decides what its mode and effort mean
2835
2986
  * (ADR-0001), and this adds only the protocol around them. No tool list and
@@ -2838,6 +2989,13 @@ declare class ClaudeCodeAdapter extends StdioAgentAdapter implements TaskModeAge
2838
2989
  * without it `-p` refuses them silently and nothing can ever surface one.
2839
2990
  */
2840
2991
  private taskSpawnSpec;
2992
+ /**
2993
+ * What user-only settings stop the CLI loading that is not the
2994
+ * repository's to choose (ADR-0028): the user's own local-scope servers,
2995
+ * under the `CLAUDE_CONFIG_DIR` the planner runs with, and the project
2996
+ * servers approved for it.
2997
+ */
2998
+ private plannerServers;
2841
2999
  /** The file holding the token lives exactly as long as the process that reads it. */
2842
3000
  private ordewellServerArgs;
2843
3001
  private removeMcpConfig;
@@ -3235,7 +3393,7 @@ declare class OpenCodeAdapter implements TaskModeAgentAdapter {
3235
3393
  * only 2.x answers `/api/info` with its version. A 1.x server — or one that
3236
3394
  * answers with anything else — keeps the 1.x protocol this class speaks.
3237
3395
  */
3238
- private speaksV2;
3396
+ private v2Version;
3239
3397
  private role;
3240
3398
  /**
3241
3399
  * Turn one settled assistant message into events. The settled response is
@@ -3303,6 +3461,7 @@ declare class OpenCodeAdapter implements TaskModeAgentAdapter {
3303
3461
  private request;
3304
3462
  private headers;
3305
3463
  nativeSessionId(): string | null;
3464
+ unusableProjectServers(): ProjectMcpServer[];
3306
3465
  /** Asks the server, which lists every MCP server it has with the state of its connection. */
3307
3466
  mcpAttached(): Promise<boolean>;
3308
3467
  /** 2.x lists its servers as `{ name, status: { status } }`, and only once it has started connecting them. */
@@ -5938,4 +6097,4 @@ declare const DEFAULT_MAX_PARALLEL = 5;
5938
6097
  */
5939
6098
  declare function parseMaxParallel(value: unknown): number | null;
5940
6099
 
5941
- export { ALL_PROVIDERS, AUTO_COMMANDS, AbstractRunner, AbstractRunnerSession, ActiveTaskSession, type AdoptSessionResponse, type Adr0013IsolationRun, type Adr0013PlanIsolation, type Adr0013TaskRecord, AgentAdapter, AgentEvent, AgentProcessDeps, AgentStartOptions, AiProvider, AlreadyExecutingError, ApplyTaskOpsResult, ApprovalAnswer, type ApprovalAnswerResponse, ApprovalDecision, ApprovalModeSetting, ApprovalPolicy, ApprovalRequest, type ApprovalsResponse, type AskOptions, AwaitingReason, BaseAiService, BaseConfig, BaseFileSystem, BufferedTaskOutputSource, CLAUDE_CODE_MANIFEST, CLI_PROVIDERS, CMD_EXE_MAX_COMMAND_LINE, CORE_PLANNER_PROMPT, type CancelPlanningResponse, type CappedRows, CheckpointAnswer, type CheckpointListener, ClaudeCodeAdapter, CliAgentAiService, type CliAgentAiServiceDeps, CodexAdapter, type CommandClassification, type CommandDescriptor, CommandLineTooLongError, type CommandTier, type CommandsResponse, type ComposeOptions, ConsoleLogger, ContextCollector, type Continuability, ConversationBusyError, type ConversationCompactResponse, type ConversationCompaction, ConversationEditError, type ConversationFork, type ConversationForkResponse, ConversationMessage, type ConversationRequest, type ConversationRewind, type ConversationRewindResponse, type ConversationTurn, type ConversationVariant, DAEMON_SUBPROTOCOL, DAEMON_TOKEN_SUBPROTOCOL_PREFIX, DEFAULT_MAX_PARALLEL, type DaemonErrorCode, DiscoveredModel, EmbeddedNewlineError, type EnvAdmission, EnvConfig, type ErrorBody, type ExecImpl, ExecutableNotFoundError, type ExecuteResponse, FindSymbolOptions, type ForceSendResponse, GIT_READONLY_SUBCOMMANDS, GREP_PCRE_PROBE_ARGS, GeminiService, type GeneratePlanResponse, type GitExecFn, GlobOptions, type GrepInvocation, GrepOptions, type GrepRegex, HARNESS_PLANNER_REASON, type HasBinFn, type IAiService, IApproval, IConfig, IFileSystem, type ILogger, type INotification, IRunner, IRunnerSession, IWorktreeIsolation, type IsolationDiffResponse, IsolationHandoff, IsolationLandedTask, type IsolationMergeResponse, IsolationMergeResult, IsolationOutcome, IsolationRun, IsolationTaskRecord, IsolationTaskStatus, IsolationView, JSON_REPAIR_INSTRUCTION, type KillTreeDeps, type LaunchDeps, type LaunchPlan, LegacyPlanState, type LiveOutputLookup, type LiveTail, type LiveTailOptions, type MappedTool, type MergeGateEntry, type MergeGateResponse, MergeGateView, ModelResolver, type ModelResolverDeps, type ModelsResponse, type ModifyPlanRequest, NoPlanError, type NotificationAction, OPENCODE_MANIFEST, ORDEWELL_SETTABLE_ENV, OUTPUT_LINES_DEFAULT, OUTPUT_LINES_MAX, type OkResponse, OpenAiService, OpenCodeAdapter, type OrchestratorObserver, OrchestratorOption, OrdewellMcpServer, PROVIDER_CREDENTIAL_ENV, PROVIDER_DETECT_PRIORITY, PROVIDER_LABEL, PROVIDER_PRIORITY, PROVIDER_SHORT_LABEL, type PendingApproval, PendingApprovals, type PendingApprovalsOptions, PlanEditError, PlanIsolation, PlanParseError, type PlanRequest, type PlanResponse, PlanState, PlanStatus, PlanStore, Planner, PlannerAllowlist, type PlannerBackendEntry, type PlannerModelCandidate, type PlannerModelChoice, PlannerModelMemory, type PlannerModelRecall, type PlannerModelStore, type PlannerReplyClassification, type PlannerSubmission, PlannerToolHandler, PlannerTurnDiscardedError, PlannerTurnStoppedError, type PlannerUsability, PlannerUsage, type PrdBlock, type PrdResponse, type PriorOutput, ProviderModelLists, type ProviderRegistration, QueuedMessage, QueuedTaskMessage, REFUSED_COMMANDS, ReadFileOpts, type RemoveMessageResponse, RepairEvidence, type RepairLoopOpts, type RepairVerdict, RepoGroupLayout, type ResearchChat, ResearchLogEntry, ResearchProgress, type ResearchShell, type ResearchShellDeps, ResearchToolType, type ResearchTurn, type ResolveConflictResponse, type RewindTarget, type RewindTargetsResponse, RunnerApprovals, type RunnerCatalog, RunnerId, RunnerInstallation, RunnerManifest, RunnerModeInfo, RunnerRegistry, type RunnerSpawnOptions, type RunnersResponse, SETTINGS_ENV_ALLOWLIST, SETTINGS_ENV_REFUSED, STATE_DIR, STATE_DIR_IGNORE, STOPPED_TOOL_RESULT, SYMBOL_LANGUAGES, type SaveSession, Session, SessionBroadcaster, type SessionData, type SessionDeps, type SessionListResponse, SessionMessage, type SessionMeta, SessionNotFoundError, SessionNotice, type SessionPlanner, type SessionResponse, type SessionRuntimeSettings, type SettingsResponse, SettingsService, type SettingsUpdateResponse, type ShellDialect, SkillInfo, type SkillInvocation, SkillLoad, SkillsService, type SpawnSpec, StdioAgentAdapter, type StopResponse, StructuredEvent, StructuredRunner, type StructuredRunnerDeps, StructuredSession, StructuredTurnEnd, SurfacePlan, SurfacePlanState, TASK_QUERY_ANSWER_MAX_CHARS, TASK_QUERY_ANSWER_OR_OPS, TASK_QUERY_FIELDS, TASK_QUERY_PROTOCOL, TASK_QUERY_REMINDER, TRUNCATED_PLAN_REPAIR_INSTRUCTION, Task, type TaskAttemptSnapshot, TaskCompleteArgs, TaskControlError, TaskIsolation, type TaskLogAttemptsResponse, TaskLogEvent, type TaskLogFile, type TaskLogLocation, TaskLogRecorder, type TaskLogRecorderDeps, type TaskLogResponse, type TaskMessageResponse, TaskModeAgentAdapter, TaskOp, TaskOrchestrator, type TaskOrchestratorDeps, type TaskOrchestratorOptions, type TaskOutputSource, type TaskQuery, type TaskQueryCatalog, type TaskQueryField, TaskRunnerFlags, TaskSkillSnapshot, TaskSnapshot, TaskStartOptions, TaskTokenScope, type TokenCarriers, type ToolCall, ToolOutcome, type ToolResult, TreeSnapshot, UsageRecord, type UserSettings, Verdict, VerdictEngine, type VerdictListener, WINDOWS_MAX_COMMAND_LINE, type WorkspaceCheckDeps, WorkspaceNotAProjectError, WorkspaceNotFoundError, type WorkspaceProjectCheckDeps, type WorkspacesResponse, type WorktreeIsolationDeps, abbreviateHome, admitSettingsEnv, applyHeadLimit, assertWorkspaceExists, assertWorkspaceIsProject, augmentPromptWithPriorOutputs, augmentedPath, bearerHeaderValue, buildConflictRepairPrompt, buildConflictResolutionPrompt, buildConversationSystemPrompt, buildFallbackGrepArgs, buildGlobArgs, buildGrepArgs, buildMergePrompt, buildModifyDuringExecutionPrompt, buildModifyPlanPrompt, buildPlanWithResults, buildResearchPrompt, buildResearchToolsPrompt, buildSplitPrompt, buildSubagentSystemPrompt, canContinue, clampThinkingEffort, classifyCommand, classifyPlannerReply, clearAugmentedPathCache, clearDaemonToken, clearResearchShellCache, clearState, clipboardCopyCommand, coerceAssignments, collectDirectDependencyOutputs, composeAugmentedPrompt, composeContinuationPrompt, configuredProviders, continuability, createAiService, createSession, createTaskAdapter, createWorktreeIsolation, daemonTokenPath, defaultLogger, definitionPattern, deleteSession, describeMergeResult, discoverGeminiModels, effectiveAllowlist, ensureDir, ensurePrivateDir, ensureStateDirIgnored, executionLogBlock, extractPrdBlock, extractPresentedToken, filterFallbackByAnchoredInclude, filterModelsForPrompt, formatSearchOutput, generatePlanWithRepair, getLatestSession, getProviderMeta, getSettingsPath, getStateDir, globalDataDir, grantScopeFor, grepRegexFromProbe, idSegment, includeGlobFor, isCliProvider, isExecutableResolved, isOpenAiProvider, killTree, languageForId, listSessions, listTaskLogAttempts, loadSession, loadSessionPlanState, loadState, mapAgentTool, migrateOldConfigDir, migratePlanIsolation, mintDaemonToken, mintSessionId, modelContextBlock, modifyValidationFeedback, normalizeAgentArgs, normalizeGeminiModel, openTaskLog, parseMaxParallel, parseTaskQueryJson, pendingEditRulesBlock, planDirectLaunch, plannerBackendEntries, plannerMessage, plannerTranscript, prefixModelId, providerForRunner, reEmitPlanPrompt, reEmitTaskOpsPrompt, reEmitTaskQueryPrompt, readDaemonToken, readTaskLog, referencePattern, removedPluginNotice, renderPlanMap, renderPriorOutputs, renderTaskQueryAnswer, repairLoop, researchShellWarning, researchToolsPath, resolveProviderFromPrefix, resolveResearchShell, resolveSkillInvocation, resolveTaskRunnerFlags, resolveWithin, resubmitEditPrompt, resubmitPlanPrompt, retargetTaskRunner, runnerAssignment, runnerForProvider, sanitizeSlug, savePrdMarkdown, saveSession, saveState, sessionDataDir, sessionRuntimeSettings, stateExists, stripAnsi, stripModelPrefix, summarizeOutput, supportsTaskMode, taskOpsRejectedPrompt, taskQuerySignature, textHasTaskQuery, tokenSubprotocols, tokensMatch, truncatedPlanReEmitPrompt, wellKnownBinDirs, windowsCommandLine, withPath, writePrivateFile };
6100
+ export { ALL_PROVIDERS, AbstractRunner, AbstractRunnerSession, ActiveTaskSession, type AdoptSessionResponse, type Adr0013IsolationRun, type Adr0013PlanIsolation, type Adr0013TaskRecord, AgentAdapter, AgentEvent, AgentProcessDeps, AgentStartOptions, AiProvider, AlreadyExecutingError, ApplyTaskOpsResult, ApprovalAnswer, type ApprovalAnswerResponse, ApprovalDecision, ApprovalModeSetting, ApprovalPolicy, ApprovalRequest, type ApprovalsResponse, type AskOptions, AwaitingReason, BaseAiService, BaseConfig, BaseFileSystem, BufferedTaskOutputSource, CLAUDE_CODE_MANIFEST, CLI_PROVIDERS, CMD_EXE_MAX_COMMAND_LINE, CORE_PLANNER_PROMPT, type CachedPage, type CancelPlanningResponse, type CappedRows, CheckpointAnswer, type CheckpointListener, ClaudeCodeAdapter, CliAgentAiService, type CliAgentAiServiceDeps, CodexAdapter, type CommandDescriptor, CommandLineTooLongError, type CommandsResponse, type ComposeOptions, ConsoleLogger, ContextCollector, type Continuability, ConversationBusyError, type ConversationCompactResponse, type ConversationCompaction, ConversationEditError, type ConversationFork, type ConversationForkResponse, ConversationMessage, type ConversationRequest, type ConversationRewind, type ConversationRewindResponse, type ConversationTurn, type ConversationVariant, DAEMON_SUBPROTOCOL, DAEMON_TOKEN_SUBPROTOCOL_PREFIX, DEFAULT_MAX_PARALLEL, DEFAULT_WEB_READ_BUDGET, type DaemonErrorCode, type DecideOptions, DiscoveredModel, DuckDuckGoSearchProvider, EmbeddedNewlineError, type EnvAdmission, EnvConfig, type ErrorBody, type ExecImpl, ExecutableNotFoundError, type ExecuteResponse, FindSymbolOptions, type ForceSendResponse, GREP_PCRE_PROBE_ARGS, GeminiService, type GeneratePlanResponse, type GitExecFn, GlobOptions, type GrepInvocation, GrepOptions, type GrepRegex, HARNESS_PLANNER_REASON, type HasBinFn, HttpWebFetcher, type HttpWebFetcherOptions, type IAiService, IApproval, IConfig, IFileSystem, type ILogger, type INotification, IRunner, IRunnerSession, type ISearchProvider, IWorktreeIsolation, type IsolationDiffResponse, IsolationHandoff, IsolationLandedTask, type IsolationMergeResponse, IsolationMergeResult, IsolationOutcome, IsolationRun, IsolationTaskRecord, IsolationTaskStatus, IsolationView, JSON_REPAIR_INSTRUCTION, type KillTreeDeps, type LaunchDeps, type LaunchPlan, LegacyPlanState, type LiveOutputLookup, type LiveTail, type LiveTailOptions, type MappedTool, type MergeGateEntry, type MergeGateResponse, MergeGateView, ModelResolver, type ModelResolverDeps, type ModelsResponse, type ModifyPlanRequest, NoPlanError, type NotificationAction, OPENCODE_MANIFEST, ORDEWELL_SETTABLE_ENV, OUTPUT_LINES_DEFAULT, OUTPUT_LINES_MAX, type OkResponse, OpenAiService, OpenCodeAdapter, type OrchestratorObserver, OrchestratorOption, OrdewellMcpServer, PROVIDER_CREDENTIAL_ENV, PROVIDER_DETECT_PRIORITY, PROVIDER_LABEL, PROVIDER_PRIORITY, PROVIDER_SHORT_LABEL, type PendingApproval, PendingApprovals, type PendingApprovalsOptions, PlanEditError, PlanIsolation, PlanParseError, type PlanRequest, type PlanResponse, PlanState, PlanStatus, PlanStore, Planner, type PlannerBackendEntry, type PlannerModelCandidate, type PlannerModelChoice, PlannerModelMemory, type PlannerModelRecall, type PlannerModelStore, type PlannerReplyClassification, type PlannerSubmission, PlannerToolHandler, PlannerTurnDiscardedError, PlannerTurnStoppedError, type PlannerUsability, PlannerUsage, type PrdBlock, type PrdResponse, type PriorOutput, ProviderModelLists, type ProviderRegistration, QueuedMessage, QueuedTaskMessage, ReadFileOpts, type RemoveMessageResponse, RepairEvidence, type RepairLoopOpts, type RepairVerdict, RepoGroupLayout, type ResearchChat, ResearchLogEntry, ResearchProgress, ResearchShell, ResearchToolType, type ResearchTurn, type ResolveConflictResponse, type RewindTarget, type RewindTargetsResponse, RunnerApprovals, type RunnerCatalog, RunnerId, RunnerInstallation, RunnerManifest, RunnerModeInfo, RunnerRegistry, type RunnerSpawnOptions, type RunnersResponse, SETTINGS_ENV_ALLOWLIST, SETTINGS_ENV_REFUSED, STATE_DIR, STATE_DIR_IGNORE, STOPPED_TOOL_RESULT, SYMBOL_LANGUAGES, type SaveSession, type SearchHit, type SearchOutcome, type SearchPage, type SearchTransport, Session, SessionBroadcaster, type SessionData, type SessionDeps, type SessionListResponse, SessionMessage, type SessionMeta, SessionNotFoundError, SessionNotice, type SessionPlanner, type SessionResponse, type SessionRuntimeSettings, type SettingsResponse, SettingsService, type SettingsUpdateResponse, SkillInfo, type SkillInvocation, SkillLoad, SkillsService, type SpawnSpec, StdioAgentAdapter, type StopResponse, StructuredEvent, StructuredRunner, type StructuredRunnerDeps, StructuredSession, StructuredTurnEnd, SurfacePlan, SurfacePlanState, TASK_QUERY_ANSWER_MAX_CHARS, TASK_QUERY_ANSWER_OR_OPS, TASK_QUERY_FIELDS, TASK_QUERY_PROTOCOL, TASK_QUERY_REMINDER, TRUNCATED_PLAN_REPAIR_INSTRUCTION, Task, type TaskAttemptSnapshot, TaskCompleteArgs, TaskControlError, TaskIsolation, type TaskLogAttemptsResponse, TaskLogEvent, type TaskLogFile, type TaskLogLocation, TaskLogRecorder, type TaskLogRecorderDeps, type TaskLogResponse, type TaskMessageResponse, TaskModeAgentAdapter, TaskOp, TaskOrchestrator, type TaskOrchestratorDeps, type TaskOrchestratorOptions, type TaskOutputSource, type TaskQuery, type TaskQueryCatalog, type TaskQueryField, TaskRunnerFlags, TaskSkillSnapshot, TaskSnapshot, TaskStartOptions, TaskTokenScope, type TokenCarriers, type ToolCall, ToolOutcome, type ToolResult, TreeSnapshot, UsageRecord, type UserSettings, Verdict, 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, augmentPromptWithPriorOutputs, augmentedPath, bearerHeaderValue, buildConflictRepairPrompt, buildConflictResolutionPrompt, buildConversationSystemPrompt, buildFallbackGrepArgs, buildGlobArgs, buildGrepArgs, buildMergePrompt, buildModifyDuringExecutionPrompt, buildModifyPlanPrompt, buildPlanWithResults, buildResearchPrompt, buildResearchToolsPrompt, buildSplitPrompt, buildSubagentSystemPrompt, canContinue, clampThinkingEffort, classifyPlannerReply, clearAugmentedPathCache, clearDaemonToken, clearState, clipboardCopyCommand, coerceAssignments, collectDirectDependencyOutputs, composeAugmentedPrompt, composeContinuationPrompt, configuredProviders, continuability, createAiService, createSession, createTaskAdapter, createWorktreeIsolation, daemonTokenPath, defaultLogger, definitionPattern, deleteSession, describeMergeResult, discoverGeminiModels, effectiveAllowlist, ensureDir, ensurePrivateDir, ensureStateDirIgnored, executionLogBlock, extractPrdBlock, extractPresentedToken, filterFallbackByAnchoredInclude, filterModelsForPrompt, formatSearchOutput, generatePlanWithRepair, getLatestSession, getProviderMeta, getSettingsPath, getStateDir, globalDataDir, grantScopeFor, grepRegexFromProbe, idSegment, includeGlobFor, isCliProvider, isExecutableResolved, isOpenAiProvider, killTree, languageForId, listSessions, listTaskLogAttempts, loadSession, loadSessionPlanState, loadState, looksLikeChallenge, mapAgentTool, migrateOldConfigDir, migratePlanIsolation, mintDaemonToken, mintSessionId, modelContextBlock, modifyValidationFeedback, normalizeAgentArgs, normalizeGeminiModel, openTaskLog, parseMaxParallel, parseSearchHits, parseTaskQueryJson, pendingEditRulesBlock, planDirectLaunch, plannerBackendEntries, plannerMessage, plannerTranscript, prefixModelId, providerForRunner, reEmitPlanPrompt, reEmitTaskOpsPrompt, reEmitTaskQueryPrompt, readDaemonToken, readTaskLog, referencePattern, removedPluginNotice, renderPlanMap, renderPriorOutputs, renderTaskQueryAnswer, repairLoop, resolveProviderFromPrefix, resolveSkillInvocation, resolveTaskRunnerFlags, resolveWithin, resubmitEditPrompt, resubmitPlanPrompt, retargetTaskRunner, runnerAssignment, runnerForProvider, sanitizeSlug, savePrdMarkdown, saveSession, saveState, sessionDataDir, sessionRuntimeSettings, sharedSearchProvider, sharedWebResponseCache, stateExists, stripAnsi, stripModelPrefix, summarizeOutput, supportsTaskMode, taskOpsRejectedPrompt, taskQuerySignature, textHasTaskQuery, tokenSubprotocols, tokensMatch, truncatedPlanReEmitPrompt, wellKnownBinDirs, windowsCommandLine, withPath, writePrivateFile };