@themoltnet/pi-extension 0.33.0 → 0.33.1
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 +8 -11
- package/dist/index.js +211 -168
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -308,6 +308,12 @@ export declare interface ExecutePiTaskOptions {
|
|
|
308
308
|
sandboxConfig?: SandboxConfig;
|
|
309
309
|
/** Host environment variable names to forward into the Pi VM. */
|
|
310
310
|
forwardEnv?: string[];
|
|
311
|
+
/**
|
|
312
|
+
* Runtime profile context defaults. Merged with task.input.context at
|
|
313
|
+
* execution time because the selected runtime profile is known only after
|
|
314
|
+
* claim. Task entries override profile entries with the same slug.
|
|
315
|
+
*/
|
|
316
|
+
runtimeProfileContext?: readonly ContextRef[];
|
|
311
317
|
/**
|
|
312
318
|
* Forwarded to `buildTaskUserPrompt` for per-type builders. Static
|
|
313
319
|
* across tasks. Today no built-in builder needs per-task `extras` —
|
|
@@ -480,14 +486,14 @@ export declare interface InjectedTaskContext {
|
|
|
480
486
|
}
|
|
481
487
|
|
|
482
488
|
/**
|
|
483
|
-
* Resolve
|
|
489
|
+
* Resolve effective runtime context and inject the side effects Pi
|
|
484
490
|
* needs. Safe to call with an empty array — returns an inert result.
|
|
485
491
|
*/
|
|
486
492
|
export declare function injectTaskContext(args: InjectTaskContextArgs): Promise<InjectedTaskContext>;
|
|
487
493
|
|
|
488
494
|
export declare interface InjectTaskContextArgs {
|
|
489
495
|
/** Empty array (the default for any non-eval task) is a no-op. */
|
|
490
|
-
context:
|
|
496
|
+
context: readonly ContextRef[];
|
|
491
497
|
/** Guest filesystem handle. In production this is `managed.vm.fs`. */
|
|
492
498
|
fs: VmFsForContext;
|
|
493
499
|
/** Guest path where the active host workspace is mounted. */
|
|
@@ -922,15 +928,6 @@ declare const Task: Type.TObject<{
|
|
|
922
928
|
|
|
923
929
|
declare type Task = Static<typeof Task>;
|
|
924
930
|
|
|
925
|
-
/** Reusable input fragment for any task type. Soft cap at 5 items. */
|
|
926
|
-
declare const TaskContext: Type.TArray<Type.TObject<{
|
|
927
|
-
slug: Type.TString;
|
|
928
|
-
binding: Type.TUnion<[Type.TLiteral<"skill">, Type.TLiteral<"context_inline">, Type.TLiteral<"prompt_prefix">, Type.TLiteral<"user_inline">]>;
|
|
929
|
-
content: Type.TString;
|
|
930
|
-
}>>;
|
|
931
|
-
|
|
932
|
-
declare type TaskContext = Static<typeof TaskContext>;
|
|
933
|
-
|
|
934
931
|
declare const TaskMessage: Type.TObject<{
|
|
935
932
|
taskId: Type.TString;
|
|
936
933
|
attemptN: Type.TNumber;
|
package/dist/index.js
CHANGED
|
@@ -1965,7 +1965,7 @@ var findLatestRuntimeSlotForAttempt = (options) => (options.client ?? client).ge
|
|
|
1965
1965
|
...options
|
|
1966
1966
|
});
|
|
1967
1967
|
/**
|
|
1968
|
-
* Queue asynchronous deletion of terminal tasks in bulk. By default,
|
|
1968
|
+
* Queue asynchronous deletion of waiting, queued, and terminal tasks in bulk. By default, dispatched, running, unauthorized, missing, and protected tasks are skipped. Set force: true with a reason to delete protected terminal tasks.
|
|
1969
1969
|
*/
|
|
1970
1970
|
var batchDeleteTasks = (options) => (options.client ?? client).delete({
|
|
1971
1971
|
security: [
|
|
@@ -10030,7 +10030,7 @@ _Object_({
|
|
|
10030
10030
|
* (server-side schema check). Self-assessment is a truthful self-rating,
|
|
10031
10031
|
* NOT enforcement — `verification.passed=false` does not block /complete
|
|
10032
10032
|
* and does not affect `acceptedAttemptN`. See
|
|
10033
|
-
* `docs/
|
|
10033
|
+
* `docs/use/tasks-and-runtime.md` for the full producer/judge flow.
|
|
10034
10034
|
*
|
|
10035
10035
|
* **Binding evaluation** (judgment tasks: `assess_brief`, `judge_pack`).
|
|
10036
10036
|
* A separate task whose IS the application of `successCriteria` to
|
|
@@ -19937,8 +19937,19 @@ async function resolvePersistentSessionManager(args) {
|
|
|
19937
19937
|
//#endregion
|
|
19938
19938
|
//#region ../agent-runtime/src/context-bindings.ts
|
|
19939
19939
|
var PROMPT_SEPARATOR = "\n\n---\n\n";
|
|
19940
|
+
var MAX_MERGED_RUNTIME_CONTEXT_ENTRIES = 10;
|
|
19940
19941
|
/**
|
|
19941
|
-
*
|
|
19942
|
+
* Merge runtime-profile context defaults with task-scoped context. Profile
|
|
19943
|
+
* entries are defaults; task entries with the same slug override them.
|
|
19944
|
+
*/
|
|
19945
|
+
function mergeRuntimeProfileContext(profileContext, taskContext) {
|
|
19946
|
+
const taskSlugs = new Set(taskContext.map((ref) => ref.slug));
|
|
19947
|
+
const merged = [...profileContext.filter((ref) => !taskSlugs.has(ref.slug)), ...taskContext];
|
|
19948
|
+
if (merged.length > MAX_MERGED_RUNTIME_CONTEXT_ENTRIES) throw new Error(`merged runtime context has ${merged.length} entries; maximum is ${MAX_MERGED_RUNTIME_CONTEXT_ENTRIES}`);
|
|
19949
|
+
return merged;
|
|
19950
|
+
}
|
|
19951
|
+
/**
|
|
19952
|
+
* Resolve runtime context entries into delivered side-effects (skills
|
|
19942
19953
|
* persisted via `deliver.skill`) and prompt fragments
|
|
19943
19954
|
* (`systemPromptPrefix`, `userInlineSuffix`) the caller weaves into the
|
|
19944
19955
|
* built prompt.
|
|
@@ -19958,9 +19969,10 @@ var PROMPT_SEPARATOR = "\n\n---\n\n";
|
|
|
19958
19969
|
* - `user_inline` → content appended to `userInlineSuffix` in
|
|
19959
19970
|
* declared order, same separator.
|
|
19960
19971
|
*
|
|
19961
|
-
* No fetching, no hashing — bytes are inlined in `ContextRef.content
|
|
19962
|
-
*
|
|
19963
|
-
*
|
|
19972
|
+
* No fetching, no hashing — bytes are inlined in `ContextRef.content`.
|
|
19973
|
+
* Task-scoped entries are pinned by the task's `inputCid`; profile-scoped
|
|
19974
|
+
* entries are pinned by the runtime profile revision/source the daemon
|
|
19975
|
+
* resolved. The resolver just dispatches already-selected bytes.
|
|
19964
19976
|
*
|
|
19965
19977
|
* The function is pure with respect to its arguments: file writes are
|
|
19966
19978
|
* confined to the injected `deliver` callback, which makes the
|
|
@@ -20006,12 +20018,13 @@ function formatInlineContextBlock(slug, content) {
|
|
|
20006
20018
|
"### Injected Task Context",
|
|
20007
20019
|
"",
|
|
20008
20020
|
`Context id: \`${slug}\``,
|
|
20009
|
-
"The following raw context was
|
|
20010
|
-
"as task-relevant background that may
|
|
20011
|
-
"when it contains repo- or
|
|
20021
|
+
"The following raw context was selected for this task by its task input",
|
|
20022
|
+
"or runtime profile. Treat it as task-relevant background that may",
|
|
20023
|
+
"override generic coding instincts when it contains repo- or",
|
|
20024
|
+
"workflow-specific constraints.",
|
|
20012
20025
|
"The same content may also be materialized by the runtime under",
|
|
20013
20026
|
"`/moltnet-task-context/context` for tool-based inspection. Do not",
|
|
20014
|
-
"create or rely on workspace mirror files for this
|
|
20027
|
+
"create or rely on workspace mirror files for this runtime context.",
|
|
20015
20028
|
"",
|
|
20016
20029
|
"<context>",
|
|
20017
20030
|
content,
|
|
@@ -21334,11 +21347,11 @@ function buildRenderPackUserPrompt(input, ctx) {
|
|
|
21334
21347
|
* `judge_eval_attempt` task(s) grade against their own hidden rubric.
|
|
21335
21348
|
*
|
|
21336
21349
|
* Context delivery is handled by `resolveTaskContext` (see
|
|
21337
|
-
* libs/agent-runtime/src/context-bindings.ts) and
|
|
21338
|
-
* prompt is rendered
|
|
21339
|
-
*
|
|
21340
|
-
*
|
|
21341
|
-
*
|
|
21350
|
+
* libs/agent-runtime/src/context-bindings.ts) and is selected BEFORE this
|
|
21351
|
+
* prompt is rendered. Task-scoped context lives in `input.context`; runtime
|
|
21352
|
+
* profile defaults arrive as `ctx.effectiveRuntimeContext` after the runtime
|
|
21353
|
+
* merges them with task context. This builder only renders context
|
|
21354
|
+
* discipline; it does NOT inline context bytes itself.
|
|
21342
21355
|
*
|
|
21343
21356
|
* Prompt-shape notes (issue #1175, area 1):
|
|
21344
21357
|
* - No `Correlation` section: the agent never acts on it. The id is
|
|
@@ -21356,12 +21369,13 @@ function buildRenderPackUserPrompt(input, ctx) {
|
|
|
21356
21369
|
*/
|
|
21357
21370
|
function buildRunEvalUserPrompt(input, ctx) {
|
|
21358
21371
|
const { scenario, variantLabel, successCriteria } = input;
|
|
21359
|
-
const
|
|
21360
|
-
const
|
|
21372
|
+
const effectiveRuntimeContext = ctx.effectiveRuntimeContext ?? input.context;
|
|
21373
|
+
const hasContext = effectiveRuntimeContext.length > 0;
|
|
21374
|
+
const hasInlineContext = effectiveRuntimeContext.some((entry) => entry.binding === "context_inline");
|
|
21361
21375
|
const header = `# Run Eval Agent\n\nYou are running an evaluation scenario as variant \`${variantLabel}\`.\nTask id: \`${ctx.taskId}\``;
|
|
21362
21376
|
const contextDiscipline = hasContext ? [
|
|
21363
21377
|
"This task includes Injected Task Context supplied by the task",
|
|
21364
|
-
"
|
|
21378
|
+
"input or runtime profile. You MUST inspect it BEFORE you write solution files or",
|
|
21365
21379
|
"draft your final answer — not after.",
|
|
21366
21380
|
"",
|
|
21367
21381
|
"Reconcile every constraint from that context **into the code path",
|
|
@@ -21534,7 +21548,8 @@ function buildTaskUserPrompt(task, ctx) {
|
|
|
21534
21548
|
return buildRunEvalUserPrompt(task.input, {
|
|
21535
21549
|
diaryId: ctx.diaryId,
|
|
21536
21550
|
taskId: ctx.taskId,
|
|
21537
|
-
correlationId: task.correlationId
|
|
21551
|
+
correlationId: task.correlationId,
|
|
21552
|
+
effectiveRuntimeContext: ctx.effectiveRuntimeContext
|
|
21538
21553
|
});
|
|
21539
21554
|
default: throw new Error(`No prompt builder registered for taskType="${task.taskType}"`);
|
|
21540
21555
|
}
|
|
@@ -25007,119 +25022,6 @@ var require_multistream = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
25007
25022
|
module.exports.pino = pino;
|
|
25008
25023
|
})))();
|
|
25009
25024
|
//#endregion
|
|
25010
|
-
//#region src/runtime/inject-task-context.ts
|
|
25011
|
-
/**
|
|
25012
|
-
* Slice 1.5 of #943 — wire the agent-runtime resolver into the
|
|
25013
|
-
* pi-extension execution path.
|
|
25014
|
-
*
|
|
25015
|
-
* `resolveTaskContext` is a pure dispatcher; this module provides the
|
|
25016
|
-
* Gondolin-aware deliverer and the post-resolution shape the
|
|
25017
|
-
* `execute-pi-task` caller needs to splice into pi's setup:
|
|
25018
|
-
*
|
|
25019
|
-
* - `systemPromptPrefix` → fed into `appendSystemPrompt` alongside
|
|
25020
|
-
* the runtime instructor (it IS a system-prompt fragment).
|
|
25021
|
-
* - `userInlineSuffix` → appended to the `buildTaskUserPrompt`
|
|
25022
|
-
* output BEFORE `session.prompt(text)`.
|
|
25023
|
-
* - `skills` → spliced into the `skillsOverride` callback's
|
|
25024
|
-
* return value. pi includes them in `<available_skills>` in the
|
|
25025
|
-
* system prompt; the agent fetches the body on demand via the
|
|
25026
|
-
* Read tool.
|
|
25027
|
-
*
|
|
25028
|
-
* Task-context files are written into a memory-backed VM mount. pi only reads
|
|
25029
|
-
* `<available_skills>` metadata (name, description, location), never the skill
|
|
25030
|
-
* body, so we construct synthetic `Skill` objects pointing at the in-VM path
|
|
25031
|
-
* without ever materialising the file on the host.
|
|
25032
|
-
*/
|
|
25033
|
-
/**
|
|
25034
|
-
* Where in the VM we write task-context bodies — the memory-backed mount
|
|
25035
|
-
* declared in `vm-manager.ts`. See the comment on
|
|
25036
|
-
* `GUEST_TASK_CONTEXT_MOUNT` there for the full rationale (ephemeral by
|
|
25037
|
-
* intent + the worktree symlink interaction with Gondolin's sandbox-escape
|
|
25038
|
-
* protection). The agent's Gondolin Read tool accepts paths under this mount
|
|
25039
|
-
* via `toGuestPath` in `tool-operations.ts`.
|
|
25040
|
-
*/
|
|
25041
|
-
var SKILL_ROOT_IN_VM = `${GUEST_TASK_CONTEXT_MOUNT}/skills`;
|
|
25042
|
-
var INLINE_CONTEXT_ROOT_IN_VM = `${GUEST_TASK_CONTEXT_MOUNT}/context`;
|
|
25043
|
-
/** Bounds borrowed from pi's skill validation; conservative caps so a
|
|
25044
|
-
* malformed SKILL.md doesn't bloat the system prompt. */
|
|
25045
|
-
var MAX_SKILL_NAME = 64;
|
|
25046
|
-
var MAX_SKILL_DESCRIPTION = 1024;
|
|
25047
|
-
/**
|
|
25048
|
-
* Resolve a task's `input.context[]` and inject the side effects pi
|
|
25049
|
-
* needs. Safe to call with an empty array — returns an inert result.
|
|
25050
|
-
*/
|
|
25051
|
-
async function injectTaskContext(args) {
|
|
25052
|
-
const skills = [];
|
|
25053
|
-
args.guestWorkspace;
|
|
25054
|
-
const resolved = await resolveTaskContext({
|
|
25055
|
-
context: args.context,
|
|
25056
|
-
deliver: {
|
|
25057
|
-
skill: async ({ slug, content }) => {
|
|
25058
|
-
const dir = `${SKILL_ROOT_IN_VM}/${slug}`;
|
|
25059
|
-
const filePath = `${dir}/SKILL.md`;
|
|
25060
|
-
await args.fs.mkdir(dir, { recursive: true });
|
|
25061
|
-
await args.fs.writeFile(filePath, content, { mode: 420 });
|
|
25062
|
-
skills.push(buildSyntheticSkill({
|
|
25063
|
-
slug,
|
|
25064
|
-
content,
|
|
25065
|
-
filePath,
|
|
25066
|
-
dir
|
|
25067
|
-
}));
|
|
25068
|
-
},
|
|
25069
|
-
contextFile: async ({ suggestedFileName, content }) => {
|
|
25070
|
-
await args.fs.mkdir(INLINE_CONTEXT_ROOT_IN_VM, { recursive: true });
|
|
25071
|
-
const filePath = `${INLINE_CONTEXT_ROOT_IN_VM}/${suggestedFileName}`;
|
|
25072
|
-
await args.fs.writeFile(filePath, content, { mode: 420 });
|
|
25073
|
-
}
|
|
25074
|
-
}
|
|
25075
|
-
});
|
|
25076
|
-
return {
|
|
25077
|
-
injected: resolved.injected,
|
|
25078
|
-
skills,
|
|
25079
|
-
systemPromptPrefix: resolved.systemPromptPrefix,
|
|
25080
|
-
userInlineSuffix: resolved.userInlineSuffix
|
|
25081
|
-
};
|
|
25082
|
-
}
|
|
25083
|
-
/**
|
|
25084
|
-
* Build a `Skill` object pi will faithfully render in
|
|
25085
|
-
* `<available_skills>`. We extract `name` and `description` from the
|
|
25086
|
-
* skill content's YAML frontmatter using pi's own `parseFrontmatter`
|
|
25087
|
-
* helper (proper YAML, not a regex hack) and fall back to the slug +
|
|
25088
|
-
* a generic description so a SKILL.md without frontmatter still
|
|
25089
|
-
* renders something meaningful.
|
|
25090
|
-
*
|
|
25091
|
-
* Frontmatter parsing is best-effort: a malformed YAML block is
|
|
25092
|
-
* optional metadata, not a reason to fail the task. We swallow parser
|
|
25093
|
-
* errors and fall back to the slug-derived metadata; the skill body
|
|
25094
|
-
* is unaffected.
|
|
25095
|
-
*
|
|
25096
|
-
* pi's `formatSkillsForPrompt` only reads `name`, `description`, and
|
|
25097
|
-
* `filePath` — `sourceInfo`/`baseDir` exist on the type but never
|
|
25098
|
-
* surface in the prompt, so a synthetic `SourceInfo` is enough.
|
|
25099
|
-
*/
|
|
25100
|
-
function buildSyntheticSkill(args) {
|
|
25101
|
-
let fm = {};
|
|
25102
|
-
try {
|
|
25103
|
-
fm = parseFrontmatter(args.content).frontmatter;
|
|
25104
|
-
} catch {}
|
|
25105
|
-
return {
|
|
25106
|
-
name: clip(typeof fm.name === "string" && fm.name.trim().length > 0 ? fm.name.trim() : args.slug, MAX_SKILL_NAME),
|
|
25107
|
-
description: clip(typeof fm.description === "string" && fm.description.trim().length > 0 ? fm.description.trim() : `Task-injected context skill (${args.slug})`, MAX_SKILL_DESCRIPTION),
|
|
25108
|
-
filePath: args.filePath,
|
|
25109
|
-
baseDir: args.dir,
|
|
25110
|
-
sourceInfo: createSyntheticSourceInfo(args.filePath, {
|
|
25111
|
-
source: "moltnet:task-context",
|
|
25112
|
-
scope: "temporary",
|
|
25113
|
-
origin: "top-level",
|
|
25114
|
-
baseDir: args.dir
|
|
25115
|
-
}),
|
|
25116
|
-
disableModelInvocation: fm["disable-model-invocation"] === true
|
|
25117
|
-
};
|
|
25118
|
-
}
|
|
25119
|
-
function clip(s, max) {
|
|
25120
|
-
return s.length > max ? s.slice(0, max) : s;
|
|
25121
|
-
}
|
|
25122
|
-
//#endregion
|
|
25123
25025
|
//#region src/runtime/resolve-prior-context.ts
|
|
25124
25026
|
/**
|
|
25125
25027
|
* Fetch the named attempt's output and project it into the prompt's
|
|
@@ -25306,6 +25208,113 @@ async function withTimeout(promise, timeoutMs, onTimeout) {
|
|
|
25306
25208
|
}
|
|
25307
25209
|
}
|
|
25308
25210
|
//#endregion
|
|
25211
|
+
//#region src/runtime/runtime-context.ts
|
|
25212
|
+
/**
|
|
25213
|
+
* Pi-specific runtime context handling.
|
|
25214
|
+
*
|
|
25215
|
+
* `@themoltnet/agent-runtime` owns generic context semantics: merge profile
|
|
25216
|
+
* defaults with task context, resolve bindings, and produce prompt fragments.
|
|
25217
|
+
* This module owns the Pi/Gondolin boundary: validate effective context for an
|
|
25218
|
+
* attempt, write skill/context files into the VM, and build synthetic Pi Skill
|
|
25219
|
+
* metadata for injected skill bindings.
|
|
25220
|
+
*/
|
|
25221
|
+
/**
|
|
25222
|
+
* Where in the VM we write runtime-context bodies — the memory-backed mount
|
|
25223
|
+
* declared in `vm-manager.ts`. See the comment on
|
|
25224
|
+
* `GUEST_TASK_CONTEXT_MOUNT` there for the full rationale (ephemeral by
|
|
25225
|
+
* intent + the worktree symlink interaction with Gondolin's sandbox-escape
|
|
25226
|
+
* protection). The agent's Gondolin Read tool accepts paths under this mount
|
|
25227
|
+
* via `toGuestPath` in `tool-operations.ts`.
|
|
25228
|
+
*/
|
|
25229
|
+
var SKILL_ROOT_IN_VM = `${GUEST_TASK_CONTEXT_MOUNT}/skills`;
|
|
25230
|
+
var INLINE_CONTEXT_ROOT_IN_VM = `${GUEST_TASK_CONTEXT_MOUNT}/context`;
|
|
25231
|
+
/** Bounds borrowed from pi's skill validation; conservative caps so a
|
|
25232
|
+
* malformed SKILL.md doesn't bloat the system prompt. */
|
|
25233
|
+
var MAX_SKILL_NAME = 64;
|
|
25234
|
+
var MAX_SKILL_DESCRIPTION = 1024;
|
|
25235
|
+
function resolveEffectiveRuntimeContext(args) {
|
|
25236
|
+
const taskContext = args.rawTaskContext === void 0 ? [] : args.rawTaskContext;
|
|
25237
|
+
if (!Check(TaskContext, taskContext)) throw new Error(`task.input.context failed TaskContext validation: ${JSON.stringify([...Errors(TaskContext, taskContext)].slice(0, 3))}`);
|
|
25238
|
+
const profileContext = args.runtimeProfileContext ?? [];
|
|
25239
|
+
if (!Check(TaskContext, profileContext)) throw new Error(`runtime profile context failed TaskContext validation: ${JSON.stringify([...Errors(TaskContext, profileContext)].slice(0, 3))}`);
|
|
25240
|
+
return mergeRuntimeProfileContext(profileContext, taskContext);
|
|
25241
|
+
}
|
|
25242
|
+
/**
|
|
25243
|
+
* Resolve effective runtime context and inject the side effects Pi
|
|
25244
|
+
* needs. Safe to call with an empty array — returns an inert result.
|
|
25245
|
+
*/
|
|
25246
|
+
async function injectRuntimeContext(args) {
|
|
25247
|
+
const skills = [];
|
|
25248
|
+
args.guestWorkspace;
|
|
25249
|
+
const resolved = await resolveTaskContext({
|
|
25250
|
+
context: args.context,
|
|
25251
|
+
deliver: {
|
|
25252
|
+
skill: async ({ slug, content }) => {
|
|
25253
|
+
const dir = `${SKILL_ROOT_IN_VM}/${slug}`;
|
|
25254
|
+
const filePath = `${dir}/SKILL.md`;
|
|
25255
|
+
await args.fs.mkdir(dir, { recursive: true });
|
|
25256
|
+
await args.fs.writeFile(filePath, content, { mode: 420 });
|
|
25257
|
+
skills.push(buildSyntheticSkill({
|
|
25258
|
+
slug,
|
|
25259
|
+
content,
|
|
25260
|
+
filePath,
|
|
25261
|
+
dir
|
|
25262
|
+
}));
|
|
25263
|
+
},
|
|
25264
|
+
contextFile: async ({ suggestedFileName, content }) => {
|
|
25265
|
+
await args.fs.mkdir(INLINE_CONTEXT_ROOT_IN_VM, { recursive: true });
|
|
25266
|
+
const filePath = `${INLINE_CONTEXT_ROOT_IN_VM}/${suggestedFileName}`;
|
|
25267
|
+
await args.fs.writeFile(filePath, content, { mode: 420 });
|
|
25268
|
+
}
|
|
25269
|
+
}
|
|
25270
|
+
});
|
|
25271
|
+
return {
|
|
25272
|
+
injected: resolved.injected,
|
|
25273
|
+
skills,
|
|
25274
|
+
systemPromptPrefix: resolved.systemPromptPrefix,
|
|
25275
|
+
userInlineSuffix: resolved.userInlineSuffix
|
|
25276
|
+
};
|
|
25277
|
+
}
|
|
25278
|
+
/**
|
|
25279
|
+
* Build a `Skill` object pi will faithfully render in
|
|
25280
|
+
* `<available_skills>`. We extract `name` and `description` from the
|
|
25281
|
+
* skill content's YAML frontmatter using pi's own `parseFrontmatter`
|
|
25282
|
+
* helper (proper YAML, not a regex hack) and fall back to the slug +
|
|
25283
|
+
* a generic description so a SKILL.md without frontmatter still
|
|
25284
|
+
* renders something meaningful.
|
|
25285
|
+
*
|
|
25286
|
+
* Frontmatter parsing is best-effort: a malformed YAML block is
|
|
25287
|
+
* optional metadata, not a reason to fail the task. We swallow parser
|
|
25288
|
+
* errors and fall back to the slug-derived metadata; the skill body
|
|
25289
|
+
* is unaffected.
|
|
25290
|
+
*
|
|
25291
|
+
* pi's `formatSkillsForPrompt` only reads `name`, `description`, and
|
|
25292
|
+
* `filePath` — `sourceInfo`/`baseDir` exist on the type but never
|
|
25293
|
+
* surface in the prompt, so a synthetic `SourceInfo` is enough.
|
|
25294
|
+
*/
|
|
25295
|
+
function buildSyntheticSkill(args) {
|
|
25296
|
+
let fm = {};
|
|
25297
|
+
try {
|
|
25298
|
+
fm = parseFrontmatter(args.content).frontmatter;
|
|
25299
|
+
} catch {}
|
|
25300
|
+
return {
|
|
25301
|
+
name: clip(typeof fm.name === "string" && fm.name.trim().length > 0 ? fm.name.trim() : args.slug, MAX_SKILL_NAME),
|
|
25302
|
+
description: clip(typeof fm.description === "string" && fm.description.trim().length > 0 ? fm.description.trim() : `Runtime-injected context skill (${args.slug})`, MAX_SKILL_DESCRIPTION),
|
|
25303
|
+
filePath: args.filePath,
|
|
25304
|
+
baseDir: args.dir,
|
|
25305
|
+
sourceInfo: createSyntheticSourceInfo(args.filePath, {
|
|
25306
|
+
source: "moltnet:runtime-context",
|
|
25307
|
+
scope: "temporary",
|
|
25308
|
+
origin: "top-level",
|
|
25309
|
+
baseDir: args.dir
|
|
25310
|
+
}),
|
|
25311
|
+
disableModelInvocation: fm["disable-model-invocation"] === true
|
|
25312
|
+
};
|
|
25313
|
+
}
|
|
25314
|
+
function clip(s, max) {
|
|
25315
|
+
return s.length > max ? s.slice(0, max) : s;
|
|
25316
|
+
}
|
|
25317
|
+
//#endregion
|
|
25309
25318
|
//#region src/runtime/subagent-tool.ts
|
|
25310
25319
|
var SUBAGENT_SUBMIT_TOOL_NAME = "submit_subagent_output";
|
|
25311
25320
|
var DEFAULT_SUBAGENT_SUBMIT_VALIDATION_RETRIES = 2;
|
|
@@ -25841,6 +25850,46 @@ function resolveSubmitTools(taskType, opts = {}) {
|
|
|
25841
25850
|
};
|
|
25842
25851
|
}
|
|
25843
25852
|
//#endregion
|
|
25853
|
+
//#region src/runtime/task-event-emitter.ts
|
|
25854
|
+
var LOG_TRUNCATE_LIMIT = 4 * 1024;
|
|
25855
|
+
async function emitTaskEvent(input) {
|
|
25856
|
+
try {
|
|
25857
|
+
input.onTurnEvent(input.kind, summarizePayloadForLog(input.kind, input.payload));
|
|
25858
|
+
} catch (err) {
|
|
25859
|
+
process.stderr.write(`[emit] onTurnEvent threw for kind="${input.kind}": ${err instanceof Error ? err.message : String(err)}\n`);
|
|
25860
|
+
}
|
|
25861
|
+
try {
|
|
25862
|
+
await input.reporter.record({
|
|
25863
|
+
kind: input.kind,
|
|
25864
|
+
payload: input.payload
|
|
25865
|
+
});
|
|
25866
|
+
} catch (err) {
|
|
25867
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
25868
|
+
input.log(`executePiTask: reporter.record() failed for task ${input.taskId} attempt ${input.attemptN} kind="${input.kind}": ${detail}`);
|
|
25869
|
+
}
|
|
25870
|
+
}
|
|
25871
|
+
function summarizePayloadForLog(kind, payload) {
|
|
25872
|
+
switch (kind) {
|
|
25873
|
+
case "text_delta": {
|
|
25874
|
+
const delta = payload.delta;
|
|
25875
|
+
return { chars: typeof delta === "string" ? delta.length : 0 };
|
|
25876
|
+
}
|
|
25877
|
+
case "tool_call_start": return { tool: payload.tool_name };
|
|
25878
|
+
case "tool_call_end": return {
|
|
25879
|
+
tool: payload.tool_name,
|
|
25880
|
+
is_error: payload.is_error === true,
|
|
25881
|
+
...payload.is_error === true && payload.result !== void 0 ? { result: payload.result } : {}
|
|
25882
|
+
};
|
|
25883
|
+
case "turn_end": return { stop_reason: payload.stop_reason };
|
|
25884
|
+
case "error": return {
|
|
25885
|
+
phase: payload.phase,
|
|
25886
|
+
message: typeof payload.message === "string" ? payload.message.slice(0, LOG_TRUNCATE_LIMIT) : payload.message
|
|
25887
|
+
};
|
|
25888
|
+
case "info": return Object.fromEntries(Object.entries(payload).map(([k, v]) => [k, typeof v === "string" ? v.slice(0, LOG_TRUNCATE_LIMIT) : v]));
|
|
25889
|
+
default: return payload;
|
|
25890
|
+
}
|
|
25891
|
+
}
|
|
25892
|
+
//#endregion
|
|
25844
25893
|
//#region src/runtime/task-workspace.ts
|
|
25845
25894
|
function prepareTaskWorkspace(task, requestedMountPath, executionPlan) {
|
|
25846
25895
|
const branch = executionPlan?.worktreeBranch ?? null;
|
|
@@ -26209,15 +26258,17 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
26209
26258
|
onTurnEvent = noopTurnEventHandler;
|
|
26210
26259
|
}
|
|
26211
26260
|
else onTurnEvent = opts.onTurnEvent ?? noopTurnEventHandler;
|
|
26212
|
-
const emit = (kind, payload) => {
|
|
26213
|
-
|
|
26214
|
-
onTurnEvent(kind, summarizePayloadForLog(kind, payload));
|
|
26215
|
-
} catch (err) {
|
|
26216
|
-
process.stderr.write(`[emit] onTurnEvent threw for kind="${kind}": ${err instanceof Error ? err.message : String(err)}\n`);
|
|
26217
|
-
}
|
|
26218
|
-
return reporter.record({
|
|
26261
|
+
const emit = async (kind, payload) => {
|
|
26262
|
+
await emitTaskEvent({
|
|
26219
26263
|
kind,
|
|
26220
|
-
payload
|
|
26264
|
+
payload,
|
|
26265
|
+
onTurnEvent,
|
|
26266
|
+
reporter,
|
|
26267
|
+
taskId: task.id,
|
|
26268
|
+
attemptN,
|
|
26269
|
+
log: (message) => {
|
|
26270
|
+
process.stderr.write(`${message}\n`);
|
|
26271
|
+
}
|
|
26221
26272
|
});
|
|
26222
26273
|
};
|
|
26223
26274
|
const emitError = async (phase, message, extra = {}) => {
|
|
@@ -26321,6 +26372,21 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
26321
26372
|
message
|
|
26322
26373
|
});
|
|
26323
26374
|
}
|
|
26375
|
+
const rawContext = task.input.context;
|
|
26376
|
+
let effectiveRuntimeContext;
|
|
26377
|
+
try {
|
|
26378
|
+
effectiveRuntimeContext = resolveEffectiveRuntimeContext({
|
|
26379
|
+
rawTaskContext: rawContext,
|
|
26380
|
+
runtimeProfileContext: opts.runtimeProfileContext
|
|
26381
|
+
});
|
|
26382
|
+
} catch (err) {
|
|
26383
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
26384
|
+
await emit("error", {
|
|
26385
|
+
message,
|
|
26386
|
+
phase: "context_resolution"
|
|
26387
|
+
});
|
|
26388
|
+
return makeFailedOutput("context_resolution_failed", message);
|
|
26389
|
+
}
|
|
26324
26390
|
let taskPrompt;
|
|
26325
26391
|
try {
|
|
26326
26392
|
const assembled = buildTaskUserPrompt(task, {
|
|
@@ -26333,7 +26399,8 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
26333
26399
|
source: executionPlan?.workspaceSeed?.source === "producer" ? "producer_copy" : executionPlan?.workspaceAttachment !== void 0 ? "producer_attachment" : void 0
|
|
26334
26400
|
},
|
|
26335
26401
|
extras: opts.promptExtras,
|
|
26336
|
-
priorContext: resolvedPriorContext
|
|
26402
|
+
priorContext: resolvedPriorContext,
|
|
26403
|
+
effectiveRuntimeContext
|
|
26337
26404
|
});
|
|
26338
26405
|
taskPrompt = assembled.text;
|
|
26339
26406
|
await emit("info", {
|
|
@@ -26350,13 +26417,10 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
26350
26417
|
});
|
|
26351
26418
|
return makeFailedOutput("prompt_build_failed", message);
|
|
26352
26419
|
}
|
|
26353
|
-
const rawContext = task.input.context;
|
|
26354
26420
|
let injectedContext;
|
|
26355
26421
|
try {
|
|
26356
|
-
|
|
26357
|
-
|
|
26358
|
-
injectedContext = await injectTaskContext({
|
|
26359
|
-
context: contextArray,
|
|
26422
|
+
injectedContext = await injectRuntimeContext({
|
|
26423
|
+
context: effectiveRuntimeContext,
|
|
26360
26424
|
fs: managed.vm.fs,
|
|
26361
26425
|
guestWorkspace: managed.guestWorkspace
|
|
26362
26426
|
});
|
|
@@ -26948,27 +27012,6 @@ function wireSessionAbort(cancelSignal, session) {
|
|
|
26948
27012
|
* `task_messages.payload` row. Bodies above 4 KiB are replaced with a
|
|
26949
27013
|
* `{ truncated, original_size }` marker so the JSONL/DB size stays bounded.
|
|
26950
27014
|
*/
|
|
26951
|
-
function summarizePayloadForLog(kind, payload) {
|
|
26952
|
-
switch (kind) {
|
|
26953
|
-
case "text_delta": {
|
|
26954
|
-
const delta = payload.delta;
|
|
26955
|
-
return { chars: typeof delta === "string" ? delta.length : 0 };
|
|
26956
|
-
}
|
|
26957
|
-
case "tool_call_start": return { tool: payload.tool_name };
|
|
26958
|
-
case "tool_call_end": return {
|
|
26959
|
-
tool: payload.tool_name,
|
|
26960
|
-
is_error: payload.is_error === true,
|
|
26961
|
-
...payload.is_error === true && payload.result !== void 0 ? { result: payload.result } : {}
|
|
26962
|
-
};
|
|
26963
|
-
case "turn_end": return { stop_reason: payload.stop_reason };
|
|
26964
|
-
case "error": return {
|
|
26965
|
-
phase: payload.phase,
|
|
26966
|
-
message: typeof payload.message === "string" ? payload.message.slice(0, TRUNCATE_LIMIT) : payload.message
|
|
26967
|
-
};
|
|
26968
|
-
case "info": return Object.fromEntries(Object.entries(payload).map(([k, v]) => [k, typeof v === "string" ? v.slice(0, TRUNCATE_LIMIT) : v]));
|
|
26969
|
-
default: return payload;
|
|
26970
|
-
}
|
|
26971
|
-
}
|
|
26972
27015
|
/**
|
|
26973
27016
|
* Classify a `tool_execution_end` event for telemetry purposes.
|
|
26974
27017
|
*
|
|
@@ -27565,4 +27608,4 @@ function moltnetExtension(pi) {
|
|
|
27565
27608
|
registerMoltnetReflectCommand(pi, state);
|
|
27566
27609
|
}
|
|
27567
27610
|
//#endregion
|
|
27568
|
-
export { HOST_EXEC_DEFAULT_BASE_ENV, activateAgentEnv, buildAgentSession, createGondolinBashOps, createGondolinEditOps, createGondolinReadOps, createGondolinWriteOps, createMoltNetTools, createPiOtelExtension, createPiProviderErrorRetryUi, createPiRetryTriage, createPiTaskExecutor, createSubagentTool, moltnetExtension as default, ensureSnapshot, executePiTask, findMainWorktree, injectTaskContext, loadCredentials, normalizeRetryTriageResult, redactRetryTriageSecrets, resolveTaskWorktreePath, resumeVm, toGuestPath };
|
|
27611
|
+
export { HOST_EXEC_DEFAULT_BASE_ENV, activateAgentEnv, buildAgentSession, createGondolinBashOps, createGondolinEditOps, createGondolinReadOps, createGondolinWriteOps, createMoltNetTools, createPiOtelExtension, createPiProviderErrorRetryUi, createPiRetryTriage, createPiTaskExecutor, createSubagentTool, moltnetExtension as default, ensureSnapshot, executePiTask, findMainWorktree, injectRuntimeContext as 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.33.
|
|
3
|
+
"version": "0.33.1",
|
|
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,7 +36,7 @@
|
|
|
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.35.
|
|
39
|
+
"@themoltnet/agent-runtime": "0.35.1",
|
|
40
40
|
"@themoltnet/sdk": "0.119.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|