@themoltnet/pi-extension 0.13.5 → 0.15.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.
Files changed (3) hide show
  1. package/dist/index.d.ts +228 -1
  2. package/dist/index.js +913 -188
  3. package/package.json +3 -3
package/dist/index.d.ts CHANGED
@@ -1,8 +1,13 @@
1
+ import { AgentSession } from '@earendil-works/pi-coding-agent';
2
+ import { Api } from '@earendil-works/pi-ai';
1
3
  import { BashOperations } from '@earendil-works/pi-coding-agent';
2
4
  import { connect } from '@themoltnet/sdk';
3
5
  import { EditOperations } from '@earendil-works/pi-coding-agent';
4
6
  import { ExtensionAPI } from '@earendil-works/pi-coding-agent';
7
+ import { LoadSkillsResult } from '@earendil-works/pi-coding-agent';
8
+ import { Model } from '@earendil-works/pi-ai';
5
9
  import { ReadOperations } from '@earendil-works/pi-coding-agent';
10
+ import { Skill } from '@earendil-works/pi-coding-agent';
6
11
  import { Static } from '@sinclair/typebox';
7
12
  import { TArray } from '@sinclair/typebox';
8
13
  import { TBoolean } from '@sinclair/typebox';
@@ -26,6 +31,33 @@ import { WriteOperations } from '@earendil-works/pi-coding-agent';
26
31
  */
27
32
  export declare function activateAgentEnv(agentEnv: Record<string, string | undefined>, repoRoot: string): void;
28
33
 
34
+ /**
35
+ * Construct an in-memory `AgentSession`. The caller is responsible for
36
+ * eventually invoking `session.prompt(...)` and for tearing down — the
37
+ * helper does no lifecycle management beyond construction.
38
+ */
39
+ export declare function buildAgentSession(args: BuildAgentSessionArgs): Promise<AgentSession>;
40
+
41
+ declare interface BuildAgentSessionArgs {
42
+ /** Host directory mounted at /workspace inside the VM. */
43
+ mountPath: string;
44
+ /** pi auth directory (resolved from `PI_CODING_AGENT_DIR` or `~/.pi/agent`). */
45
+ piAuthDir: string;
46
+ /** Resolved pi model handle (provider + model id). */
47
+ modelHandle: Model<Api>;
48
+ /** Pre-built customTools array. Caller composes Gondolin + MoltNet + submit tools. */
49
+ customTools: ToolDefinition[];
50
+ /** System-prompt fragments appended after pi's defaults. Parent passes the
51
+ * runtime instructor; subagents pass their narrower variant. */
52
+ appendSystemPrompt: string[];
53
+ /** Skills to advertise in `<available_skills>`. Default: empty list. */
54
+ skillsOverride?: () => LoadSkillsResult;
55
+ /** Span attributes merged onto every OTel span the session emits. */
56
+ otelSpanAttrs: Record<string, string | number | boolean>;
57
+ /** Agent name for `gen_ai.agent.name` on the root span. */
58
+ agentName: string;
59
+ }
60
+
29
61
  declare interface ClaimedTask {
30
62
  /** The claimed task payload itself. */
31
63
  task: Task;
@@ -35,6 +67,31 @@ declare interface ClaimedTask {
35
67
  traceHeaders: Record<string, string>;
36
68
  }
37
69
 
70
+ /**
71
+ * One context entry. Bytes are inlined: the imposer chose them, and the
72
+ * task's `inputCid` already pins the entire input — including
73
+ * `context[]` — so we don't need a separate per-entry hash, fetcher, or
74
+ * flagged-content gate. Tasks reference rendered packs (or any other
75
+ * external content) by copying their bytes into `content` at task
76
+ * creation time.
77
+ *
78
+ * - `slug` — short identifier the daemon uses to disambiguate
79
+ * entries. For `skill` binding it becomes the directory
80
+ * name under the runtime's skill discovery path. Must be
81
+ * kebab-case-safe (alphanumeric + dashes/underscores).
82
+ * - `binding` — how the bytes are delivered to the LLM (see above).
83
+ * - `content` — the actual bytes (UTF-8 text). Capped at 32 KiB per
84
+ * entry; total per-task context bytes are bounded by the
85
+ * soft `maxItems` cap and per-binding daemon limits.
86
+ */
87
+ declare const ContextRef: TObject< {
88
+ slug: TString;
89
+ binding: TUnion<[TLiteral<"skill">, TLiteral<"prompt_prefix">, TLiteral<"user_inline">]>;
90
+ content: TString;
91
+ }>;
92
+
93
+ declare type ContextRef = Static<typeof ContextRef>;
94
+
38
95
  export declare function createGondolinBashOps(vm: VM, localCwd: string): BashOperations;
39
96
 
40
97
  export declare function createGondolinEditOps(vm: VM, localCwd: string): EditOperations;
@@ -57,6 +114,73 @@ export declare function createPiOtelExtension(options?: PiOtelOptions): (pi: Ext
57
114
  */
58
115
  export declare function createPiTaskExecutor(opts: ExecutePiTaskOptions): (claimedTask: ClaimedTask, reporter: TaskReporter) => Promise<TaskOutput>;
59
116
 
117
+ /**
118
+ * Build the subagent custom tool for a parent session. The handle
119
+ * exposes the call counter so executors can emit summary telemetry
120
+ * when the parent terminates.
121
+ */
122
+ export declare function createSubagentTool(args: CreateSubagentToolArgs): SubagentToolHandle;
123
+
124
+ export declare interface CreateSubagentToolArgs {
125
+ /** Host directory mounted at /workspace inside the VM. */
126
+ mountPath: string;
127
+ /** pi auth directory the parent resolved. */
128
+ piAuthDir: string;
129
+ /** Resolved pi model handle — subagents share it. */
130
+ modelHandle: Model<Api>;
131
+ /** Agent name for telemetry. */
132
+ agentName: string;
133
+ /**
134
+ * Custom tools every subagent inherits (Gondolin-routed
135
+ * Read/Write/Edit/Bash + moltnet_* tools, etc). MUST NOT include
136
+ * the parent's submit-output tool, the parent's `subagent` tool,
137
+ * or any other parent-only artefact — the caller is responsible
138
+ * for filtering. The subagent appends its own submit tool.
139
+ */
140
+ inheritedCustomTools: ToolDefinition[];
141
+ /**
142
+ * The parent runtime instructor verbatim. Subagents prepend it to
143
+ * their own short "you are a subagent" preamble so the same
144
+ * invariants (gh auth, diary discipline, accountable commits)
145
+ * apply if the subagent takes those actions. The parent's task
146
+ * description dictates whether they should.
147
+ */
148
+ parentRuntimeInstructor: string;
149
+ parentTaskId: string;
150
+ parentTaskType: string;
151
+ parentAttemptN: number;
152
+ /**
153
+ * Parent task's cancel signal. When the daemon cancels the parent
154
+ * task (operator cancel or task-level `runningTimeoutSec` expiry),
155
+ * each in-flight subagent's inner `session.abort()` is invoked so
156
+ * it tears down promptly instead of running until its own LLM
157
+ * call resolves. Mirrors the existing `wireSessionAbort` pattern
158
+ * the parent session uses.
159
+ *
160
+ * Optional only because the test seam can omit it; production
161
+ * callers (executePiTask) pass `reporter.cancelSignal`.
162
+ */
163
+ parentCancelSignal?: AbortSignal;
164
+ /**
165
+ * Per-call fallback timeout. Defends against an inner session that
166
+ * ignores `abort()` for any reason (LLM provider stuck, tool call
167
+ * hanging on I/O, etc.). When the timeout fires, `session.abort()`
168
+ * is invoked and the tool returns `isError: true` with a
169
+ * `subagent_timed_out` reason the parent LLM can recover from.
170
+ *
171
+ * Default: 5 minutes. Set to `0` to disable (relying purely on
172
+ * parentCancelSignal). Negative values are treated as the default.
173
+ */
174
+ timeoutMs?: number;
175
+ /**
176
+ * Test seam. Production callers leave this undefined and get
177
+ * `buildAgentSession` from the factory module. Tests inject a mock
178
+ * that returns a stub session implementing only `prompt()` to
179
+ * exercise the tool's logic without booting a VM.
180
+ */
181
+ buildAgentSession?: (args: BuildAgentSessionArgs) => Promise<AgentSession>;
182
+ }
183
+
60
184
  /**
61
185
  * Ensure a cached snapshot exists, building one if needed.
62
186
  * Returns the absolute path to the qcow2 checkpoint file.
@@ -91,7 +215,7 @@ export declare interface ExecutePiTaskOptions {
91
215
  /** Sandbox overrides (env, VFS shadows, resources). */
92
216
  sandboxConfig?: SandboxConfig;
93
217
  /**
94
- * Forwarded to `buildPromptForTask` for per-type builders. Static
218
+ * Forwarded to `buildTaskUserPrompt` for per-type builders. Static
95
219
  * across tasks. Today no built-in builder needs per-task `extras` —
96
220
  * judges fetch their own dependent data via MoltNet tools
97
221
  * (`moltnet_get_task`, `moltnet_list_task_attempts`, etc.) at run
@@ -107,6 +231,24 @@ export declare interface ExecutePiTaskOptions {
107
231
  * across tasks.
108
232
  */
109
233
  checkpointPath?: string;
234
+ /**
235
+ * Optional callback invoked alongside every `reporter.record()` so
236
+ * the daemon can mirror task messages into its local logger.
237
+ * Bound at executor-construction time — use when one task runs per
238
+ * process (e.g. `once.ts`) and per-task context is known before
239
+ * the executor is built. For poll mode, prefer `makeOnTurnEvent`
240
+ * below. If both are set, `makeOnTurnEvent` wins.
241
+ * See `TurnEventHandler` for payload shape. Defaults to a no-op.
242
+ */
243
+ onTurnEvent?: TurnEventHandler;
244
+ /**
245
+ * Per-task factory variant for `onTurnEvent`. Invoked once per
246
+ * task with the claimed task before any emit, so the returned
247
+ * handler can bind taskId / attemptN into a pino child.
248
+ * Use in poll mode where N tasks run sequentially in the same
249
+ * process. See #1078.
250
+ */
251
+ makeOnTurnEvent?: TurnEventHandlerFactory;
110
252
  }
111
253
 
112
254
  /**
@@ -121,6 +263,32 @@ export declare function findMainWorktree(): string;
121
263
  */
122
264
  export declare const HOST_EXEC_DEFAULT_BASE_ENV: ReadonlySet<string>;
123
265
 
266
+ export declare interface InjectedTaskContext {
267
+ /** Refs that were delivered, in declared order, for audit. */
268
+ injected: ContextRef[];
269
+ /** Synthetic Skill objects to splice into pi's skillsOverride. */
270
+ skills: Skill[];
271
+ /** Prepend this to `appendSystemPrompt`. Empty when nothing
272
+ * contributed (omit the array entry rather than pass an empty
273
+ * string to keep pi's prompt assembly tidy). */
274
+ systemPromptPrefix: string;
275
+ /** Append this to the task user prompt BEFORE `session.prompt()`. */
276
+ userInlineSuffix: string;
277
+ }
278
+
279
+ /**
280
+ * Resolve a task's `input.context[]` and inject the side effects pi
281
+ * needs. Safe to call with an empty array — returns an inert result.
282
+ */
283
+ export declare function injectTaskContext(args: InjectTaskContextArgs): Promise<InjectedTaskContext>;
284
+
285
+ export declare interface InjectTaskContextArgs {
286
+ /** Empty array (the default for any non-eval task) is a no-op. */
287
+ context: TaskContext;
288
+ /** Guest filesystem handle. In production this is `managed.vm.fs`. */
289
+ fs: VmFsForContext;
290
+ }
291
+
124
292
  export declare function loadCredentials(agentDir: string): VmCredentials;
125
293
 
126
294
  export declare interface ManagedVm {
@@ -230,6 +398,29 @@ export declare interface SandboxConfig {
230
398
  /** Extract snapshot-specific config for backwards compat with ensureSnapshot. */
231
399
  export declare type SnapshotConfig = NonNullable<SandboxConfig['snapshot']>;
232
400
 
401
+ export declare interface SubagentToolHandle {
402
+ /** ToolDefinition to register via `customTools` on the parent session. */
403
+ readonly tool: ToolDefinition;
404
+ /** How many times the parent LLM has called this tool. */
405
+ getCallCount: () => number;
406
+ }
407
+
408
+ /**
409
+ * Parameters shape the parent LLM sees when calling the subagent tool.
410
+ *
411
+ * - `task` — natural-language instructions for the subagent.
412
+ * The parent authors this per call. Must be
413
+ * non-empty.
414
+ * - `output_schema` — name of a registered SubagentOutputContract.
415
+ * Resolved at call time; unknown names error.
416
+ */
417
+ export declare const SubagentToolParameters: TObject<{
418
+ task: TString;
419
+ output_schema: TString;
420
+ }>;
421
+
422
+ export declare type SubagentToolParameters = Static<typeof SubagentToolParameters>;
423
+
233
424
  /**
234
425
  * The Task promise body.
235
426
  *
@@ -264,6 +455,10 @@ declare const Task: TObject< {
264
455
  imposedByHumanId: TUnion<[TString, TNull]>;
265
456
  acceptedAttemptN: TUnion<[TNumber, TNull]>;
266
457
  requiredExecutorTrustLevel: TUnion<[TLiteral<"selfDeclared">, TLiteral<"agentSigned">, TLiteral<"releaseVerifiedTool">, TLiteral<"sandboxAttested">]>;
458
+ allowedExecutors: TArray<TObject< {
459
+ provider: TString;
460
+ model: TString;
461
+ }>>;
267
462
  status: TUnion<[TLiteral<"queued">, TLiteral<"dispatched">, TLiteral<"running">, TLiteral<"completed">, TLiteral<"failed">, TLiteral<"cancelled">, TLiteral<"expired">]>;
268
463
  queuedAt: TString;
269
464
  completedAt: TUnion<[TString, TNull]>;
@@ -278,6 +473,15 @@ declare const Task: TObject< {
278
473
 
279
474
  declare type Task = Static<typeof Task>;
280
475
 
476
+ /** Reusable input fragment for any task type. Soft cap at 5 items. */
477
+ declare const TaskContext: TArray<TObject< {
478
+ slug: TString;
479
+ binding: TUnion<[TLiteral<"skill">, TLiteral<"prompt_prefix">, TLiteral<"user_inline">]>;
480
+ content: TString;
481
+ }>>;
482
+
483
+ declare type TaskContext = Static<typeof TaskContext>;
484
+
281
485
  declare const TaskMessage: TObject< {
282
486
  taskId: TString;
283
487
  attemptN: TNumber;
@@ -410,6 +614,14 @@ declare interface TrackedError {
410
614
  timestamp: number;
411
615
  }
412
616
 
617
+ export declare interface TurnEventHandler {
618
+ (event: TurnEventKind, summary: Record<string, unknown>): void;
619
+ }
620
+
621
+ export declare type TurnEventHandlerFactory = (claimedTask: ClaimedTask) => TurnEventHandler;
622
+
623
+ export declare type TurnEventKind = Parameters<TaskReporter['record']>[0]['kind'];
624
+
413
625
  export declare interface VmConfig {
414
626
  /** Absolute path to the qcow2 checkpoint. */
415
627
  checkpointPath: string;
@@ -444,4 +656,19 @@ export declare interface VmCredentials {
444
656
  githubAppPemFilename: string | null;
445
657
  }
446
658
 
659
+ /**
660
+ * Subset of `@earendil-works/gondolin`'s `VmFs` we actually use. We
661
+ * narrow the dependency surface so unit tests can hand in a
662
+ * vitest-mocked object without instantiating a real VM. We use `any`
663
+ * for the options parameter to make this interface bivariantly
664
+ * compatible with `VmFs` (whose options types differ between
665
+ * `mkdir` and `writeFile`); the orchestrator only ever calls these
666
+ * methods with the documented option shape, so the looseness is
667
+ * confined to this seam.
668
+ */
669
+ export declare interface VmFsForContext {
670
+ mkdir: (dirPath: string, options?: any) => Promise<void>;
671
+ writeFile: (filePath: string, data: string | Uint8Array, options?: any) => Promise<void>;
672
+ }
673
+
447
674
  export { }