@themoltnet/pi-extension 0.14.0 → 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.
- package/dist/index.d.ts +121 -0
- package/dist/index.js +287 -29
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
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';
|
|
6
10
|
import { Skill } from '@earendil-works/pi-coding-agent';
|
|
7
11
|
import { Static } from '@sinclair/typebox';
|
|
@@ -27,6 +31,33 @@ import { WriteOperations } from '@earendil-works/pi-coding-agent';
|
|
|
27
31
|
*/
|
|
28
32
|
export declare function activateAgentEnv(agentEnv: Record<string, string | undefined>, repoRoot: string): void;
|
|
29
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
|
+
|
|
30
61
|
declare interface ClaimedTask {
|
|
31
62
|
/** The claimed task payload itself. */
|
|
32
63
|
task: Task;
|
|
@@ -83,6 +114,73 @@ export declare function createPiOtelExtension(options?: PiOtelOptions): (pi: Ext
|
|
|
83
114
|
*/
|
|
84
115
|
export declare function createPiTaskExecutor(opts: ExecutePiTaskOptions): (claimedTask: ClaimedTask, reporter: TaskReporter) => Promise<TaskOutput>;
|
|
85
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
|
+
|
|
86
184
|
/**
|
|
87
185
|
* Ensure a cached snapshot exists, building one if needed.
|
|
88
186
|
* Returns the absolute path to the qcow2 checkpoint file.
|
|
@@ -300,6 +398,29 @@ export declare interface SandboxConfig {
|
|
|
300
398
|
/** Extract snapshot-specific config for backwards compat with ensureSnapshot. */
|
|
301
399
|
export declare type SnapshotConfig = NonNullable<SandboxConfig['snapshot']>;
|
|
302
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
|
+
|
|
303
424
|
/**
|
|
304
425
|
* The Task promise body.
|
|
305
426
|
*
|
package/dist/index.js
CHANGED
|
@@ -8580,6 +8580,39 @@ function extractUsage(message) {
|
|
|
8580
8580
|
};
|
|
8581
8581
|
}
|
|
8582
8582
|
//#endregion
|
|
8583
|
+
//#region src/runtime/agent-session-factory.ts
|
|
8584
|
+
var NO_SKILLS = () => ({
|
|
8585
|
+
skills: [],
|
|
8586
|
+
diagnostics: []
|
|
8587
|
+
});
|
|
8588
|
+
/**
|
|
8589
|
+
* Construct an in-memory `AgentSession`. The caller is responsible for
|
|
8590
|
+
* eventually invoking `session.prompt(...)` and for tearing down — the
|
|
8591
|
+
* helper does no lifecycle management beyond construction.
|
|
8592
|
+
*/
|
|
8593
|
+
async function buildAgentSession(args) {
|
|
8594
|
+
const piOtelExtension = createPiOtelExtension({
|
|
8595
|
+
agentName: args.agentName,
|
|
8596
|
+
spanAttributes: args.otelSpanAttrs
|
|
8597
|
+
});
|
|
8598
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
8599
|
+
cwd: args.mountPath,
|
|
8600
|
+
agentDir: args.piAuthDir,
|
|
8601
|
+
extensionFactories: [piOtelExtension],
|
|
8602
|
+
appendSystemPrompt: args.appendSystemPrompt,
|
|
8603
|
+
skillsOverride: args.skillsOverride ?? NO_SKILLS
|
|
8604
|
+
});
|
|
8605
|
+
await resourceLoader.reload();
|
|
8606
|
+
return (await createAgentSession({
|
|
8607
|
+
agentDir: args.piAuthDir,
|
|
8608
|
+
cwd: args.mountPath,
|
|
8609
|
+
model: args.modelHandle,
|
|
8610
|
+
customTools: args.customTools,
|
|
8611
|
+
sessionManager: SessionManager.inMemory(),
|
|
8612
|
+
resourceLoader
|
|
8613
|
+
})).session;
|
|
8614
|
+
}
|
|
8615
|
+
//#endregion
|
|
8583
8616
|
//#region ../agent-runtime/src/context-bindings.ts
|
|
8584
8617
|
var PROMPT_SEPARATOR = "\n\n---\n\n";
|
|
8585
8618
|
/**
|
|
@@ -9440,6 +9473,15 @@ function validateTaskOutput(taskType, output, input) {
|
|
|
9440
9473
|
function getTaskOutputSchema(taskType) {
|
|
9441
9474
|
return getTaskTypeEntry(taskType)?.outputSchema ?? null;
|
|
9442
9475
|
}
|
|
9476
|
+
/**
|
|
9477
|
+
* Whether sessions running this task type should have the generic
|
|
9478
|
+
* `subagent` custom tool registered. Returns `false` for unknown task
|
|
9479
|
+
* types and for task types that didn't opt in. See `TaskTypeEntry`
|
|
9480
|
+
* for the design rationale.
|
|
9481
|
+
*/
|
|
9482
|
+
function taskTypeUsesSubagents(taskType) {
|
|
9483
|
+
return getTaskTypeEntry(taskType)?.usesSubagents === true;
|
|
9484
|
+
}
|
|
9443
9485
|
//#endregion
|
|
9444
9486
|
//#region ../tasks/src/wire.ts
|
|
9445
9487
|
/**
|
|
@@ -13935,6 +13977,25 @@ var require_multistream = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
13935
13977
|
module.exports.pino = pino;
|
|
13936
13978
|
})))();
|
|
13937
13979
|
//#endregion
|
|
13980
|
+
//#region ../agent-runtime/src/subagent-output-contracts.ts
|
|
13981
|
+
var REGISTRY = /* @__PURE__ */ new Map();
|
|
13982
|
+
/**
|
|
13983
|
+
* Resolve a subagent output contract by name. Returns `null` for
|
|
13984
|
+
* unknown names — callers (the subagent custom tool) decide whether
|
|
13985
|
+
* that's a tool error the parent LLM can recover from or a hard fail.
|
|
13986
|
+
*/
|
|
13987
|
+
function getSubagentOutputContract(name) {
|
|
13988
|
+
return REGISTRY.get(name) ?? null;
|
|
13989
|
+
}
|
|
13990
|
+
/**
|
|
13991
|
+
* List all registered contracts. Useful for diagnostics and for the
|
|
13992
|
+
* subagent tool's parameter description so a parent LLM can see what
|
|
13993
|
+
* contracts are available without enumerating them in its prompt.
|
|
13994
|
+
*/
|
|
13995
|
+
function listSubagentOutputContracts() {
|
|
13996
|
+
return [...REGISTRY.values()];
|
|
13997
|
+
}
|
|
13998
|
+
//#endregion
|
|
13938
13999
|
//#region src/runtime/inject-task-context.ts
|
|
13939
14000
|
/**
|
|
13940
14001
|
* Slice 1.5 of #943 — wire the agent-runtime resolver into the
|
|
@@ -14128,6 +14189,190 @@ function buildRuntimeInstructor(ctx) {
|
|
|
14128
14189
|
].join("\n");
|
|
14129
14190
|
}
|
|
14130
14191
|
//#endregion
|
|
14192
|
+
//#region src/runtime/subagent-tool.ts
|
|
14193
|
+
var SUBAGENT_SUBMIT_TOOL_NAME = "submit_subagent_output";
|
|
14194
|
+
/**
|
|
14195
|
+
* Parameters shape the parent LLM sees when calling the subagent tool.
|
|
14196
|
+
*
|
|
14197
|
+
* - `task` — natural-language instructions for the subagent.
|
|
14198
|
+
* The parent authors this per call. Must be
|
|
14199
|
+
* non-empty.
|
|
14200
|
+
* - `output_schema` — name of a registered SubagentOutputContract.
|
|
14201
|
+
* Resolved at call time; unknown names error.
|
|
14202
|
+
*/
|
|
14203
|
+
var SubagentToolParameters = Type$1.Object({
|
|
14204
|
+
task: Type$1.String({
|
|
14205
|
+
minLength: 1,
|
|
14206
|
+
description: "Natural-language instructions for the subagent. The subagent starts with a fresh conversation and a narrowed system prompt; this is the only context it has from you."
|
|
14207
|
+
}),
|
|
14208
|
+
output_schema: Type$1.String({
|
|
14209
|
+
minLength: 1,
|
|
14210
|
+
description: "Name of a registered subagent output contract. The subagent must submit a structured payload via `submit_subagent_output` matching this contract."
|
|
14211
|
+
})
|
|
14212
|
+
}, { additionalProperties: false });
|
|
14213
|
+
var DEFAULT_SUBAGENT_TIMEOUT_MS = 300 * 1e3;
|
|
14214
|
+
/**
|
|
14215
|
+
* Build the subagent custom tool for a parent session. The handle
|
|
14216
|
+
* exposes the call counter so executors can emit summary telemetry
|
|
14217
|
+
* when the parent terminates.
|
|
14218
|
+
*/
|
|
14219
|
+
function createSubagentTool(args) {
|
|
14220
|
+
const buildSession = args.buildAgentSession ?? buildAgentSession;
|
|
14221
|
+
let callCount = 0;
|
|
14222
|
+
return {
|
|
14223
|
+
tool: defineTool({
|
|
14224
|
+
name: "subagent",
|
|
14225
|
+
label: "Delegate to subagent",
|
|
14226
|
+
description: subagentToolDescription(),
|
|
14227
|
+
parameters: SubagentToolParameters,
|
|
14228
|
+
async execute(_id, params) {
|
|
14229
|
+
if (!Value.Check(SubagentToolParameters, params)) return toolError(`subagent: invalid parameters: ${JSON.stringify([...Value.Errors(SubagentToolParameters, params)].slice(0, 3))}`);
|
|
14230
|
+
const { task, output_schema } = params;
|
|
14231
|
+
const contract = getSubagentOutputContract(output_schema);
|
|
14232
|
+
if (!contract) return toolError(`subagent: unknown output_schema "${output_schema}". Registered contracts: [${listSubagentOutputContracts().map((c) => c.name).join(", ")}]`);
|
|
14233
|
+
callCount += 1;
|
|
14234
|
+
const callIndex = callCount;
|
|
14235
|
+
let captured = null;
|
|
14236
|
+
const submitTool = defineTool({
|
|
14237
|
+
name: SUBAGENT_SUBMIT_TOOL_NAME,
|
|
14238
|
+
label: `Submit ${output_schema}`,
|
|
14239
|
+
description: `Submit your structured output for this subagent task. Call exactly once when done. Args MUST match the ${output_schema} contract; mismatches return a tool error you can recover from in the same session.`,
|
|
14240
|
+
parameters: contract.parametersSchema,
|
|
14241
|
+
async execute(_innerId, innerParams) {
|
|
14242
|
+
if (!Value.Check(contract.parametersSchema, innerParams)) return toolError(`submit_subagent_output: schema validation failed: ${[...Value.Errors(contract.parametersSchema, innerParams)].slice(0, 3).map((e) => `${e.path}: ${e.message}`).join("; ")}. Re-call with a corrected payload.`);
|
|
14243
|
+
captured = innerParams;
|
|
14244
|
+
return {
|
|
14245
|
+
content: [{
|
|
14246
|
+
type: "text",
|
|
14247
|
+
text: "Output captured. Subagent session will terminate; no further action needed."
|
|
14248
|
+
}],
|
|
14249
|
+
details: { captured: true },
|
|
14250
|
+
terminate: true
|
|
14251
|
+
};
|
|
14252
|
+
}
|
|
14253
|
+
});
|
|
14254
|
+
const subagentInstructor = buildSubagentInstructor({
|
|
14255
|
+
contractName: output_schema,
|
|
14256
|
+
contractDescription: contract.description,
|
|
14257
|
+
parentTaskId: args.parentTaskId,
|
|
14258
|
+
callIndex
|
|
14259
|
+
});
|
|
14260
|
+
const session = await buildSession({
|
|
14261
|
+
mountPath: args.mountPath,
|
|
14262
|
+
piAuthDir: args.piAuthDir,
|
|
14263
|
+
modelHandle: args.modelHandle,
|
|
14264
|
+
agentName: args.agentName,
|
|
14265
|
+
customTools: [...args.inheritedCustomTools, submitTool],
|
|
14266
|
+
appendSystemPrompt: [args.parentRuntimeInstructor, subagentInstructor],
|
|
14267
|
+
skillsOverride: () => ({
|
|
14268
|
+
skills: [],
|
|
14269
|
+
diagnostics: []
|
|
14270
|
+
}),
|
|
14271
|
+
otelSpanAttrs: {
|
|
14272
|
+
"moltnet.task.id": args.parentTaskId,
|
|
14273
|
+
"moltnet.task.type": args.parentTaskType,
|
|
14274
|
+
"moltnet.task.attempt": args.parentAttemptN,
|
|
14275
|
+
"moltnet.subagent.contract": output_schema,
|
|
14276
|
+
"moltnet.subagent.index": callIndex
|
|
14277
|
+
}
|
|
14278
|
+
});
|
|
14279
|
+
let abortReason = null;
|
|
14280
|
+
let abortInvoked = false;
|
|
14281
|
+
const fireAbort = (reason) => {
|
|
14282
|
+
if (abortInvoked) return;
|
|
14283
|
+
abortInvoked = true;
|
|
14284
|
+
abortReason = reason;
|
|
14285
|
+
session.abort().catch((err) => {
|
|
14286
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
14287
|
+
process.stderr.write(`[subagent] inner session.abort() failed: ${message}\n`);
|
|
14288
|
+
});
|
|
14289
|
+
};
|
|
14290
|
+
const cancelListener = args.parentCancelSignal ? (() => {
|
|
14291
|
+
const signal = args.parentCancelSignal;
|
|
14292
|
+
const listener = () => fireAbort("parent_cancelled");
|
|
14293
|
+
if (signal.aborted) listener();
|
|
14294
|
+
else signal.addEventListener("abort", listener, { once: true });
|
|
14295
|
+
return () => signal.removeEventListener("abort", listener);
|
|
14296
|
+
})() : null;
|
|
14297
|
+
const timeoutMs = args.timeoutMs === void 0 || args.timeoutMs < 0 ? DEFAULT_SUBAGENT_TIMEOUT_MS : args.timeoutMs;
|
|
14298
|
+
const timeoutHandle = timeoutMs > 0 ? setTimeout(() => fireAbort("subagent_timed_out"), timeoutMs) : null;
|
|
14299
|
+
try {
|
|
14300
|
+
await session.prompt(task);
|
|
14301
|
+
} catch (err) {
|
|
14302
|
+
return toolError(`subagent: inner session.prompt() threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
14303
|
+
} finally {
|
|
14304
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
14305
|
+
if (cancelListener) cancelListener();
|
|
14306
|
+
}
|
|
14307
|
+
if (abortReason !== null) return toolError(`subagent: ${abortReason === "subagent_timed_out" ? `subagent timed out after ${timeoutMs}ms` : "parent task was cancelled"}. The parent should fail this task or retry with a clearer scope.`);
|
|
14308
|
+
if (captured === null) return toolError(`subagent: inner session ended without calling ${SUBAGENT_SUBMIT_TOOL_NAME}. The parent should retry with clearer instructions or fail the task.`);
|
|
14309
|
+
return {
|
|
14310
|
+
content: [{
|
|
14311
|
+
type: "text",
|
|
14312
|
+
text: JSON.stringify(captured)
|
|
14313
|
+
}],
|
|
14314
|
+
details: {
|
|
14315
|
+
captured: true,
|
|
14316
|
+
contract: output_schema,
|
|
14317
|
+
callIndex
|
|
14318
|
+
}
|
|
14319
|
+
};
|
|
14320
|
+
}
|
|
14321
|
+
}),
|
|
14322
|
+
getCallCount: () => callCount
|
|
14323
|
+
};
|
|
14324
|
+
}
|
|
14325
|
+
function subagentToolDescription() {
|
|
14326
|
+
return [
|
|
14327
|
+
"Delegate a sub-task to a fresh subagent session with isolated context.",
|
|
14328
|
+
"",
|
|
14329
|
+
"The subagent starts with no conversation history and only the `task` ",
|
|
14330
|
+
"string you provide as its instructions. It runs in the same VM with ",
|
|
14331
|
+
"the same tools you have (Gondolin-routed Read/Write/Edit/Bash, ",
|
|
14332
|
+
"moltnet_* tools), and is expected to call ",
|
|
14333
|
+
`\`${SUBAGENT_SUBMIT_TOOL_NAME}\` with a payload matching the named `,
|
|
14334
|
+
"contract before its session ends.",
|
|
14335
|
+
"",
|
|
14336
|
+
"On success, the tool result is the JSON-stringified subagent payload.",
|
|
14337
|
+
"On failure (unknown contract, validation error, subagent did not ",
|
|
14338
|
+
"submit) the tool returns isError:true with a recoverable message."
|
|
14339
|
+
].join("\n");
|
|
14340
|
+
}
|
|
14341
|
+
function buildSubagentInstructor(args) {
|
|
14342
|
+
return [
|
|
14343
|
+
"# You are a subagent",
|
|
14344
|
+
"",
|
|
14345
|
+
`Parent task: \`${args.parentTaskId}\` (subagent call #${args.callIndex}).`,
|
|
14346
|
+
"",
|
|
14347
|
+
`Your assigned output contract is \`${args.contractName}\`:`,
|
|
14348
|
+
`${args.contractDescription}`,
|
|
14349
|
+
"",
|
|
14350
|
+
"Rules for this session:",
|
|
14351
|
+
"",
|
|
14352
|
+
`- You MUST call \`${SUBAGENT_SUBMIT_TOOL_NAME}\` exactly once with a `,
|
|
14353
|
+
" payload matching the contract above. Your session terminates on ",
|
|
14354
|
+
" the valid call.",
|
|
14355
|
+
"- The parent's message above is your task. Do not invent additional ",
|
|
14356
|
+
" steps the parent did not request.",
|
|
14357
|
+
"- All MoltNet runtime invariants from the parent runtime instructor ",
|
|
14358
|
+
" apply (diary discipline, gh-auth pattern, etc.) IF you take any ",
|
|
14359
|
+
" action that would trigger them. Most subagents do not commit code ",
|
|
14360
|
+
" or open PRs — only do so if your task message explicitly requires it.",
|
|
14361
|
+
"- You do NOT have access to the `subagent` tool. Do not attempt nested ",
|
|
14362
|
+
" delegation; do the work yourself."
|
|
14363
|
+
].join("\n");
|
|
14364
|
+
}
|
|
14365
|
+
function toolError(text) {
|
|
14366
|
+
return {
|
|
14367
|
+
content: [{
|
|
14368
|
+
type: "text",
|
|
14369
|
+
text
|
|
14370
|
+
}],
|
|
14371
|
+
details: { captured: false },
|
|
14372
|
+
isError: true
|
|
14373
|
+
};
|
|
14374
|
+
}
|
|
14375
|
+
//#endregion
|
|
14131
14376
|
//#region src/runtime/task-output.ts
|
|
14132
14377
|
var METER_NAME = "@themoltnet/pi-extension/task-output";
|
|
14133
14378
|
var parseResultCounter = null;
|
|
@@ -14439,6 +14684,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
14439
14684
|
const taskTeamId = task.teamId ?? "";
|
|
14440
14685
|
let reporterOpen = false;
|
|
14441
14686
|
let session = null;
|
|
14687
|
+
let subagentHandle = null;
|
|
14442
14688
|
const finalUsage = emptyUsage(opts.provider, opts.model);
|
|
14443
14689
|
let cancelListener = null;
|
|
14444
14690
|
const makeFailedOutput = (code, message, usage = finalUsage) => ({
|
|
@@ -14556,47 +14802,55 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
14556
14802
|
});
|
|
14557
14803
|
const piAuthDir = process.env.PI_CODING_AGENT_DIR ?? join(homedir(), ".pi", "agent");
|
|
14558
14804
|
const modelHandle = getModel(opts.provider, opts.model);
|
|
14559
|
-
const
|
|
14560
|
-
agentName: opts.agentName,
|
|
14561
|
-
spanAttributes: {
|
|
14562
|
-
"moltnet.task.id": task.id,
|
|
14563
|
-
"moltnet.task.attempt": attemptN,
|
|
14564
|
-
"moltnet.task.type": task.taskType
|
|
14565
|
-
}
|
|
14566
|
-
});
|
|
14567
|
-
const appendSystemPrompt = [buildRuntimeInstructor({
|
|
14805
|
+
const runtimeInstructor = buildRuntimeInstructor({
|
|
14568
14806
|
taskId: task.id,
|
|
14569
14807
|
taskType: task.taskType,
|
|
14570
14808
|
attemptN,
|
|
14571
14809
|
diaryId,
|
|
14572
14810
|
agentName: opts.agentName,
|
|
14573
14811
|
correlationId: task.correlationId ?? null
|
|
14574
|
-
})
|
|
14812
|
+
});
|
|
14813
|
+
const appendSystemPrompt = [runtimeInstructor];
|
|
14575
14814
|
if (injectedContext.systemPromptPrefix) appendSystemPrompt.push(injectedContext.systemPromptPrefix);
|
|
14576
14815
|
const injectedSkills = injectedContext.skills;
|
|
14577
|
-
const
|
|
14578
|
-
|
|
14579
|
-
|
|
14580
|
-
|
|
14816
|
+
const parentSubagentTools = [];
|
|
14817
|
+
if (taskTypeUsesSubagents(task.taskType)) {
|
|
14818
|
+
subagentHandle = createSubagentTool({
|
|
14819
|
+
mountPath,
|
|
14820
|
+
piAuthDir,
|
|
14821
|
+
modelHandle,
|
|
14822
|
+
agentName: opts.agentName,
|
|
14823
|
+
inheritedCustomTools: [...gondolinCustomTools, ...moltnetTools],
|
|
14824
|
+
parentRuntimeInstructor: runtimeInstructor,
|
|
14825
|
+
parentTaskId: task.id,
|
|
14826
|
+
parentTaskType: task.taskType,
|
|
14827
|
+
parentAttemptN: attemptN,
|
|
14828
|
+
parentCancelSignal: reporter.cancelSignal
|
|
14829
|
+
});
|
|
14830
|
+
parentSubagentTools.push(subagentHandle.tool);
|
|
14831
|
+
}
|
|
14832
|
+
session = await buildAgentSession({
|
|
14833
|
+
mountPath,
|
|
14834
|
+
piAuthDir,
|
|
14835
|
+
modelHandle,
|
|
14836
|
+
agentName: opts.agentName,
|
|
14837
|
+
customTools: [
|
|
14838
|
+
...gondolinCustomTools,
|
|
14839
|
+
...moltnetTools,
|
|
14840
|
+
...submitTools,
|
|
14841
|
+
...parentSubagentTools
|
|
14842
|
+
],
|
|
14581
14843
|
appendSystemPrompt,
|
|
14582
14844
|
skillsOverride: () => ({
|
|
14583
14845
|
skills: injectedSkills,
|
|
14584
14846
|
diagnostics: []
|
|
14585
|
-
})
|
|
14847
|
+
}),
|
|
14848
|
+
otelSpanAttrs: {
|
|
14849
|
+
"moltnet.task.id": task.id,
|
|
14850
|
+
"moltnet.task.attempt": attemptN,
|
|
14851
|
+
"moltnet.task.type": task.taskType
|
|
14852
|
+
}
|
|
14586
14853
|
});
|
|
14587
|
-
await resourceLoader.reload();
|
|
14588
|
-
session = (await createAgentSession({
|
|
14589
|
-
agentDir: piAuthDir,
|
|
14590
|
-
cwd: mountPath,
|
|
14591
|
-
model: modelHandle,
|
|
14592
|
-
customTools: [
|
|
14593
|
-
...gondolinCustomTools,
|
|
14594
|
-
...moltnetTools,
|
|
14595
|
-
...submitTools
|
|
14596
|
-
],
|
|
14597
|
-
sessionManager: SessionManager.inMemory(),
|
|
14598
|
-
resourceLoader
|
|
14599
|
-
})).session;
|
|
14600
14854
|
} catch (err) {
|
|
14601
14855
|
const message = err instanceof Error ? err.message : String(err);
|
|
14602
14856
|
await emit("error", {
|
|
@@ -14667,6 +14921,10 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
14667
14921
|
phase: "session_prompt"
|
|
14668
14922
|
});
|
|
14669
14923
|
}
|
|
14924
|
+
if (subagentHandle && subagentHandle.getCallCount() > 0) await emit("info", {
|
|
14925
|
+
event: "subagent_summary",
|
|
14926
|
+
callCount: subagentHandle.getCallCount()
|
|
14927
|
+
});
|
|
14670
14928
|
await Promise.all(recordingPromise);
|
|
14671
14929
|
const cancelled = reporter.cancelSignal.aborted;
|
|
14672
14930
|
let parsedOutput = null;
|
|
@@ -15126,4 +15384,4 @@ function moltnetExtension(pi) {
|
|
|
15126
15384
|
registerMoltnetReflectCommand(pi, state);
|
|
15127
15385
|
}
|
|
15128
15386
|
//#endregion
|
|
15129
|
-
export { HOST_EXEC_DEFAULT_BASE_ENV, activateAgentEnv, createGondolinBashOps, createGondolinEditOps, createGondolinReadOps, createGondolinWriteOps, createMoltNetTools, createPiOtelExtension, createPiTaskExecutor, moltnetExtension as default, ensureSnapshot, executePiTask, findMainWorktree, injectTaskContext, loadCredentials, resumeVm, toGuestPath };
|
|
15387
|
+
export { HOST_EXEC_DEFAULT_BASE_ENV, activateAgentEnv, buildAgentSession, createGondolinBashOps, createGondolinEditOps, createGondolinReadOps, createGondolinWriteOps, createMoltNetTools, createPiOtelExtension, createPiTaskExecutor, createSubagentTool, moltnetExtension as default, ensureSnapshot, executePiTask, findMainWorktree, injectTaskContext, loadCredentials, resumeVm, toGuestPath };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/pi-extension",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MoltNet pi extension — sandboxed tool execution in Gondolin VMs with MoltNet identity and persistent memory",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@earendil-works/gondolin": "^0.9.1",
|
|
32
32
|
"@opentelemetry/api": "^1.9.0",
|
|
33
33
|
"@sinclair/typebox": "^0.34.0",
|
|
34
|
-
"@themoltnet/agent-runtime": "0.
|
|
34
|
+
"@themoltnet/agent-runtime": "0.13.0",
|
|
35
35
|
"@themoltnet/sdk": "0.100.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|