@ordewell/core 0.9.0 → 0.10.0
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/{IFileSystem-DpaaFdit.d.ts → IFileSystem-BCJBHFwB.d.ts} +3 -1
- package/dist/{IFileSystem-_m7HdoxJ.d.mts → IFileSystem-Bj6fy1cG.d.mts} +3 -1
- package/dist/{IRunner-BiQio8gJ.d.ts → IRunner-C7_SSpzG.d.ts} +614 -77
- package/dist/{IRunner-CxiymkP_.d.mts → IRunner-hWAJC4hg.d.mts} +614 -77
- package/dist/ModeResolver-m1d8Cb-j.d.mts +35 -0
- package/dist/ModeResolver-u5ZhRC8x.d.ts +35 -0
- package/dist/{Task-1Q19y7sZ.d.mts → Task-CxwnCJLi.d.mts} +346 -127
- package/dist/{Task-1Q19y7sZ.d.ts → Task-CxwnCJLi.d.ts} +346 -127
- package/dist/{chunk-UUU46PVQ.mjs → chunk-2IIRSBEQ.mjs} +109 -142
- package/dist/chunk-2IIRSBEQ.mjs.map +1 -0
- package/dist/{chunk-QUARDKVT.mjs → chunk-CH5IGWFS.mjs} +546 -967
- package/dist/chunk-CH5IGWFS.mjs.map +1 -0
- package/dist/chunk-DJAPDO6E.mjs +1358 -0
- package/dist/chunk-DJAPDO6E.mjs.map +1 -0
- package/dist/chunk-DNFT4SQG.mjs +97 -0
- package/dist/chunk-DNFT4SQG.mjs.map +1 -0
- package/dist/index.d.mts +3204 -3926
- package/dist/index.d.ts +3204 -3926
- package/dist/index.js +22283 -16472
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +20220 -14690
- package/dist/index.mjs.map +1 -1
- package/dist/parsing.d.mts +55 -3
- package/dist/parsing.d.ts +55 -3
- package/dist/parsing.js +311 -124
- package/dist/parsing.js.map +1 -1
- package/dist/parsing.mjs +86 -6
- package/dist/parsing.mjs.map +1 -1
- package/dist/plan-utils-B6yJdpgi.d.ts +2902 -0
- package/dist/plan-utils-CKXdD1AD.d.mts +2902 -0
- package/dist/plan-utils.d.mts +4 -5
- package/dist/plan-utils.d.ts +4 -5
- package/dist/plan-utils.js +471 -86
- package/dist/plan-utils.js.map +1 -1
- package/dist/plan-utils.mjs +68 -19
- package/dist/testing.d.mts +22 -4
- package/dist/testing.d.ts +22 -4
- package/dist/testing.js +46 -9
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +47 -11
- package/dist/testing.mjs.map +1 -1
- package/package.json +2 -10
- package/skills/tdd/SKILL.md +0 -1
- package/dist/ModeResolver-DE-o2lRF.d.mts +0 -48
- package/dist/ModeResolver-EWlpmVAy.d.ts +0 -48
- package/dist/chunk-2M4X3VB7.mjs +0 -432
- package/dist/chunk-2M4X3VB7.mjs.map +0 -1
- package/dist/chunk-H5O74L77.mjs +0 -185
- package/dist/chunk-H5O74L77.mjs.map +0 -1
- package/dist/chunk-QUARDKVT.mjs.map +0 -1
- package/dist/chunk-UUU46PVQ.mjs.map +0 -1
- package/dist/chunk-V3JHS7AX.mjs +0 -27
- package/dist/chunk-V3JHS7AX.mjs.map +0 -1
- package/dist/order-labels.d.mts +0 -30
- package/dist/order-labels.d.ts +0 -30
- package/dist/order-labels.js +0 -54
- package/dist/order-labels.js.map +0 -1
- package/dist/order-labels.mjs +0 -13
- package/dist/order-labels.mjs.map +0 -1
- package/dist/parsing-CZdp2p_n.d.ts +0 -154
- package/dist/parsing-GgUJoYKs.d.mts +0 -154
- package/dist/plan-utils-6rUr7ZOx.d.ts +0 -1222
- package/dist/plan-utils-P3PIEwNa.d.mts +0 -1222
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ChildProcess } from 'child_process';
|
|
2
|
-
import { ao as UsageRecord,
|
|
2
|
+
import { ao as UsageRecord, aa as SubagentOutcome, $ as RunnerId, aj as TaskSkillSnapshot } from './Task-CxwnCJLi.js';
|
|
3
3
|
import { z } from 'zod/v4';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -20,7 +20,7 @@ import { z } from 'zod/v4';
|
|
|
20
20
|
* - an **MCP tool rule**, `mcp:find-*`: a glob over the tool's own name,
|
|
21
21
|
* without the server, case-insensitive.
|
|
22
22
|
* - a **scope pattern**, anything starting with `/`, `~`, `http://`,
|
|
23
|
-
* `https://`, `mcp__` or a drive letter: matched against an approval's
|
|
23
|
+
* `https://`, `web_search:`, `mcp__`, `mcp-server:` or a drive letter: matched against an approval's
|
|
24
24
|
* scope as ADR-0008 always matched pre-approved entries, `*` matching any
|
|
25
25
|
* run of characters.
|
|
26
26
|
*
|
|
@@ -74,6 +74,290 @@ declare function scopeAllowed(list: PlannerAllowlist, scope: string): boolean;
|
|
|
74
74
|
*/
|
|
75
75
|
declare const DEFAULT_PLANNER_ALLOWLIST: readonly string[];
|
|
76
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Which interpreter runs the planner's `bash` tool, and in which language.
|
|
79
|
+
*
|
|
80
|
+
* `AUTO_COMMANDS` — the read-only tier that runs with no prompt — is `ls`, `cat`,
|
|
81
|
+
* `wc`, `head`, `tail`, `du`, `df`, `file`, `sort`, `uniq`, `basename`, `find`,
|
|
82
|
+
* `grep`. That set is POSIX, and passing it to `shell: true` on Windows means
|
|
83
|
+
* cmd.exe, where most of those names do not exist and `find` is an unrelated
|
|
84
|
+
* program that returns plausible wrong output instead of an error. The planner's
|
|
85
|
+
* whole research surface degraded to guessing, quietly.
|
|
86
|
+
*
|
|
87
|
+
* Rather than write a Windows dialect of every research command — a second
|
|
88
|
+
* behavior to keep in step forever — this finds a POSIX shell to run the
|
|
89
|
+
* existing ones in. Git for Windows ships a full one, and git is already a
|
|
90
|
+
* prerequisite for everything Ordewell does, so in practice it is there. Failing
|
|
91
|
+
* that, cmd.exe is used and the classifier is told so, because the one thing
|
|
92
|
+
* that must never happen is classifying a command in one language and running it
|
|
93
|
+
* in another: `commandPolicy` reads {@link ResearchShell.dialect} for exactly
|
|
94
|
+
* that reason.
|
|
95
|
+
*
|
|
96
|
+
* POSIX resolves to `{ file: null }`, meaning "use `shell: true`" — byte for
|
|
97
|
+
* byte what the adapters did before this module existed.
|
|
98
|
+
*
|
|
99
|
+
* Windows paths are built with `path.win32` explicitly, not the host-flavoured
|
|
100
|
+
* `path`, so the probe is the same on a Windows host as it is under a Linux
|
|
101
|
+
* test runner.
|
|
102
|
+
*/
|
|
103
|
+
type ShellDialect = 'posix' | 'cmd';
|
|
104
|
+
interface ResearchShell {
|
|
105
|
+
/**
|
|
106
|
+
* Executable that takes a command string, or null to use the host default
|
|
107
|
+
* via `shell: true`.
|
|
108
|
+
*/
|
|
109
|
+
file: string | null;
|
|
110
|
+
/** Arguments preceding the command string. */
|
|
111
|
+
args: string[];
|
|
112
|
+
/** The language the command string will be interpreted in. */
|
|
113
|
+
dialect: ShellDialect;
|
|
114
|
+
/**
|
|
115
|
+
* Directory holding this shell's POSIX utilities, when it brings its own.
|
|
116
|
+
* Prepended to PATH for the search subprocesses (`grep`, `tree`) that
|
|
117
|
+
* `execFile` starts without a shell, so they resolve too.
|
|
118
|
+
*/
|
|
119
|
+
utilsDir: string | null;
|
|
120
|
+
}
|
|
121
|
+
interface ResearchShellDeps {
|
|
122
|
+
platform?: NodeJS.Platform;
|
|
123
|
+
exists?: (candidate: string) => boolean;
|
|
124
|
+
env?: NodeJS.ProcessEnv;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The shell the planner's `bash` tool should use on this host. Resolved once per
|
|
128
|
+
* process — the answer cannot change while Ordewell runs, and the probe costs a
|
|
129
|
+
* handful of `stat` calls.
|
|
130
|
+
*/
|
|
131
|
+
declare function resolveResearchShell(deps?: ResearchShellDeps): ResearchShell;
|
|
132
|
+
/** Test seam: drop the per-process cache so the next call re-probes. */
|
|
133
|
+
declare function clearResearchShellCache(): void;
|
|
134
|
+
/**
|
|
135
|
+
* PATH for the search subprocesses the adapters start without a shell, with the
|
|
136
|
+
* research shell's own utilities in front. Without this, `grep` — the fallback
|
|
137
|
+
* when ripgrep is absent — is unresolvable on a Windows box even when a
|
|
138
|
+
* perfectly good `grep.exe` sits in the Git tree beside the shell.
|
|
139
|
+
*/
|
|
140
|
+
declare function researchToolsPath(shell: ResearchShell, basePath: string | undefined): string;
|
|
141
|
+
/**
|
|
142
|
+
* A message explaining a degraded research surface, or null when there is
|
|
143
|
+
* nothing to explain. Surfaced by the adapters on the first refused command so
|
|
144
|
+
* the limitation is visible rather than inferred from bad answers.
|
|
145
|
+
*/
|
|
146
|
+
declare function researchShellWarning(shell: ResearchShell): string | null;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Tiered classification for the planner's `bash` tool.
|
|
150
|
+
*
|
|
151
|
+
* The planner is a read-only researcher: it never edits files, and the runners
|
|
152
|
+
* do the real work. But research legitimately means running things — querying a
|
|
153
|
+
* cloud control plane (`az`, `gh`), reproducing a failure (`npm test`), or
|
|
154
|
+
* shaping output (`jq`). The old allowlist refused all of that, so the model's
|
|
155
|
+
* only escape was to guess.
|
|
156
|
+
*
|
|
157
|
+
* Three tiers replace the flat allowlist:
|
|
158
|
+
*
|
|
159
|
+
* auto read-only inspection — runs with no prompt (the historical list)
|
|
160
|
+
* ask anything else that is not obviously destructive — one approval,
|
|
161
|
+
* remembered for the rest of the session at `scope` granularity
|
|
162
|
+
* refuse writes, privilege escalation, and anything that would smuggle
|
|
163
|
+
* arbitrary code past this classifier — never runs, never prompts
|
|
164
|
+
*
|
|
165
|
+
* `refuse` is deliberately not promptable. A planner that can `rm` is a planner
|
|
166
|
+
* that can silently break the workspace it was asked to reason about, and the
|
|
167
|
+
* architecture already says mutation belongs to the runners.
|
|
168
|
+
*
|
|
169
|
+
* Classification walks every segment of the command line (pipes, `&&`, `;`, and
|
|
170
|
+
* command substitution) rather than matching substrings against the raw string.
|
|
171
|
+
* The old substring denylist both over-matched (`ls docs/removed` tripped `rm`)
|
|
172
|
+
* and under-matched (`$(rm -rf /)` was invisible once chaining was allowed).
|
|
173
|
+
*
|
|
174
|
+
* A segment is classified by the command that will actually execute, not by the
|
|
175
|
+
* name at the front of it: wrappers are unwrapped first, recursively, so
|
|
176
|
+
* `timeout 10 env nice rm -rf x` is an `rm`. See {@link WRAPPER_FAMILY}. A
|
|
177
|
+
* runner that feeds its command arguments nobody can see is unwrapped the same
|
|
178
|
+
* way but never runs unprompted ({@link XARGS_TARGETS}), and one that hands its
|
|
179
|
+
* command to a shell is refused ({@link REFUSED_RUNNERS}).
|
|
180
|
+
*
|
|
181
|
+
* A permitted binary is permitted with the flags it is known to be read-only
|
|
182
|
+
* with, not with any flag at all: several of them will run a helper program or
|
|
183
|
+
* write a file when asked to, so `auto` is a per-binary flag allowlist and an
|
|
184
|
+
* unrecognised flag is refused. See {@link FLAG_POLICY}.
|
|
185
|
+
*
|
|
186
|
+
* The lexer is dialect-aware, because the interpreter that will actually run
|
|
187
|
+
* the command decides what the tokens are, and getting that wrong is not a
|
|
188
|
+
* cosmetic error here — it is the difference between classifying what runs and
|
|
189
|
+
* classifying something else. See {@link Dialect}.
|
|
190
|
+
*/
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* `env` is deliberately absent. Given a command it is a wrapper, classified by
|
|
194
|
+
* what it actually runs (see {@link WRAPPER_FAMILY}); given none it prints the
|
|
195
|
+
* whole process environment — provider credentials included — into the research
|
|
196
|
+
* log, which is a disclosure the developer should get to see coming.
|
|
197
|
+
*
|
|
198
|
+
* Membership here is necessary but not sufficient: every name in this list also
|
|
199
|
+
* declares the flags it is read-only with in {@link FLAG_POLICY}.
|
|
200
|
+
*/
|
|
201
|
+
declare const AUTO_COMMANDS: string[];
|
|
202
|
+
/**
|
|
203
|
+
* `ls-remote` is deliberately absent, and it is the one entry here that is
|
|
204
|
+
* read-only against the repository. It reaches the network: it contacts
|
|
205
|
+
* whatever host the named remote or URL resolves to, which routes around the
|
|
206
|
+
* web fetcher's per-origin approval and its request-forgery guard. So it
|
|
207
|
+
* prompts rather than running silently.
|
|
208
|
+
*
|
|
209
|
+
* Prompting only became the right answer once {@link scopeFor} widened. Under
|
|
210
|
+
* the old scope it would have been remembered as `git ls-remote`, so one
|
|
211
|
+
* approval of the workspace's own remote would have authorised a listing
|
|
212
|
+
* against any host at all — which is the same hole with an approval dialog in
|
|
213
|
+
* front of it. The scope now carries the destination.
|
|
214
|
+
*/
|
|
215
|
+
declare const GIT_READONLY_SUBCOMMANDS: string[];
|
|
216
|
+
/**
|
|
217
|
+
* Never runs, with or without approval.
|
|
218
|
+
*
|
|
219
|
+
* The `cmd.exe` builtins at the end matter as much as the POSIX names above
|
|
220
|
+
* them. `del`, `rd`, `move`, and friends mutate exactly what `rm` and `mv` do,
|
|
221
|
+
* and on Windows they are what a model reaches for — so without them the whole
|
|
222
|
+
* refusal tier was bypassable on that platform by writing the command the way
|
|
223
|
+
* the platform spells it. Listed unconditionally rather than per-platform: a
|
|
224
|
+
* POSIX box has no `del` to refuse, so the extra names cost nothing there.
|
|
225
|
+
*/
|
|
226
|
+
declare const REFUSED_COMMANDS: string[];
|
|
227
|
+
/**
|
|
228
|
+
* Options threaded through classification.
|
|
229
|
+
*
|
|
230
|
+
* `dialect` is keyed to the interpreter rather than the OS on purpose: a Windows
|
|
231
|
+
* host that has Git Bash runs the planner's commands in a POSIX shell (see
|
|
232
|
+
* {@link resolveResearchShell}), and classifying those under cmd.exe rules
|
|
233
|
+
* would be the same mismatch in the other direction. Callers pass the dialect of
|
|
234
|
+
* the shell they are actually going to use; omitting it falls back to the host
|
|
235
|
+
* default.
|
|
236
|
+
*/
|
|
237
|
+
interface CommandPolicyOptions {
|
|
238
|
+
dialect?: ShellDialect;
|
|
239
|
+
/** Only flags generated from vetted DNS answers by BaseFileSystem, never caller input. */
|
|
240
|
+
trustedWebResolves?: ReadonlySet<string>;
|
|
241
|
+
autonomous?: boolean;
|
|
242
|
+
approvalMode?: 'ask' | 'allow' | 'deny';
|
|
243
|
+
/**
|
|
244
|
+
* `CDPATH` is set in the environment the command will run in. A relative
|
|
245
|
+
* `cd` may then land in a directory the command line never names, so `cd`
|
|
246
|
+
* stops being navigation the classifier can follow.
|
|
247
|
+
*/
|
|
248
|
+
cdpathSet?: boolean;
|
|
249
|
+
/** The planner's standing approvals (ADR-0026); an `ask` command they cover carries {@link CommandClassification.allowedBy}. */
|
|
250
|
+
allow?: PlannerAllowlist;
|
|
251
|
+
}
|
|
252
|
+
type CommandTier = 'auto' | 'ask' | 'refuse';
|
|
253
|
+
interface CommandClassification {
|
|
254
|
+
tier: CommandTier;
|
|
255
|
+
/** A human must approve this invocation even when its origin was already granted. */
|
|
256
|
+
perInvocation?: boolean;
|
|
257
|
+
/** Separate grants for commands that name several origins. */
|
|
258
|
+
scopes?: string[];
|
|
259
|
+
webUrls?: URL[];
|
|
260
|
+
webOutboundTexts?: string[];
|
|
261
|
+
/** What a grant covers, for `ask`. Derived from the non-auto segments only. */
|
|
262
|
+
scope: string;
|
|
263
|
+
/** Populated for `refuse`: why, in a sentence the model can act on. */
|
|
264
|
+
reason?: string;
|
|
265
|
+
/** For `ask`: the allowlist rule that pre-approves every part of the command that needed asking. */
|
|
266
|
+
allowedBy?: string;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Classify one command line. Output redirection is refused outright unless the
|
|
270
|
+
* target provably writes nothing — `/dev/null`, or an fd duplication like
|
|
271
|
+
* `2>&1` — since a planner that writes files has stopped being a planner.
|
|
272
|
+
*/
|
|
273
|
+
declare function classifyCommand(command: string, opts?: CommandPolicyOptions): CommandClassification;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Decides whether one out-of-envelope capability may run, and remembers the
|
|
277
|
+
* answer for the rest of the session.
|
|
278
|
+
*
|
|
279
|
+
* Grants are keyed on {@link ApprovalRequest.scope}, never on the concrete
|
|
280
|
+
* subject: approving a read of `/tmp/foo/a.log` grants `/tmp/foo/*`, and
|
|
281
|
+
* approving `az group list` grants `az group list`. Without that, a planner
|
|
282
|
+
* doing real research would prompt on every single call and the feature would
|
|
283
|
+
* be unusable.
|
|
284
|
+
*
|
|
285
|
+
* `mode` is the policy floor:
|
|
286
|
+
* ask consult the human channel; no channel means deny (headless, web)
|
|
287
|
+
* allow grant everything the tier system did not already refuse
|
|
288
|
+
* deny grant nothing beyond `preApproved` — the allowlist-only mode
|
|
289
|
+
*
|
|
290
|
+
* Note the ordering: `preApproved` is honored under every mode including
|
|
291
|
+
* `deny`, because it is an explicit operator decision rather than a default.
|
|
292
|
+
* Its entries are allowlist rules (see `plannerAllowlist.ts`), and both it and
|
|
293
|
+
* `mode` may be read afresh on every request, so a settings change applies to
|
|
294
|
+
* the next call.
|
|
295
|
+
*/
|
|
296
|
+
type ApprovalMode = 'ask' | 'allow' | 'deny';
|
|
297
|
+
/**
|
|
298
|
+
* The configured mode. `auto` follows the autonomy level (ADR-0026): Guarded
|
|
299
|
+
* asks, Full runs the allowlist alone and asks nobody, so a planner in Full
|
|
300
|
+
* never waits on a person and never acts outside its standing approvals.
|
|
301
|
+
* Web reads are the exception at both levels.
|
|
302
|
+
*/
|
|
303
|
+
type ApprovalModeSetting = ApprovalMode | 'auto';
|
|
304
|
+
declare function effectiveApprovalMode(setting: ApprovalModeSetting, autonomous: boolean, kind?: ApprovalKind): ApprovalMode;
|
|
305
|
+
/** A configured mode as written: `allowlist` is the user-facing name of `deny`, and anything unknown is `auto`. */
|
|
306
|
+
declare function parseApprovalModeSetting(raw: string | undefined): ApprovalModeSetting;
|
|
307
|
+
interface ApprovalPolicyOptions {
|
|
308
|
+
/** Read per request, for the kind being asked about. */
|
|
309
|
+
mode?: ApprovalMode | ((kind: ApprovalKind) => ApprovalMode);
|
|
310
|
+
autonomous?: () => boolean;
|
|
311
|
+
/** Allowlist entries granted up front from config (ADR-0026). */
|
|
312
|
+
preApproved?: readonly string[] | (() => readonly string[]);
|
|
313
|
+
/** The human channel. Absent means there is nobody to ask. */
|
|
314
|
+
ask?: (req: ApprovalRequest) => Promise<boolean>;
|
|
315
|
+
/** Called whenever a decision is reached, for surfacing in a UI or a log. */
|
|
316
|
+
onDecision?: (req: ApprovalRequest, granted: boolean, source: ApprovalSource) => void;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Why a request was decided as it was. The next four are refusals reached
|
|
320
|
+
* before the policy itself is consulted (ADR-0027's web-read guards), handed
|
|
321
|
+
* to {@link ApprovalPolicy.record} so they surface like any other decision:
|
|
322
|
+
* an `!` allowlist exclusion, the scheme and SSRF guard, the outbound secret
|
|
323
|
+
* check, and the per-turn web-read budget. `unsupported` comes after: an
|
|
324
|
+
* approved project MCP server the planner's runner version cannot give the
|
|
325
|
+
* planner as tools of their own (ADR-0028).
|
|
326
|
+
*/
|
|
327
|
+
type ApprovalSource = 'pre-approved' | 'remembered' | 'mode' | 'asked' | 'no-channel' | 'excluded' | 'guard' | 'secret' | 'budget' | 'unsupported';
|
|
328
|
+
declare class ApprovalPolicy implements IApproval {
|
|
329
|
+
private readonly modeOf;
|
|
330
|
+
private readonly autonomousOf;
|
|
331
|
+
private readonly entriesOf;
|
|
332
|
+
private parsed;
|
|
333
|
+
private readonly asker?;
|
|
334
|
+
private readonly onDecision?;
|
|
335
|
+
private readonly granted;
|
|
336
|
+
private readonly refused;
|
|
337
|
+
/** One in-flight ask per scope: a parallel tool round must not prompt twice for the same thing. */
|
|
338
|
+
private readonly inFlight;
|
|
339
|
+
private generation;
|
|
340
|
+
constructor(opts?: ApprovalPolicyOptions);
|
|
341
|
+
request(req: ApprovalRequest): Promise<boolean>;
|
|
342
|
+
commandPolicy(): Pick<CommandPolicyOptions, 'autonomous' | 'approvalMode'>;
|
|
343
|
+
/**
|
|
344
|
+
* What `request` would answer without a person, for a caller that may never
|
|
345
|
+
* prompt (a research subagent): the same standing grants, remembered
|
|
346
|
+
* refusals and mode the interactive path uses, stopping at `ask`.
|
|
347
|
+
*/
|
|
348
|
+
peek(req: ApprovalRequest): StandingDecision;
|
|
349
|
+
private standing;
|
|
350
|
+
/** Report a decision a guard reached before this policy was asked. */
|
|
351
|
+
record(req: ApprovalRequest, granted: boolean, source: ApprovalSource): void;
|
|
352
|
+
grantsUnasked({ kind, scope }: Pick<ApprovalRequest, 'kind' | 'scope'>): boolean;
|
|
353
|
+
/** The standing approvals, parsed from the entries in force now. */
|
|
354
|
+
allowlist(): PlannerAllowlist;
|
|
355
|
+
/** Scopes the user has granted this session — for display and for persistence. */
|
|
356
|
+
grantedScopes(): string[];
|
|
357
|
+
/** Drop every session-scoped decision. Called on session reset. */
|
|
358
|
+
reset(): void;
|
|
359
|
+
}
|
|
360
|
+
|
|
77
361
|
/**
|
|
78
362
|
* The planner's single approval seam. Every capability that reaches beyond the
|
|
79
363
|
* default read-only, in-workspace envelope — a path outside the workspace root,
|
|
@@ -91,11 +375,17 @@ declare const DEFAULT_PLANNER_ALLOWLIST: readonly string[];
|
|
|
91
375
|
* for an answer as long as it takes, and never passes through the planner's
|
|
92
376
|
* policy — the runner's mode already decided it needed asking. `mcp_tool` is
|
|
93
377
|
* a harness planner reaching for an MCP tool the user configured (ADR-0026),
|
|
94
|
-
* scoped to that one tool.
|
|
378
|
+
* scoped to that one tool. `web_search` is scoped to the search provider, the
|
|
379
|
+
* party that receives every query (ADR-0027). `mcp_server` is starting an MCP
|
|
380
|
+
* server the repository declares for a planner, scoped to its definition
|
|
381
|
+
* (ADR-0028). `task_start` is the planner asking to start work through the
|
|
382
|
+
* command seam in Guarded (ADR-0029), asked afresh every time.
|
|
95
383
|
*/
|
|
96
|
-
type ApprovalKind = 'external_path' | 'shell_command' | 'url_fetch' | 'mcp_tool' | 'runner_tool';
|
|
384
|
+
type ApprovalKind = 'external_path' | 'shell_command' | 'url_fetch' | 'web_search' | 'mcp_tool' | 'mcp_server' | 'runner_tool' | 'task_start';
|
|
97
385
|
interface ApprovalRequest {
|
|
98
386
|
kind: ApprovalKind;
|
|
387
|
+
/** Credentials, data and redirects need a fresh human answer for each invocation. */
|
|
388
|
+
perInvocation?: boolean;
|
|
99
389
|
/** The concrete thing being asked about: an absolute path, a command line, a URL. */
|
|
100
390
|
subject: string;
|
|
101
391
|
/**
|
|
@@ -143,10 +433,32 @@ declare function toApprovalDecision(answer: ApprovalAnswer): ApprovalDecision;
|
|
|
143
433
|
declare function isGranted(decision: ApprovalDecision): boolean;
|
|
144
434
|
declare function approvalScopes(request: ApprovalRequest): string[];
|
|
145
435
|
declare function isRunnerApproval(request: ApprovalRequest): boolean;
|
|
436
|
+
type StandingDecision = {
|
|
437
|
+
decision: 'allow' | 'deny';
|
|
438
|
+
source: 'pre-approved' | 'remembered' | 'mode';
|
|
439
|
+
} | {
|
|
440
|
+
decision: 'deny';
|
|
441
|
+
source: 'excluded';
|
|
442
|
+
} | {
|
|
443
|
+
decision: 'ask';
|
|
444
|
+
source: 'no-channel';
|
|
445
|
+
};
|
|
146
446
|
interface IApproval {
|
|
147
447
|
request(req: ApprovalRequest): Promise<boolean>;
|
|
148
448
|
/** The standing approvals a command is classified against, when this approval has any (ADR-0026). */
|
|
149
449
|
allowlist?(): PlannerAllowlist;
|
|
450
|
+
commandPolicy?(): Pick<CommandPolicyOptions, 'autonomous' | 'approvalMode'>;
|
|
451
|
+
/** Surface a decision reached before `request` was called, as `request`'s own are. */
|
|
452
|
+
record?(req: ApprovalRequest, granted: boolean, source: ApprovalSource): void;
|
|
453
|
+
/**
|
|
454
|
+
* Resolve a request without a person (ADR-0027): `allow` when it is already
|
|
455
|
+
* granted or the mode allows without asking, `deny` when a refusal is
|
|
456
|
+
* remembered or the mode forbids, `ask` when it would still need a prompt. A
|
|
457
|
+
* background research subagent never prompts, so `ask` means refused.
|
|
458
|
+
*/
|
|
459
|
+
peek?(req: ApprovalRequest): StandingDecision;
|
|
460
|
+
/** Whether `request` would grant this scope now without asking anyone. Nothing is asked or recorded. */
|
|
461
|
+
grantsUnasked?(req: Pick<ApprovalRequest, 'kind' | 'scope'>): boolean;
|
|
150
462
|
}
|
|
151
463
|
/** Denies everything. The safe default when a surface wires no approval channel. */
|
|
152
464
|
declare const DENY_ALL: IApproval;
|
|
@@ -179,6 +491,12 @@ declare const listModelsInput: z.ZodObject<{
|
|
|
179
491
|
declare const runCommandInput: z.ZodObject<{
|
|
180
492
|
command: z.ZodString;
|
|
181
493
|
}, z.core.$strip>;
|
|
494
|
+
declare const fetchInput: z.ZodObject<{
|
|
495
|
+
url: z.ZodString;
|
|
496
|
+
}, z.core.$strip>;
|
|
497
|
+
declare const webSearchInput: z.ZodObject<{
|
|
498
|
+
query: z.ZodString;
|
|
499
|
+
}, z.core.$strip>;
|
|
182
500
|
declare const loadSkillInput: z.ZodObject<{
|
|
183
501
|
name: z.ZodString;
|
|
184
502
|
}, z.core.$strip>;
|
|
@@ -189,8 +507,8 @@ declare const submitPlanInput: z.ZodObject<{
|
|
|
189
507
|
title: z.ZodString;
|
|
190
508
|
description: z.ZodOptional<z.ZodString>;
|
|
191
509
|
type: z.ZodOptional<z.ZodEnum<{
|
|
192
|
-
ai: "ai";
|
|
193
510
|
user: "user";
|
|
511
|
+
ai: "ai";
|
|
194
512
|
}>>;
|
|
195
513
|
dependencies: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
196
514
|
prompt: z.ZodOptional<z.ZodString>;
|
|
@@ -204,7 +522,7 @@ declare const submitPlanInput: z.ZodObject<{
|
|
|
204
522
|
modelLabel: z.ZodOptional<z.ZodString>;
|
|
205
523
|
thinkingEffort: z.ZodOptional<z.ZodString>;
|
|
206
524
|
}, z.core.$loose>>;
|
|
207
|
-
|
|
525
|
+
readOnly: z.ZodOptional<z.ZodBoolean>;
|
|
208
526
|
autonomy: z.ZodOptional<z.ZodEnum<{
|
|
209
527
|
AFK: "AFK";
|
|
210
528
|
HITL: "HITL";
|
|
@@ -264,20 +582,91 @@ declare const taskQueryInput: z.ZodObject<{
|
|
|
264
582
|
catalog: z.ZodOptional<z.ZodBoolean>;
|
|
265
583
|
}, z.core.$strip>;
|
|
266
584
|
declare const taskOutputInput: z.ZodObject<{
|
|
585
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
586
|
+
kinds: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
587
|
+
text: "text";
|
|
588
|
+
error: "error";
|
|
589
|
+
command: "command";
|
|
590
|
+
thinking: "thinking";
|
|
591
|
+
task_skills: "task_skills";
|
|
592
|
+
turn_start: "turn_start";
|
|
593
|
+
command_output: "command_output";
|
|
594
|
+
compaction: "compaction";
|
|
595
|
+
runner_loaded: "runner_loaded";
|
|
596
|
+
turn_end: "turn_end";
|
|
597
|
+
text_delta: "text_delta";
|
|
598
|
+
thinking_delta: "thinking_delta";
|
|
599
|
+
tool_call: "tool_call";
|
|
600
|
+
tool_result: "tool_result";
|
|
601
|
+
subagent_started: "subagent_started";
|
|
602
|
+
subagent_finished: "subagent_finished";
|
|
603
|
+
usage: "usage";
|
|
604
|
+
message_queued: "message_queued";
|
|
605
|
+
message_removed: "message_removed";
|
|
606
|
+
message_undelivered: "message_undelivered";
|
|
607
|
+
message_handed_over: "message_handed_over";
|
|
608
|
+
message_delivered: "message_delivered";
|
|
609
|
+
approval_requested: "approval_requested";
|
|
610
|
+
approval_decided: "approval_decided";
|
|
611
|
+
approval_withdrawn: "approval_withdrawn";
|
|
612
|
+
runner_ended: "runner_ended";
|
|
613
|
+
}>>>;
|
|
614
|
+
task: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
615
|
+
lines: z.ZodOptional<z.ZodNumber>;
|
|
616
|
+
since: z.ZodOptional<z.ZodNumber>;
|
|
617
|
+
}, z.core.$strip>;
|
|
618
|
+
declare const taskDiffInput: z.ZodObject<{
|
|
267
619
|
task: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
620
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
621
|
+
path: z.ZodOptional<z.ZodString>;
|
|
268
622
|
lines: z.ZodOptional<z.ZodNumber>;
|
|
269
623
|
since: z.ZodOptional<z.ZodNumber>;
|
|
270
624
|
}, z.core.$strip>;
|
|
625
|
+
declare const runPlanInput: z.ZodObject<{}, z.core.$strip>;
|
|
626
|
+
declare const oneTaskInput: z.ZodObject<{
|
|
627
|
+
task: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
628
|
+
}, z.core.$strip>;
|
|
629
|
+
declare const retryTaskInput: z.ZodObject<{
|
|
630
|
+
task: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
631
|
+
from: z.ZodEnum<{
|
|
632
|
+
fresh: "fresh";
|
|
633
|
+
session: "session";
|
|
634
|
+
}>;
|
|
635
|
+
message: z.ZodOptional<z.ZodString>;
|
|
636
|
+
}, z.core.$strip>;
|
|
637
|
+
declare const skipTaskInput: z.ZodObject<{
|
|
638
|
+
task: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
639
|
+
reason: z.ZodString;
|
|
640
|
+
}, z.core.$strip>;
|
|
641
|
+
declare const messageTaskInput: z.ZodObject<{
|
|
642
|
+
task: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
643
|
+
text: z.ZodString;
|
|
644
|
+
}, z.core.$strip>;
|
|
645
|
+
declare const answerApprovalInput: z.ZodObject<{
|
|
646
|
+
task: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
647
|
+
approval: z.ZodOptional<z.ZodString>;
|
|
648
|
+
answer: z.ZodString;
|
|
649
|
+
note: z.ZodOptional<z.ZodString>;
|
|
650
|
+
}, z.core.$strip>;
|
|
271
651
|
type TaskCompleteArgs = z.infer<typeof taskCompleteInput>;
|
|
272
652
|
type CheckpointArgs = z.infer<typeof checkpointInput>;
|
|
273
653
|
type ListRunnersArgs = z.infer<typeof listRunnersInput>;
|
|
274
654
|
type ListModelsArgs = z.infer<typeof listModelsInput>;
|
|
275
655
|
type LoadSkillArgs = z.infer<typeof loadSkillInput>;
|
|
276
656
|
type RunCommandArgs = z.infer<typeof runCommandInput>;
|
|
657
|
+
type FetchArgs = z.infer<typeof fetchInput>;
|
|
658
|
+
type WebSearchArgs = z.infer<typeof webSearchInput>;
|
|
277
659
|
type SubmitPlanArgs = z.infer<typeof submitPlanInput>;
|
|
278
660
|
type EditPlanArgs = z.infer<typeof editPlanInput>;
|
|
279
661
|
type TaskQueryArgs = z.infer<typeof taskQueryInput>;
|
|
280
662
|
type TaskOutputArgs = z.infer<typeof taskOutputInput>;
|
|
663
|
+
type TaskDiffArgs = z.infer<typeof taskDiffInput>;
|
|
664
|
+
type RunPlanArgs = z.infer<typeof runPlanInput>;
|
|
665
|
+
type OneTaskArgs = z.infer<typeof oneTaskInput>;
|
|
666
|
+
type RetryTaskArgs = z.infer<typeof retryTaskInput>;
|
|
667
|
+
type MessageTaskArgs = z.infer<typeof messageTaskInput>;
|
|
668
|
+
type SkipTaskArgs = z.infer<typeof skipTaskInput>;
|
|
669
|
+
type AnswerApprovalArgs = z.infer<typeof answerApprovalInput>;
|
|
281
670
|
/** What a handler answers with. `isError` marks a refusal the caller should act on. */
|
|
282
671
|
interface McpToolReply {
|
|
283
672
|
text: string;
|
|
@@ -309,12 +698,24 @@ interface TaskToolHandler {
|
|
|
309
698
|
interface PlannerToolHandler {
|
|
310
699
|
loadSkill?: Run<LoadSkillArgs>;
|
|
311
700
|
runCommand?: Run<RunCommandArgs>;
|
|
701
|
+
fetch?: Run<FetchArgs>;
|
|
702
|
+
webSearch?: Run<WebSearchArgs>;
|
|
312
703
|
listRunners?: Run<ListRunnersArgs>;
|
|
313
704
|
listModels?: Run<ListModelsArgs>;
|
|
314
705
|
submitPlan?: Run<SubmitPlanArgs>;
|
|
315
706
|
editPlan?: Run<EditPlanArgs>;
|
|
316
707
|
taskQuery?: Run<TaskQueryArgs>;
|
|
317
708
|
taskOutput?: Run<TaskOutputArgs>;
|
|
709
|
+
taskDiff?: Run<TaskDiffArgs>;
|
|
710
|
+
runPlan?: Run<RunPlanArgs>;
|
|
711
|
+
runTask?: Run<OneTaskArgs>;
|
|
712
|
+
stopTask?: Run<OneTaskArgs>;
|
|
713
|
+
stopRun?: Run<RunPlanArgs>;
|
|
714
|
+
retryTask?: Run<RetryTaskArgs>;
|
|
715
|
+
skipTask?: Run<SkipTaskArgs>;
|
|
716
|
+
messageTask?: Run<MessageTaskArgs>;
|
|
717
|
+
answerApproval?: Run<AnswerApprovalArgs>;
|
|
718
|
+
mergeAll?: Run<RunPlanArgs>;
|
|
318
719
|
}
|
|
319
720
|
interface McpTool<H> {
|
|
320
721
|
name: string;
|
|
@@ -331,7 +732,19 @@ interface McpTool<H> {
|
|
|
331
732
|
}
|
|
332
733
|
/** The task tool whose call settles the attempt (ADR-0022, V1). */
|
|
333
734
|
declare const TASK_COMPLETE_TOOL = "task_complete";
|
|
735
|
+
/**
|
|
736
|
+
* How a task is told to end. It rides in the runner's instructions rather than
|
|
737
|
+
* in the first message: a runner compacts its own context and rewrites that
|
|
738
|
+
* message into a summary, while its instructions are re-applied whole.
|
|
739
|
+
*/
|
|
740
|
+
declare const TASK_COMPLETION_CONTRACT: string;
|
|
334
741
|
declare const TASK_TOOLS: readonly McpTool<TaskToolHandler>[];
|
|
742
|
+
/**
|
|
743
|
+
* The planner's execution tools (ADR-0029, #91): each call goes to the
|
|
744
|
+
* session's command seam, which checks it, asks the user where consent needs
|
|
745
|
+
* one, and records it. The API planner offers the same tools as its own.
|
|
746
|
+
*/
|
|
747
|
+
declare const PLANNER_EXECUTION_TOOLS: readonly McpTool<PlannerToolHandler>[];
|
|
335
748
|
declare const PLANNER_TOOLS: readonly McpTool<PlannerToolHandler>[];
|
|
336
749
|
|
|
337
750
|
/**
|
|
@@ -352,11 +765,19 @@ interface TaskTokenScope {
|
|
|
352
765
|
}
|
|
353
766
|
interface PlannerTokenScope {
|
|
354
767
|
sessionId: string;
|
|
768
|
+
/**
|
|
769
|
+
* Planner tools this token neither lists nor serves, by their names on the
|
|
770
|
+
* server: the ones a runner carries natively instead (ADR-0027), which a
|
|
771
|
+
* model would otherwise pick over its own.
|
|
772
|
+
*/
|
|
773
|
+
hidden?: readonly string[];
|
|
355
774
|
}
|
|
356
775
|
/** Where a runner reaches the server and what it presents there. Never put either half in a prompt. */
|
|
357
776
|
interface McpCredential {
|
|
358
777
|
url: string;
|
|
359
778
|
token: string;
|
|
779
|
+
/** The planner tools the token hides ({@link PlannerTokenScope.hidden}), so the runner is told of and pre-allows only what it is served. */
|
|
780
|
+
hidden?: readonly string[];
|
|
360
781
|
}
|
|
361
782
|
declare class OrdewellMcpServer {
|
|
362
783
|
private readonly grants;
|
|
@@ -370,6 +791,8 @@ declare class OrdewellMcpServer {
|
|
|
370
791
|
get url(): string | undefined;
|
|
371
792
|
issueTaskToken(scope: TaskTokenScope, handler?: TaskToolHandler): Promise<McpCredential>;
|
|
372
793
|
issuePlannerToken(scope: PlannerTokenScope, handler?: PlannerToolHandler): Promise<McpCredential>;
|
|
794
|
+
/** Every token still good here: secrets the outbound check keeps out of a URL (ADR-0027). */
|
|
795
|
+
heldTokens(): string[];
|
|
373
796
|
/** Refuse every later request carrying `token`, and abort the calls it has in flight. */
|
|
374
797
|
revoke(token: string): void;
|
|
375
798
|
dispose(): Promise<void>;
|
|
@@ -386,6 +809,8 @@ interface McpClientConfig {
|
|
|
386
809
|
name: typeof ORDEWELL_MCP_SERVER_NAME;
|
|
387
810
|
url: string;
|
|
388
811
|
headers: Record<string, string>;
|
|
812
|
+
/** Planner tools the token hides: a runner must neither pre-allow nor mention them. */
|
|
813
|
+
hidden?: readonly string[];
|
|
389
814
|
}
|
|
390
815
|
/**
|
|
391
816
|
* The one shape every runner adapter renders its own MCP configuration from.
|
|
@@ -407,6 +832,50 @@ interface OwnerOnlyFile {
|
|
|
407
832
|
*/
|
|
408
833
|
declare function ownerOnlyConfigFile(fileName: string, contents: string): OwnerOnlyFile;
|
|
409
834
|
|
|
835
|
+
/**
|
|
836
|
+
* One MCP server a repository declares. `definition` is the entry as
|
|
837
|
+
* Ordewell parsed it, in the runner's own shape: an approved server is
|
|
838
|
+
* handed to the runner as exactly this, never re-read from the file.
|
|
839
|
+
*/
|
|
840
|
+
interface ProjectMcpServer {
|
|
841
|
+
name: string;
|
|
842
|
+
/** The file that declares it, relative to the workspace. */
|
|
843
|
+
source: string;
|
|
844
|
+
transport: 'stdio' | 'remote';
|
|
845
|
+
/** What would run, as written: the command line, or the URL. */
|
|
846
|
+
target: string;
|
|
847
|
+
/** The variables it sets, or the headers it sends, by name. */
|
|
848
|
+
names: string[];
|
|
849
|
+
definition: Record<string, unknown>;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* What a runner command is to the runner that offers it: a `builtin` of the
|
|
854
|
+
* harness itself (`/compact`, `/model`), a `skill`, or any other `command` —
|
|
855
|
+
* a user's or a project's custom command, an MCP prompt.
|
|
856
|
+
*/
|
|
857
|
+
type RunnerCommandKind = 'command' | 'skill' | 'builtin';
|
|
858
|
+
/**
|
|
859
|
+
* One slash command a task's runner offers, as the runner itself reported it
|
|
860
|
+
* (#66). Ordewell transports it and records it; it never interprets one.
|
|
861
|
+
*/
|
|
862
|
+
interface RunnerCommand {
|
|
863
|
+
name: string;
|
|
864
|
+
description: string;
|
|
865
|
+
/** How the runner describes its arguments (`<model>`, `[name]`); absent when it takes none it names. */
|
|
866
|
+
argumentHint?: string;
|
|
867
|
+
kind: RunnerCommandKind;
|
|
868
|
+
/** Other names the runner answers to for the same command. */
|
|
869
|
+
aliases?: string[];
|
|
870
|
+
}
|
|
871
|
+
/** An MCP server a runner loaded for a task, and the state the runner reported it in. */
|
|
872
|
+
interface RunnerMcpServer {
|
|
873
|
+
name: string;
|
|
874
|
+
status?: string;
|
|
875
|
+
}
|
|
876
|
+
/** The command `name` names, by its own name or an alias. */
|
|
877
|
+
declare function findRunnerCommand(commands: readonly RunnerCommand[], name: string): RunnerCommand | undefined;
|
|
878
|
+
|
|
410
879
|
/**
|
|
411
880
|
* The harness-planner transport contract (ADR-0009).
|
|
412
881
|
*
|
|
@@ -559,6 +1028,28 @@ type AgentEvent =
|
|
|
559
1028
|
| {
|
|
560
1029
|
type: 'error';
|
|
561
1030
|
message: string;
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* What the runner loaded for this process from the user's and the
|
|
1034
|
+
* project's own setup (#66): its commands and skills, and its MCP servers.
|
|
1035
|
+
* Once per process, as it starts its first turn.
|
|
1036
|
+
*/
|
|
1037
|
+
| {
|
|
1038
|
+
type: 'runner_loaded';
|
|
1039
|
+
commands: RunnerCommand[];
|
|
1040
|
+
mcpServers: RunnerMcpServer[];
|
|
1041
|
+
}
|
|
1042
|
+
/** The runner compacted its own context: asked to (`manual`) or on its own (`auto`). */
|
|
1043
|
+
| {
|
|
1044
|
+
type: 'compaction';
|
|
1045
|
+
trigger: 'manual' | 'auto';
|
|
1046
|
+
preTokens?: number;
|
|
1047
|
+
postTokens?: number;
|
|
1048
|
+
}
|
|
1049
|
+
/** What a runner command that runs without the model printed, as the runner reported it. */
|
|
1050
|
+
| {
|
|
1051
|
+
type: 'command_output';
|
|
1052
|
+
text: string;
|
|
562
1053
|
};
|
|
563
1054
|
/**
|
|
564
1055
|
* What a planner's permission request is for, read from the runner's own
|
|
@@ -575,12 +1066,25 @@ type PlannerAsk =
|
|
|
575
1066
|
} | {
|
|
576
1067
|
kind: 'fetch';
|
|
577
1068
|
url: string;
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* A search the runner carries itself, at `provider`; Ordewell decides it, it does not run it.
|
|
1072
|
+
* `providerLabel` names that party in the approval.
|
|
1073
|
+
*/
|
|
1074
|
+
| {
|
|
1075
|
+
kind: 'search';
|
|
1076
|
+
query: string;
|
|
1077
|
+
provider: string;
|
|
1078
|
+
providerLabel?: string;
|
|
578
1079
|
} | {
|
|
579
1080
|
kind: 'path';
|
|
580
1081
|
path: string;
|
|
581
1082
|
directory: boolean;
|
|
582
|
-
}
|
|
1083
|
+
}
|
|
1084
|
+
/** `note` is why, for a tool the runner knows the planner must not use, in place of the envelope's generic refusal. */
|
|
1085
|
+
| {
|
|
583
1086
|
kind: 'other';
|
|
1087
|
+
note?: string;
|
|
584
1088
|
};
|
|
585
1089
|
interface AgentStartCommon {
|
|
586
1090
|
/** Workspace root. The agent works from here and, in read-only mode, cannot leave it. */
|
|
@@ -603,6 +1107,20 @@ interface PlannerStartOptions extends AgentStartCommon {
|
|
|
603
1107
|
effort?: string;
|
|
604
1108
|
/** The Ordewell MCP server to inject, pre-authorized (ADR-0022). Only for an adapter with {@link AgentAdapter.mcpAttached}. */
|
|
605
1109
|
mcp?: McpClientConfig;
|
|
1110
|
+
/**
|
|
1111
|
+
* Whether the runner's own search is on, for a runner whose search raises
|
|
1112
|
+
* no permission request and is set on or off when its session starts
|
|
1113
|
+
* (ADR-0027). Decided once, before the planner token is issued, so the
|
|
1114
|
+
* tools the token serves and the session's config cannot disagree. Absent
|
|
1115
|
+
* means off.
|
|
1116
|
+
*/
|
|
1117
|
+
nativeSearch?: boolean;
|
|
1118
|
+
/**
|
|
1119
|
+
* The repository's own MCP servers this planner may start, as approved
|
|
1120
|
+
* (ADR-0028). The runner is launched without the repository's config, so
|
|
1121
|
+
* these are the only ones it gets, and each is handed over as parsed.
|
|
1122
|
+
*/
|
|
1123
|
+
projectServers?: ProjectMcpServer[];
|
|
606
1124
|
}
|
|
607
1125
|
/**
|
|
608
1126
|
* What a runner manifest says the task's mode and effort mean (ADR-0001),
|
|
@@ -670,6 +1188,19 @@ interface AgentAdapter {
|
|
|
670
1188
|
* adapter that cannot inject the server at all, which cannot run tasks.
|
|
671
1189
|
*/
|
|
672
1190
|
mcpAttached?(): Promise<boolean>;
|
|
1191
|
+
/**
|
|
1192
|
+
* The approved project servers (ADR-0028) this runner started but cannot
|
|
1193
|
+
* give the planner as tools of their own, known once `start` resolved.
|
|
1194
|
+
* Absent on an adapter that gives the planner every one it starts.
|
|
1195
|
+
*/
|
|
1196
|
+
unusableProjectServers?(): ProjectMcpServer[];
|
|
1197
|
+
/**
|
|
1198
|
+
* Whether `abs`, already resolved on disk, is output this runner saved
|
|
1199
|
+
* outside the workspace for this session and told its planner to read back
|
|
1200
|
+
* (ADR-0032). The planner reads it as it reads the workspace. Absent on a
|
|
1201
|
+
* runner that saves none.
|
|
1202
|
+
*/
|
|
1203
|
+
savedOutput?(abs: string): boolean;
|
|
673
1204
|
/**
|
|
674
1205
|
* Answer an open `permission_request`. False when the id is not open — it
|
|
675
1206
|
* was answered, cancelled, or never asked. Absent on an adapter that leaves
|
|
@@ -694,8 +1225,11 @@ interface TaskModeAgentAdapter extends AgentAdapter {
|
|
|
694
1225
|
* for a planner and wrong for a task, which may report completion there.
|
|
695
1226
|
*/
|
|
696
1227
|
onOutOfTurn?(listener: (event: AgentEvent) => void): void;
|
|
697
|
-
/**
|
|
698
|
-
|
|
1228
|
+
/**
|
|
1229
|
+
* Registers a listener for the process ending, for any reason. Fires at most
|
|
1230
|
+
* once. `lastWords` is how it ended, with its stderr tail.
|
|
1231
|
+
*/
|
|
1232
|
+
onProcessExit(listener: (code: number, lastWords?: string) => void): void;
|
|
699
1233
|
/**
|
|
700
1234
|
* Hand a message to the running turn, for the runner to show the model at
|
|
701
1235
|
* its next step boundary (ADR-0023, D1–D2). Resolves true once the runner
|
|
@@ -707,6 +1241,18 @@ interface TaskModeAgentAdapter extends AgentAdapter {
|
|
|
707
1241
|
*/
|
|
708
1242
|
steer?(id: string, text: string): Promise<boolean>;
|
|
709
1243
|
answerPermission(id: string, decision: ApprovalDecision): boolean;
|
|
1244
|
+
/**
|
|
1245
|
+
* The slash commands the runner offers this session — its built-ins, the
|
|
1246
|
+
* user's and the project's skills and custom commands (#66) — as it
|
|
1247
|
+
* reported them. Empty until it has; absent on a runner that reports none.
|
|
1248
|
+
*/
|
|
1249
|
+
commands?(): RunnerCommand[];
|
|
1250
|
+
/**
|
|
1251
|
+
* Run the runner's own command `name` as a turn, the way {@link send} runs
|
|
1252
|
+
* a message: resolves when the turn ends. Absent on a runner whose
|
|
1253
|
+
* commands cannot be invoked over its protocol.
|
|
1254
|
+
*/
|
|
1255
|
+
invokeCommand?(name: string, args: string, onEvent: (event: AgentEvent) => void, signal?: AbortSignal): Promise<void>;
|
|
710
1256
|
}
|
|
711
1257
|
type SpawnFn = (command: string, args: string[], options: {
|
|
712
1258
|
env: NodeJS.ProcessEnv;
|
|
@@ -860,73 +1406,21 @@ interface RunnerMode {
|
|
|
860
1406
|
autonomous?: boolean;
|
|
861
1407
|
/** Marks this mode as the runner's conservative build mode — the resolved default when autonomous mode is OFF. */
|
|
862
1408
|
safe?: boolean;
|
|
1409
|
+
/** Marks this mode as the runner's read-only mode — what a task the planner marks read-only runs in (ADR-0031). */
|
|
1410
|
+
readOnly?: boolean;
|
|
1411
|
+
/** The mode runs under an OS sandbox, so a host where the runner's sandbox cannot start cannot offer it. */
|
|
1412
|
+
sandboxed?: boolean;
|
|
1413
|
+
/**
|
|
1414
|
+
* Why this mode cannot be used on this host. Set by the registry from a host
|
|
1415
|
+
* probe, never by a manifest: a mode with a reason is listed, so the user is
|
|
1416
|
+
* told, but never assigned, kept or run.
|
|
1417
|
+
*/
|
|
1418
|
+
unavailable?: string;
|
|
863
1419
|
}
|
|
864
1420
|
interface RunnerEntry {
|
|
865
1421
|
manifest: RunnerManifest;
|
|
866
1422
|
}
|
|
867
1423
|
|
|
868
|
-
/**
|
|
869
|
-
* Decides whether one out-of-envelope capability may run, and remembers the
|
|
870
|
-
* answer for the rest of the session.
|
|
871
|
-
*
|
|
872
|
-
* Grants are keyed on {@link ApprovalRequest.scope}, never on the concrete
|
|
873
|
-
* subject: approving a read of `/tmp/foo/a.log` grants `/tmp/foo/*`, and
|
|
874
|
-
* approving `az group list` grants `az group list`. Without that, a planner
|
|
875
|
-
* doing real research would prompt on every single call and the feature would
|
|
876
|
-
* be unusable.
|
|
877
|
-
*
|
|
878
|
-
* `mode` is the policy floor:
|
|
879
|
-
* ask consult the human channel; no channel means deny (headless, web)
|
|
880
|
-
* allow grant everything the tier system did not already refuse
|
|
881
|
-
* deny grant nothing beyond `preApproved` — the allowlist-only mode
|
|
882
|
-
*
|
|
883
|
-
* Note the ordering: `preApproved` is honored under every mode including
|
|
884
|
-
* `deny`, because it is an explicit operator decision rather than a default.
|
|
885
|
-
* Its entries are allowlist rules (see `plannerAllowlist.ts`), and both it and
|
|
886
|
-
* `mode` may be read afresh on every request, so a settings change applies to
|
|
887
|
-
* the next call.
|
|
888
|
-
*/
|
|
889
|
-
type ApprovalMode = 'ask' | 'allow' | 'deny';
|
|
890
|
-
/**
|
|
891
|
-
* The configured mode. `auto` follows the autonomy level (ADR-0026): Guarded
|
|
892
|
-
* asks, Full runs the allowlist alone and asks nobody, so a planner in Full
|
|
893
|
-
* never waits on a person and never acts outside its standing approvals.
|
|
894
|
-
*/
|
|
895
|
-
type ApprovalModeSetting = ApprovalMode | 'auto';
|
|
896
|
-
declare function effectiveApprovalMode(setting: ApprovalModeSetting, autonomous: boolean): ApprovalMode;
|
|
897
|
-
/** A configured mode as written: `allowlist` is the user-facing name of `deny`, and anything unknown is `auto`. */
|
|
898
|
-
declare function parseApprovalModeSetting(raw: string | undefined): ApprovalModeSetting;
|
|
899
|
-
interface ApprovalPolicyOptions {
|
|
900
|
-
mode?: ApprovalMode | (() => ApprovalMode);
|
|
901
|
-
/** Allowlist entries granted up front from config (ADR-0026). */
|
|
902
|
-
preApproved?: readonly string[] | (() => readonly string[]);
|
|
903
|
-
/** The human channel. Absent means there is nobody to ask. */
|
|
904
|
-
ask?: (req: ApprovalRequest) => Promise<boolean>;
|
|
905
|
-
/** Called whenever a decision is reached, for surfacing in a UI or a log. */
|
|
906
|
-
onDecision?: (req: ApprovalRequest, granted: boolean, source: ApprovalSource) => void;
|
|
907
|
-
}
|
|
908
|
-
type ApprovalSource = 'pre-approved' | 'remembered' | 'mode' | 'asked' | 'no-channel';
|
|
909
|
-
declare class ApprovalPolicy implements IApproval {
|
|
910
|
-
private readonly modeOf;
|
|
911
|
-
private readonly entriesOf;
|
|
912
|
-
private parsed;
|
|
913
|
-
private readonly asker?;
|
|
914
|
-
private readonly onDecision?;
|
|
915
|
-
private readonly granted;
|
|
916
|
-
private readonly refused;
|
|
917
|
-
/** One in-flight ask per scope: a parallel tool round must not prompt twice for the same thing. */
|
|
918
|
-
private readonly inFlight;
|
|
919
|
-
private generation;
|
|
920
|
-
constructor(opts?: ApprovalPolicyOptions);
|
|
921
|
-
request(req: ApprovalRequest): Promise<boolean>;
|
|
922
|
-
/** The standing approvals, parsed from the entries in force now. */
|
|
923
|
-
allowlist(): PlannerAllowlist;
|
|
924
|
-
/** Scopes the user has granted this session — for display and for persistence. */
|
|
925
|
-
grantedScopes(): string[];
|
|
926
|
-
/** Drop every session-scoped decision. Called on session reset. */
|
|
927
|
-
reset(): void;
|
|
928
|
-
}
|
|
929
|
-
|
|
930
1424
|
interface CatalogModel {
|
|
931
1425
|
id: string;
|
|
932
1426
|
name: string;
|
|
@@ -1102,6 +1596,8 @@ interface IConfig {
|
|
|
1102
1596
|
approvalPreApproved: string[];
|
|
1103
1597
|
/** Whether the built-in allowlist of read-only command families and MCP tools applies. */
|
|
1104
1598
|
approvalDefaults: boolean;
|
|
1599
|
+
/** How many fetches and searches one planner turn may make (ADR-0027). User-only, like the approval settings. */
|
|
1600
|
+
webReadBudget: number;
|
|
1105
1601
|
/** Get the base URL for an OpenAI-compatible provider. */
|
|
1106
1602
|
getProviderBaseUrl(provider: AiProvider): string;
|
|
1107
1603
|
/** Get the API key for a provider. */
|
|
@@ -1116,6 +1612,9 @@ declare function enabledRunners(cfg: IConfig): RunnerId[];
|
|
|
1116
1612
|
|
|
1117
1613
|
declare class RunnerRegistry {
|
|
1118
1614
|
private readonly runners;
|
|
1615
|
+
private readonly unavailableModes;
|
|
1616
|
+
/** Mark modes of a runner unusable on this host, by mode id and reason; an empty record clears them. */
|
|
1617
|
+
setUnavailableModes(id: string, reasons: Record<string, string>): void;
|
|
1119
1618
|
get(id: string): RunnerEntry | undefined;
|
|
1120
1619
|
getManifest(id: string): RunnerManifest | undefined;
|
|
1121
1620
|
list(): RunnerEntry[];
|
|
@@ -1125,6 +1624,11 @@ declare class RunnerRegistry {
|
|
|
1125
1624
|
|
|
1126
1625
|
/** How a structured turn ended. `failed` carries the agent's own words in the preceding `error` event. */
|
|
1127
1626
|
type StructuredTurnEnd = 'completed' | 'interrupted' | 'failed';
|
|
1627
|
+
/**
|
|
1628
|
+
* Who sent a task message that is not the user's: Ordewell itself, for the
|
|
1629
|
+
* completion reminder (ADR-0018, W1). Absent means the user.
|
|
1630
|
+
*/
|
|
1631
|
+
type TaskMessageFrom = 'ordewell';
|
|
1128
1632
|
/**
|
|
1129
1633
|
* One normalized event from a structured task (ADR-0018, O1b): the adapter's
|
|
1130
1634
|
* events, with the turn made explicit at both ends and the message queue (M1)
|
|
@@ -1136,13 +1640,17 @@ type StructuredEvent = Exclude<AgentEvent, {
|
|
|
1136
1640
|
}>
|
|
1137
1641
|
/**
|
|
1138
1642
|
* `text` is the user message the turn answers; `messageId` is set when it had
|
|
1139
|
-
* waited in the queue, `forced` when it was force sent (ADR-0023, Q2)
|
|
1643
|
+
* waited in the queue, `forced` when it was force sent (ADR-0023, Q2), `from`
|
|
1644
|
+
* when Ordewell sent it. `command` is set on a turn that runs the runner's
|
|
1645
|
+
* own command (#66); `text` is then that command as the runner read it.
|
|
1140
1646
|
*/
|
|
1141
1647
|
| {
|
|
1142
1648
|
type: 'turn_start';
|
|
1143
1649
|
text: string;
|
|
1144
1650
|
messageId?: string;
|
|
1145
1651
|
forced?: boolean;
|
|
1652
|
+
from?: TaskMessageFrom;
|
|
1653
|
+
command?: InvokedCommand;
|
|
1146
1654
|
} | {
|
|
1147
1655
|
type: 'turn_end';
|
|
1148
1656
|
reason: StructuredTurnEnd;
|
|
@@ -1150,13 +1658,15 @@ type StructuredEvent = Exclude<AgentEvent, {
|
|
|
1150
1658
|
/**
|
|
1151
1659
|
* Waiting in Ordewell's queue — again, for a message the runner was handed
|
|
1152
1660
|
* and let go of (ADR-0023, D4). `forced` puts it ahead of the rest, behind
|
|
1153
|
-
* earlier forced ones (F2); a message already listed moves there
|
|
1661
|
+
* earlier forced ones (F2); a message already listed moves there, still
|
|
1662
|
+
* handed over if it was.
|
|
1154
1663
|
*/
|
|
1155
1664
|
| {
|
|
1156
1665
|
type: 'message_queued';
|
|
1157
1666
|
messageId: string;
|
|
1158
1667
|
text: string;
|
|
1159
1668
|
forced?: boolean;
|
|
1669
|
+
from?: TaskMessageFrom;
|
|
1160
1670
|
} | {
|
|
1161
1671
|
type: 'message_removed';
|
|
1162
1672
|
messageId: string;
|
|
@@ -1164,6 +1674,7 @@ type StructuredEvent = Exclude<AgentEvent, {
|
|
|
1164
1674
|
type: 'message_undelivered';
|
|
1165
1675
|
messageId: string;
|
|
1166
1676
|
text: string;
|
|
1677
|
+
from?: TaskMessageFrom;
|
|
1167
1678
|
}
|
|
1168
1679
|
/** The runner accepted the message into its running turn; it can no longer be taken back (ADR-0023, D3). */
|
|
1169
1680
|
| {
|
|
@@ -1175,6 +1686,7 @@ type StructuredEvent = Exclude<AgentEvent, {
|
|
|
1175
1686
|
type: 'message_delivered';
|
|
1176
1687
|
messageId: string;
|
|
1177
1688
|
text: string;
|
|
1689
|
+
from?: TaskMessageFrom;
|
|
1178
1690
|
}
|
|
1179
1691
|
/** An open `permission_request` was answered, by whoever answered it (ADR-0018, A1). */
|
|
1180
1692
|
| {
|
|
@@ -1187,13 +1699,19 @@ type StructuredEvent = Exclude<AgentEvent, {
|
|
|
1187
1699
|
type: 'permission_withdrawn';
|
|
1188
1700
|
id: string;
|
|
1189
1701
|
};
|
|
1702
|
+
/** A runner command a user invoked, with its arguments as typed. */
|
|
1703
|
+
interface InvokedCommand {
|
|
1704
|
+
name: string;
|
|
1705
|
+
args?: string;
|
|
1706
|
+
}
|
|
1190
1707
|
interface QueuedTaskMessage {
|
|
1191
1708
|
id: string;
|
|
1192
1709
|
text: string;
|
|
1193
1710
|
/** The runner has it and owes a delivery, so it cannot be taken back (ADR-0023, Q1). */
|
|
1194
1711
|
handedOver?: boolean;
|
|
1195
|
-
/** Force sent: it goes
|
|
1712
|
+
/** Force sent: it goes first once the running turn is interrupted (ADR-0023, F1). Every listed message can be, handed over or not. */
|
|
1196
1713
|
forced?: boolean;
|
|
1714
|
+
from?: TaskMessageFrom;
|
|
1197
1715
|
}
|
|
1198
1716
|
interface IRunnerSession {
|
|
1199
1717
|
id: string;
|
|
@@ -1211,8 +1729,9 @@ interface IRunnerSession {
|
|
|
1211
1729
|
* Send a user message: at once when idle; while a turn runs, handed to the
|
|
1212
1730
|
* runner for its next step where it can take one, otherwise queued until
|
|
1213
1731
|
* the turn ends (ADR-0023). Returns its id, for {@link removeQueued}.
|
|
1732
|
+
* `from` marks a message Ordewell sent rather than the user.
|
|
1214
1733
|
*/
|
|
1215
|
-
sendMessage(text: string): string;
|
|
1734
|
+
sendMessage(text: string, from?: TaskMessageFrom): string;
|
|
1216
1735
|
/**
|
|
1217
1736
|
* Force send (ADR-0023, F1–F3): interrupt the running turn, as
|
|
1218
1737
|
* {@link interrupt} does, and deliver this message as the turn that
|
|
@@ -1220,7 +1739,11 @@ interface IRunnerSession {
|
|
|
1220
1739
|
* input in between. With no turn running it is {@link sendMessage}.
|
|
1221
1740
|
*/
|
|
1222
1741
|
forceSend(text: string): string;
|
|
1223
|
-
/**
|
|
1742
|
+
/**
|
|
1743
|
+
* Force send a message not yet delivered, by id — queued, or handed over,
|
|
1744
|
+
* which is not sent twice: the interrupt makes it go first (ADR-0023, F1).
|
|
1745
|
+
* False once it was delivered.
|
|
1746
|
+
*/
|
|
1224
1747
|
forceSendQueued(id: string): boolean;
|
|
1225
1748
|
/** Take a message back before the runner has it. False once it was handed over or delivered. */
|
|
1226
1749
|
removeQueued(id: string): boolean;
|
|
@@ -1234,6 +1757,20 @@ interface IRunnerSession {
|
|
|
1234
1757
|
interrupt(): Promise<void>;
|
|
1235
1758
|
/** The runner's own session id once announced — what a continue resumes (ADR-0018, K1). */
|
|
1236
1759
|
nativeSessionId(): string | null;
|
|
1760
|
+
/**
|
|
1761
|
+
* The slash commands the runner offers this task (#66): its built-ins and
|
|
1762
|
+
* the user's and the project's skills and custom commands. Empty for a
|
|
1763
|
+
* runner that reports none.
|
|
1764
|
+
*/
|
|
1765
|
+
commands(): RunnerCommand[];
|
|
1766
|
+
/**
|
|
1767
|
+
* Run the runner's own command `name` as a turn of its own: at once when
|
|
1768
|
+
* idle, otherwise queued behind the running turn — never handed into it,
|
|
1769
|
+
* where a runner reads it as text. A runner whose commands cannot be invoked
|
|
1770
|
+
* over its protocol is sent `/name args` as a message. Returns its id, as
|
|
1771
|
+
* {@link sendMessage} does.
|
|
1772
|
+
*/
|
|
1773
|
+
invokeCommand(name: string, args?: string): string;
|
|
1237
1774
|
/**
|
|
1238
1775
|
* Answer a `permission_request` this session emitted, by its event id. False
|
|
1239
1776
|
* when it is no longer open — answered, withdrawn, or never asked.
|
|
@@ -1296,4 +1833,4 @@ interface IRunner {
|
|
|
1296
1833
|
activeCount: number;
|
|
1297
1834
|
}
|
|
1298
1835
|
|
|
1299
|
-
export { type
|
|
1836
|
+
export { type OwnerOnlyFile as $, AUTO_COMMANDS as A, type IRunnerSession as B, type CatalogModel as C, DEFAULT_PLANNER_ALLOWLIST as D, type EditPlanArgs as E, type FetchAllProviderModelsOptions as F, GIT_READONLY_SUBCOMMANDS as G, type InvokedCommand as H, type IApproval as I, type ListModelsArgs as J, type ListRunnersArgs as K, LineBuffer as L, type LoadSkillArgs as M, type McpClientConfig as N, type McpCredential as O, type McpTool as P, type McpToolContext as Q, type McpToolReply as R, type MessageTaskArgs as S, ModelCatalog as T, type ModelShortcut as U, ORCHESTRATOR_SHORTCUTS as V, ORDEWELL_MCP_PATH as W, ORDEWELL_MCP_SERVER_NAME as X, type OneTaskArgs as Y, type OrchestratorOption as Z, OrdewellMcpServer as _, type AgentAdapter as a, researchToolsPath as a$, PLANNER_EXECUTION_TOOLS as a0, PLANNER_TOOLS as a1, type PlannerAllowlist as a2, type PlannerStartOptions as a3, type PlannerTokenScope as a4, type PlannerToolHandler as a5, type ProjectMcpServer as a6, type ProviderCredentialSource as a7, type ProviderModelLists as a8, type ProviderModelsResult as a9, type TaskDiffArgs as aA, type TaskMessageFrom as aB, type TaskModeAgentAdapter as aC, TaskModeUnsupportedError as aD, type TaskOutputArgs as aE, type TaskQueryArgs as aF, type TaskRunnerFlags as aG, type TaskStartOptions as aH, type TaskTokenScope as aI, type TaskToolHandler as aJ, approvalScopes as aK, classifyCommand as aL, clearResearchShellCache as aM, collectProviderCredentials as aN, commandAllowedBy as aO, effectiveApprovalMode as aP, enabledRunners as aQ, fetchAllProviderModels as aR, findRunnerCommand as aS, isGranted as aT, isRunnerApproval as aU, knownModelId as aV, mcpClientConfig as aW, ownerOnlyConfigFile as aX, parseAllowlist as aY, parseApprovalModeSetting as aZ, researchShellWarning as a_, type QueuedTaskMessage as aa, REFUSED_COMMANDS as ab, type ResearchShell as ac, type ResearchShellDeps as ad, type RetryTaskArgs as ae, type RunCommandArgs as af, type RunPlanArgs as ag, type RunnerCommand as ah, type RunnerCommandKind as ai, type RunnerEntry as aj, type RunnerFeatures as ak, type RunnerManifest as al, type RunnerMcpServer as am, type RunnerMode as an, type RunnerModelDiscovery as ao, RunnerRegistry as ap, type ShellDialect as aq, type SpawnFn as ar, type StandingDecision as as, type StructuredEvent as at, type StructuredTurnEnd as au, type SubmitPlanArgs as av, TASK_COMPLETE_TOOL as aw, TASK_COMPLETION_CONTRACT as ax, TASK_TOOLS as ay, type TaskCompleteArgs as az, type AgentAdapterFactory as b, resolveModelShortcut as b0, resolveProvider as b1, resolveResearchShell as b2, scopeAllowed as b3, sharedMcpServer as b4, toApprovalDecision as b5, toOrchestratorOptions as b6, toolAllowed as b7, type AgentEvent as c, type AgentProcessDeps as d, type AgentStartOptions as e, type AiProvider as f, type AllProviderModels as g, type AnswerApprovalArgs as h, type ApprovalAnswer as i, type ApprovalDecision as j, type ApprovalKind as k, type ApprovalMode as l, type ApprovalModeSetting as m, ApprovalPolicy as n, type ApprovalPolicyOptions as o, type ApprovalRequest as p, type ApprovalSource as q, type CheckpointAnswer as r, type CheckpointArgs as s, type CommandClassification as t, type CommandTier as u, type CommandWords as v, DENY_ALL as w, type DiscoveryCommand as x, type IConfig as y, type IRunner as z };
|