@themoltnet/pi-runtime 0.6.2 → 0.7.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 CHANGED
@@ -5,6 +5,7 @@ import { BashOperations } from '@earendil-works/pi-coding-agent';
5
5
  import { ClaimedTask } from '@themoltnet/agent-runtime';
6
6
  import { CommandAnalysis } from '@themoltnet/shell-command-analyzer';
7
7
  import { connect } from '@themoltnet/sdk';
8
+ import { Context } from '@opentelemetry/api';
8
9
  import { ContextRef } from '@themoltnet/agent-runtime';
9
10
  import { EditOperations } from '@earendil-works/pi-coding-agent';
10
11
  import { ExtensionAPI } from '@earendil-works/pi-coding-agent';
@@ -97,6 +98,12 @@ declare interface BuildAgentSessionArgs {
97
98
  skillsOverride?: () => LoadSkillsResult;
98
99
  /** Span attributes merged onto every OTel span the session emits. */
99
100
  otelSpanAttrs: Record<string, string | number | boolean>;
101
+ /** Parent context for the Pi session span, normally the task execute span. */
102
+ otelSessionParentContext?: Context;
103
+ /** Resolves the provider-request context used to parent each Pi turn. */
104
+ getOtelTurnParentContext?: () => Context | undefined;
105
+ /** Receives the live Pi session context for provider-span parenting. */
106
+ onOtelSessionContextChange?: (context: Context | undefined) => void;
100
107
  /** Agent name for `gen_ai.agent.name` on the root span. */
101
108
  agentName: string;
102
109
  /**
@@ -885,6 +892,12 @@ export declare interface PiOtelOptions {
885
892
  * since the extension is authoritative for those.
886
893
  */
887
894
  spanAttributes?: Record<string, string | number | boolean>;
895
+ /** Explicit parent for the session-level `invoke_agent` span. */
896
+ sessionParentContext?: Context;
897
+ /** Parent context for the next Pi turn, normally a provider-request span. */
898
+ getTurnParentContext?: () => Context | undefined;
899
+ /** Publishes the live session span context to the embedding runtime. */
900
+ onSessionContextChange?: (context: Context | undefined) => void;
888
901
  }
889
902
 
890
903
  export declare type PiRetryTriage = (input: PiRetryTriageInput) => Promise<PiRetryTriageResult>;
package/dist/index.js CHANGED
@@ -18,12 +18,11 @@ import { createHash } from "crypto";
18
18
  import { createHash as createHash$1 } from "node:crypto";
19
19
  import * as json from "multiformats/codecs/json";
20
20
  import "@ipld/dag-cbor";
21
- import { FREEFORM_TYPE, SUBMIT_OUTPUT_GATE_ID, TaskContext, buildTaskUserPrompt, getSubmitOutputContract, materializeTaskOutput, mergeRuntimeProfileContext, resolveTaskContext, taskTypeUsesSubagents, validateTaskOutput, validateTaskSubmission } from "@themoltnet/agent-runtime";
21
+ import { FREEFORM_TYPE, SUBMIT_OUTPUT_GATE_ID, TaskContext, buildTaskUserPrompt, getSubmitOutputContract, materializeTaskOutput, mergeRuntimeProfileContext, resolveTaskContext, taskTypeUsesSubagents, traceRuntimePhase, validateTaskOutput, validateTaskSubmission } from "@themoltnet/agent-runtime";
22
22
  import { connect } from "@themoltnet/sdk";
23
23
  import { ShellCommandAnalyzer } from "@themoltnet/shell-command-analyzer";
24
24
  import { homedir } from "node:os";
25
25
  import { MemoryProvider, RealFSProvider, ShadowProvider, VM, VmCheckpoint, createHttpHooks, createShadowPathPredicate, ensureImageSelector, isWriteFlag, loadGuestAssets } from "@earendil-works/gondolin";
26
- import * as Format from "typebox/format";
27
26
  import { Type as Type$1 } from "typebox";
28
27
  import { Value } from "typebox/value";
29
28
  import { parseEnv } from "node:util";
@@ -1057,20 +1056,23 @@ function createPiOtelExtension(options = {}) {
1057
1056
  sessionSpan.end();
1058
1057
  sessionSpan = void 0;
1059
1058
  sessionCtx = context.active();
1059
+ options.onSessionContextChange?.(void 0);
1060
1060
  }
1061
1061
  currentModel = void 0;
1062
1062
  }
1063
1063
  pi.on("session_start", (event, ctx) => {
1064
1064
  endSessionSpan();
1065
1065
  const agentName = options.agentName ?? "pi";
1066
+ const parentContext = options.sessionParentContext ?? context.active();
1066
1067
  sessionSpan = tracer.startSpan(`invoke_agent ${agentName}`, { attributes: {
1067
1068
  ...extraAttrs,
1068
1069
  "gen_ai.operation.name": "invoke_agent",
1069
1070
  "gen_ai.agent.name": agentName,
1070
1071
  "session.reason": event.reason,
1071
1072
  "session.cwd": ctx.cwd
1072
- } }, context.active());
1073
- sessionCtx = trace.setSpan(context.active(), sessionSpan);
1073
+ } }, parentContext);
1074
+ sessionCtx = trace.setSpan(parentContext, sessionSpan);
1075
+ options.onSessionContextChange?.(sessionCtx);
1074
1076
  turnCtx = sessionCtx;
1075
1077
  });
1076
1078
  pi.on("session_shutdown", () => {
@@ -1089,14 +1091,15 @@ function createPiOtelExtension(options = {}) {
1089
1091
  pi.on("turn_start", (event) => {
1090
1092
  if (!sessionSpan) return;
1091
1093
  const modelLabel = currentModel?.id ?? "unknown";
1094
+ const turnParentContext = options.getTurnParentContext?.() ?? sessionCtx;
1092
1095
  turnSpan = tracer.startSpan(`chat ${modelLabel}`, { attributes: {
1093
1096
  ...extraAttrs,
1094
1097
  "gen_ai.operation.name": "chat",
1095
1098
  "gen_ai.request.model": currentModel?.id ?? "unknown",
1096
1099
  "gen_ai.provider.name": currentModel?.provider ?? "unknown",
1097
1100
  "turn.index": event.turnIndex
1098
- } }, sessionCtx);
1099
- turnCtx = trace.setSpan(sessionCtx, turnSpan);
1101
+ } }, turnParentContext);
1102
+ turnCtx = trace.setSpan(turnParentContext, turnSpan);
1100
1103
  });
1101
1104
  pi.on("turn_end", (event) => {
1102
1105
  if (!turnSpan) return;
@@ -1247,7 +1250,10 @@ var NO_SKILLS = () => ({
1247
1250
  async function buildAgentSession(args) {
1248
1251
  const piOtelExtension = createPiOtelExtension({
1249
1252
  agentName: args.agentName,
1250
- spanAttributes: args.otelSpanAttrs
1253
+ spanAttributes: args.otelSpanAttrs,
1254
+ sessionParentContext: args.otelSessionParentContext,
1255
+ getTurnParentContext: args.getOtelTurnParentContext,
1256
+ onSessionContextChange: args.onOtelSessionContextChange
1251
1257
  });
1252
1258
  const modelOptions = {
1253
1259
  temperature: args.temperature,
@@ -1339,19 +1345,6 @@ function resolvePiCodingAgentDir() {
1339
1345
  return process.env["PI_CODING_AGENT_DIR"] ?? path.join(homedir(), ".pi", "agent");
1340
1346
  }
1341
1347
  //#endregion
1342
- //#region ../tasks/src/formats.ts
1343
- /**
1344
- * Register TypeBox string formats used across Task / TaskOutput / task-type
1345
- * schemas. Import this module for its side effect (the package index does so
1346
- * automatically) before compiling or Check()ing any schema that references
1347
- * `format: 'uuid'` or `format: 'date-time'`.
1348
- *
1349
- * Idempotent: registration is guarded by `Format.Has(...)`.
1350
- */
1351
- var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1352
- if (!Format.Has("uuid")) Format.Set("uuid", (v) => UUID_RE.test(v));
1353
- if (!Format.Has("date-time")) Format.Set("date-time", (v) => !Number.isNaN(Date.parse(v)));
1354
- //#endregion
1355
1348
  //#region ../tasks/src/context.ts
1356
1349
  /**
1357
1350
  * How an executor delivers a context entry to its underlying LLM.
@@ -3898,7 +3891,7 @@ Type$1.Object({
3898
3891
  daemonState: Type$1.Union([DaemonState, Type$1.Null()])
3899
3892
  }, {
3900
3893
  $id: "TaskAttempt",
3901
- additionalProperties: false
3894
+ additionalProperties: true
3902
3895
  });
3903
3896
  Type$1.Object({
3904
3897
  taskId: Uuid,
@@ -7194,6 +7187,7 @@ async function executePiTask(claimedTask, reporter, opts) {
7194
7187
  const task = claimedTask.task;
7195
7188
  const attemptN = claimedTask.attemptN;
7196
7189
  const startTime = Date.now();
7190
+ const taskOtelContext = context.active();
7197
7191
  const requestedMountPath = opts.mountPath ?? process.cwd();
7198
7192
  const agentRootDir = opts.agentRootDir ?? requestedMountPath;
7199
7193
  const executionPlan = await opts.makeExecutionPlan?.(claimedTask) ?? null;
@@ -7217,6 +7211,8 @@ async function executePiTask(claimedTask, reporter, opts) {
7217
7211
  let reporterOpen = opts.reporterAlreadyOpened ?? false;
7218
7212
  let managed = null;
7219
7213
  let session = null;
7214
+ let piSessionContext;
7215
+ let providerRequestContext;
7220
7216
  let subagentHandle = null;
7221
7217
  const finalUsage = emptyUsage(opts.provider, opts.model);
7222
7218
  let cancelListener = null;
@@ -7287,19 +7283,22 @@ async function executePiTask(claimedTask, reporter, opts) {
7287
7283
  let effectiveSandboxConfig;
7288
7284
  try {
7289
7285
  if (!resolvedVmTemplate && opts.runtimeDefinition) resolvedVmTemplate = opts.resolveVmTemplate ? await opts.resolveVmTemplate() : await opts.runtimeDefinition.vm.resolve({ onProgress: opts.onSnapshotProgress });
7290
- checkpointPath = resolvedVmTemplate?.checkpointPath ?? opts.checkpointPath ?? (opts.resolveCheckpointPath ? await opts.resolveCheckpointPath() : await ensureSnapshot({
7286
+ checkpointPath = await traceRuntimePhase("moltnet.execution.snapshot.prepare", { "moltnet.snapshot.source": resolvedVmTemplate ? "resolved_template" : opts.checkpointPath ? "configured_checkpoint" : opts.resolveCheckpointPath ? "resolver" : "build_or_cache" }, async () => resolvedVmTemplate?.checkpointPath ?? opts.checkpointPath ?? (opts.resolveCheckpointPath ? await opts.resolveCheckpointPath() : await ensureSnapshot({
7291
7287
  config: opts.sandboxConfig?.snapshot,
7292
7288
  onProgress: opts.onSnapshotProgress ?? ((m) => {
7293
7289
  process.stderr.write(`[snapshot] ${m}\n`);
7294
7290
  })
7295
- }));
7291
+ })));
7296
7292
  } catch (err) {
7297
7293
  const message = err instanceof Error ? err.message : String(err);
7298
7294
  await emitError("snapshot", message);
7299
7295
  return makeFailedOutput("snapshot_failed", message);
7300
7296
  }
7301
7297
  try {
7302
- workspace = prepareTaskWorkspace(task, requestedMountPath, executionPlan);
7298
+ workspace = await traceRuntimePhase("moltnet.execution.workspace.prepare", {
7299
+ "moltnet.workspace.mode": executionPlan?.workspaceMode ?? "shared_mount",
7300
+ "moltnet.workspace.scope": executionPlan?.workspaceScope ?? "attempt"
7301
+ }, async () => prepareTaskWorkspace(task, requestedMountPath, executionPlan));
7303
7302
  mountPath = workspace.mountPath;
7304
7303
  cwdPath = workspace.cwdPath;
7305
7304
  } catch (err) {
@@ -7307,23 +7306,25 @@ async function executePiTask(claimedTask, reporter, opts) {
7307
7306
  await emitError("worktree_setup", message);
7308
7307
  return makeFailedOutput("worktree_setup_failed", message);
7309
7308
  }
7309
+ if (!workspace) throw new Error("task workspace not prepared");
7310
+ const preparedWorkspace = workspace;
7310
7311
  try {
7311
7312
  effectiveSandboxConfig = applyExecutionPlanSandboxOverrides(resolvedVmTemplate ? {
7312
7313
  ...opts.sandboxConfig,
7313
7314
  snapshot: void 0,
7314
7315
  resumeCommands: [...resolvedVmTemplate.resumeCommands]
7315
7316
  } : opts.sandboxConfig, executionPlan);
7316
- managed = await resumeVm({
7317
+ managed = await traceRuntimePhase("moltnet.execution.vm.resume", { "moltnet.workspace.mode": preparedWorkspace.mode }, () => resumeVm({
7317
7318
  checkpointPath,
7318
7319
  agentName: opts.agentName,
7319
7320
  agentRootDir,
7320
7321
  mountPath,
7321
- workspaceMode: workspace.mode,
7322
+ workspaceMode: preparedWorkspace.mode,
7322
7323
  extraAllowedHosts: opts.extraAllowedHosts,
7323
7324
  sandboxConfig: effectiveSandboxConfig,
7324
7325
  forwardEnv: opts.forwardEnv,
7325
7326
  signal: reporter.cancelSignal
7326
- });
7327
+ }));
7327
7328
  } catch (err) {
7328
7329
  const message = err instanceof Error ? err.message : String(err);
7329
7330
  if (reporter.cancelSignal.aborted) {
@@ -7336,9 +7337,8 @@ async function executePiTask(claimedTask, reporter, opts) {
7336
7337
  const diaryId = task.diaryId ?? "";
7337
7338
  const taskTeamId = task.teamId ?? "";
7338
7339
  activateAgentEnv(managed.credentials.agentEnv, agentRootDir);
7339
- const activeWorkspace = workspace;
7340
+ const activeWorkspace = preparedWorkspace;
7340
7341
  const activeManaged = managed;
7341
- if (!activeWorkspace) throw new Error("task workspace not prepared");
7342
7342
  await emit("info", {
7343
7343
  event: "execute_start",
7344
7344
  correlationId: task.correlationId ?? null,
@@ -7381,10 +7381,10 @@ async function executePiTask(claimedTask, reporter, opts) {
7381
7381
  const rawContext = task.input.context;
7382
7382
  let effectiveRuntimeContext;
7383
7383
  try {
7384
- effectiveRuntimeContext = resolveEffectiveRuntimeContext({
7384
+ effectiveRuntimeContext = await traceRuntimePhase("moltnet.execution.context.resolve", {}, async () => resolveEffectiveRuntimeContext({
7385
7385
  rawTaskContext: rawContext,
7386
7386
  runtimeProfileContext: opts.runtimeProfileContext
7387
- });
7387
+ }));
7388
7388
  } catch (err) {
7389
7389
  const message = err instanceof Error ? err.message : String(err);
7390
7390
  await emit("error", {
@@ -7426,11 +7426,11 @@ async function executePiTask(claimedTask, reporter, opts) {
7426
7426
  }
7427
7427
  let injectedContext;
7428
7428
  try {
7429
- injectedContext = await injectRuntimeContext({
7429
+ injectedContext = await traceRuntimePhase("moltnet.execution.context.inject", { "moltnet.context.ref_count": effectiveRuntimeContext.length }, () => injectRuntimeContext({
7430
7430
  context: effectiveRuntimeContext,
7431
- fs: managed.vm.fs,
7432
- guestWorkspace: managed.guestWorkspace
7433
- });
7431
+ fs: activeManaged.vm.fs,
7432
+ guestWorkspace: activeManaged.guestWorkspace
7433
+ }));
7434
7434
  } catch (err) {
7435
7435
  const message = err instanceof Error ? err.message : String(err);
7436
7436
  await emit("error", {
@@ -7658,7 +7658,10 @@ async function executePiTask(claimedTask, reporter, opts) {
7658
7658
  ...submitTools,
7659
7659
  ...parentSubagentTools
7660
7660
  ];
7661
- session = await buildAgentSession({
7661
+ session = await traceRuntimePhase("moltnet.execution.session.create", {
7662
+ "gen_ai.provider.name": opts.provider,
7663
+ "gen_ai.request.model": opts.model
7664
+ }, () => buildAgentSession({
7662
7665
  mountPath,
7663
7666
  cwdPath,
7664
7667
  piAuthDir,
@@ -7686,13 +7689,18 @@ async function executePiTask(claimedTask, reporter, opts) {
7686
7689
  "moltnet.task.attempt": attemptN,
7687
7690
  "moltnet.task.type": task.taskType
7688
7691
  },
7692
+ otelSessionParentContext: taskOtelContext,
7693
+ getOtelTurnParentContext: () => providerRequestContext,
7694
+ onOtelSessionContextChange: (context) => {
7695
+ piSessionContext = context;
7696
+ },
7689
7697
  sessionPersistence: executionPlan?.sessionPersistence ?? void 0,
7690
7698
  extraExtensionFactories: [
7691
7699
  ...runtimeParentExtensions,
7692
7700
  ...toolPolicyExtensions,
7693
7701
  submitCompletion.extension
7694
7702
  ]
7695
- });
7703
+ }));
7696
7704
  } catch (err) {
7697
7705
  const message = err instanceof Error ? err.message : String(err);
7698
7706
  await emit("error", {
@@ -7774,7 +7782,11 @@ async function executePiTask(claimedTask, reporter, opts) {
7774
7782
  onPromptError: (message) => emit("error", {
7775
7783
  message,
7776
7784
  phase: "session_prompt"
7777
- })
7785
+ }),
7786
+ parentContext: piSessionContext,
7787
+ onRequestContextChange: (context) => {
7788
+ providerRequestContext = context;
7789
+ }
7778
7790
  });
7779
7791
  const submitMissingConfig = resolveSubmitMissingConfig({
7780
7792
  submitToolHandle,
@@ -8330,7 +8342,15 @@ async function promptWithProviderErrorRetries(args) {
8330
8342
  let promptText = args.initialPrompt;
8331
8343
  while (true) {
8332
8344
  try {
8333
- await args.session.prompt(promptText);
8345
+ await traceRuntimePhase("moltnet.execution.provider.request", { "moltnet.provider.retry": retryCount }, async (span) => {
8346
+ const requestContext = trace.setSpan(args.parentContext ?? context.active(), span);
8347
+ args.onRequestContextChange?.(requestContext);
8348
+ try {
8349
+ await args.session.prompt(promptText);
8350
+ } finally {
8351
+ args.onRequestContextChange?.(void 0);
8352
+ }
8353
+ }, args.parentContext);
8334
8354
  } catch (err) {
8335
8355
  const message = err instanceof Error ? err.message : String(err);
8336
8356
  await args.onPromptError?.(message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/pi-runtime",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Composable MoltNet runtime kernel for Pi agents in Gondolin VMs",
@@ -31,9 +31,9 @@
31
31
  "@opentelemetry/api": "^1.9.0",
32
32
  "multiformats": "^13.3.0",
33
33
  "typebox": "^1.2.8",
34
- "@themoltnet/sdk": "0.129.0",
34
+ "@themoltnet/agent-runtime": "0.41.0",
35
35
  "@themoltnet/shell-command-analyzer": "0.3.0",
36
- "@themoltnet/agent-runtime": "0.40.1"
36
+ "@themoltnet/sdk": "0.129.0"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@earendil-works/pi-ai": "0.79.4",
@@ -50,9 +50,9 @@
50
50
  "vite": "^8.0.0",
51
51
  "vite-plugin-dts": "^4.5.4",
52
52
  "vitest": "^3.0.0",
53
- "@moltnet/models": "0.1.0",
54
53
  "@moltnet/tasks": "0.1.0",
55
- "@moltnet/crypto-service": "0.1.0"
54
+ "@moltnet/crypto-service": "0.1.0",
55
+ "@moltnet/models": "0.1.0"
56
56
  },
57
57
  "engines": {
58
58
  "node": ">=22"