@themoltnet/pi-extension 0.36.2 → 0.37.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.
Files changed (3) hide show
  1. package/dist/index.d.ts +5 -1358
  2. package/dist/index.js +24027 -29161
  3. package/package.json +8 -15
package/dist/index.d.ts CHANGED
@@ -1,1369 +1,16 @@
1
- import { AgentSession } from '@earendil-works/pi-coding-agent';
2
- import { Api } from '@earendil-works/pi-ai';
3
- import { BashOperations } from '@earendil-works/pi-coding-agent';
4
- import { CommandAnalysis } from '@themoltnet/shell-command-analyzer';
5
- import { connect } from '@themoltnet/sdk';
6
- import { EditOperations } from '@earendil-works/pi-coding-agent';
1
+ import { createPiOtelExtension } from '@themoltnet/pi-runtime';
7
2
  import { ExtensionAPI } from '@earendil-works/pi-coding-agent';
8
3
  import { ExtensionContext } from '@earendil-works/pi-coding-agent';
9
- import { LoadSkillsResult } from '@earendil-works/pi-coding-agent';
10
- import { Model } from '@earendil-works/pi-ai';
11
- import { Readable } from 'node:stream';
12
- import { ReadOperations } from '@earendil-works/pi-coding-agent';
13
- import { ShellCommandAnalyzer } from '@themoltnet/shell-command-analyzer';
14
- import { Skill } from '@earendil-works/pi-coding-agent';
15
- import { Static } from 'typebox';
16
- import { TObject } from 'typebox';
17
- import { ToolCallEvent } from '@earendil-works/pi-coding-agent';
18
- import { ToolDefinition } from '@earendil-works/pi-coding-agent';
19
- import { TSchema } from 'typebox';
20
- import { Type } from 'typebox';
21
- import { VM } from '@earendil-works/gondolin';
22
- import { WriteOperations } from '@earendil-works/pi-coding-agent';
4
+ import { PiOtelOptions } from '@themoltnet/pi-runtime';
5
+ import { ProviderErrorRetryUi } from '@themoltnet/pi-runtime';
23
6
 
24
- /**
25
- * Apply agent env vars to the host process, mirroring `moltnet start`.
26
- * Resolves relative paths (e.g. GIT_CONFIG_GLOBAL) against the repo root.
27
- */
28
- export declare function activateAgentEnv(agentEnv: Record<string, string | undefined>, repoRoot: string): void;
29
-
30
- /** Minimal shape of the SDK method the resolver needs (keeps deps testable). */
31
- export declare interface AllowedToolsClient {
32
- runtimeProfiles: {
33
- allowedTools: (profileId: string, options: {
34
- teamId: string;
35
- }) => Promise<{
36
- enforcement: ToolEnforcement;
37
- allowedTools: string[];
38
- }>;
39
- };
40
- }
41
-
42
- /**
43
- * Construct an `AgentSession`. By default it is in-memory; callers may opt
44
- * parent sessions into daemon-owned file persistence via `sessionPersistence`.
45
- * The caller is responsible for eventually invoking `session.prompt(...)` and
46
- * for tearing down — the helper does no lifecycle management beyond
47
- * construction.
48
- */
49
- export declare function buildAgentSession(args: BuildAgentSessionArgs): Promise<AgentSession>;
50
-
51
- declare interface BuildAgentSessionArgs {
52
- /** Host directory mounted into the VM. */
53
- mountPath: string;
54
- /** Host working directory where the agent session should start. */
55
- cwdPath: string;
56
- /** pi auth directory (resolved from `PI_CODING_AGENT_DIR` or `~/.pi/agent`). */
57
- piAuthDir: string;
58
- /** Resolved pi model handle (provider + model id). */
59
- modelHandle: Model<Api>;
60
- /** Optional runtime-profile thinking/reasoning level applied at session start. */
61
- thinkingLevel?: PiThinkingLevel | null;
62
- /** Optional runtime-profile sampling temperature applied to provider requests. */
63
- temperature?: number | null;
64
- /** Optional runtime-profile nucleus sampling probability mass. */
65
- topP?: number | null;
66
- /** Optional runtime-profile top-k sampling cutoff. */
67
- topK?: number | null;
68
- /** Optional runtime-profile generated output token cap. */
69
- maxOutputTokens?: number | null;
70
- /** Pre-built customTools array. Caller composes Gondolin + MoltNet + submit tools. */
71
- customTools: ToolDefinition[];
72
- /** System-prompt fragments appended after pi's defaults. Parent passes the
73
- * runtime instructor; subagents pass their narrower variant. */
74
- appendSystemPrompt: string[];
75
- /** Skills to advertise in `<available_skills>`. Default: empty list. */
76
- skillsOverride?: () => LoadSkillsResult;
77
- /** Span attributes merged onto every OTel span the session emits. */
78
- otelSpanAttrs: Record<string, string | number | boolean>;
79
- /** Agent name for `gen_ai.agent.name` on the root span. */
80
- agentName: string;
81
- /**
82
- * Extra pi extension factories appended after the always-on telemetry (and
83
- * model-options) extensions — e.g. the tool-policy `tool_call` gate. Each is a
84
- * plain `(pi) => void` registrar, same shape as the OTel extension.
85
- */
86
- extraExtensionFactories?: ((pi: ExtensionAPI) => void)[];
87
- /**
88
- * Parent sessions may persist their conversation history in a daemon-owned
89
- * directory. Subagents should leave this unset and stay in-memory.
90
- */
91
- sessionPersistence?: {
92
- sessionDir: string;
93
- forkFromSessionPath?: string | null;
94
- };
95
- }
96
-
97
- declare type ClaimCondition = {
98
- op: 'all';
99
- conditions: ClaimCondition[];
100
- } | {
101
- op: 'any';
102
- conditions: ClaimCondition[];
103
- } | {
104
- op: 'task_status';
105
- taskId: string;
106
- statuses: TaskStatus[];
107
- } | {
108
- op: 'task_accepted';
109
- taskId: string;
110
- };
111
-
112
- declare const ClaimCondition: Type.TUnsafe<ClaimCondition>;
113
-
114
- declare interface ClaimedTask {
115
- /** The claimed task payload itself. */
116
- task: Task;
117
- /** Attempt number assigned by the source/queue. */
118
- attemptN: number;
119
- /** Runtime profile id selected by the source when claim routing is profile-scoped. */
120
- profileId?: string;
121
- /** W3C trace headers from the claim response for OTel context propagation. */
122
- traceHeaders: Record<string, string>;
123
- }
124
-
125
- declare const CONTEXT_BINDINGS: readonly ["skill", "context_inline", "prompt_prefix", "user_inline"];
126
-
127
- declare type ContextBinding = (typeof CONTEXT_BINDINGS)[number];
128
-
129
- declare const ContextBinding: Type.TUnsafe<"skill" | "context_inline" | "prompt_prefix" | "user_inline">;
130
-
131
- /**
132
- * One context entry. Bytes are inlined: the proposer chose them, and the
133
- * task's `inputCid` already pins the entire input — including
134
- * `context[]` — so we don't need a separate per-entry hash, fetcher, or
135
- * flagged-content gate. Tasks reference rendered packs (or any other
136
- * external content) by copying their bytes into `content` at task
137
- * creation time.
138
- *
139
- * - `slug` — short identifier the daemon uses to disambiguate
140
- * entries. For `skill` binding it becomes the directory
141
- * name under the runtime's skill discovery path. Must be
142
- * kebab-case-safe (alphanumeric + dashes/underscores).
143
- * - `binding` — how the bytes are delivered to the LLM (see above).
144
- * - `content` — UTF-8 text. Capped at 65,536 UTF-16 code units per
145
- * entry; total per-task context bytes are bounded by the
146
- * soft `maxItems` cap and per-binding daemon limits.
147
- * Raised from 32 KiB in 2026-05 — protocol-heavy operator
148
- * skills (e.g. `.claude/skills/legreffier/SKILL.md`) ship
149
- * at ~35 KiB inline, and the original cap was sized for
150
- * short example skills, not the kind of skill the eval
151
- * substrate is dogfooded on (#943, #823).
152
- */
153
- declare const ContextRef: Type.TObject<{
154
- slug: Type.TString;
155
- binding: Type.TUnsafe<"skill" | "context_inline" | "prompt_prefix" | "user_inline">;
156
- content: Type.TString;
157
- }>;
158
-
159
- declare type ContextRef = {
160
- slug: string;
161
- binding: ContextBinding;
162
- content: string;
163
- };
164
-
165
- export declare function createGondolinBashOps(vm: VM, localCwd: string, guestWorkspace: string): BashOperations;
166
-
167
- export declare function createGondolinEditOps(vm: VM, localCwd: string, guestWorkspace: string): EditOperations;
168
-
169
- export declare function createGondolinReadOps(vm: VM, localCwd: string, guestWorkspace: string): ReadOperations;
170
-
171
- export declare function createGondolinWriteOps(vm: VM, localCwd: string, guestWorkspace: string): WriteOperations;
172
-
173
- /**
174
- * Create all MoltNet tool definitions, ready to pass to `pi.registerTool()`.
175
- */
176
- export declare function createMoltNetTools(config: MoltNetToolsConfig): ToolDefinition<any, any>[];
177
-
178
- export declare function createPiOtelExtension(options?: PiOtelOptions): (pi: ExtensionAPI) => void;
7
+ export { createPiOtelExtension }
179
8
 
180
9
  export declare function createPiProviderErrorRetryUi(ctx: ExtensionContext): ProviderErrorRetryUi | undefined;
181
10
 
182
- export declare function createPiRetryTriage(options: {
183
- model: Model<Api>;
184
- thinkingLevel?: PiRetryTriageThinkingLevel | null;
185
- piAgentDir: string;
186
- timeoutMs?: number;
187
- cwd?: string;
188
- }): PiRetryTriage;
189
-
190
- /**
191
- * Factory that builds a pi-specific `executeTask` function suitable for
192
- * injection into `AgentRuntime`. The returned function caches the resolved
193
- * checkpoint across tasks so the second task hits the snapshot cache.
194
- */
195
- export declare function createPiTaskExecutor(opts: ExecutePiTaskOptions): (claimedTask: ClaimedTask, reporter: TaskReporter) => Promise<TaskOutput>;
196
-
197
- /**
198
- * Build the subagent custom tool for a parent session. The handle
199
- * exposes the call counter so executors can emit summary telemetry
200
- * when the parent terminates.
201
- */
202
- export declare function createSubagentTool(args: CreateSubagentToolArgs): SubagentToolHandle;
203
-
204
- export declare interface CreateSubagentToolArgs {
205
- /** Host directory mounted into the VM. */
206
- mountPath: string;
207
- /** Host working directory the subagent should start in. Defaults to mountPath. */
208
- cwdPath?: string;
209
- /** pi auth directory the parent resolved. */
210
- piAuthDir: string;
211
- /** Resolved pi model handle — subagents share it. */
212
- modelHandle: Model<Api>;
213
- /** Runtime-profile thinking/reasoning level — subagents inherit it. */
214
- thinkingLevel?: PiThinkingLevel | null;
215
- /** Runtime-profile sampling temperature — subagents inherit it. */
216
- temperature?: number | null;
217
- /** Runtime-profile nucleus sampling probability mass — subagents inherit it. */
218
- topP?: number | null;
219
- /** Runtime-profile top-k sampling cutoff — subagents inherit it. */
220
- topK?: number | null;
221
- /** Runtime-profile generated output token cap — subagents inherit it. */
222
- maxOutputTokens?: number | null;
223
- /** Agent name for telemetry. */
224
- agentName: string;
225
- /**
226
- * Custom tools every subagent inherits (Gondolin-routed
227
- * built-ins + moltnet_* tools, etc). MUST NOT include
228
- * the parent's submit-output tool, the parent's `subagent` tool,
229
- * or any other parent-only artefact — the caller is responsible
230
- * for filtering. The subagent appends its own submit tool.
231
- */
232
- inheritedCustomTools: ToolDefinition[];
233
- /**
234
- * The parent runtime instructor verbatim. Subagents prepend it to
235
- * their own short "you are a subagent" preamble so the same
236
- * invariants (gh auth, diary discipline, accountable commits)
237
- * apply if the subagent takes those actions. The parent's task
238
- * description dictates whether they should.
239
- */
240
- parentRuntimeInstructor: string;
241
- parentTaskId: string;
242
- parentTaskType: string;
243
- parentAttemptN: number;
244
- /**
245
- * Parent task's cancel signal. When the daemon cancels the parent
246
- * task (operator cancel or task-level `runningTimeoutSec` expiry),
247
- * each in-flight subagent's inner `session.abort()` is invoked so
248
- * it tears down promptly instead of running until its own LLM
249
- * call resolves. Mirrors the existing `wireSessionAbort` pattern
250
- * the parent session uses.
251
- *
252
- * Optional only because the test seam can omit it; production
253
- * callers (executePiTask) pass `reporter.cancelSignal`.
254
- */
255
- parentCancelSignal?: AbortSignal;
256
- /**
257
- * Per-call fallback timeout. Defends against an inner session that
258
- * ignores `abort()` for any reason (LLM provider stuck, tool call
259
- * hanging on I/O, etc.). When the timeout fires, `session.abort()`
260
- * is invoked and the tool returns `isError: true` with a
261
- * `subagent_timed_out` reason the parent LLM can recover from.
262
- *
263
- * Default: 5 minutes. Set to `0` to disable (relying purely on
264
- * parentCancelSignal). Negative values are treated as the default.
265
- */
266
- timeoutMs?: number;
267
- /**
268
- * Test seam. Production callers leave this undefined and get
269
- * `buildAgentSession` from the factory module. Tests inject a mock
270
- * that returns a stub session implementing only `prompt()` to
271
- * exercise the tool's logic without booting a VM.
272
- */
273
- buildAgentSession?: (args: BuildAgentSessionArgs) => Promise<AgentSession>;
274
- /**
275
- * Contract registry for resolving output_schema names to TypeBox
276
- * schemas at call time. The subagent tool reads ONLY via `.get()`
277
- * and `.list()` — the registry is immutable after construction.
278
- *
279
- * Production callers (executePiTask) create the registry with
280
- * built-in contracts at session-setup; tests inject a registry
281
- * with whatever stubs they need.
282
- */
283
- contractRegistry: SubagentContractRegistry;
284
- /**
285
- * Extra pi extension factories every subagent session registers — chiefly the
286
- * tool-policy `tool_call` gate. A subagent runs in its own `AgentSession`, so
287
- * without re-registering the gate here it would execute tools un-checked,
288
- * escaping the parent's enforcement (#1348 B2). Empty/undefined when tool
289
- * enforcement is `off`, so subagents then match the parent's un-gated
290
- * behaviour. The factories are read-only closures, safe to share across the
291
- * parent and every subagent session.
292
- */
293
- extraExtensionFactories?: ((pi: ExtensionAPI) => void)[];
294
- }
295
-
296
- /**
297
- * A pi extension factory that gates every `tool_call` against the resolved
298
- * policy. Blocks in `enforce`, audits (and allows) in `watch`, and is a no-op in
299
- * `off`. Register it in a session's `extensionFactories`.
300
- */
301
- export declare function createToolPolicyExtension(deps: ToolPolicyExtensionDeps): (pi: ExtensionAPI) => void;
302
-
303
- /**
304
- * Map a pi `tool_call` event to a gate decision, extracting the shell command
305
- * for `bash` and delegating to {@link decideToolCall}.
306
- */
307
- export declare function decideForEvent(event: ToolCallEvent, policy: SessionToolPolicy, analyze: ShellCommandAnalyzer['analyze']): GateDecision;
308
-
309
- /**
310
- * Decide whether a tool call is permitted by the resolved policy.
311
- *
312
- * Fail-closed in `enforce` (audited-but-allowed in `watch`, no-op in `off`) for:
313
- *
314
- * 1. **Unresolvable commands** — a `bash` command whose executables cannot be
315
- * statically resolved (command substitution, `eval`, non-literal command
316
- * names, unparseable input).
317
- * 2. **Arbitrary-code interpreters** — a `bash` command that invokes a shell or
318
- * language interpreter (`bash -c`, `python`, `node`, `perl`, …; the
319
- * analyzer's `arbitrary-code` risk tier). Being name-listed is NOT enough:
320
- * we cannot statically see the code such an interpreter runs, so the policy's
321
- * allow-set can't bound it. This is the interim conservative stance for
322
- * issue #1348 — an operator who lists `bash` still cannot smuggle
323
- * `bash -c "curl … | sh"` past `enforce`.
324
- * 3. **Unlisted executables** — any resolved executable not in `allowedTools`.
325
- *
326
- * KNOWN LIMITATION (follow-up): the `escapable` risk tier (GTFOBins binaries
327
- * like `find`, `tar`, `awk` that document shell-spawn / file-write techniques)
328
- * is NOT blocked on the tier alone. The analyzer already re-analyzes the
329
- * sub-commands it can see through documented escape flags (`find -exec`,
330
- * `tar --to-command`, …), but techniques it cannot parse statically could still
331
- * escape a name-based allow-set. Tightening `escapable` (e.g. an LLM judge or a
332
- * capability-aware allow-set) is tracked as future work.
333
- */
334
- export declare function decideToolCall(input: GateInput): GateDecision;
335
-
336
- /**
337
- * Ensure a cached snapshot exists, building one if needed.
338
- * Returns the absolute path to the qcow2 checkpoint file.
339
- */
340
- export declare function ensureSnapshot(options?: EnsureSnapshotOptions): Promise<string>;
341
-
342
- export declare interface EnsureSnapshotOptions {
343
- config?: SnapshotConfig;
344
- onProgress?: (message: string) => void;
345
- /** Max number of old snapshots to keep (default 1). */
346
- maxCached?: number;
347
- }
348
-
349
- /**
350
- * Run one attempt of `task` in a freshly-resumed Gondolin VM. Owns the full
351
- * lifecycle: resume VM → wire tools → pi session → close VM. Always returns
352
- * a `TaskOutput` (failures surface as `status: 'failed'`); throws only on
353
- * unrecoverable setup errors.
354
- */
355
- export declare function executePiTask(claimedTask: ClaimedTask, reporter: TaskReporter, opts: ExecutePiTaskOptions): Promise<TaskOutput>;
356
-
357
- export declare interface ExecutePiTaskOptions {
358
- /** MoltNet agent whose credentials the VM boots with. */
359
- agentName: string;
360
- /**
361
- * Host root that owns `.moltnet/<agentName>/`.
362
- *
363
- * Defaults to `mountPath`, but callers that mount scratch workspaces should
364
- * pass the stable sandbox root.
365
- */
366
- agentRootDir?: string;
367
- /** Host cwd that the VM mounts into the guest (defaults to `process.cwd()`). */
368
- mountPath?: string;
369
- /** LLM selection. */
370
- provider: string;
371
- model: string;
372
- /**
373
- * Runtime-profile reasoning/thinking level. Null/undefined means use Pi's
374
- * configured default; explicit `off` disables provider thinking where
375
- * supported.
376
- */
377
- thinkingLevel?: PiThinkingLevel | null;
378
- /** Optional sampling temperature. Null/undefined means provider default. */
379
- temperature?: number | null;
380
- /** Optional nucleus-sampling probability mass. Null/undefined means provider default. */
381
- topP?: number | null;
382
- /** Optional top-k sampling cutoff. Null/undefined means provider default. */
383
- topK?: number | null;
384
- /** Optional cap on generated output tokens. Null/undefined means provider/model default. */
385
- maxOutputTokens?: number | null;
386
- /** Extra hosts to allow in the sandbox egress policy. */
387
- extraAllowedHosts?: string[];
388
- /** Sandbox overrides (env, VFS shadows, resources). */
389
- sandboxConfig?: SandboxConfig;
390
- /** Host environment variable names to forward into the Pi VM. */
391
- forwardEnv?: string[];
392
- /**
393
- * Runtime profile context defaults. Merged with task.input.context at
394
- * execution time because the selected runtime profile is known only after
395
- * claim. Task entries override profile entries with the same slug.
396
- */
397
- runtimeProfileContext?: readonly ContextRef[];
398
- /**
399
- * Runtime profile id, used to resolve the tool-policy allow-set at session
400
- * start. Required together with a non-`off` `toolEnforcement` for the
401
- * `tool_call` gate to run.
402
- */
403
- runtimeProfileId?: string;
404
- /** Tool-policy enforcement mode for the selected runtime profile. */
405
- toolEnforcement?: ToolEnforcement;
406
- /**
407
- * Forwarded to `buildTaskUserPrompt` for per-type builders. Static
408
- * across tasks. Today no built-in builder needs per-task `extras` —
409
- * judges fetch their own dependent data via MoltNet tools
410
- * (`moltnet_get_task`, `moltnet_list_task_attempts`, etc.) at run
411
- * time, which keeps this layer task-type-agnostic. Field is kept
412
- * for forward compat with custom prompt builders that might want it.
413
- */
414
- promptExtras?: Record<string, unknown>;
415
- /** Snapshot progress callback; defaults to stderr logging. */
416
- onSnapshotProgress?: (message: string) => void;
417
- /**
418
- * Optional pre-resolved checkpoint path. If omitted, `ensureSnapshot` is
419
- * invoked. Useful for batch execution where the caller wants to cache
420
- * across tasks.
421
- */
422
- checkpointPath?: string;
423
- /**
424
- * Lazy checkpoint resolver used by `createPiTaskExecutor` so snapshot
425
- * creation can happen after the reporter has been opened and can surface
426
- * setup failures as task messages.
427
- */
428
- resolveCheckpointPath?: () => Promise<string>;
429
- /**
430
- * Set when the caller already opened the reporter before handing control
431
- * to `executePiTask`.
432
- */
433
- reporterAlreadyOpened?: boolean;
434
- /**
435
- * Optional callback invoked alongside every `reporter.record()` so
436
- * the daemon can mirror task messages into its local logger.
437
- * Bound at executor-construction time — use when one task runs per
438
- * process (e.g. `once.ts`) and per-task context is known before
439
- * the executor is built. For poll mode, prefer `makeOnTurnEvent`
440
- * below. If both are set, `makeOnTurnEvent` wins.
441
- * See `TurnEventHandler` for payload shape. Defaults to a no-op.
442
- */
443
- onTurnEvent?: TurnEventHandler;
444
- /**
445
- * Per-task factory variant for `onTurnEvent`. Invoked once per
446
- * task with the claimed task before any emit, so the returned
447
- * handler can bind taskId / attemptN into a pino child.
448
- * Use in poll mode where N tasks run sequentially in the same
449
- * process. See #1078.
450
- */
451
- makeOnTurnEvent?: TurnEventHandlerFactory;
452
- /**
453
- * Cap the number of tool-use turns per attempt. When the limit is
454
- * reached, the pi session is aborted and the attempt finalizes with
455
- * `error.code: max_turns_exceeded`. A tool-use turn = any `turn_end`
456
- * whose `stopReason !== 'end_turn'` (matches the Anthropic SDK
457
- * `max_turns` semantics: the model's final text-only response doesn't
458
- * count). Default `0` = disabled. Recommended `30` for `fulfill_brief`.
459
- * Closes part of #1094.
460
- */
461
- maxTurns?: number;
462
- /**
463
- * Cap the number of `bash` tool timeouts per attempt. A timeout is a
464
- * `tool_execution_end` for `bash` whose result text contains
465
- * "Command timed out after" (pi's stable error wrapper from
466
- * `@earendil-works/pi-coding-agent`'s bash tool). When the limit is
467
- * reached, the pi session is aborted and the attempt finalizes with
468
- * `error.code: max_bash_timeouts_exceeded`. Catches the death-spiral
469
- * pattern from task `a3762f44` where the model kept retrying
470
- * long-blocking shell commands until the host job timeout fired.
471
- * Default `3`. Set to `0` to disable. Closes part of #1094.
472
- */
473
- maxBashTimeouts?: number;
474
- /**
475
- * Number of correction turns allowed after the first invalid submit-output
476
- * tool call. A value of 2 permits three invalid submit calls total before
477
- * the attempt fails with output_validation_failed.
478
- */
479
- maxSubmitValidationRetries?: number;
480
- /**
481
- * Number of same-session re-prompts when the model ends its turn WITHOUT
482
- * calling the submit-output tool at all (no captured payload and no
483
- * exhausted validation budget). Distinct from
484
- * `maxSubmitValidationRetries`, which recovers *invalid-args* submit calls.
485
- * Each re-prompt names the submit tool and forbids a prose reply. When the
486
- * budget is spent the attempt still fails with `submit_output_missing`.
487
- * Only applies to task types that register a submit tool. Default `3`. Set
488
- * to `0` to disable. See #1528.
489
- */
490
- maxSubmitMissingReprompts?: number;
491
- /**
492
- * Continuation prompt sent when a turn ends without a submit call. Defaults
493
- * to `buildSubmitMissingPrompt(<tool name>)`.
494
- */
495
- submitMissingPrompt?: string;
496
- /**
497
- * Cap provider-error retries inside the same Pi session. A retry is attempted
498
- * only after a Pi assistant turn ends with `stopReason: "error"` and the
499
- * provider diagnostic is not a known credential/model/config failure. This is
500
- * distinct from daemon attempt retry: the active session keeps its context and
501
- * receives a short continuation prompt.
502
- *
503
- * Default `4`. Set to `0` to disable.
504
- */
505
- maxProviderErrorRetries?: number;
506
- /** Base delay for same-session provider-error retries. Default `2000`. */
507
- providerErrorRetryBaseDelayMs?: number;
508
- /** Maximum delay for same-session provider-error retries. Default `30000`. */
509
- providerErrorRetryMaxDelayMs?: number;
510
- /** Continuation prompt sent after a retryable provider error. Default `Go on`. */
511
- providerErrorRetryPrompt?: string;
512
- /**
513
- * Optional UI adapter for interactive pi/TUI callers. The daemon normally
514
- * leaves this unset and consumes the structured `provider_error_retry` task
515
- * message instead.
516
- */
517
- providerErrorRetryUi?: ProviderErrorRetryUi;
518
- /**
519
- * Skip per-call UI approval for matching `moltnet_host_exec` commands.
520
- * Keep false/undefined for interactive consumers. `true` skips every dialog
521
- * after HOST_EXEC_ALLOWED; an array limits auto-approval to matching rules.
522
- */
523
- hostExecAutoApprove?: HostExecAutoApproveConfig;
524
- /**
525
- * Optional daemon-supplied execution plan. Keeps task semantics out of
526
- * `pi-extension` while still letting callers opt into stable worktrees and
527
- * file-backed Pi sessions for selected task classes.
528
- */
529
- makeExecutionPlan?: PiTaskExecutionPlanFactory;
530
- /**
531
- * Immutable subagent contract registry used to resolve `output_schema`
532
- * names at subagent tool call time. Constructed by the daemon (or
533
- * tests) from static built-in schemas — `execute-pi-task` never hardcodes
534
- * contracts. See #1106.
535
- */
536
- subagentContractRegistry?: SubagentContractRegistry;
537
- /**
538
- * Structured logger for tool-policy resolution/gate events. The daemon passes
539
- * its task-bound pino child so these lines carry taskId/attemptN and join the
540
- * run's NDJSON stream. When omitted, tool-policy events fall back to raw
541
- * NDJSON on stderr (single-process / test callers). See #1348.
542
- */
543
- toolPolicyLogger?: ToolPolicyLogger;
544
- }
545
-
546
- /**
547
- * Resolve the main worktree root (where .moltnet/ lives — it's untracked,
548
- * only exists in the main worktree, not in git worktrees).
549
- */
550
- export declare function findMainWorktree(startPath?: string): string;
551
-
552
- /**
553
- * The gate's verdict:
554
- * - `{ allow: true }` — let the tool run.
555
- * - `{ allow: false, reason }` — block it (enforce mode).
556
- * - `{ audit, ... }` — would-block, but proceed and record it (watch mode).
557
- */
558
- export declare type GateDecision = {
559
- allow: true;
560
- } | {
561
- allow: false;
562
- reason: string;
563
- } | {
564
- audit: string;
565
- missing?: string[];
566
- };
567
-
568
- export declare interface GateInput {
569
- /** Pi tool name (e.g. 'bash', 'read', 'write', or a custom tool id). */
570
- toolName: string;
571
- /** The shell command, when `toolName === 'bash'`. */
572
- command?: string;
573
- enforcement: ToolEnforcement;
574
- /** Names the policy allows (structured tool names + shell executable names). */
575
- allowedTools: ReadonlySet<string>;
576
- /**
577
- * Synchronous shell analyzer (`ShellCommandAnalyzer.analyze`). Injected so the
578
- * decision stays pure and testable; the analyzer's async WASM init happens
579
- * once at session start.
580
- */
581
- analyze: (command: string) => CommandAnalysis;
582
- }
583
-
584
- /**
585
- * Baseline env keys forwarded to host-exec child processes.
586
- * Callers can extend this set at sandbox startup via `MoltNetToolsConfig.hostExecBaseEnv`.
587
- */
588
- export declare const HOST_EXEC_DEFAULT_BASE_ENV: ReadonlySet<string>;
589
-
590
- declare type HostExecAutoApproveConfig = boolean | readonly HostExecAutoApproveRule[];
591
-
592
- declare interface HostExecAutoApproveRule {
593
- /** Exact executable name. Must still pass HOST_EXEC_ALLOWED. */
594
- executable: string;
595
- /** Optional ordered argument prefix; flags after the prefix are allowed. */
596
- argsPrefix?: readonly string[];
597
- /** Optional unordered argument tokens that must appear somewhere. */
598
- argsContains?: readonly string[];
599
- /** Optional argument tokens that prevent auto-approval when present. */
600
- argsExcludes?: readonly string[];
601
- }
602
-
603
- export declare interface InjectedTaskContext {
604
- /** Refs that were delivered, in declared order, for audit. */
605
- injected: ContextRef[];
606
- /** Synthetic Skill objects to splice into pi's skillsOverride. */
607
- skills: Skill[];
608
- /** Prepend this to `appendSystemPrompt`. Empty when nothing
609
- * contributed (omit the array entry rather than pass an empty
610
- * string to keep pi's prompt assembly tidy). */
611
- systemPromptPrefix: string;
612
- /** Append this to the task user prompt BEFORE `session.prompt()`. */
613
- userInlineSuffix: string;
614
- }
615
-
616
- /**
617
- * Resolve effective runtime context and inject the side effects Pi
618
- * needs. Safe to call with an empty array — returns an inert result.
619
- */
620
- export declare function injectTaskContext(args: InjectTaskContextArgs): Promise<InjectedTaskContext>;
621
-
622
- export declare interface InjectTaskContextArgs {
623
- /** Empty array (the default for any non-eval task) is a no-op. */
624
- context: readonly ContextRef[];
625
- /** Guest filesystem handle. In production this is `managed.vm.fs`. */
626
- fs: VmFsForContext;
627
- /** Guest path where the active host workspace is mounted. */
628
- guestWorkspace: string;
629
- }
630
-
631
- export declare function loadCredentials(agentDir: string): VmCredentials;
632
-
633
- export declare interface ManagedVm {
634
- vm: VM;
635
- credentials: VmCredentials;
636
- mountPath: string;
637
- guestWorkspace: string;
638
- agentDir: string;
639
- }
640
-
641
- declare type MoltNetAgent = Awaited<ReturnType<typeof connect>>;
642
-
643
11
  declare function moltnetExtension(pi: ExtensionAPI): void;
644
12
  export default moltnetExtension;
645
13
 
646
- /**
647
- * Active-task context. When present, `moltnet_create_entry` is forced to
648
- * land entries in `diaryId` (the task diary), regardless of the env-derived
649
- * diary, and auto-injects provenance tags under the `task:*` namespace
650
- * (`task:id:<id>`, `task:type:<type>`, `task:attempt:<n>`, and
651
- * `task:correlation:<id>` when the task carries one). See issue #979 +
652
- * the #986 follow-up that introduced the namespace.
653
- */
654
- declare interface MoltNetTaskContext {
655
- taskId: string;
656
- taskType: string;
657
- attemptN: number;
658
- diaryId: string;
659
- /**
660
- * Optional correlation id. When set, propagated as a
661
- * `task:correlation:<id>` provenance tag so all entries from a
662
- * multi-task workflow can be grouped without enumerating individual
663
- * task ids.
664
- */
665
- correlationId: string | null;
666
- }
667
-
668
- declare interface MoltNetToolsConfig {
669
- getAgent(): MoltNetAgent | null;
670
- getDiaryId(): string | null;
671
- getTeamId(): string | null;
672
- getSessionErrors(): readonly TrackedError[];
673
- clearSessionErrors(): void;
674
- /** Host working directory for host-exec commands (worktree path or cwd). */
675
- getHostCwd?(): string;
676
- /**
677
- * Optional workspace-file reader. Daemon/Gondolin callers provide this so
678
- * artifact uploads see guest overlay writes that may not exist on the host
679
- * mount path yet.
680
- */
681
- openWorkspaceFileForRead?(filePath: string): Promise<{
682
- stream: Readable;
683
- isFile: boolean;
684
- sizeBytes?: number;
685
- displayPath?: string;
686
- }>;
687
- /**
688
- * Set of process.env keys that are safe to forward to host-exec child
689
- * processes. Configured at sandbox startup so the caller can include
690
- * agent-specific vars (e.g. MOLTNET_AGENT_NAME) alongside the defaults.
691
- * Defaults to HOST_EXEC_DEFAULT_BASE_ENV when omitted.
692
- */
693
- hostExecBaseEnv?: ReadonlySet<string>;
694
- /**
695
- * When true, `moltnet_host_exec` skips the per-call UI approval dialog.
696
- * Intended for non-interactive daemon automation only; interactive
697
- * consumers should keep the default false behavior.
698
- */
699
- autoApproveHostExec?: boolean;
700
- /**
701
- * Host-exec auto-approval policy. `true` skips all dialogs after the
702
- * executable allowlist check. An array skips only commands matching one of
703
- * the supplied executable/argument rules. Omitted/false preserves the
704
- * interactive approval flow.
705
- */
706
- hostExecAutoApprove?: HostExecAutoApproveConfig;
707
- /**
708
- * Active-task context, populated by the agent-daemon path. When set,
709
- * `moltnet_create_entry` enforces `diaryId === taskContext.diaryId` and
710
- * injects provenance tags. When absent (interactive pi-extension / TUI),
711
- * entry creation behaves as before (env-derived diary, no auto-tags).
712
- */
713
- getTaskContext?(): MoltNetTaskContext | null;
714
- }
715
-
716
- export declare function normalizeRetryTriageResult(value: unknown): PiRetryTriageResult;
717
-
718
- export declare interface PiOtelOptions {
719
- /** Agent name for `gen_ai.agent.name` on the root span. */
720
- agentName?: string;
721
- /**
722
- * Extra attributes merged onto every span. Use MoltNet-specific keys
723
- * like `moltnet.task.id` — any `gen_ai.*` keys here are filtered out
724
- * since the extension is authoritative for those.
725
- */
726
- spanAttributes?: Record<string, string | number | boolean>;
727
- }
728
-
729
- export declare type PiRetryTriage = (input: PiRetryTriageInput) => Promise<PiRetryTriageResult>;
730
-
731
- export declare type PiRetryTriageConfidence = RetryTriageConfidence;
732
-
733
- export declare type PiRetryTriageDecision = RetryTriageDecision;
734
-
735
- export declare interface PiRetryTriageInput {
736
- task: {
737
- id: string;
738
- taskType: string;
739
- teamId: string;
740
- input: unknown;
741
- };
742
- attemptN: number;
743
- maxAttempts?: number | null;
744
- remainingAttempts?: number | null;
745
- error: unknown;
746
- recentMessages?: {
747
- timestamp: string;
748
- kind: string;
749
- payload: unknown;
750
- }[];
751
- }
752
-
753
- export declare interface PiRetryTriageResult {
754
- decision: RetryTriageDecision;
755
- confidence: RetryTriageConfidence;
756
- reason: string;
757
- }
758
-
759
- export declare type PiRetryTriageThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
760
-
761
- export declare interface PiSessionPersistencePlan {
762
- sessionDir: string;
763
- forkFromSessionPath?: string | null;
764
- }
765
-
766
- export declare interface PiTaskExecutionPlan {
767
- /**
768
- * Effective workspace mode for this task instance.
769
- * `scratch_mount` means mount an empty scratch directory rather than the
770
- * daemon checkout.
771
- */
772
- workspaceMode: 'shared_mount' | 'dedicated_worktree' | 'scratch_mount';
773
- /**
774
- * Daemon-local reuse key. When set alongside `workspaceScope: 'session'`,
775
- * dedicated worktrees may be retained and reopened across related tasks.
776
- */
777
- sessionKey: string | null;
778
- /**
779
- * Workspace identity selected by the daemon. `null` means the task should
780
- * run against the shared mount path.
781
- */
782
- workspaceId: string | null;
783
- /**
784
- * Branch to create or reopen for the workspace. `null` means no dedicated
785
- * worktree is required.
786
- */
787
- worktreeBranch: string | null;
788
- /**
789
- * Base ref a NEW `worktreeBranch` is cut from. Used by `fork` continuations
790
- * to branch from the parent's tip instead of the default (main/HEAD). Ignored
791
- * when `worktreeBranch` already exists.
792
- */
793
- worktreeBaseRef?: string | null;
794
- /**
795
- * Lifetime of the task workspace from the daemon's point of view.
796
- * `attempt` = disposable; `session` = keep stable for the reuse key.
797
- */
798
- workspaceScope: 'attempt' | 'session';
799
- /**
800
- * Optional existing workspace root to attach instead of creating a fresh
801
- * shared/worktree/scratch workspace. Used for read-only-ish producer
802
- * inspection by applying VFS shadowing on top of the mounted path.
803
- */
804
- workspaceAttachment?: PiWorkspaceAttachmentPlan | null;
805
- /**
806
- * Optional seed content for a freshly created scratch workspace.
807
- */
808
- workspaceSeed?: PiWorkspaceSeedPlan | null;
809
- /**
810
- * Optional location for file-backed Pi session history. When omitted,
811
- * the executor keeps the conversation in memory for this attempt only.
812
- */
813
- sessionPersistence?: PiSessionPersistencePlan | null;
814
- }
815
-
816
- export declare type PiTaskExecutionPlanFactory = (claimedTask: ClaimedTask) => Promise<PiTaskExecutionPlan | null> | PiTaskExecutionPlan | null;
817
-
818
- declare type PiThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
819
-
820
- declare interface PiWorkspaceAttachmentPlan {
821
- mountPath: string;
822
- cwdPath: string;
823
- shadowWrites?: 'deny' | 'tmpfs';
824
- }
825
-
826
- declare interface PiWorkspaceSeedPlan {
827
- copyFromPath: string;
828
- source: 'producer';
829
- }
830
-
831
- export declare interface ProviderErrorRetryEvent extends Record<string, unknown> {
832
- event: 'provider_error_retry';
833
- retry: number;
834
- maxRetries: number;
835
- delayMs: number;
836
- reason: string;
837
- }
838
-
839
- export declare type ProviderErrorRetryLevel = 'info' | 'warning' | 'error';
840
-
841
- export declare interface ProviderErrorRetryUi {
842
- /**
843
- * Mirrors pi's `ctx.hasUI`. Undefined means "UI adapter is present"; false
844
- * lets callers pass a stable adapter from both TUI and headless contexts.
845
- */
846
- hasUI?: boolean;
847
- setStatus?: (key: string, message: string) => void | Promise<void>;
848
- notify?: (message: string, level: ProviderErrorRetryLevel) => void | Promise<void>;
849
- }
850
-
851
- export declare function redactRetryTriageSecrets(value: string): string;
852
-
853
- /**
854
- * Resolve the session's tool policy at start-up.
855
- *
856
- * `off` short-circuits without a network call. Otherwise the allowed-tool set
857
- * is fetched from the API. If that fetch fails, the mode decides the fallback:
858
- * `enforce` **fails closed** (empty allow-set → every non-`off` tool is
859
- * blocked); `watch` fails open (empty allow-set → every tool is audited but
860
- * allowed).
861
- *
862
- * The result is a **session-start snapshot**: it is resolved once and cached for
863
- * the session's lifetime. Policy edits made while a task is running do not take
864
- * effect until the next session — a deliberate trade-off (one resolution per
865
- * session, stable enforcement for the run) accepted over re-fetching per call.
866
- */
867
- export declare function resolveSessionToolPolicy(input: ResolveSessionToolPolicyInput): Promise<SessionToolPolicy>;
868
-
869
- declare interface ResolveSessionToolPolicyInput {
870
- agent: AllowedToolsClient;
871
- profileId: string;
872
- teamId: string;
873
- /**
874
- * The profile's enforcement mode, already known to the daemon from the
875
- * resolved runtime profile. Used to decide fail-open vs fail-closed when the
876
- * allowed-tools fetch fails.
877
- */
878
- enforcement: ToolEnforcement;
879
- logger: ToolPolicyLogger;
880
- /**
881
- * Deadline for the allowed-tools fetch. A hung API call must not stall session
882
- * start-up indefinitely, so on timeout we abort and fall back to the
883
- * mode-appropriate degraded policy. Defaults to
884
- * {@link DEFAULT_RESOLVE_TIMEOUT_MS}; `0`/negative disables the deadline.
885
- */
886
- timeoutMs?: number;
887
- }
888
-
889
- export declare function resolveTaskWorktreePath(mainRepo: string, workspaceId: string): string;
890
-
891
- export declare interface ResumeCommand {
892
- /** Shell command, same semantics as the string form. */
893
- run: string;
894
- /** Optional generic runtime predicate for whether this step should run. */
895
- when?: ResumeCommandWhen;
896
- /** Additional attempts on non-zero exit. Default 0. */
897
- retries?: number;
898
- /** Linear backoff between attempts in ms. Delay before attempt N+1 is
899
- * `(N + 1) * retryBackoffMs` (so 2s, 4s, … with the default). */
900
- retryBackoffMs?: number;
901
- }
902
-
903
- /** Structured form of a resume command with optional retry policy. */
904
- declare interface ResumeCommandWhen {
905
- /**
906
- * Effective workspace mode(s) that should run this command.
907
- * Evaluated by the runtime from the mounted workspace shape rather than
908
- * from task type semantics.
909
- */
910
- workspaceMode?: ('shared_mount' | 'dedicated_worktree' | 'scratch_mount')[];
911
- }
912
-
913
- /**
914
- * Resume a VM from a checkpoint, inject credentials, configure egress +
915
- * TLS. Returns the managed VM handle.
916
- */
917
- export declare function resumeVm(config: VmConfig): Promise<ManagedVm>;
918
-
919
- export declare type RetryTriageConfidence = 'low' | 'medium' | 'high';
920
-
921
- export declare type RetryTriageDecision = 'retry' | 'do_not_retry';
922
-
923
- export declare interface SandboxConfig {
924
- /** Snapshot build settings. */
925
- snapshot?: {
926
- /** Shell commands to run after the base setup. */
927
- setupCommands?: string[];
928
- /** Additional hosts to allow network access during build. */
929
- allowedHosts?: string[];
930
- /** Overlay disk size (default '3G'). */
931
- overlaySize?: string;
932
- };
933
- /** Runtime network egress policy. Separate from snapshot build access. */
934
- network?: {
935
- /** Additional host patterns allowed while the VM is running.
936
- * Internal and private address resolution remains blocked. */
937
- allowedHosts?: string[];
938
- /** Host patterns explicitly allowed to resolve to internal/private IPs. */
939
- allowedInternalHosts?: string[];
940
- };
941
- /** Shell commands to run every VM resume, after platform setup
942
- * (TLS, DNS, git safe.directory, tmpfs node_modules) and before
943
- * the agent session starts. Use for per-session bootstrap that
944
- * doesn't belong baked into the snapshot.
945
- *
946
- * Not included in the snapshot cache key — changes here apply on
947
- * every resume without triggering a snapshot rebuild. Each command
948
- * runs in a fresh shell with `set -eu` and `set -o pipefail`; a
949
- * non-zero exit (including from any segment of a pipeline) aborts
950
- * resume with the failing command's stderr/stdout tail.
951
- *
952
- * Each entry is either a raw string (no retries) or an object
953
- * `{ run, when?, retries?, retryBackoffMs? }`. `when` gates the
954
- * command on generic runtime properties such as effective
955
- * `workspaceMode`; this keeps sandbox policy decoupled from task
956
- * types. `retries` is the number of ADDITIONAL attempts after the
957
- * first failure (default 0 = no retry). Use for steps that hit the
958
- * network and may legitimately race DHCP/registry availability on a
959
- * fresh resume (e.g. `pnpm install`). The wrapped command must be
960
- * idempotent. */
961
- resumeCommands?: (string | ResumeCommand)[];
962
- /** VFS shadow settings — hide host paths from the guest. */
963
- vfs?: {
964
- /** Paths (relative to workspace root) to shadow from the host mount. */
965
- shadow?: string[];
966
- /** What to do with writes to shadowed paths: 'deny' or 'tmpfs' (default 'tmpfs'). */
967
- shadowMode?: 'deny' | 'tmpfs';
968
- };
969
- /** Environment variable overrides for the guest VM (applied on top of defaults). */
970
- env?: Record<string, string>;
971
- /** Host-side escape hatch policy. Applies only to `moltnet_host_exec`. */
972
- hostExec?: {
973
- /**
974
- * `true` auto-approves every allowed executable. An array auto-approves
975
- * only commands matching one of the executable/argument rules.
976
- */
977
- autoApprove?: boolean | {
978
- executable: string;
979
- argsPrefix?: string[];
980
- argsContains?: string[];
981
- argsExcludes?: string[];
982
- }[];
983
- };
984
- /** VM resource allocation. */
985
- resources?: {
986
- /** Memory size in qemu syntax (default '1G'). */
987
- memory?: string;
988
- /** CPU count (default 2). */
989
- cpus?: number;
990
- };
991
- }
992
-
993
- /** The resolved allow-set + enforcement mode for a runtime session. */
994
- export declare interface SessionToolPolicy {
995
- enforcement: ToolEnforcement;
996
- allowedTools: ReadonlySet<string>;
997
- /**
998
- * `true` when the allow-set is a **degraded fallback** — the allowed-tools
999
- * fetch failed or timed out and this policy is the fail-closed/fail-open
1000
- * default, NOT the operator's actual configuration. An intentional
1001
- * empty-but-resolved policy (e.g. a profile with no bound tools) has
1002
- * `degraded: false`. Surfaced in every audit/block log so an operator can tell
1003
- * "blocked because the policy is empty" from "blocked because we couldn't read
1004
- * the policy". `off` and successful resolutions are never degraded.
1005
- */
1006
- degraded: boolean;
1007
- }
1008
-
1009
- /** Extract snapshot-specific config for backwards compat with ensureSnapshot. */
1010
- export declare type SnapshotConfig = NonNullable<SandboxConfig['snapshot']>;
1011
-
1012
- declare interface SubagentContractRegistry {
1013
- /** Resolve a contract by name. Returns `null` for unknown names. */
1014
- get(name: string): SubagentOutputContract | null;
1015
- /** List all registered contracts. */
1016
- list(): SubagentOutputContract[];
1017
- }
1018
-
1019
- declare interface SubagentOutputContract {
1020
- /** Stable identifier the parent uses to reference this contract.
1021
- * Lower-snake-case by convention (e.g. `judge_eval_variant_result`). */
1022
- readonly name: string;
1023
- /** Human-readable description shown in the subagent tool's help text
1024
- * and in the inner session's submit-tool description. Useful when a
1025
- * parent LLM has multiple contracts to choose from. */
1026
- readonly description: string;
1027
- /**
1028
- * TypeBox schema the subagent's submit-tool args MUST validate
1029
- * against. The args ARE the output payload (no `{ output: ... }`
1030
- * wrapping), so the LLM gets field-level guidance directly.
1031
- */
1032
- readonly parametersSchema: TSchema;
1033
- }
1034
-
1035
- export declare interface SubagentToolHandle {
1036
- /** ToolDefinition to register via `customTools` on the parent session. */
1037
- readonly tool: ToolDefinition;
1038
- /** How many times the parent LLM has called this tool. */
1039
- getCallCount: () => number;
1040
- }
1041
-
1042
- /**
1043
- * Parameters shape the parent LLM sees when calling the subagent tool.
1044
- *
1045
- * - `task` — natural-language instructions for the subagent.
1046
- * The parent authors this per call. Must be
1047
- * non-empty.
1048
- * - `output_schema` — name of a registered SubagentOutputContract.
1049
- * Resolved at call time; unknown names error.
1050
- */
1051
- export declare const SubagentToolParameters: TObject<{
1052
- task: Type.TString;
1053
- output_schema: Type.TString;
1054
- }>;
1055
-
1056
- export declare type SubagentToolParameters = Static<typeof SubagentToolParameters>;
1057
-
1058
- /**
1059
- * The Task promise body.
1060
- *
1061
- * Type-neutrality invariant: no property on this type is specific to one
1062
- * `taskType`. Type-specific payload lives inside `input` (validated
1063
- * against the schema registered for `taskType`).
1064
- */
1065
- declare const Task: Type.TObject<{
1066
- id: Type.TString;
1067
- taskType: Type.TString;
1068
- title: Type.TUnion<[Type.TString, Type.TNull]>;
1069
- tags: Type.TArray<Type.TString>;
1070
- teamId: Type.TString;
1071
- diaryId: Type.TUnion<[Type.TString, Type.TNull]>;
1072
- outputKind: Type.TUnion<[Type.TLiteral<"artifact">, Type.TLiteral<"judgment">]>;
1073
- input: Type.TRecord<"^.*$", Type.TUnknown>;
1074
- inputSchemaCid: Type.TString;
1075
- inputCid: Type.TString;
1076
- references: Type.TArray<Type.TObject<{
1077
- taskId: Type.TUnion<[Type.TString, Type.TNull]>;
1078
- outputCid: Type.TOptional<Type.TString>;
1079
- role: Type.TUnion<[Type.TLiteral<"judged_work">, Type.TLiteral<"reviewed_diff">, Type.TLiteral<"target_source">, Type.TLiteral<"context">]>;
1080
- external: Type.TOptional<Type.TObject<{
1081
- kind: Type.TUnion<[Type.TLiteral<"github_pr">, Type.TLiteral<"github_issue">, Type.TLiteral<"http_url">]>;
1082
- pr: Type.TOptional<Type.TNumber>;
1083
- issue: Type.TOptional<Type.TNumber>;
1084
- url: Type.TOptional<Type.TString>;
1085
- commit_sha: Type.TOptional<Type.TString>;
1086
- snapshot_cid: Type.TOptional<Type.TString>;
1087
- }>>;
1088
- artifact: Type.TOptional<Type.TObject<{
1089
- cid: Type.TString;
1090
- attemptN: Type.TOptional<Type.TInteger>;
1091
- kind: Type.TOptional<Type.TString>;
1092
- title: Type.TOptional<Type.TString>;
1093
- contentType: Type.TOptional<Type.TString>;
1094
- }>>;
1095
- }>>;
1096
- correlationId: Type.TUnion<[Type.TString, Type.TNull]>;
1097
- proposedByAgentId: Type.TUnion<[Type.TString, Type.TNull]>;
1098
- proposedByHumanId: Type.TUnion<[Type.TString, Type.TNull]>;
1099
- acceptedAttemptN: Type.TUnion<[Type.TNumber, Type.TNull]>;
1100
- claimCondition: Type.TUnion<[Type.TUnsafe<ClaimCondition>, Type.TNull]>;
1101
- requiredExecutorTrustLevel: Type.TUnion<[Type.TLiteral<"selfDeclared">, Type.TLiteral<"agentSigned">, Type.TLiteral<"releaseVerifiedTool">, Type.TLiteral<"sandboxAttested">]>;
1102
- allowedProfiles: Type.TArray<Type.TObject<{
1103
- profileId: Type.TString;
1104
- }>>;
1105
- status: Type.TUnion<[Type.TLiteral<"waiting">, Type.TLiteral<"queued">, Type.TLiteral<"dispatched">, Type.TLiteral<"running">, Type.TLiteral<"completed">, Type.TLiteral<"failed">, Type.TLiteral<"cancelled">, Type.TLiteral<"expired">]>;
1106
- queuedAt: Type.TString;
1107
- completedAt: Type.TUnion<[Type.TString, Type.TNull]>;
1108
- expiresAt: Type.TUnion<[Type.TString, Type.TNull]>;
1109
- cancelledByAgentId: Type.TUnion<[Type.TString, Type.TNull]>;
1110
- cancelledByHumanId: Type.TUnion<[Type.TString, Type.TNull]>;
1111
- cancelReason: Type.TUnion<[Type.TString, Type.TNull]>;
1112
- maxAttempts: Type.TNumber;
1113
- dispatchTimeoutSec: Type.TUnion<[Type.TInteger, Type.TNull]>;
1114
- runningTimeoutSec: Type.TUnion<[Type.TInteger, Type.TNull]>;
1115
- }>;
1116
-
1117
- declare type Task = Static<typeof Task>;
1118
-
1119
- declare const TaskMessage: Type.TObject<{
1120
- taskId: Type.TString;
1121
- attemptN: Type.TNumber;
1122
- seq: Type.TNumber;
1123
- timestamp: Type.TString;
1124
- kind: Type.TUnion<[Type.TLiteral<"text_delta">, Type.TLiteral<"tool_call_start">, Type.TLiteral<"tool_call_end">, Type.TLiteral<"turn_end">, Type.TLiteral<"error">, Type.TLiteral<"info">]>;
1125
- payload: Type.TRecord<"^.*$", Type.TUnknown>;
1126
- }>;
1127
-
1128
- declare type TaskMessage = Static<typeof TaskMessage>;
1129
-
1130
- /**
1131
- * Terminal result of an attempt. Distinct from `TaskAttempt` — this is
1132
- * the compact shape the runtime surfaces back to whoever drove it
1133
- * (stdout reporter, API reporter in PR 7, etc.).
1134
- */
1135
- declare const TaskOutput: Type.TObject<{
1136
- taskId: Type.TString;
1137
- attemptN: Type.TNumber;
1138
- status: Type.TUnion<[Type.TLiteral<"completed">, Type.TLiteral<"failed">, Type.TLiteral<"cancelled">]>;
1139
- output: Type.TUnion<[Type.TRecord<"^.*$", Type.TUnknown>, Type.TNull]>;
1140
- outputCid: Type.TUnion<[Type.TString, Type.TNull]>;
1141
- usage: Type.TObject<{
1142
- inputTokens: Type.TInteger;
1143
- outputTokens: Type.TInteger;
1144
- cacheReadTokens: Type.TOptional<Type.TInteger>;
1145
- cacheWriteTokens: Type.TOptional<Type.TInteger>;
1146
- toolCalls: Type.TOptional<Type.TInteger>;
1147
- model: Type.TOptional<Type.TString>;
1148
- provider: Type.TOptional<Type.TString>;
1149
- }>;
1150
- durationMs: Type.TNumber;
1151
- error: Type.TOptional<Type.TObject<{
1152
- code: Type.TString;
1153
- message: Type.TString;
1154
- stack: Type.TOptional<Type.TString>;
1155
- retryable: Type.TOptional<Type.TBoolean>;
1156
- retry: Type.TOptional<Type.TObject<{
1157
- source: Type.TUnion<[Type.TLiteral<"explicit">, Type.TLiteral<"deterministic">, Type.TLiteral<"attempts_exhausted">, Type.TLiteral<"triage">, Type.TLiteral<"triage_failed">]>;
1158
- decision: Type.TOptional<Type.TUnion<[Type.TLiteral<"retry">, Type.TLiteral<"do_not_retry">]>>;
1159
- confidence: Type.TOptional<Type.TUnion<[Type.TLiteral<"low">, Type.TLiteral<"medium">, Type.TLiteral<"high">]>>;
1160
- reason: Type.TOptional<Type.TString>;
1161
- }>>;
1162
- }>>;
1163
- contentSignature: Type.TOptional<Type.TString>;
1164
- }>;
1165
-
1166
- declare type TaskOutput = Static<typeof TaskOutput>;
1167
-
1168
- /**
1169
- * Append-only event sink for a single task attempt.
1170
- *
1171
- * Contract: `TaskReporter` is the ONLY I/O surface `executeTask` has.
1172
- * Whether events go to stdout, a JSONL file, or an HTTP POST is the
1173
- * reporter's concern — so `executeTask` is identical in local and API
1174
- * modes (the single abstraction that lets PR 7 be pure plumbing).
1175
- *
1176
- * Records written via `record()` carry a monotonic `seq` per
1177
- * `(taskId, attemptN)`; reporters assign it internally.
1178
- *
1179
- * Reporters MUST be idempotent on replay: if the same `seq` is seen
1180
- * twice with the same payload, that's a reconnect, not a bug.
1181
- */
1182
- declare interface TaskReporter {
1183
- /**
1184
- * Open the reporter for a specific attempt. Called once before any
1185
- * `record()` calls. Reporters that don't need per-attempt state can
1186
- * return immediately.
1187
- */
1188
- open(ctx: {
1189
- taskId: string;
1190
- attemptN: number;
1191
- }): Promise<void>;
1192
- /**
1193
- * Record one event. `seq`, `timestamp`, `taskId`, `attemptN` are
1194
- * supplied by the reporter — callers pass the body only.
1195
- */
1196
- record(body: Omit<TaskMessage, 'taskId' | 'attemptN' | 'seq' | 'timestamp'>): Promise<void>;
1197
- /**
1198
- * Final accounting. Writes a summary the runtime can surface; does
1199
- * NOT imply a particular output kind (completion vs failure).
1200
- */
1201
- finalize(usage: TaskUsage): Promise<void>;
1202
- /** Flush buffers + release resources. Called once. Idempotent. */
1203
- close(): Promise<void>;
1204
- /**
1205
- * Signal that aborts when the task is cancelled by the proposer (or a
1206
- * diary writer) while the executor is running. `ApiTaskReporter`
1207
- * aborts this on the next heartbeat that observes `cancelled: true`
1208
- * in the response (#938). Local reporters (`StdoutReporter`,
1209
- * `JsonlTaskReporter`) never abort — there's no remote cancel
1210
- * channel for `FileTaskSource`.
1211
- *
1212
- * Executors should pass this signal into long-running work
1213
- * (LLM calls, sandbox execution, file ops) and surface a
1214
- * `status: 'cancelled'` output when it fires. The runtime also
1215
- * checks the signal post-execute and converts any output to
1216
- * `cancelled` if the executor returned without honoring it.
1217
- */
1218
- readonly cancelSignal: AbortSignal;
1219
- /**
1220
- * The reason supplied to `/tasks/:id/cancel` by the canceller, if
1221
- * cancellation has been observed. Null until `cancelSignal` aborts.
1222
- */
1223
- readonly cancelReason: string | null;
1224
- /**
1225
- * Request local cancellation of the in-flight task. Runtime shutdown uses
1226
- * this to trip the same executor-facing signal as proposer cancellation,
1227
- * without waiting for the next server heartbeat.
1228
- */
1229
- requestCancel?(reason: string): void;
1230
- }
1231
-
1232
- declare const TaskStatus: Type.TUnion<[Type.TLiteral<"waiting">, Type.TLiteral<"queued">, Type.TLiteral<"dispatched">, Type.TLiteral<"running">, Type.TLiteral<"completed">, Type.TLiteral<"failed">, Type.TLiteral<"cancelled">, Type.TLiteral<"expired">]>;
1233
-
1234
- declare type TaskStatus = Static<typeof TaskStatus>;
1235
-
1236
- /**
1237
- * Token / cost accounting for one attempt.
1238
- * Reported by the runtime; persisted per-attempt, also rolled up into
1239
- * `TaskOutput.usage` for convenience.
1240
- */
1241
- declare const TaskUsage: Type.TObject<{
1242
- inputTokens: Type.TInteger;
1243
- outputTokens: Type.TInteger;
1244
- cacheReadTokens: Type.TOptional<Type.TInteger>;
1245
- cacheWriteTokens: Type.TOptional<Type.TInteger>;
1246
- toolCalls: Type.TOptional<Type.TInteger>;
1247
- model: Type.TOptional<Type.TString>;
1248
- provider: Type.TOptional<Type.TString>;
1249
- }>;
1250
-
1251
- declare type TaskUsage = Static<typeof TaskUsage>;
1252
-
1253
- /**
1254
- * Map a host-side absolute path to a guest-side workspace path.
1255
- * Throws if the path escapes the workspace.
1256
- */
1257
- export declare function toGuestPath(localCwd: string, localPath: string, guestWorkspace: string): string;
1258
-
1259
- export declare type ToolEnforcement = Static<typeof ToolEnforcementSchema>;
1260
-
1261
- declare const ToolEnforcementSchema = Type.Union(toolEnforcementLiterals, {
1262
- description:
1263
- 'Runtime tool-policy enforcement mode: off (inert), watch (audit only), enforce (block disallowed tools, fail-closed).',
1264
- });
1265
-
1266
- export declare interface ToolPolicyExtensionDeps {
1267
- policy: SessionToolPolicy;
1268
- analyzer: ShellCommandAnalyzer;
1269
- logger: ToolPolicyLogger;
1270
- }
1271
-
1272
- /**
1273
- * Structured logger the resolver and gate emit to. Deliberately the pino
1274
- * `(obj, msg)` shape so the daemon can pass a task-bound pino child directly —
1275
- * every tool-policy line then carries the daemon's taskId/attemptN context and
1276
- * lands in the same NDJSON stream as the rest of the run.
1277
- */
1278
- declare interface ToolPolicyLogger {
1279
- debug: (obj: Record<string, unknown>, msg: string) => void;
1280
- info: (obj: Record<string, unknown>, msg: string) => void;
1281
- warn: (obj: Record<string, unknown>, msg: string) => void;
1282
- }
1283
-
1284
- declare interface TrackedError {
1285
- toolName: string;
1286
- toolCallId: string;
1287
- input: Record<string, unknown>;
1288
- error: string;
1289
- timestamp: number;
1290
- }
1291
-
1292
- export declare interface TurnEventHandler {
1293
- (event: TurnEventKind, summary: Record<string, unknown>): void;
1294
- }
1295
-
1296
- export declare type TurnEventHandlerFactory = (claimedTask: ClaimedTask) => TurnEventHandler;
1297
-
1298
- export declare type TurnEventKind = Parameters<TaskReporter['record']>[0]['kind'];
1299
-
1300
- export declare interface VmConfig {
1301
- /** Absolute path to the qcow2 checkpoint. */
1302
- checkpointPath: string;
1303
- /** MoltNet agent name (used to resolve credentials). */
1304
- agentName: string;
1305
- /**
1306
- * Host root that owns `.moltnet/<agentName>/`.
1307
- *
1308
- * Defaults to the main git worktree for backwards compatibility. Daemon
1309
- * callers pass the sandbox root so non-git scratch/shared tasks can boot.
1310
- */
1311
- agentRootDir?: string;
1312
- /** Host directory to mount into the VM. */
1313
- mountPath: string;
1314
- /** Effective workspace shape selected by the caller. */
1315
- workspaceMode?: 'shared_mount' | 'dedicated_worktree' | 'scratch_mount';
1316
- /** Additional hosts to allow in egress policy. */
1317
- extraAllowedHosts?: string[];
1318
- /** Full sandbox config (vfs shadows, env overrides). */
1319
- sandboxConfig?: SandboxConfig;
1320
- /**
1321
- * Host environment variable names to copy into the VM process.
1322
- *
1323
- * Runtime profiles use this for provider API keys: `requiredEnv` proves the
1324
- * daemon host has the secret, and this allowlist forwards only those names
1325
- * into the guest without storing secret values in the profile.
1326
- */
1327
- forwardEnv?: string[];
1328
- /** Abort resume/setup work, closing any live VM owned by resumeVm. */
1329
- signal?: AbortSignal;
1330
- }
1331
-
1332
- export declare interface VmCredentials {
1333
- moltnetJson: string;
1334
- agentEnvRaw: string;
1335
- /**
1336
- * Pi OAuth/API-key auth blob. Null when neither `~/.pi/agent/auth.json`
1337
- * (resolved via `PI_CODING_AGENT_DIR` when set) is present — in that
1338
- * case the daemon relies on Pi's env-var providers (`ANTHROPIC_API_KEY`,
1339
- * etc.) carried via `agentEnv` and the host environment instead. CI uses
1340
- * this path.
1341
- */
1342
- piAuthJson: string | null;
1343
- agentEnv: Record<string, string | undefined>;
1344
- gitconfig: string | null;
1345
- sshPrivateKey: string | null;
1346
- sshPublicKey: string | null;
1347
- allowedSigners: string | null;
1348
- /** Raw PEM content of the GitHub App private key, or null if not configured. */
1349
- githubAppPem: string | null;
1350
- /** VM-local filename for the GitHub App PEM (basename of host path), or null. */
1351
- githubAppPemFilename: string | null;
1352
- }
1353
-
1354
- /**
1355
- * Subset of `@earendil-works/gondolin`'s `VmFs` we actually use. We
1356
- * narrow the dependency surface so unit tests can hand in a
1357
- * vitest-mocked object without instantiating a real VM. We use `any`
1358
- * for the options parameter to make this interface bivariantly
1359
- * compatible with `VmFs` (whose options types differ between
1360
- * `mkdir` and `writeFile`); the orchestrator only ever calls these
1361
- * methods with the documented option shape, so the looseness is
1362
- * confined to this seam.
1363
- */
1364
- export declare interface VmFsForContext {
1365
- mkdir: (dirPath: string, options?: any) => Promise<void>;
1366
- writeFile: (filePath: string, data: string | Uint8Array, options?: any) => Promise<void>;
1367
- }
14
+ export { PiOtelOptions }
1368
15
 
1369
16
  export { }