@themoltnet/pi-extension 0.7.0 → 0.9.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 +23 -0
- package/dist/index.js +98 -8
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -272,6 +272,7 @@ declare const Task: TObject< {
|
|
|
272
272
|
imposedByAgentId: TUnion<[TString, TNull]>;
|
|
273
273
|
imposedByHumanId: TUnion<[TString, TNull]>;
|
|
274
274
|
acceptedAttemptN: TUnion<[TNumber, TNull]>;
|
|
275
|
+
requiredExecutorTrustLevel: TUnion<[TLiteral<"selfDeclared">, TLiteral<"agentSigned">, TLiteral<"releaseVerifiedTool">, TLiteral<"sandboxAttested">]>;
|
|
275
276
|
status: TUnion<[TLiteral<"queued">, TLiteral<"dispatched">, TLiteral<"running">, TLiteral<"completed">, TLiteral<"failed">, TLiteral<"cancelled">, TLiteral<"expired">]>;
|
|
276
277
|
queuedAt: TString;
|
|
277
278
|
completedAt: TUnion<[TString, TNull]>;
|
|
@@ -280,6 +281,8 @@ declare const Task: TObject< {
|
|
|
280
281
|
cancelledByHumanId: TUnion<[TString, TNull]>;
|
|
281
282
|
cancelReason: TUnion<[TString, TNull]>;
|
|
282
283
|
maxAttempts: TNumber;
|
|
284
|
+
dispatchTimeoutSec: TUnion<[TInteger, TNull]>;
|
|
285
|
+
runningTimeoutSec: TUnion<[TInteger, TNull]>;
|
|
283
286
|
}>;
|
|
284
287
|
|
|
285
288
|
declare type Task = Static<typeof Task>;
|
|
@@ -363,6 +366,26 @@ declare interface TaskReporter {
|
|
|
363
366
|
finalize(usage: TaskUsage): Promise<void>;
|
|
364
367
|
/** Flush buffers + release resources. Called once. Idempotent. */
|
|
365
368
|
close(): Promise<void>;
|
|
369
|
+
/**
|
|
370
|
+
* Signal that aborts when the task is cancelled by the imposer (or a
|
|
371
|
+
* diary writer) while the executor is running. `ApiTaskReporter`
|
|
372
|
+
* aborts this on the next heartbeat that observes `cancelled: true`
|
|
373
|
+
* in the response (#938). Local reporters (`StdoutReporter`,
|
|
374
|
+
* `JsonlTaskReporter`) never abort — there's no remote cancel
|
|
375
|
+
* channel for `FileTaskSource`.
|
|
376
|
+
*
|
|
377
|
+
* Executors should pass this signal into long-running work
|
|
378
|
+
* (LLM calls, sandbox execution, file ops) and surface a
|
|
379
|
+
* `status: 'cancelled'` output when it fires. The runtime also
|
|
380
|
+
* checks the signal post-execute and converts any output to
|
|
381
|
+
* `cancelled` if the executor returned without honoring it.
|
|
382
|
+
*/
|
|
383
|
+
readonly cancelSignal: AbortSignal;
|
|
384
|
+
/**
|
|
385
|
+
* The reason supplied to `/tasks/:id/cancel` by the canceller, if
|
|
386
|
+
* cancellation has been observed. Null until `cancelSignal` aborts.
|
|
387
|
+
*/
|
|
388
|
+
readonly cancelReason: string | null;
|
|
366
389
|
}
|
|
367
390
|
|
|
368
391
|
/**
|
package/dist/index.js
CHANGED
|
@@ -2536,6 +2536,7 @@ function createDiaryGrantsNamespace(context) {
|
|
|
2536
2536
|
}
|
|
2537
2537
|
};
|
|
2538
2538
|
}
|
|
2539
|
+
new TextEncoder();
|
|
2539
2540
|
//#endregion
|
|
2540
2541
|
//#region ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/utils.js
|
|
2541
2542
|
/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
|
|
@@ -4266,6 +4267,13 @@ etc.sha512Sync = (...m) => {
|
|
|
4266
4267
|
return hash.digest();
|
|
4267
4268
|
};
|
|
4268
4269
|
//#endregion
|
|
4270
|
+
//#region ../crypto-service/src/executor-attestation.ts
|
|
4271
|
+
etc.sha512Sync = (...m) => {
|
|
4272
|
+
const hash = createHash("sha512");
|
|
4273
|
+
m.forEach((msg) => hash.update(msg));
|
|
4274
|
+
return hash.digest();
|
|
4275
|
+
};
|
|
4276
|
+
//#endregion
|
|
4269
4277
|
//#region ../../node_modules/.pnpm/multiformats@13.4.2/node_modules/multiformats/dist/src/codecs/json.js
|
|
4270
4278
|
var textEncoder$1 = new TextEncoder();
|
|
4271
4279
|
new TextDecoder();
|
|
@@ -9224,6 +9232,12 @@ var TaskAttemptStatus = Type$1.Union([
|
|
|
9224
9232
|
Type$1.Literal("cancelled"),
|
|
9225
9233
|
Type$1.Literal("timed_out")
|
|
9226
9234
|
], { $id: "TaskAttemptStatus" });
|
|
9235
|
+
var ExecutorTrustLevel = Type$1.Union([
|
|
9236
|
+
Type$1.Literal("selfDeclared"),
|
|
9237
|
+
Type$1.Literal("agentSigned"),
|
|
9238
|
+
Type$1.Literal("releaseVerifiedTool"),
|
|
9239
|
+
Type$1.Literal("sandboxAttested")
|
|
9240
|
+
], { $id: "ExecutorTrustLevel" });
|
|
9227
9241
|
var OutputKind = Type$1.Union([Type$1.Literal("artifact"), Type$1.Literal("judgment")], { $id: "OutputKind" });
|
|
9228
9242
|
var TaskMessageKind = Type$1.Union([
|
|
9229
9243
|
Type$1.Literal("text_delta"),
|
|
@@ -9316,6 +9330,7 @@ Type$1.Object({
|
|
|
9316
9330
|
imposedByAgentId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
9317
9331
|
imposedByHumanId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
9318
9332
|
acceptedAttemptN: Type$1.Union([Type$1.Number(), Type$1.Null()]),
|
|
9333
|
+
requiredExecutorTrustLevel: ExecutorTrustLevel,
|
|
9319
9334
|
status: TaskStatus,
|
|
9320
9335
|
queuedAt: IsoTimestamp,
|
|
9321
9336
|
completedAt: Type$1.Union([IsoTimestamp, Type$1.Null()]),
|
|
@@ -9323,7 +9338,15 @@ Type$1.Object({
|
|
|
9323
9338
|
cancelledByAgentId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
9324
9339
|
cancelledByHumanId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
9325
9340
|
cancelReason: Type$1.Union([Type$1.String(), Type$1.Null()]),
|
|
9326
|
-
maxAttempts: Type$1.Number({ minimum: 1 })
|
|
9341
|
+
maxAttempts: Type$1.Number({ minimum: 1 }),
|
|
9342
|
+
dispatchTimeoutSec: Type$1.Union([Type$1.Integer({
|
|
9343
|
+
minimum: 1,
|
|
9344
|
+
maximum: 86400
|
|
9345
|
+
}), Type$1.Null()]),
|
|
9346
|
+
runningTimeoutSec: Type$1.Union([Type$1.Integer({
|
|
9347
|
+
minimum: 1,
|
|
9348
|
+
maximum: 86400
|
|
9349
|
+
}), Type$1.Null()])
|
|
9327
9350
|
}, {
|
|
9328
9351
|
$id: "Task",
|
|
9329
9352
|
additionalProperties: false
|
|
@@ -9339,6 +9362,10 @@ Type$1.Object({
|
|
|
9339
9362
|
status: TaskAttemptStatus,
|
|
9340
9363
|
output: Type$1.Union([Type$1.Record(Type$1.String(), Type$1.Unknown()), Type$1.Null()]),
|
|
9341
9364
|
outputCid: Type$1.Union([Cid, Type$1.Null()]),
|
|
9365
|
+
claimedExecutorFingerprint: Type$1.Union([Cid, Type$1.Null()]),
|
|
9366
|
+
claimedExecutorManifest: Type$1.Union([Type$1.Record(Type$1.String(), Type$1.Unknown()), Type$1.Null()]),
|
|
9367
|
+
completedExecutorFingerprint: Type$1.Union([Cid, Type$1.Null()]),
|
|
9368
|
+
completedExecutorManifest: Type$1.Union([Type$1.Record(Type$1.String(), Type$1.Unknown()), Type$1.Null()]),
|
|
9342
9369
|
error: Type$1.Union([TaskError, Type$1.Null()]),
|
|
9343
9370
|
usage: Type$1.Union([TaskUsage, Type$1.Null()]),
|
|
9344
9371
|
contentSignature: Type$1.Union([Type$1.String(), Type$1.Null()]),
|
|
@@ -9480,11 +9507,7 @@ function buildAssessBriefPrompt(input, ctx) {
|
|
|
9480
9507
|
*/
|
|
9481
9508
|
function buildCuratePackPrompt(input, ctx) {
|
|
9482
9509
|
const { diaryId, taskPrompt, entryTypes, tagFilters, tokenBudget, recipe } = input;
|
|
9483
|
-
const
|
|
9484
|
-
"semantic",
|
|
9485
|
-
"episodic",
|
|
9486
|
-
"procedural"
|
|
9487
|
-
];
|
|
9510
|
+
const entryTypesPinned = Boolean(entryTypes);
|
|
9488
9511
|
const resolvedRecipe = recipe ?? "topic-focused-v1";
|
|
9489
9512
|
const includeLine = tagFilters?.include?.length ? `- Hard include (ALL must be present on an entry): ${tagFilters.include.map((t) => `\`${t}\``).join(", ")}` : null;
|
|
9490
9513
|
const excludeLine = tagFilters?.exclude?.length ? `- Hard exclude (drop if ANY present): ${tagFilters.exclude.map((t) => `\`${t}\``).join(", ")}` : null;
|
|
@@ -9513,7 +9536,16 @@ function buildCuratePackPrompt(input, ctx) {
|
|
|
9513
9536
|
"",
|
|
9514
9537
|
"## Constraints",
|
|
9515
9538
|
"",
|
|
9516
|
-
`- Entry types
|
|
9539
|
+
entryTypesPinned ? `- Entry types pinned by imposer (do not widen): ${entryTypes.map((t) => `\`${t}\``).join(", ")}` : "- Entry types: **you choose**. The diary contains three kinds:",
|
|
9540
|
+
entryTypesPinned ? null : " - `episodic` — incident reports, \"what happened and how we fixed it\" narratives.",
|
|
9541
|
+
entryTypesPinned ? null : " - `semantic` — durable decisions, patterns, design rationale.",
|
|
9542
|
+
entryTypesPinned ? null : " - `procedural` — commit audit trails / changelog-style provenance.",
|
|
9543
|
+
entryTypesPinned ? null : " Pick the subset that fits the prompt. For \"failures and workarounds\"",
|
|
9544
|
+
entryTypesPinned ? null : " or \"decisions we made\" you generally do NOT want `procedural` — those",
|
|
9545
|
+
entryTypesPinned ? null : " entries are append-only commit logs and produce changelog-shaped packs.",
|
|
9546
|
+
entryTypesPinned ? null : " Include `procedural` only when the prompt explicitly asks for changelog-",
|
|
9547
|
+
entryTypesPinned ? null : " style content (e.g., \"what shipped this week\"). State your choice",
|
|
9548
|
+
entryTypesPinned ? null : " briefly in the final `summary`.",
|
|
9517
9549
|
`- Recipe tag: \`${resolvedRecipe}\` (recorded on pack params)`,
|
|
9518
9550
|
tokenBudget ? `- Token budget (soft cap on final pack): ${tokenBudget}. Pick entry count so the pack fits — estimate ~300 tok/entry as a starting heuristic, tighten after inspecting actual content lengths.` : "- No token budget — size the pack to match the prompt, not an arbitrary target.",
|
|
9519
9551
|
includeLine,
|
|
@@ -10018,6 +10050,20 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
10018
10050
|
const attemptN = claimedTask.attemptN;
|
|
10019
10051
|
const startTime = Date.now();
|
|
10020
10052
|
const mountPath = opts.mountPath ?? process.cwd();
|
|
10053
|
+
if (reporter.cancelSignal.aborted) return {
|
|
10054
|
+
taskId: task.id,
|
|
10055
|
+
attemptN,
|
|
10056
|
+
status: "cancelled",
|
|
10057
|
+
output: null,
|
|
10058
|
+
outputCid: null,
|
|
10059
|
+
usage: emptyUsage(opts.provider, opts.model),
|
|
10060
|
+
durationMs: Date.now() - startTime,
|
|
10061
|
+
error: {
|
|
10062
|
+
code: "task_cancelled",
|
|
10063
|
+
message: reporter.cancelReason ?? "Task cancelled before pi executor started.",
|
|
10064
|
+
retryable: false
|
|
10065
|
+
}
|
|
10066
|
+
};
|
|
10021
10067
|
const checkpointPath = opts.checkpointPath ?? await ensureSnapshot({
|
|
10022
10068
|
config: opts.sandboxConfig?.snapshot,
|
|
10023
10069
|
onProgress: opts.onSnapshotProgress ?? ((m) => {
|
|
@@ -10046,6 +10092,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
10046
10092
|
let reporterOpen = false;
|
|
10047
10093
|
let session = null;
|
|
10048
10094
|
const finalUsage = emptyUsage(opts.provider, opts.model);
|
|
10095
|
+
let cancelListener = null;
|
|
10049
10096
|
const makeFailedOutput = (code, message, usage = finalUsage) => ({
|
|
10050
10097
|
taskId: task.id,
|
|
10051
10098
|
attemptN,
|
|
@@ -10146,6 +10193,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
10146
10193
|
let assistantText = "";
|
|
10147
10194
|
let reporterError = null;
|
|
10148
10195
|
const usage = finalUsage;
|
|
10196
|
+
cancelListener = wireSessionAbort(reporter.cancelSignal, session);
|
|
10149
10197
|
const recordingPromise = [];
|
|
10150
10198
|
const track = (p) => {
|
|
10151
10199
|
recordingPromise.push(p.catch((err) => {
|
|
@@ -10201,10 +10249,11 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
10201
10249
|
});
|
|
10202
10250
|
}
|
|
10203
10251
|
await Promise.all(recordingPromise);
|
|
10252
|
+
const cancelled = reporter.cancelSignal.aborted;
|
|
10204
10253
|
let parsedOutput = null;
|
|
10205
10254
|
let parsedOutputCid = null;
|
|
10206
10255
|
let parseError = null;
|
|
10207
|
-
if (!runError && !llmAbort) {
|
|
10256
|
+
if (!runError && !llmAbort && !cancelled) {
|
|
10208
10257
|
const parsed = await parseStructuredTaskOutput(assistantText, task.taskType);
|
|
10209
10258
|
parsedOutput = parsed.output;
|
|
10210
10259
|
parsedOutputCid = parsed.outputCid;
|
|
@@ -10214,6 +10263,20 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
10214
10263
|
phase: "output_validation"
|
|
10215
10264
|
});
|
|
10216
10265
|
}
|
|
10266
|
+
if (cancelled) return {
|
|
10267
|
+
taskId: task.id,
|
|
10268
|
+
attemptN,
|
|
10269
|
+
status: "cancelled",
|
|
10270
|
+
output: null,
|
|
10271
|
+
outputCid: null,
|
|
10272
|
+
usage,
|
|
10273
|
+
durationMs: Date.now() - startTime,
|
|
10274
|
+
error: {
|
|
10275
|
+
code: "task_cancelled",
|
|
10276
|
+
message: reporter.cancelReason ?? "Task cancelled by imposer while pi session was running.",
|
|
10277
|
+
retryable: false
|
|
10278
|
+
}
|
|
10279
|
+
};
|
|
10217
10280
|
const status = runError || llmAbort || parseError || reporterError ? "failed" : "completed";
|
|
10218
10281
|
const errorCode = runError?.code ?? parseError?.code ?? reporterError?.code ?? (llmAbort ? "llm_api_error" : void 0);
|
|
10219
10282
|
const errorMessage = runError?.message ?? parseError?.message ?? reporterError?.message ?? (llmAbort ? "LLM API error during turn" : void 0);
|
|
@@ -10234,6 +10297,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
10234
10297
|
} catch (err) {
|
|
10235
10298
|
return makeFailedOutput("executor_unexpected_error", err instanceof Error ? err.message : String(err));
|
|
10236
10299
|
} finally {
|
|
10300
|
+
if (cancelListener) reporter.cancelSignal.removeEventListener("abort", cancelListener);
|
|
10237
10301
|
if (session) try {
|
|
10238
10302
|
session.dispose();
|
|
10239
10303
|
} catch {}
|
|
@@ -10263,6 +10327,32 @@ function emptyUsage(provider, model) {
|
|
|
10263
10327
|
};
|
|
10264
10328
|
}
|
|
10265
10329
|
/**
|
|
10330
|
+
* Wire `cancelSignal` → `session.abort()`. Returns the listener so the
|
|
10331
|
+
* caller can remove it on cleanup. If the signal is already aborted at
|
|
10332
|
+
* call time (cancel landed between session creation and wiring), fires
|
|
10333
|
+
* abort synchronously instead of waiting for an `'abort'` event that
|
|
10334
|
+
* already happened.
|
|
10335
|
+
*
|
|
10336
|
+
* Exported for unit testing without a booted Gondolin VM. The double-
|
|
10337
|
+
* invocation guard handles both the rare "fire from constructor + later
|
|
10338
|
+
* event" race and the (in-practice idempotent) double-call into
|
|
10339
|
+
* `session.abort()`.
|
|
10340
|
+
*/
|
|
10341
|
+
function wireSessionAbort(cancelSignal, session) {
|
|
10342
|
+
let abortInvoked = false;
|
|
10343
|
+
const listener = () => {
|
|
10344
|
+
if (abortInvoked) return;
|
|
10345
|
+
abortInvoked = true;
|
|
10346
|
+
session.abort().catch((err) => {
|
|
10347
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
10348
|
+
process.stderr.write(`[pi] session.abort() failed: ${message}\n`);
|
|
10349
|
+
});
|
|
10350
|
+
};
|
|
10351
|
+
if (cancelSignal.aborted) listener();
|
|
10352
|
+
else cancelSignal.addEventListener("abort", listener, { once: true });
|
|
10353
|
+
return listener;
|
|
10354
|
+
}
|
|
10355
|
+
/**
|
|
10266
10356
|
* Cap oversized tool-result payloads before embedding them in a
|
|
10267
10357
|
* `task_messages.payload` row. Bodies above 4 KiB are replaced with a
|
|
10268
10358
|
* `{ truncated, original_size }` marker so the JSONL/DB size stays bounded.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/pi-extension",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.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,8 +31,8 @@
|
|
|
31
31
|
"@earendil-works/gondolin": "^0.7.0",
|
|
32
32
|
"@opentelemetry/api": "^1.9.0",
|
|
33
33
|
"@sinclair/typebox": "^0.34.0",
|
|
34
|
-
"@themoltnet/agent-runtime": "0.
|
|
35
|
-
"@themoltnet/sdk": "0.
|
|
34
|
+
"@themoltnet/agent-runtime": "0.5.0",
|
|
35
|
+
"@themoltnet/sdk": "0.96.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@mariozechner/pi-coding-agent": ">=0.67.0",
|