@themoltnet/pi-extension 0.6.0 → 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 +13 -0
- package/dist/index.js +156 -4
- package/package.json +4 -2
package/dist/index.d.ts
CHANGED
|
@@ -52,6 +52,8 @@ export declare function createGondolinWriteOps(vm: VM, localCwd: string): WriteO
|
|
|
52
52
|
*/
|
|
53
53
|
export declare function createMoltNetTools(config: MoltNetToolsConfig): ToolDefinition<any, any>[];
|
|
54
54
|
|
|
55
|
+
export declare function createPiOtelExtension(options?: PiOtelOptions): (pi: ExtensionAPI) => void;
|
|
56
|
+
|
|
55
57
|
/**
|
|
56
58
|
* Factory that builds a pi-specific `executeTask` function suitable for
|
|
57
59
|
* injection into `AgentRuntime`. The returned function caches the resolved
|
|
@@ -186,6 +188,17 @@ declare interface PiJudgeRecipeVersions {
|
|
|
186
188
|
sdk: string | null;
|
|
187
189
|
}
|
|
188
190
|
|
|
191
|
+
export declare interface PiOtelOptions {
|
|
192
|
+
/** Agent name for `gen_ai.agent.name` on the root span. */
|
|
193
|
+
agentName?: string;
|
|
194
|
+
/**
|
|
195
|
+
* Extra attributes merged onto every span. Use MoltNet-specific keys
|
|
196
|
+
* like `moltnet.task.id` — any `gen_ai.*` keys here are filtered out
|
|
197
|
+
* since the extension is authoritative for those.
|
|
198
|
+
*/
|
|
199
|
+
spanAttributes?: Record<string, string | number | boolean>;
|
|
200
|
+
}
|
|
201
|
+
|
|
189
202
|
export declare function resolvePiJudgeRecipeVersions(): PiJudgeRecipeVersions;
|
|
190
203
|
|
|
191
204
|
/**
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { Type, complete, getModel } from "@mariozechner/pi-ai";
|
|
|
11
11
|
import { RealFSProvider, ShadowProvider, VM, VmCheckpoint, createHttpHooks, createShadowPathPredicate, ensureImageSelector, loadGuestAssets } from "@earendil-works/gondolin";
|
|
12
12
|
import { parseEnv } from "node:util";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { SpanStatusCode, context, trace } from "@opentelemetry/api";
|
|
14
15
|
import { FormatRegistry, Type as Type$1 } from "@sinclair/typebox";
|
|
15
16
|
import { Value } from "@sinclair/typebox/value";
|
|
16
17
|
//#region ../api-client/src/generated/core/bodySerializer.gen.ts
|
|
@@ -8600,6 +8601,143 @@ function computePiJudgeRecipeCid(inputs) {
|
|
|
8600
8601
|
};
|
|
8601
8602
|
}
|
|
8602
8603
|
//#endregion
|
|
8604
|
+
//#region src/otel/index.ts
|
|
8605
|
+
var TRACER_NAME = "@themoltnet/pi-extension/otel";
|
|
8606
|
+
function stripReservedAttrs(attrs) {
|
|
8607
|
+
const out = {};
|
|
8608
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
8609
|
+
if (k.startsWith("gen_ai.")) continue;
|
|
8610
|
+
out[k] = v;
|
|
8611
|
+
}
|
|
8612
|
+
return out;
|
|
8613
|
+
}
|
|
8614
|
+
function createPiOtelExtension(options = {}) {
|
|
8615
|
+
return function piOtelExtension(pi) {
|
|
8616
|
+
const tracer = trace.getTracer(TRACER_NAME);
|
|
8617
|
+
const extraAttrs = stripReservedAttrs(options.spanAttributes ?? {});
|
|
8618
|
+
let sessionSpan;
|
|
8619
|
+
let sessionCtx = context.active();
|
|
8620
|
+
let turnSpan;
|
|
8621
|
+
let turnCtx = context.active();
|
|
8622
|
+
let currentModel;
|
|
8623
|
+
const toolSpans = /* @__PURE__ */ new Map();
|
|
8624
|
+
function drainToolSpans(reason) {
|
|
8625
|
+
for (const [, entry] of toolSpans) {
|
|
8626
|
+
entry.span.setStatus({
|
|
8627
|
+
code: SpanStatusCode.ERROR,
|
|
8628
|
+
message: reason
|
|
8629
|
+
});
|
|
8630
|
+
entry.span.end();
|
|
8631
|
+
}
|
|
8632
|
+
toolSpans.clear();
|
|
8633
|
+
}
|
|
8634
|
+
function endTurnSpan() {
|
|
8635
|
+
if (!turnSpan) return;
|
|
8636
|
+
drainToolSpans("tool span not closed before turn end");
|
|
8637
|
+
turnSpan.end();
|
|
8638
|
+
turnSpan = void 0;
|
|
8639
|
+
turnCtx = sessionCtx;
|
|
8640
|
+
}
|
|
8641
|
+
function endSessionSpan() {
|
|
8642
|
+
drainToolSpans("tool span not closed before session shutdown");
|
|
8643
|
+
endTurnSpan();
|
|
8644
|
+
if (sessionSpan) {
|
|
8645
|
+
sessionSpan.setStatus({ code: SpanStatusCode.OK });
|
|
8646
|
+
sessionSpan.end();
|
|
8647
|
+
sessionSpan = void 0;
|
|
8648
|
+
sessionCtx = context.active();
|
|
8649
|
+
}
|
|
8650
|
+
currentModel = void 0;
|
|
8651
|
+
}
|
|
8652
|
+
pi.on("session_start", (event, ctx) => {
|
|
8653
|
+
endSessionSpan();
|
|
8654
|
+
const agentName = options.agentName ?? "pi";
|
|
8655
|
+
sessionSpan = tracer.startSpan(`invoke_agent ${agentName}`, { attributes: {
|
|
8656
|
+
...extraAttrs,
|
|
8657
|
+
"gen_ai.operation.name": "invoke_agent",
|
|
8658
|
+
"gen_ai.agent.name": agentName,
|
|
8659
|
+
"session.reason": event.reason,
|
|
8660
|
+
"session.cwd": ctx.cwd
|
|
8661
|
+
} }, context.active());
|
|
8662
|
+
sessionCtx = trace.setSpan(context.active(), sessionSpan);
|
|
8663
|
+
turnCtx = sessionCtx;
|
|
8664
|
+
});
|
|
8665
|
+
pi.on("session_shutdown", () => {
|
|
8666
|
+
endSessionSpan();
|
|
8667
|
+
});
|
|
8668
|
+
pi.on("model_select", (event) => {
|
|
8669
|
+
currentModel = {
|
|
8670
|
+
provider: event.model.provider,
|
|
8671
|
+
id: event.model.id
|
|
8672
|
+
};
|
|
8673
|
+
if (sessionSpan) {
|
|
8674
|
+
sessionSpan.setAttribute("gen_ai.request.model", event.model.id);
|
|
8675
|
+
sessionSpan.setAttribute("gen_ai.provider.name", event.model.provider);
|
|
8676
|
+
}
|
|
8677
|
+
});
|
|
8678
|
+
pi.on("turn_start", (event) => {
|
|
8679
|
+
if (!sessionSpan) return;
|
|
8680
|
+
const modelLabel = currentModel?.id ?? "unknown";
|
|
8681
|
+
turnSpan = tracer.startSpan(`chat ${modelLabel}`, { attributes: {
|
|
8682
|
+
...extraAttrs,
|
|
8683
|
+
"gen_ai.operation.name": "chat",
|
|
8684
|
+
"gen_ai.request.model": currentModel?.id ?? "unknown",
|
|
8685
|
+
"gen_ai.provider.name": currentModel?.provider ?? "unknown",
|
|
8686
|
+
"turn.index": event.turnIndex
|
|
8687
|
+
} }, sessionCtx);
|
|
8688
|
+
turnCtx = trace.setSpan(sessionCtx, turnSpan);
|
|
8689
|
+
});
|
|
8690
|
+
pi.on("turn_end", (event) => {
|
|
8691
|
+
if (!turnSpan) return;
|
|
8692
|
+
const usage = extractUsage(event.message);
|
|
8693
|
+
if (usage) {
|
|
8694
|
+
turnSpan.setAttribute("gen_ai.usage.input_tokens", usage.input);
|
|
8695
|
+
turnSpan.setAttribute("gen_ai.usage.output_tokens", usage.output);
|
|
8696
|
+
}
|
|
8697
|
+
turnSpan.setAttribute("turn.tool_results", event.toolResults?.length ?? 0);
|
|
8698
|
+
turnSpan.setStatus({ code: SpanStatusCode.OK });
|
|
8699
|
+
endTurnSpan();
|
|
8700
|
+
});
|
|
8701
|
+
pi.on("tool_execution_start", (event) => {
|
|
8702
|
+
const parentCtx = turnSpan ? turnCtx : sessionCtx;
|
|
8703
|
+
const span = tracer.startSpan(`execute_tool ${event.toolName}`, { attributes: {
|
|
8704
|
+
...extraAttrs,
|
|
8705
|
+
"gen_ai.operation.name": "execute_tool",
|
|
8706
|
+
"gen_ai.tool.name": event.toolName,
|
|
8707
|
+
"gen_ai.tool.call.id": event.toolCallId
|
|
8708
|
+
} }, parentCtx);
|
|
8709
|
+
toolSpans.set(event.toolCallId, {
|
|
8710
|
+
span,
|
|
8711
|
+
startedAt: Date.now()
|
|
8712
|
+
});
|
|
8713
|
+
});
|
|
8714
|
+
pi.on("tool_execution_end", (event) => {
|
|
8715
|
+
const entry = toolSpans.get(event.toolCallId);
|
|
8716
|
+
if (!entry) return;
|
|
8717
|
+
const durationMs = Date.now() - entry.startedAt;
|
|
8718
|
+
entry.span.setAttribute("tool.duration_ms", durationMs);
|
|
8719
|
+
if (event.isError) {
|
|
8720
|
+
entry.span.setAttribute("error.type", "tool_execution_error");
|
|
8721
|
+
entry.span.setStatus({
|
|
8722
|
+
code: SpanStatusCode.ERROR,
|
|
8723
|
+
message: "tool execution failed"
|
|
8724
|
+
});
|
|
8725
|
+
} else entry.span.setStatus({ code: SpanStatusCode.OK });
|
|
8726
|
+
entry.span.end();
|
|
8727
|
+
toolSpans.delete(event.toolCallId);
|
|
8728
|
+
});
|
|
8729
|
+
};
|
|
8730
|
+
}
|
|
8731
|
+
function extractUsage(message) {
|
|
8732
|
+
if (!message || typeof message !== "object" || !("usage" in message) || !("role" in message)) return null;
|
|
8733
|
+
const msg = message;
|
|
8734
|
+
if (msg.role !== "assistant" || !msg.usage) return null;
|
|
8735
|
+
return {
|
|
8736
|
+
input: msg.usage.input ?? 0,
|
|
8737
|
+
output: msg.usage.output ?? 0
|
|
8738
|
+
};
|
|
8739
|
+
}
|
|
8740
|
+
//#endregion
|
|
8603
8741
|
//#region ../tasks/src/formats.ts
|
|
8604
8742
|
/**
|
|
8605
8743
|
* Register TypeBox string formats used across Task / TaskOutput / task-type
|
|
@@ -9977,7 +10115,15 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
9977
10115
|
const modelHandle = getModel(opts.provider, opts.model);
|
|
9978
10116
|
const resourceLoader = new DefaultResourceLoader({
|
|
9979
10117
|
cwd: mountPath,
|
|
9980
|
-
agentDir: piAuthDir
|
|
10118
|
+
agentDir: piAuthDir,
|
|
10119
|
+
extensionFactories: [createPiOtelExtension({
|
|
10120
|
+
agentName: opts.agentName,
|
|
10121
|
+
spanAttributes: {
|
|
10122
|
+
"moltnet.task.id": task.id,
|
|
10123
|
+
"moltnet.task.attempt": attemptN,
|
|
10124
|
+
"moltnet.task.type": task.taskType
|
|
10125
|
+
}
|
|
10126
|
+
})]
|
|
9981
10127
|
});
|
|
9982
10128
|
await resourceLoader.reload();
|
|
9983
10129
|
session = (await createAgentSession({
|
|
@@ -10094,10 +10240,16 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
10094
10240
|
if (reporterOpen) {
|
|
10095
10241
|
try {
|
|
10096
10242
|
await reporter.finalize(finalUsage);
|
|
10097
|
-
} catch {
|
|
10243
|
+
} catch (err) {
|
|
10244
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
10245
|
+
console.error(`executePiTask: reporter.finalize() failed for task ${task.id} attempt ${attemptN}: ${detail}`);
|
|
10246
|
+
}
|
|
10098
10247
|
try {
|
|
10099
10248
|
await reporter.close();
|
|
10100
|
-
} catch {
|
|
10249
|
+
} catch (err) {
|
|
10250
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
10251
|
+
console.error(`executePiTask: reporter.close() failed for task ${task.id} attempt ${attemptN}: ${detail}`);
|
|
10252
|
+
}
|
|
10101
10253
|
}
|
|
10102
10254
|
await managed.vm.close();
|
|
10103
10255
|
}
|
|
@@ -10415,4 +10567,4 @@ function moltnetExtension(pi) {
|
|
|
10415
10567
|
registerMoltnetReflectCommand(pi, state);
|
|
10416
10568
|
}
|
|
10417
10569
|
//#endregion
|
|
10418
|
-
export { HOST_EXEC_DEFAULT_BASE_ENV, activateAgentEnv, buildPiJudgeRecipeManifest, computePiJudgeRecipeCid, createGondolinBashOps, createGondolinEditOps, createGondolinReadOps, createGondolinWriteOps, createMoltNetTools, createPiTaskExecutor, moltnetExtension as default, ensureSnapshot, executePiTask, findMainWorktree, loadCredentials, resolvePiJudgeRecipeVersions, resumeVm, toGuestPath };
|
|
10570
|
+
export { HOST_EXEC_DEFAULT_BASE_ENV, activateAgentEnv, buildPiJudgeRecipeManifest, computePiJudgeRecipeCid, createGondolinBashOps, createGondolinEditOps, createGondolinReadOps, createGondolinWriteOps, createMoltNetTools, createPiOtelExtension, createPiTaskExecutor, moltnetExtension as default, ensureSnapshot, executePiTask, findMainWorktree, loadCredentials, resolvePiJudgeRecipeVersions, resumeVm, toGuestPath };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/pi-extension",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MoltNet pi extension — sandboxed tool execution in Gondolin VMs with MoltNet identity and persistent memory",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,8 +29,9 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@earendil-works/gondolin": "^0.7.0",
|
|
32
|
+
"@opentelemetry/api": "^1.9.0",
|
|
32
33
|
"@sinclair/typebox": "^0.34.0",
|
|
33
|
-
"@themoltnet/agent-runtime": "0.
|
|
34
|
+
"@themoltnet/agent-runtime": "0.3.0",
|
|
34
35
|
"@themoltnet/sdk": "0.95.0"
|
|
35
36
|
},
|
|
36
37
|
"peerDependencies": {
|
|
@@ -48,6 +49,7 @@
|
|
|
48
49
|
"devDependencies": {
|
|
49
50
|
"@mariozechner/pi-ai": "^0.67.68",
|
|
50
51
|
"@mariozechner/pi-coding-agent": "^0.67.68",
|
|
52
|
+
"@opentelemetry/sdk-trace-base": "^2.5.1",
|
|
51
53
|
"@types/node": "^20.11.0",
|
|
52
54
|
"typescript": "^5.3.3",
|
|
53
55
|
"vite": "^8.0.0",
|