@themoltnet/pi-extension 0.30.0 → 0.31.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 +54 -0
- package/dist/index.js +189 -6
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -145,6 +145,14 @@ export declare function createMoltNetTools(config: MoltNetToolsConfig): ToolDefi
|
|
|
145
145
|
|
|
146
146
|
export declare function createPiOtelExtension(options?: PiOtelOptions): (pi: ExtensionAPI) => void;
|
|
147
147
|
|
|
148
|
+
export declare function createPiRetryTriage(options: {
|
|
149
|
+
model: Model<Api>;
|
|
150
|
+
thinkingLevel?: PiRetryTriageThinkingLevel | null;
|
|
151
|
+
piAgentDir: string;
|
|
152
|
+
timeoutMs?: number;
|
|
153
|
+
cwd?: string;
|
|
154
|
+
}): PiRetryTriage;
|
|
155
|
+
|
|
148
156
|
/**
|
|
149
157
|
* Factory that builds a pi-specific `executeTask` function suitable for
|
|
150
158
|
* injection into `AgentRuntime`. The returned function caches the resolved
|
|
@@ -524,6 +532,8 @@ declare interface MoltNetToolsConfig {
|
|
|
524
532
|
getTaskContext?(): MoltNetTaskContext | null;
|
|
525
533
|
}
|
|
526
534
|
|
|
535
|
+
export declare function normalizeRetryTriageResult(value: unknown): PiRetryTriageResult;
|
|
536
|
+
|
|
527
537
|
export declare interface PiOtelOptions {
|
|
528
538
|
/** Agent name for `gen_ai.agent.name` on the root span. */
|
|
529
539
|
agentName?: string;
|
|
@@ -535,6 +545,38 @@ export declare interface PiOtelOptions {
|
|
|
535
545
|
spanAttributes?: Record<string, string | number | boolean>;
|
|
536
546
|
}
|
|
537
547
|
|
|
548
|
+
export declare type PiRetryTriage = (input: PiRetryTriageInput) => Promise<PiRetryTriageResult>;
|
|
549
|
+
|
|
550
|
+
export declare type PiRetryTriageConfidence = RetryTriageConfidence;
|
|
551
|
+
|
|
552
|
+
export declare type PiRetryTriageDecision = RetryTriageDecision;
|
|
553
|
+
|
|
554
|
+
export declare interface PiRetryTriageInput {
|
|
555
|
+
task: {
|
|
556
|
+
id: string;
|
|
557
|
+
taskType: string;
|
|
558
|
+
teamId: string;
|
|
559
|
+
input: unknown;
|
|
560
|
+
};
|
|
561
|
+
attemptN: number;
|
|
562
|
+
maxAttempts?: number | null;
|
|
563
|
+
remainingAttempts?: number | null;
|
|
564
|
+
error: unknown;
|
|
565
|
+
recentMessages?: {
|
|
566
|
+
timestamp: string;
|
|
567
|
+
kind: string;
|
|
568
|
+
payload: unknown;
|
|
569
|
+
}[];
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
export declare interface PiRetryTriageResult {
|
|
573
|
+
decision: RetryTriageDecision;
|
|
574
|
+
confidence: RetryTriageConfidence;
|
|
575
|
+
reason: string;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
export declare type PiRetryTriageThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
|
|
579
|
+
|
|
538
580
|
export declare interface PiSessionPersistencePlan {
|
|
539
581
|
sessionDir: string;
|
|
540
582
|
forkFromSessionPath?: string | null;
|
|
@@ -605,6 +647,8 @@ declare interface PiWorkspaceSeedPlan {
|
|
|
605
647
|
source: 'producer';
|
|
606
648
|
}
|
|
607
649
|
|
|
650
|
+
export declare function redactRetryTriageSecrets(value: string): string;
|
|
651
|
+
|
|
608
652
|
export declare function resolveTaskWorktreePath(mainRepo: string, workspaceId: string): string;
|
|
609
653
|
|
|
610
654
|
export declare interface ResumeCommand {
|
|
@@ -635,6 +679,10 @@ declare interface ResumeCommandWhen {
|
|
|
635
679
|
*/
|
|
636
680
|
export declare function resumeVm(config: VmConfig): Promise<ManagedVm>;
|
|
637
681
|
|
|
682
|
+
export declare type RetryTriageConfidence = 'low' | 'medium' | 'high';
|
|
683
|
+
|
|
684
|
+
export declare type RetryTriageDecision = 'retry' | 'do_not_retry';
|
|
685
|
+
|
|
638
686
|
export declare interface SandboxConfig {
|
|
639
687
|
/** Snapshot build settings. */
|
|
640
688
|
snapshot?: {
|
|
@@ -853,6 +901,12 @@ declare const TaskOutput: Type.TObject<{
|
|
|
853
901
|
message: Type.TString;
|
|
854
902
|
stack: Type.TOptional<Type.TString>;
|
|
855
903
|
retryable: Type.TOptional<Type.TBoolean>;
|
|
904
|
+
retry: Type.TOptional<Type.TObject<{
|
|
905
|
+
source: Type.TUnion<[Type.TLiteral<"explicit">, Type.TLiteral<"deterministic">, Type.TLiteral<"attempts_exhausted">, Type.TLiteral<"triage">, Type.TLiteral<"triage_failed">]>;
|
|
906
|
+
decision: Type.TOptional<Type.TUnion<[Type.TLiteral<"retry">, Type.TLiteral<"do_not_retry">]>>;
|
|
907
|
+
confidence: Type.TOptional<Type.TUnion<[Type.TLiteral<"low">, Type.TLiteral<"medium">, Type.TLiteral<"high">]>>;
|
|
908
|
+
reason: Type.TOptional<Type.TString>;
|
|
909
|
+
}>>;
|
|
856
910
|
}>>;
|
|
857
911
|
contentSignature: Type.TOptional<Type.TString>;
|
|
858
912
|
}>;
|
package/dist/index.js
CHANGED
|
@@ -1159,7 +1159,7 @@ var listDiaryPacks = (options) => (options.client ?? client).get({
|
|
|
1159
1159
|
...options
|
|
1160
1160
|
});
|
|
1161
1161
|
/**
|
|
1162
|
-
* Create and persist a custom context pack from an explicit entry selection.
|
|
1162
|
+
* Create and persist a custom context pack from an explicit entry selection. Returns 409 if any selected entry is flagged as a prompt-injection risk; the response lists the flagged entries. Set `force: true` to override and persist anyway.
|
|
1163
1163
|
*/
|
|
1164
1164
|
var createDiaryCustomPack = (options) => (options.client ?? client).post({
|
|
1165
1165
|
security: [
|
|
@@ -2159,7 +2159,7 @@ var completeTask = (options) => (options.client ?? client).post({
|
|
|
2159
2159
|
/**
|
|
2160
2160
|
* Mark an attempt as failed with error details.
|
|
2161
2161
|
*/
|
|
2162
|
-
var
|
|
2162
|
+
var failTaskAttempt = (options) => (options.client ?? client).post({
|
|
2163
2163
|
security: [
|
|
2164
2164
|
{
|
|
2165
2165
|
scheme: "bearer",
|
|
@@ -14019,6 +14019,27 @@ var TaskUsage = _Object_({
|
|
|
14019
14019
|
$id: "TaskUsage",
|
|
14020
14020
|
additionalProperties: false
|
|
14021
14021
|
});
|
|
14022
|
+
var TaskRetryDecision = Union([Literal("retry"), Literal("do_not_retry")]);
|
|
14023
|
+
var TaskRetryConfidence = Union([
|
|
14024
|
+
Literal("low"),
|
|
14025
|
+
Literal("medium"),
|
|
14026
|
+
Literal("high")
|
|
14027
|
+
]);
|
|
14028
|
+
var TaskRetryInfo = _Object_({
|
|
14029
|
+
source: Union([
|
|
14030
|
+
Literal("explicit"),
|
|
14031
|
+
Literal("deterministic"),
|
|
14032
|
+
Literal("attempts_exhausted"),
|
|
14033
|
+
Literal("triage"),
|
|
14034
|
+
Literal("triage_failed")
|
|
14035
|
+
]),
|
|
14036
|
+
decision: Optional(TaskRetryDecision),
|
|
14037
|
+
confidence: Optional(TaskRetryConfidence),
|
|
14038
|
+
reason: Optional(String$1())
|
|
14039
|
+
}, {
|
|
14040
|
+
$id: "TaskRetryInfo",
|
|
14041
|
+
additionalProperties: false
|
|
14042
|
+
});
|
|
14022
14043
|
/**
|
|
14023
14044
|
* Structured error returned from a failed attempt.
|
|
14024
14045
|
*/
|
|
@@ -14026,7 +14047,8 @@ var TaskError = _Object_({
|
|
|
14026
14047
|
code: String$1(),
|
|
14027
14048
|
message: String$1(),
|
|
14028
14049
|
stack: Optional(String$1()),
|
|
14029
|
-
retryable: Optional(Boolean$1())
|
|
14050
|
+
retryable: Optional(Boolean$1()),
|
|
14051
|
+
retry: Optional(TaskRetryInfo)
|
|
14030
14052
|
}, {
|
|
14031
14053
|
$id: "TaskError",
|
|
14032
14054
|
additionalProperties: false
|
|
@@ -14992,8 +15014,8 @@ function createTasksNamespace(context) {
|
|
|
14992
15014
|
body
|
|
14993
15015
|
}));
|
|
14994
15016
|
},
|
|
14995
|
-
async
|
|
14996
|
-
return unwrapResult(await
|
|
15017
|
+
async failAttempt(id, n, body) {
|
|
15018
|
+
return unwrapResult(await failTaskAttempt({
|
|
14997
15019
|
client,
|
|
14998
15020
|
auth,
|
|
14999
15021
|
path: {
|
|
@@ -26342,6 +26364,167 @@ function describeToolErrorMessage(result) {
|
|
|
26342
26364
|
}
|
|
26343
26365
|
}
|
|
26344
26366
|
//#endregion
|
|
26367
|
+
//#region src/runtime/retry-triage.ts
|
|
26368
|
+
var MAX_TRIAGE_JSON_CHARS = 12e3;
|
|
26369
|
+
var MAX_TRIAGE_FIELD_CHARS = 2e3;
|
|
26370
|
+
var REDACTED = "[redacted]";
|
|
26371
|
+
var SECRET_KEY_PATTERN = /(?:api[_-]?key|token|secret|password|passwd|credential|authorization|private[_-]?key|access[_-]?token|refresh[_-]?token)/i;
|
|
26372
|
+
function createPiRetryTriage(options) {
|
|
26373
|
+
return async (input) => {
|
|
26374
|
+
const cwd = options.cwd ?? process.cwd();
|
|
26375
|
+
const capture = createRetryTriageTool();
|
|
26376
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
26377
|
+
cwd,
|
|
26378
|
+
agentDir: options.piAgentDir,
|
|
26379
|
+
appendSystemPrompt: [TRIAGE_SYSTEM_PROMPT],
|
|
26380
|
+
skillsOverride: () => ({
|
|
26381
|
+
skills: [],
|
|
26382
|
+
diagnostics: []
|
|
26383
|
+
})
|
|
26384
|
+
});
|
|
26385
|
+
await resourceLoader.reload();
|
|
26386
|
+
const sessionManager = SessionManager.inMemory(cwd);
|
|
26387
|
+
const created = await createAgentSession({
|
|
26388
|
+
agentDir: options.piAgentDir,
|
|
26389
|
+
cwd,
|
|
26390
|
+
model: options.model,
|
|
26391
|
+
thinkingLevel: options.thinkingLevel ?? void 0,
|
|
26392
|
+
customTools: [capture.tool],
|
|
26393
|
+
sessionManager,
|
|
26394
|
+
resourceLoader
|
|
26395
|
+
});
|
|
26396
|
+
await withTimeout(created.session.prompt(buildTriagePrompt(input)), options.timeoutMs ?? 3e4, () => created.session.abort());
|
|
26397
|
+
const result = capture.getCaptured();
|
|
26398
|
+
if (!result) throw new Error("Retry triage did not submit a decision");
|
|
26399
|
+
return normalizeRetryTriageResult(result);
|
|
26400
|
+
};
|
|
26401
|
+
}
|
|
26402
|
+
function createRetryTriageTool() {
|
|
26403
|
+
let captured = null;
|
|
26404
|
+
return {
|
|
26405
|
+
tool: defineTool({
|
|
26406
|
+
name: "submit_retry_triage",
|
|
26407
|
+
label: "Submit retry triage",
|
|
26408
|
+
description: "Submit the retry decision for a failed MoltNet task attempt.",
|
|
26409
|
+
parameters: {
|
|
26410
|
+
type: "object",
|
|
26411
|
+
additionalProperties: false,
|
|
26412
|
+
required: [
|
|
26413
|
+
"decision",
|
|
26414
|
+
"confidence",
|
|
26415
|
+
"reason"
|
|
26416
|
+
],
|
|
26417
|
+
properties: {
|
|
26418
|
+
decision: {
|
|
26419
|
+
type: "string",
|
|
26420
|
+
enum: ["retry", "do_not_retry"]
|
|
26421
|
+
},
|
|
26422
|
+
confidence: {
|
|
26423
|
+
type: "string",
|
|
26424
|
+
enum: [
|
|
26425
|
+
"low",
|
|
26426
|
+
"medium",
|
|
26427
|
+
"high"
|
|
26428
|
+
]
|
|
26429
|
+
},
|
|
26430
|
+
reason: {
|
|
26431
|
+
type: "string",
|
|
26432
|
+
minLength: 1
|
|
26433
|
+
}
|
|
26434
|
+
}
|
|
26435
|
+
},
|
|
26436
|
+
execute(_id, params) {
|
|
26437
|
+
captured = normalizeRetryTriageResult(params);
|
|
26438
|
+
return Promise.resolve({
|
|
26439
|
+
content: [{
|
|
26440
|
+
type: "text",
|
|
26441
|
+
text: "Retry triage captured."
|
|
26442
|
+
}],
|
|
26443
|
+
details: captured,
|
|
26444
|
+
terminate: true
|
|
26445
|
+
});
|
|
26446
|
+
}
|
|
26447
|
+
}),
|
|
26448
|
+
getCaptured: () => captured
|
|
26449
|
+
};
|
|
26450
|
+
}
|
|
26451
|
+
function normalizeRetryTriageResult(value) {
|
|
26452
|
+
const record = value && typeof value === "object" ? value : {};
|
|
26453
|
+
return {
|
|
26454
|
+
decision: record.decision === "retry" ? "retry" : "do_not_retry",
|
|
26455
|
+
confidence: record.confidence === "high" || record.confidence === "medium" ? record.confidence : "low",
|
|
26456
|
+
reason: typeof record.reason === "string" && record.reason.trim() ? record.reason.trim().slice(0, 500) : "retry triage did not provide a reason"
|
|
26457
|
+
};
|
|
26458
|
+
}
|
|
26459
|
+
function buildTriagePrompt(input) {
|
|
26460
|
+
const payload = {
|
|
26461
|
+
task: {
|
|
26462
|
+
id: input.task.id,
|
|
26463
|
+
type: input.task.taskType,
|
|
26464
|
+
teamId: input.task.teamId,
|
|
26465
|
+
input: prepareTriagePayload(input.task.input)
|
|
26466
|
+
},
|
|
26467
|
+
attempt: {
|
|
26468
|
+
attemptN: input.attemptN,
|
|
26469
|
+
maxAttempts: input.maxAttempts ?? null,
|
|
26470
|
+
remainingAttempts: input.remainingAttempts ?? null
|
|
26471
|
+
},
|
|
26472
|
+
error: prepareTriagePayload(input.error),
|
|
26473
|
+
recentMessages: prepareTriagePayload((input.recentMessages ?? []).slice(-12))
|
|
26474
|
+
};
|
|
26475
|
+
return [
|
|
26476
|
+
"Classify whether this failed task attempt should be retried.",
|
|
26477
|
+
"",
|
|
26478
|
+
"Retry only when a fresh attempt can plausibly recover without changing the task input.",
|
|
26479
|
+
"Do not retry for policy, validation, credentials, cancellation, model/config, or task-contract failures.",
|
|
26480
|
+
"Use confidence=low when evidence is weak; low confidence must choose do_not_retry.",
|
|
26481
|
+
"Call submit_retry_triage exactly once.",
|
|
26482
|
+
"",
|
|
26483
|
+
truncateString(JSON.stringify(payload, null, 2), MAX_TRIAGE_JSON_CHARS)
|
|
26484
|
+
].join("\n");
|
|
26485
|
+
}
|
|
26486
|
+
function prepareTriagePayload(value) {
|
|
26487
|
+
return redactAndTruncate(value, []);
|
|
26488
|
+
}
|
|
26489
|
+
function redactAndTruncate(value, path) {
|
|
26490
|
+
const currentKey = path[path.length - 1] ?? "";
|
|
26491
|
+
if (SECRET_KEY_PATTERN.test(currentKey)) return REDACTED;
|
|
26492
|
+
if (typeof value === "string") return truncateString(redactRetryTriageSecrets(value), MAX_TRIAGE_FIELD_CHARS);
|
|
26493
|
+
if (Array.isArray(value)) return value.map((item, index) => redactAndTruncate(item, [...path, String(index)]));
|
|
26494
|
+
if (value && typeof value === "object") {
|
|
26495
|
+
const entries = Object.entries(value).map(([key, child]) => [key, redactAndTruncate(child, [...path, key])]);
|
|
26496
|
+
return Object.fromEntries(entries);
|
|
26497
|
+
}
|
|
26498
|
+
return value;
|
|
26499
|
+
}
|
|
26500
|
+
function redactRetryTriageSecrets(value) {
|
|
26501
|
+
return value.replace(/((?:bearer|basic)\s+)[a-z0-9._~+/=-]{16,}/gi, `$1${REDACTED}`).replace(/\bgh[pousr]_[a-z0-9_]{20,}\b/gi, REDACTED).replace(/\bsk-[a-z0-9_-]{16,}\b/gi, REDACTED).replace(/\beyJ[a-z0-9_-]{20,}\.[a-z0-9_-]{20,}\.[a-z0-9_-]{20,}\b/gi, REDACTED);
|
|
26502
|
+
}
|
|
26503
|
+
function truncateString(value, maxChars) {
|
|
26504
|
+
if (value.length <= maxChars) return value;
|
|
26505
|
+
return `${value.slice(0, maxChars)}...[truncated ${value.length - maxChars} chars]`;
|
|
26506
|
+
}
|
|
26507
|
+
var TRIAGE_SYSTEM_PROMPT = [
|
|
26508
|
+
"You are MoltNet retry triage.",
|
|
26509
|
+
"You classify one failed execution attempt, not the whole task.",
|
|
26510
|
+
"Return retry only for likely transient/runtime failures or clear evidence a new attempt can recover.",
|
|
26511
|
+
"The agent may have already tried local recovery; do not ask for more work."
|
|
26512
|
+
].join("\n");
|
|
26513
|
+
async function withTimeout(promise, timeoutMs, onTimeout) {
|
|
26514
|
+
let timeout;
|
|
26515
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
26516
|
+
timeout = setTimeout(() => {
|
|
26517
|
+
Promise.resolve(onTimeout?.()).catch(() => {});
|
|
26518
|
+
reject(/* @__PURE__ */ new Error(`Retry triage timed out after ${timeoutMs}ms`));
|
|
26519
|
+
}, timeoutMs);
|
|
26520
|
+
});
|
|
26521
|
+
try {
|
|
26522
|
+
return await Promise.race([promise, timeoutPromise]);
|
|
26523
|
+
} finally {
|
|
26524
|
+
if (timeout) clearTimeout(timeout);
|
|
26525
|
+
}
|
|
26526
|
+
}
|
|
26527
|
+
//#endregion
|
|
26345
26528
|
//#region src/index.ts
|
|
26346
26529
|
/**
|
|
26347
26530
|
* @themoltnet/pi-extension — MoltNet pi extension
|
|
@@ -26642,4 +26825,4 @@ function moltnetExtension(pi) {
|
|
|
26642
26825
|
registerMoltnetReflectCommand(pi, state);
|
|
26643
26826
|
}
|
|
26644
26827
|
//#endregion
|
|
26645
|
-
export { HOST_EXEC_DEFAULT_BASE_ENV, activateAgentEnv, buildAgentSession, createGondolinBashOps, createGondolinEditOps, createGondolinReadOps, createGondolinWriteOps, createMoltNetTools, createPiOtelExtension, createPiTaskExecutor, createSubagentTool, moltnetExtension as default, ensureSnapshot, executePiTask, findMainWorktree, injectTaskContext, loadCredentials, resolveTaskWorktreePath, resumeVm, toGuestPath };
|
|
26828
|
+
export { HOST_EXEC_DEFAULT_BASE_ENV, activateAgentEnv, buildAgentSession, createGondolinBashOps, createGondolinEditOps, createGondolinReadOps, createGondolinWriteOps, createMoltNetTools, createPiOtelExtension, createPiRetryTriage, createPiTaskExecutor, createSubagentTool, moltnetExtension as default, ensureSnapshot, executePiTask, findMainWorktree, injectTaskContext, loadCredentials, normalizeRetryTriageResult, redactRetryTriageSecrets, resolveTaskWorktreePath, resumeVm, toGuestPath };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/pi-extension",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.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
|
"keywords": [
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"@earendil-works/gondolin": "^0.9.1",
|
|
37
37
|
"@opentelemetry/api": "^1.9.0",
|
|
38
38
|
"typebox": "^1.2.8",
|
|
39
|
-
"@themoltnet/agent-runtime": "0.33.
|
|
40
|
-
"@themoltnet/sdk": "0.
|
|
39
|
+
"@themoltnet/agent-runtime": "0.33.1",
|
|
40
|
+
"@themoltnet/sdk": "0.117.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@earendil-works/pi-coding-agent": ">=0.74.0",
|