@themoltnet/pi-extension 0.29.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 +76 -0
- package/dist/index.js +294 -70
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { EditOperations } from '@earendil-works/pi-coding-agent';
|
|
|
6
6
|
import { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
7
7
|
import { LoadSkillsResult } from '@earendil-works/pi-coding-agent';
|
|
8
8
|
import { Model } from '@earendil-works/pi-ai';
|
|
9
|
+
import { Readable } from 'node:stream';
|
|
9
10
|
import { ReadOperations } from '@earendil-works/pi-coding-agent';
|
|
10
11
|
import { Skill } from '@earendil-works/pi-coding-agent';
|
|
11
12
|
import { Static } from 'typebox';
|
|
@@ -144,6 +145,14 @@ export declare function createMoltNetTools(config: MoltNetToolsConfig): ToolDefi
|
|
|
144
145
|
|
|
145
146
|
export declare function createPiOtelExtension(options?: PiOtelOptions): (pi: ExtensionAPI) => void;
|
|
146
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
|
+
|
|
147
156
|
/**
|
|
148
157
|
* Factory that builds a pi-specific `executeTask` function suitable for
|
|
149
158
|
* injection into `AgentRuntime`. The returned function caches the resolved
|
|
@@ -294,6 +303,8 @@ export declare interface ExecutePiTaskOptions {
|
|
|
294
303
|
extraAllowedHosts?: string[];
|
|
295
304
|
/** Sandbox overrides (env, VFS shadows, resources). */
|
|
296
305
|
sandboxConfig?: SandboxConfig;
|
|
306
|
+
/** Host environment variable names to forward into the Pi VM. */
|
|
307
|
+
forwardEnv?: string[];
|
|
297
308
|
/**
|
|
298
309
|
* Forwarded to `buildTaskUserPrompt` for per-type builders. Static
|
|
299
310
|
* across tasks. Today no built-in builder needs per-task `extras` —
|
|
@@ -481,6 +492,17 @@ declare interface MoltNetToolsConfig {
|
|
|
481
492
|
clearSessionErrors(): void;
|
|
482
493
|
/** Host working directory for host-exec commands (worktree path or cwd). */
|
|
483
494
|
getHostCwd?(): string;
|
|
495
|
+
/**
|
|
496
|
+
* Optional workspace-file reader. Daemon/Gondolin callers provide this so
|
|
497
|
+
* artifact uploads see guest overlay writes that may not exist on the host
|
|
498
|
+
* mount path yet.
|
|
499
|
+
*/
|
|
500
|
+
openWorkspaceFileForRead?(filePath: string): Promise<{
|
|
501
|
+
stream: Readable;
|
|
502
|
+
isFile: boolean;
|
|
503
|
+
sizeBytes?: number;
|
|
504
|
+
displayPath?: string;
|
|
505
|
+
}>;
|
|
484
506
|
/**
|
|
485
507
|
* Set of process.env keys that are safe to forward to host-exec child
|
|
486
508
|
* processes. Configured at sandbox startup so the caller can include
|
|
@@ -510,6 +532,8 @@ declare interface MoltNetToolsConfig {
|
|
|
510
532
|
getTaskContext?(): MoltNetTaskContext | null;
|
|
511
533
|
}
|
|
512
534
|
|
|
535
|
+
export declare function normalizeRetryTriageResult(value: unknown): PiRetryTriageResult;
|
|
536
|
+
|
|
513
537
|
export declare interface PiOtelOptions {
|
|
514
538
|
/** Agent name for `gen_ai.agent.name` on the root span. */
|
|
515
539
|
agentName?: string;
|
|
@@ -521,6 +545,38 @@ export declare interface PiOtelOptions {
|
|
|
521
545
|
spanAttributes?: Record<string, string | number | boolean>;
|
|
522
546
|
}
|
|
523
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
|
+
|
|
524
580
|
export declare interface PiSessionPersistencePlan {
|
|
525
581
|
sessionDir: string;
|
|
526
582
|
forkFromSessionPath?: string | null;
|
|
@@ -591,6 +647,8 @@ declare interface PiWorkspaceSeedPlan {
|
|
|
591
647
|
source: 'producer';
|
|
592
648
|
}
|
|
593
649
|
|
|
650
|
+
export declare function redactRetryTriageSecrets(value: string): string;
|
|
651
|
+
|
|
594
652
|
export declare function resolveTaskWorktreePath(mainRepo: string, workspaceId: string): string;
|
|
595
653
|
|
|
596
654
|
export declare interface ResumeCommand {
|
|
@@ -621,6 +679,10 @@ declare interface ResumeCommandWhen {
|
|
|
621
679
|
*/
|
|
622
680
|
export declare function resumeVm(config: VmConfig): Promise<ManagedVm>;
|
|
623
681
|
|
|
682
|
+
export declare type RetryTriageConfidence = 'low' | 'medium' | 'high';
|
|
683
|
+
|
|
684
|
+
export declare type RetryTriageDecision = 'retry' | 'do_not_retry';
|
|
685
|
+
|
|
624
686
|
export declare interface SandboxConfig {
|
|
625
687
|
/** Snapshot build settings. */
|
|
626
688
|
snapshot?: {
|
|
@@ -839,6 +901,12 @@ declare const TaskOutput: Type.TObject<{
|
|
|
839
901
|
message: Type.TString;
|
|
840
902
|
stack: Type.TOptional<Type.TString>;
|
|
841
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
|
+
}>>;
|
|
842
910
|
}>>;
|
|
843
911
|
contentSignature: Type.TOptional<Type.TString>;
|
|
844
912
|
}>;
|
|
@@ -972,6 +1040,14 @@ export declare interface VmConfig {
|
|
|
972
1040
|
extraAllowedHosts?: string[];
|
|
973
1041
|
/** Full sandbox config (vfs shadows, env overrides). */
|
|
974
1042
|
sandboxConfig?: SandboxConfig;
|
|
1043
|
+
/**
|
|
1044
|
+
* Host environment variable names to copy into the VM process.
|
|
1045
|
+
*
|
|
1046
|
+
* Runtime profiles use this for provider API keys: `requiredEnv` proves the
|
|
1047
|
+
* daemon host has the secret, and this allowlist forwards only those names
|
|
1048
|
+
* into the guest without storing secret values in the profile.
|
|
1049
|
+
*/
|
|
1050
|
+
forwardEnv?: string[];
|
|
975
1051
|
/** Abort resume/setup work, closing any live VM owned by resumeVm. */
|
|
976
1052
|
signal?: AbortSignal;
|
|
977
1053
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { execFileSync } from "node:child_process";
|
|
3
3
|
import { cpSync, createReadStream, createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
|
|
4
|
-
import path, { join, relative, sep } from "node:path";
|
|
4
|
+
import path, { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { DEFAULT_MAX_BYTES, DefaultResourceLoader, SessionManager, createAgentSession, createBashTool, createBashToolDefinition, createEditTool, createEditToolDefinition, createFindTool, createFindToolDefinition, createGrepTool, createGrepToolDefinition, createLsTool, createLsToolDefinition, createReadTool, createReadToolDefinition, createSyntheticSourceInfo, createWriteTool, createWriteToolDefinition, defineTool, formatSize, parseFrontmatter, truncateHead, truncateLine } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import { createHash } from "node:crypto";
|
|
7
7
|
import { Readable } from "node:stream";
|
|
@@ -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",
|
|
@@ -2874,6 +2874,15 @@ function unwrapResult(result) {
|
|
|
2874
2874
|
networkError.stack = error.stack;
|
|
2875
2875
|
throw networkError;
|
|
2876
2876
|
}
|
|
2877
|
+
const responseSummary = summarizeResponse(result.response);
|
|
2878
|
+
if (responseSummary) {
|
|
2879
|
+
const detail = stringifyUnknown(error);
|
|
2880
|
+
throw new MoltNetError(`MoltNet API request failed with HTTP ${responseSummary.status} ${responseSummary.statusText}: ${detail}`, {
|
|
2881
|
+
code: `HTTP_${responseSummary.status}`,
|
|
2882
|
+
detail,
|
|
2883
|
+
statusCode: responseSummary.status
|
|
2884
|
+
});
|
|
2885
|
+
}
|
|
2877
2886
|
throw new MoltNetError(`Unexpected error from MoltNet API: ${stringifyUnknown(error)}`, { code: "UNKNOWN" });
|
|
2878
2887
|
}
|
|
2879
2888
|
if (result.data === void 0) throw new MoltNetError("Unexpected empty response from MoltNet API", { code: "EMPTY_RESPONSE" });
|
|
@@ -2891,6 +2900,15 @@ function stringifyUnknown(value) {
|
|
|
2891
2900
|
return String(value);
|
|
2892
2901
|
}
|
|
2893
2902
|
}
|
|
2903
|
+
function summarizeResponse(response) {
|
|
2904
|
+
if (!response || typeof response !== "object") return null;
|
|
2905
|
+
const candidate = response;
|
|
2906
|
+
if (typeof candidate.status !== "number") return null;
|
|
2907
|
+
return {
|
|
2908
|
+
status: candidate.status,
|
|
2909
|
+
statusText: typeof candidate.statusText === "string" && candidate.statusText ? candidate.statusText : "Error"
|
|
2910
|
+
};
|
|
2911
|
+
}
|
|
2894
2912
|
function unwrapRequired(result, message, code) {
|
|
2895
2913
|
if (result.error || !result.data) throw new MoltNetError(message, { code });
|
|
2896
2914
|
return result.data;
|
|
@@ -14001,6 +14019,27 @@ var TaskUsage = _Object_({
|
|
|
14001
14019
|
$id: "TaskUsage",
|
|
14002
14020
|
additionalProperties: false
|
|
14003
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
|
+
});
|
|
14004
14043
|
/**
|
|
14005
14044
|
* Structured error returned from a failed attempt.
|
|
14006
14045
|
*/
|
|
@@ -14008,7 +14047,8 @@ var TaskError = _Object_({
|
|
|
14008
14047
|
code: String$1(),
|
|
14009
14048
|
message: String$1(),
|
|
14010
14049
|
stack: Optional(String$1()),
|
|
14011
|
-
retryable: Optional(Boolean$1())
|
|
14050
|
+
retryable: Optional(Boolean$1()),
|
|
14051
|
+
retry: Optional(TaskRetryInfo)
|
|
14012
14052
|
}, {
|
|
14013
14053
|
$id: "TaskError",
|
|
14014
14054
|
additionalProperties: false
|
|
@@ -14974,8 +15014,8 @@ function createTasksNamespace(context) {
|
|
|
14974
15014
|
body
|
|
14975
15015
|
}));
|
|
14976
15016
|
},
|
|
14977
|
-
async
|
|
14978
|
-
return unwrapResult(await
|
|
15017
|
+
async failAttempt(id, n, body) {
|
|
15018
|
+
return unwrapResult(await failTaskAttempt({
|
|
14979
15019
|
client,
|
|
14980
15020
|
auth,
|
|
14981
15021
|
path: {
|
|
@@ -17542,11 +17582,34 @@ function shouldAutoApproveHostExec(params, config) {
|
|
|
17542
17582
|
}
|
|
17543
17583
|
async function resolveWorkspaceFilePath(cwd, filePath) {
|
|
17544
17584
|
const resolved = path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(cwd, filePath);
|
|
17545
|
-
const
|
|
17585
|
+
const realCwd = await realpath(cwd);
|
|
17586
|
+
let realResolved;
|
|
17587
|
+
try {
|
|
17588
|
+
realResolved = await realpath(resolved);
|
|
17589
|
+
} catch (err) {
|
|
17590
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") throw new Error(`task artifact input path does not exist: ${filePath}. Write the file before calling moltnet_upload_task_artifact.`);
|
|
17591
|
+
throw err;
|
|
17592
|
+
}
|
|
17546
17593
|
const rel = path.relative(realCwd, realResolved);
|
|
17547
17594
|
if (rel === "" || rel.startsWith("..") || path.isAbsolute(rel)) throw new Error(`task artifact path escapes workspace: ${filePath}`);
|
|
17548
17595
|
return realResolved;
|
|
17549
17596
|
}
|
|
17597
|
+
async function openWorkspaceArtifactInput(config, cwd, filePath) {
|
|
17598
|
+
if (config.openWorkspaceFileForRead) try {
|
|
17599
|
+
return await config.openWorkspaceFileForRead(filePath);
|
|
17600
|
+
} catch (err) {
|
|
17601
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") throw new Error(`task artifact input path does not exist: ${filePath}. Write the file before calling moltnet_upload_task_artifact.`);
|
|
17602
|
+
throw err;
|
|
17603
|
+
}
|
|
17604
|
+
const resolved = await resolveWorkspaceFilePath(cwd, filePath);
|
|
17605
|
+
const info = await stat(resolved);
|
|
17606
|
+
return {
|
|
17607
|
+
stream: createReadStream(resolved),
|
|
17608
|
+
isFile: info.isFile(),
|
|
17609
|
+
sizeBytes: info.size,
|
|
17610
|
+
displayPath: path.relative(cwd, resolved)
|
|
17611
|
+
};
|
|
17612
|
+
}
|
|
17550
17613
|
async function resolveWorkspaceOutputPath(cwd, filePath) {
|
|
17551
17614
|
const resolved = path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(cwd, filePath);
|
|
17552
17615
|
const [realCwd, realParent] = await Promise.all([realpath(cwd), realpath(path.dirname(resolved))]);
|
|
@@ -18054,14 +18117,12 @@ function createMoltNetTools(config) {
|
|
|
18054
18117
|
if (!teamId) throw new Error("moltnet_upload_task_artifact requires a team context");
|
|
18055
18118
|
const taskCtx = config.getTaskContext?.() ?? null;
|
|
18056
18119
|
if (!taskCtx) throw new Error("moltnet_upload_task_artifact is only available during an active task attempt");
|
|
18057
|
-
const
|
|
18058
|
-
|
|
18059
|
-
const info = await stat(resolved);
|
|
18060
|
-
if (!info.isFile()) throw new Error(`task artifact path is not a file: ${params.filePath}`);
|
|
18120
|
+
const input = await openWorkspaceArtifactInput(config, config.getHostCwd?.() ?? process.cwd(), params.filePath);
|
|
18121
|
+
if (!input.isFile) throw new Error(`task artifact path is not a file: ${params.filePath}`);
|
|
18061
18122
|
const artifact = await agent.tasks.artifacts.upload({
|
|
18062
18123
|
taskId: taskCtx.taskId,
|
|
18063
18124
|
attemptN: taskCtx.attemptN
|
|
18064
|
-
},
|
|
18125
|
+
}, input.stream, {
|
|
18065
18126
|
kind: params.kind,
|
|
18066
18127
|
title: params.title,
|
|
18067
18128
|
contentType: params.contentType ?? "application/octet-stream",
|
|
@@ -18072,8 +18133,8 @@ function createMoltNetTools(config) {
|
|
|
18072
18133
|
type: "text",
|
|
18073
18134
|
text: JSON.stringify({
|
|
18074
18135
|
...artifact,
|
|
18075
|
-
filePath:
|
|
18076
|
-
localSizeBytes:
|
|
18136
|
+
filePath: input.displayPath ?? params.filePath,
|
|
18137
|
+
localSizeBytes: input.sizeBytes ?? null
|
|
18077
18138
|
}, null, 2)
|
|
18078
18139
|
}],
|
|
18079
18140
|
details: {}
|
|
@@ -18657,13 +18718,13 @@ async function delay(ms, signal, label) {
|
|
|
18657
18718
|
//#endregion
|
|
18658
18719
|
//#region src/vm-manager.ts
|
|
18659
18720
|
/**
|
|
18660
|
-
* Memory-backed VFS mount used by the daemon to inject task
|
|
18661
|
-
*
|
|
18662
|
-
*
|
|
18663
|
-
*
|
|
18721
|
+
* Memory-backed VFS mount used by the daemon to inject task context
|
|
18722
|
+
* (#943 slice 1.5). This is a separate top-level mount because Gondolin
|
|
18723
|
+
* mounts can't nest. The agent's Gondolin-bound Read tool accepts paths
|
|
18724
|
+
* under this prefix (see toGuestPath in tool-operations.ts).
|
|
18664
18725
|
*
|
|
18665
18726
|
* Why MemoryProvider rather than a path under the workspace mount:
|
|
18666
|
-
* - Injected
|
|
18727
|
+
* - Injected task context is ephemeral by intent: per-task-attempt input
|
|
18667
18728
|
* scoped to the VM lifetime. MemoryProvider models that exactly —
|
|
18668
18729
|
* in-memory, per-VM-instance, zero host artefacts, automatic
|
|
18669
18730
|
* cleanup on VM close.
|
|
@@ -18676,7 +18737,7 @@ async function delay(ms, signal, label) {
|
|
|
18676
18737
|
* and episodic 7affbfeb-18a2-4963-aeac-c177eb2afa2d for the full
|
|
18677
18738
|
* investigation and the alternatives we rejected.
|
|
18678
18739
|
*/
|
|
18679
|
-
var
|
|
18740
|
+
var GUEST_TASK_CONTEXT_MOUNT = "/moltnet-task-context";
|
|
18680
18741
|
function shouldRunResumeCommand(entry, ctx) {
|
|
18681
18742
|
if (typeof entry === "string") return true;
|
|
18682
18743
|
const workspaceModes = entry.when?.workspaceMode;
|
|
@@ -18840,10 +18901,17 @@ async function resumeVm(config) {
|
|
|
18840
18901
|
writeMode: vfsConfig.shadowMode ?? "tmpfs"
|
|
18841
18902
|
});
|
|
18842
18903
|
}
|
|
18904
|
+
const forwardedEnv = {};
|
|
18905
|
+
for (const name of config.forwardEnv ?? []) {
|
|
18906
|
+
const value = process.env[name];
|
|
18907
|
+
if (value === void 0 || value === "") continue;
|
|
18908
|
+
forwardedEnv[name] = value;
|
|
18909
|
+
}
|
|
18843
18910
|
const envOverrides = config.sandboxConfig?.env ?? {};
|
|
18844
18911
|
const vmEnv = {
|
|
18845
18912
|
...secretEnv,
|
|
18846
18913
|
...vmAgentEnv,
|
|
18914
|
+
...forwardedEnv,
|
|
18847
18915
|
PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/go/bin",
|
|
18848
18916
|
HOME: "/home/agent",
|
|
18849
18917
|
NODE_NO_WARNINGS: "1",
|
|
@@ -18861,7 +18929,7 @@ async function resumeVm(config) {
|
|
|
18861
18929
|
...resources?.cpus && { cpus: resources.cpus },
|
|
18862
18930
|
vfs: { mounts: {
|
|
18863
18931
|
[guestWorkspace]: workspaceProvider,
|
|
18864
|
-
[
|
|
18932
|
+
[GUEST_TASK_CONTEXT_MOUNT]: new MemoryProvider()
|
|
18865
18933
|
} }
|
|
18866
18934
|
}),
|
|
18867
18935
|
signal: config.signal,
|
|
@@ -19060,9 +19128,9 @@ function toHostToolPath(localCwd, guestWorkspace, guestPath) {
|
|
|
19060
19128
|
function toGuestPath(localCwd, localPath, guestWorkspace) {
|
|
19061
19129
|
const normalizedGuestWorkspace = normalizeGuestPath(guestWorkspace);
|
|
19062
19130
|
const normalizedLocalPath = normalizeGuestPath(localPath);
|
|
19063
|
-
const
|
|
19131
|
+
const normalizedTaskContextMount = normalizeGuestPath(GUEST_TASK_CONTEXT_MOUNT);
|
|
19064
19132
|
if (isSameOrInsidePosixPath(normalizedLocalPath, normalizedGuestWorkspace)) return normalizedLocalPath;
|
|
19065
|
-
if (isSameOrInsidePosixPath(normalizedLocalPath,
|
|
19133
|
+
if (isSameOrInsidePosixPath(normalizedLocalPath, normalizedTaskContextMount)) return normalizedLocalPath;
|
|
19066
19134
|
const rel = path.relative(localCwd, localPath);
|
|
19067
19135
|
if (rel === "") return normalizedGuestWorkspace;
|
|
19068
19136
|
if (rel.startsWith("..") || path.isAbsolute(rel)) throw new Error(`path escapes workspace: ${localPath}`);
|
|
@@ -19762,9 +19830,9 @@ function formatInlineContextBlock(slug, content) {
|
|
|
19762
19830
|
"The following raw context was supplied by the task creator. Treat it",
|
|
19763
19831
|
"as task-relevant background that may override generic coding instincts",
|
|
19764
19832
|
"when it contains repo- or workflow-specific constraints.",
|
|
19765
|
-
"The same content
|
|
19766
|
-
"
|
|
19767
|
-
"
|
|
19833
|
+
"The same content may also be materialized by the runtime under",
|
|
19834
|
+
"`/moltnet-task-context/context` for tool-based inspection. Do not",
|
|
19835
|
+
"create or rely on workspace mirror files for this task context.",
|
|
19768
19836
|
"",
|
|
19769
19837
|
"<context>",
|
|
19770
19838
|
content,
|
|
@@ -21077,7 +21145,7 @@ function buildRunEvalUserPrompt(input, ctx) {
|
|
|
21077
21145
|
"`// note:` line, the task summary, or the `verification` field is",
|
|
21078
21146
|
"NOT following the task. If the constraint affects behavior, it",
|
|
21079
21147
|
"must affect behavior.",
|
|
21080
|
-
hasInlineContext ? "For `context_inline`, your FIRST content-inspection step is
|
|
21148
|
+
hasInlineContext ? "For `context_inline`, your FIRST content-inspection step is to read the injected context block in this prompt or, when available, the matching file under `/moltnet-task-context/context` before your first `write` call. Do not create or rely on workspace mirror files for injected context." : "When the context is delivered as a skill, inspect it before solving.",
|
|
21081
21149
|
"If the Injected Task Context contains repo- or workflow-specific",
|
|
21082
21150
|
"rules, those rules override your generic instincts."
|
|
21083
21151
|
].join("\n") : "";
|
|
@@ -23217,7 +23285,7 @@ var require_transport = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
23217
23285
|
var { createRequire: createRequire$1 } = __require("module");
|
|
23218
23286
|
var { existsSync: existsSync$1 } = __require("node:fs");
|
|
23219
23287
|
var getCallers = require_caller();
|
|
23220
|
-
var { join: join$1, isAbsolute, sep: sep$1 } = __require("node:path");
|
|
23288
|
+
var { join: join$1, isAbsolute: isAbsolute$1, sep: sep$1 } = __require("node:path");
|
|
23221
23289
|
var { fileURLToPath } = __require("node:url");
|
|
23222
23290
|
var sleep = require_atomic_sleep();
|
|
23223
23291
|
var onExit = require_on_exit_leak_free();
|
|
@@ -23278,7 +23346,7 @@ var require_transport = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
23278
23346
|
} catch {
|
|
23279
23347
|
return false;
|
|
23280
23348
|
}
|
|
23281
|
-
return isAbsolute(path) && !existsSync$1(path);
|
|
23349
|
+
return isAbsolute$1(path) && !existsSync$1(path);
|
|
23282
23350
|
}
|
|
23283
23351
|
function stripQuotes(value) {
|
|
23284
23352
|
const first = value[0];
|
|
@@ -23381,7 +23449,7 @@ var require_transport = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
23381
23449
|
return buildStream(fixTarget(target), options, worker, sync, name);
|
|
23382
23450
|
function fixTarget(origin) {
|
|
23383
23451
|
origin = bundlerOverrides[origin] || origin;
|
|
23384
|
-
if (isAbsolute(origin) || origin.indexOf("file://") === 0) return origin;
|
|
23452
|
+
if (isAbsolute$1(origin) || origin.indexOf("file://") === 0) return origin;
|
|
23385
23453
|
if (origin === "pino/file") return join$1(__dirname, "..", "file.js");
|
|
23386
23454
|
let fixTarget;
|
|
23387
23455
|
for (const filePath of callers) try {
|
|
@@ -24732,20 +24800,21 @@ var require_multistream = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
24732
24800
|
* system prompt; the agent fetches the body on demand via the
|
|
24733
24801
|
* Read tool.
|
|
24734
24802
|
*
|
|
24735
|
-
*
|
|
24736
|
-
* `<available_skills>` metadata (name, description, location), never the
|
|
24803
|
+
* Task-context files are written into a memory-backed VM mount. pi only reads
|
|
24804
|
+
* `<available_skills>` metadata (name, description, location), never the skill
|
|
24737
24805
|
* body, so we construct synthetic `Skill` objects pointing at the in-VM path
|
|
24738
24806
|
* without ever materialising the file on the host.
|
|
24739
24807
|
*/
|
|
24740
24808
|
/**
|
|
24741
|
-
* Where in the VM we write
|
|
24809
|
+
* Where in the VM we write task-context bodies — the memory-backed mount
|
|
24742
24810
|
* declared in `vm-manager.ts`. See the comment on
|
|
24743
|
-
* `
|
|
24744
|
-
*
|
|
24745
|
-
*
|
|
24746
|
-
*
|
|
24811
|
+
* `GUEST_TASK_CONTEXT_MOUNT` there for the full rationale (ephemeral by
|
|
24812
|
+
* intent + the worktree symlink interaction with Gondolin's sandbox-escape
|
|
24813
|
+
* protection). The agent's Gondolin Read tool accepts paths under this mount
|
|
24814
|
+
* via `toGuestPath` in `tool-operations.ts`.
|
|
24747
24815
|
*/
|
|
24748
|
-
var SKILL_ROOT_IN_VM =
|
|
24816
|
+
var SKILL_ROOT_IN_VM = `${GUEST_TASK_CONTEXT_MOUNT}/skills`;
|
|
24817
|
+
var INLINE_CONTEXT_ROOT_IN_VM = `${GUEST_TASK_CONTEXT_MOUNT}/context`;
|
|
24749
24818
|
/** Bounds borrowed from pi's skill validation; conservative caps so a
|
|
24750
24819
|
* malformed SKILL.md doesn't bloat the system prompt. */
|
|
24751
24820
|
var MAX_SKILL_NAME = 64;
|
|
@@ -24756,13 +24825,7 @@ var MAX_SKILL_DESCRIPTION = 1024;
|
|
|
24756
24825
|
*/
|
|
24757
24826
|
async function injectTaskContext(args) {
|
|
24758
24827
|
const skills = [];
|
|
24759
|
-
|
|
24760
|
-
const { guestWorkspace } = args;
|
|
24761
|
-
const inlineContextRoot = `${guestWorkspace}/.moltnet/context`;
|
|
24762
|
-
const workspaceContextPack = `${guestWorkspace}/context-pack.md`;
|
|
24763
|
-
const workspaceAgentsMd = `${guestWorkspace}/AGENTS.md`;
|
|
24764
|
-
const workspaceClaudeDir = `${guestWorkspace}/.claude`;
|
|
24765
|
-
const workspaceClaudeMd = `${workspaceClaudeDir}/CLAUDE.md`;
|
|
24828
|
+
args.guestWorkspace;
|
|
24766
24829
|
const resolved = await resolveTaskContext({
|
|
24767
24830
|
context: args.context,
|
|
24768
24831
|
deliver: {
|
|
@@ -24779,23 +24842,12 @@ async function injectTaskContext(args) {
|
|
|
24779
24842
|
}));
|
|
24780
24843
|
},
|
|
24781
24844
|
contextFile: async ({ suggestedFileName, content }) => {
|
|
24782
|
-
await args.fs.mkdir(
|
|
24783
|
-
const filePath = `${
|
|
24845
|
+
await args.fs.mkdir(INLINE_CONTEXT_ROOT_IN_VM, { recursive: true });
|
|
24846
|
+
const filePath = `${INLINE_CONTEXT_ROOT_IN_VM}/${suggestedFileName}`;
|
|
24784
24847
|
await args.fs.writeFile(filePath, content, { mode: 420 });
|
|
24785
|
-
inlineContexts.push({
|
|
24786
|
-
slug: suggestedFileName.replace(/\.md$/u, ""),
|
|
24787
|
-
content
|
|
24788
|
-
});
|
|
24789
24848
|
}
|
|
24790
24849
|
}
|
|
24791
24850
|
});
|
|
24792
|
-
if (inlineContexts.length > 0) {
|
|
24793
|
-
const packContent = buildWorkspaceContextPack(inlineContexts);
|
|
24794
|
-
await args.fs.writeFile(workspaceContextPack, packContent, { mode: 420 });
|
|
24795
|
-
await args.fs.writeFile(workspaceAgentsMd, packContent, { mode: 420 });
|
|
24796
|
-
await args.fs.mkdir(workspaceClaudeDir, { recursive: true });
|
|
24797
|
-
await args.fs.writeFile(workspaceClaudeMd, "@../context-pack.md\n", { mode: 420 });
|
|
24798
|
-
}
|
|
24799
24851
|
return {
|
|
24800
24852
|
injected: resolved.injected,
|
|
24801
24853
|
skills,
|
|
@@ -24803,17 +24855,6 @@ async function injectTaskContext(args) {
|
|
|
24803
24855
|
userInlineSuffix: resolved.userInlineSuffix
|
|
24804
24856
|
};
|
|
24805
24857
|
}
|
|
24806
|
-
function buildWorkspaceContextPack(contexts) {
|
|
24807
|
-
return [
|
|
24808
|
-
"# Context Pack",
|
|
24809
|
-
"",
|
|
24810
|
-
...contexts.map(({ slug, content }) => [
|
|
24811
|
-
`## ${slug}`,
|
|
24812
|
-
"",
|
|
24813
|
-
content.trimEnd()
|
|
24814
|
-
].join("\n"))
|
|
24815
|
-
].join("\n\n").trimEnd() + "\n";
|
|
24816
|
-
}
|
|
24817
24858
|
/**
|
|
24818
24859
|
* Build a `Skill` object pi will faithfully render in
|
|
24819
24860
|
* `<available_skills>`. We extract `name` and `description` from the
|
|
@@ -25527,6 +25568,17 @@ function shouldSkipSeedEntry(sourceEntry, entryName, resolvedTargetDir) {
|
|
|
25527
25568
|
* `AgentRuntime`.
|
|
25528
25569
|
*/
|
|
25529
25570
|
var noopTurnEventHandler = () => {};
|
|
25571
|
+
async function openVmWorkspaceFileForRead(config) {
|
|
25572
|
+
const localPath = isAbsolute(config.filePath) ? config.filePath : resolve(config.cwdPath, config.filePath);
|
|
25573
|
+
const guestPath = toGuestPath(config.cwdPath, localPath, config.guestWorkspace);
|
|
25574
|
+
const info = await config.vm.fs.stat(guestPath);
|
|
25575
|
+
return {
|
|
25576
|
+
stream: await config.vm.fs.readFileStream(guestPath),
|
|
25577
|
+
isFile: info.isFile(),
|
|
25578
|
+
sizeBytes: typeof info.size === "number" ? info.size : void 0,
|
|
25579
|
+
displayPath: config.filePath
|
|
25580
|
+
};
|
|
25581
|
+
}
|
|
25530
25582
|
function createGondolinToolDefinitions(config) {
|
|
25531
25583
|
const { vm, mountPath, guestWorkspace } = config;
|
|
25532
25584
|
const grepTool = createGrepToolDefinition(mountPath);
|
|
@@ -25703,6 +25755,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
25703
25755
|
workspaceMode: workspace.mode,
|
|
25704
25756
|
extraAllowedHosts: opts.extraAllowedHosts,
|
|
25705
25757
|
sandboxConfig,
|
|
25758
|
+
forwardEnv: opts.forwardEnv,
|
|
25706
25759
|
signal: reporter.cancelSignal
|
|
25707
25760
|
});
|
|
25708
25761
|
} catch (err) {
|
|
@@ -25718,9 +25771,11 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
25718
25771
|
const taskTeamId = task.teamId ?? "";
|
|
25719
25772
|
activateAgentEnv(managed.credentials.agentEnv, agentRootDir);
|
|
25720
25773
|
const activeWorkspace = workspace;
|
|
25774
|
+
const activeManaged = managed;
|
|
25721
25775
|
if (!activeWorkspace) throw new Error("task workspace not prepared");
|
|
25722
25776
|
await emit("info", {
|
|
25723
25777
|
event: "execute_start",
|
|
25778
|
+
correlationId: task.correlationId ?? null,
|
|
25724
25779
|
taskType: task.taskType,
|
|
25725
25780
|
teamId: task.teamId,
|
|
25726
25781
|
provider: opts.provider,
|
|
@@ -25773,6 +25828,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
25773
25828
|
taskPrompt = assembled.text;
|
|
25774
25829
|
await emit("info", {
|
|
25775
25830
|
event: "prompt_assembled",
|
|
25831
|
+
correlationId: task.correlationId ?? null,
|
|
25776
25832
|
taskType: assembled.taskType,
|
|
25777
25833
|
sections: assembled.trace
|
|
25778
25834
|
});
|
|
@@ -25804,6 +25860,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
25804
25860
|
}
|
|
25805
25861
|
if (injectedContext.injected.length > 0) await emit("info", {
|
|
25806
25862
|
event: "context_injected",
|
|
25863
|
+
correlationId: task.correlationId ?? null,
|
|
25807
25864
|
count: injectedContext.injected.length,
|
|
25808
25865
|
bindings: injectedContext.injected.map((r) => r.binding),
|
|
25809
25866
|
slugs: injectedContext.injected.map((r) => r.slug)
|
|
@@ -25828,6 +25885,12 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
25828
25885
|
getSessionErrors: () => [],
|
|
25829
25886
|
clearSessionErrors: () => {},
|
|
25830
25887
|
getHostCwd: () => cwdPath,
|
|
25888
|
+
openWorkspaceFileForRead: (filePath) => openVmWorkspaceFileForRead({
|
|
25889
|
+
vm: activeManaged.vm,
|
|
25890
|
+
cwdPath,
|
|
25891
|
+
guestWorkspace: activeManaged.guestWorkspace,
|
|
25892
|
+
filePath
|
|
25893
|
+
}),
|
|
25831
25894
|
hostExecBaseEnv: new Set([...HOST_EXEC_DEFAULT_BASE_ENV, ...Object.keys(managed.credentials.agentEnv)]),
|
|
25832
25895
|
hostExecAutoApprove: opts.hostExecAutoApprove ?? opts.sandboxConfig?.hostExec?.autoApprove ?? false,
|
|
25833
25896
|
getTaskContext: () => ({
|
|
@@ -26301,6 +26364,167 @@ function describeToolErrorMessage(result) {
|
|
|
26301
26364
|
}
|
|
26302
26365
|
}
|
|
26303
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
|
|
26304
26528
|
//#region src/index.ts
|
|
26305
26529
|
/**
|
|
26306
26530
|
* @themoltnet/pi-extension — MoltNet pi extension
|
|
@@ -26601,4 +26825,4 @@ function moltnetExtension(pi) {
|
|
|
26601
26825
|
registerMoltnetReflectCommand(pi, state);
|
|
26602
26826
|
}
|
|
26603
26827
|
//#endregion
|
|
26604
|
-
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.
|
|
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",
|