@themoltnet/pi-extension 0.6.0 → 0.8.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 +14 -0
- package/dist/index.js +175 -4
- package/package.json +5 -3
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
|
/**
|
|
@@ -259,6 +272,7 @@ declare const Task: TObject< {
|
|
|
259
272
|
imposedByAgentId: TUnion<[TString, TNull]>;
|
|
260
273
|
imposedByHumanId: TUnion<[TString, TNull]>;
|
|
261
274
|
acceptedAttemptN: TUnion<[TNumber, TNull]>;
|
|
275
|
+
requiredExecutorTrustLevel: TUnion<[TLiteral<"selfDeclared">, TLiteral<"agentSigned">, TLiteral<"releaseVerifiedTool">, TLiteral<"sandboxAttested">]>;
|
|
262
276
|
status: TUnion<[TLiteral<"queued">, TLiteral<"dispatched">, TLiteral<"running">, TLiteral<"completed">, TLiteral<"failed">, TLiteral<"cancelled">, TLiteral<"expired">]>;
|
|
263
277
|
queuedAt: TString;
|
|
264
278
|
completedAt: TUnion<[TString, TNull]>;
|
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
|
|
@@ -2535,6 +2536,7 @@ function createDiaryGrantsNamespace(context) {
|
|
|
2535
2536
|
}
|
|
2536
2537
|
};
|
|
2537
2538
|
}
|
|
2539
|
+
new TextEncoder();
|
|
2538
2540
|
//#endregion
|
|
2539
2541
|
//#region ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/utils.js
|
|
2540
2542
|
/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
|
|
@@ -4265,6 +4267,13 @@ etc.sha512Sync = (...m) => {
|
|
|
4265
4267
|
return hash.digest();
|
|
4266
4268
|
};
|
|
4267
4269
|
//#endregion
|
|
4270
|
+
//#region ../crypto-service/src/executor-attestation.ts
|
|
4271
|
+
etc.sha512Sync = (...m) => {
|
|
4272
|
+
const hash = createHash("sha512");
|
|
4273
|
+
m.forEach((msg) => hash.update(msg));
|
|
4274
|
+
return hash.digest();
|
|
4275
|
+
};
|
|
4276
|
+
//#endregion
|
|
4268
4277
|
//#region ../../node_modules/.pnpm/multiformats@13.4.2/node_modules/multiformats/dist/src/codecs/json.js
|
|
4269
4278
|
var textEncoder$1 = new TextEncoder();
|
|
4270
4279
|
new TextDecoder();
|
|
@@ -8600,6 +8609,143 @@ function computePiJudgeRecipeCid(inputs) {
|
|
|
8600
8609
|
};
|
|
8601
8610
|
}
|
|
8602
8611
|
//#endregion
|
|
8612
|
+
//#region src/otel/index.ts
|
|
8613
|
+
var TRACER_NAME = "@themoltnet/pi-extension/otel";
|
|
8614
|
+
function stripReservedAttrs(attrs) {
|
|
8615
|
+
const out = {};
|
|
8616
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
8617
|
+
if (k.startsWith("gen_ai.")) continue;
|
|
8618
|
+
out[k] = v;
|
|
8619
|
+
}
|
|
8620
|
+
return out;
|
|
8621
|
+
}
|
|
8622
|
+
function createPiOtelExtension(options = {}) {
|
|
8623
|
+
return function piOtelExtension(pi) {
|
|
8624
|
+
const tracer = trace.getTracer(TRACER_NAME);
|
|
8625
|
+
const extraAttrs = stripReservedAttrs(options.spanAttributes ?? {});
|
|
8626
|
+
let sessionSpan;
|
|
8627
|
+
let sessionCtx = context.active();
|
|
8628
|
+
let turnSpan;
|
|
8629
|
+
let turnCtx = context.active();
|
|
8630
|
+
let currentModel;
|
|
8631
|
+
const toolSpans = /* @__PURE__ */ new Map();
|
|
8632
|
+
function drainToolSpans(reason) {
|
|
8633
|
+
for (const [, entry] of toolSpans) {
|
|
8634
|
+
entry.span.setStatus({
|
|
8635
|
+
code: SpanStatusCode.ERROR,
|
|
8636
|
+
message: reason
|
|
8637
|
+
});
|
|
8638
|
+
entry.span.end();
|
|
8639
|
+
}
|
|
8640
|
+
toolSpans.clear();
|
|
8641
|
+
}
|
|
8642
|
+
function endTurnSpan() {
|
|
8643
|
+
if (!turnSpan) return;
|
|
8644
|
+
drainToolSpans("tool span not closed before turn end");
|
|
8645
|
+
turnSpan.end();
|
|
8646
|
+
turnSpan = void 0;
|
|
8647
|
+
turnCtx = sessionCtx;
|
|
8648
|
+
}
|
|
8649
|
+
function endSessionSpan() {
|
|
8650
|
+
drainToolSpans("tool span not closed before session shutdown");
|
|
8651
|
+
endTurnSpan();
|
|
8652
|
+
if (sessionSpan) {
|
|
8653
|
+
sessionSpan.setStatus({ code: SpanStatusCode.OK });
|
|
8654
|
+
sessionSpan.end();
|
|
8655
|
+
sessionSpan = void 0;
|
|
8656
|
+
sessionCtx = context.active();
|
|
8657
|
+
}
|
|
8658
|
+
currentModel = void 0;
|
|
8659
|
+
}
|
|
8660
|
+
pi.on("session_start", (event, ctx) => {
|
|
8661
|
+
endSessionSpan();
|
|
8662
|
+
const agentName = options.agentName ?? "pi";
|
|
8663
|
+
sessionSpan = tracer.startSpan(`invoke_agent ${agentName}`, { attributes: {
|
|
8664
|
+
...extraAttrs,
|
|
8665
|
+
"gen_ai.operation.name": "invoke_agent",
|
|
8666
|
+
"gen_ai.agent.name": agentName,
|
|
8667
|
+
"session.reason": event.reason,
|
|
8668
|
+
"session.cwd": ctx.cwd
|
|
8669
|
+
} }, context.active());
|
|
8670
|
+
sessionCtx = trace.setSpan(context.active(), sessionSpan);
|
|
8671
|
+
turnCtx = sessionCtx;
|
|
8672
|
+
});
|
|
8673
|
+
pi.on("session_shutdown", () => {
|
|
8674
|
+
endSessionSpan();
|
|
8675
|
+
});
|
|
8676
|
+
pi.on("model_select", (event) => {
|
|
8677
|
+
currentModel = {
|
|
8678
|
+
provider: event.model.provider,
|
|
8679
|
+
id: event.model.id
|
|
8680
|
+
};
|
|
8681
|
+
if (sessionSpan) {
|
|
8682
|
+
sessionSpan.setAttribute("gen_ai.request.model", event.model.id);
|
|
8683
|
+
sessionSpan.setAttribute("gen_ai.provider.name", event.model.provider);
|
|
8684
|
+
}
|
|
8685
|
+
});
|
|
8686
|
+
pi.on("turn_start", (event) => {
|
|
8687
|
+
if (!sessionSpan) return;
|
|
8688
|
+
const modelLabel = currentModel?.id ?? "unknown";
|
|
8689
|
+
turnSpan = tracer.startSpan(`chat ${modelLabel}`, { attributes: {
|
|
8690
|
+
...extraAttrs,
|
|
8691
|
+
"gen_ai.operation.name": "chat",
|
|
8692
|
+
"gen_ai.request.model": currentModel?.id ?? "unknown",
|
|
8693
|
+
"gen_ai.provider.name": currentModel?.provider ?? "unknown",
|
|
8694
|
+
"turn.index": event.turnIndex
|
|
8695
|
+
} }, sessionCtx);
|
|
8696
|
+
turnCtx = trace.setSpan(sessionCtx, turnSpan);
|
|
8697
|
+
});
|
|
8698
|
+
pi.on("turn_end", (event) => {
|
|
8699
|
+
if (!turnSpan) return;
|
|
8700
|
+
const usage = extractUsage(event.message);
|
|
8701
|
+
if (usage) {
|
|
8702
|
+
turnSpan.setAttribute("gen_ai.usage.input_tokens", usage.input);
|
|
8703
|
+
turnSpan.setAttribute("gen_ai.usage.output_tokens", usage.output);
|
|
8704
|
+
}
|
|
8705
|
+
turnSpan.setAttribute("turn.tool_results", event.toolResults?.length ?? 0);
|
|
8706
|
+
turnSpan.setStatus({ code: SpanStatusCode.OK });
|
|
8707
|
+
endTurnSpan();
|
|
8708
|
+
});
|
|
8709
|
+
pi.on("tool_execution_start", (event) => {
|
|
8710
|
+
const parentCtx = turnSpan ? turnCtx : sessionCtx;
|
|
8711
|
+
const span = tracer.startSpan(`execute_tool ${event.toolName}`, { attributes: {
|
|
8712
|
+
...extraAttrs,
|
|
8713
|
+
"gen_ai.operation.name": "execute_tool",
|
|
8714
|
+
"gen_ai.tool.name": event.toolName,
|
|
8715
|
+
"gen_ai.tool.call.id": event.toolCallId
|
|
8716
|
+
} }, parentCtx);
|
|
8717
|
+
toolSpans.set(event.toolCallId, {
|
|
8718
|
+
span,
|
|
8719
|
+
startedAt: Date.now()
|
|
8720
|
+
});
|
|
8721
|
+
});
|
|
8722
|
+
pi.on("tool_execution_end", (event) => {
|
|
8723
|
+
const entry = toolSpans.get(event.toolCallId);
|
|
8724
|
+
if (!entry) return;
|
|
8725
|
+
const durationMs = Date.now() - entry.startedAt;
|
|
8726
|
+
entry.span.setAttribute("tool.duration_ms", durationMs);
|
|
8727
|
+
if (event.isError) {
|
|
8728
|
+
entry.span.setAttribute("error.type", "tool_execution_error");
|
|
8729
|
+
entry.span.setStatus({
|
|
8730
|
+
code: SpanStatusCode.ERROR,
|
|
8731
|
+
message: "tool execution failed"
|
|
8732
|
+
});
|
|
8733
|
+
} else entry.span.setStatus({ code: SpanStatusCode.OK });
|
|
8734
|
+
entry.span.end();
|
|
8735
|
+
toolSpans.delete(event.toolCallId);
|
|
8736
|
+
});
|
|
8737
|
+
};
|
|
8738
|
+
}
|
|
8739
|
+
function extractUsage(message) {
|
|
8740
|
+
if (!message || typeof message !== "object" || !("usage" in message) || !("role" in message)) return null;
|
|
8741
|
+
const msg = message;
|
|
8742
|
+
if (msg.role !== "assistant" || !msg.usage) return null;
|
|
8743
|
+
return {
|
|
8744
|
+
input: msg.usage.input ?? 0,
|
|
8745
|
+
output: msg.usage.output ?? 0
|
|
8746
|
+
};
|
|
8747
|
+
}
|
|
8748
|
+
//#endregion
|
|
8603
8749
|
//#region ../tasks/src/formats.ts
|
|
8604
8750
|
/**
|
|
8605
8751
|
* Register TypeBox string formats used across Task / TaskOutput / task-type
|
|
@@ -9086,6 +9232,12 @@ var TaskAttemptStatus = Type$1.Union([
|
|
|
9086
9232
|
Type$1.Literal("cancelled"),
|
|
9087
9233
|
Type$1.Literal("timed_out")
|
|
9088
9234
|
], { $id: "TaskAttemptStatus" });
|
|
9235
|
+
var ExecutorTrustLevel = Type$1.Union([
|
|
9236
|
+
Type$1.Literal("selfDeclared"),
|
|
9237
|
+
Type$1.Literal("agentSigned"),
|
|
9238
|
+
Type$1.Literal("releaseVerifiedTool"),
|
|
9239
|
+
Type$1.Literal("sandboxAttested")
|
|
9240
|
+
], { $id: "ExecutorTrustLevel" });
|
|
9089
9241
|
var OutputKind = Type$1.Union([Type$1.Literal("artifact"), Type$1.Literal("judgment")], { $id: "OutputKind" });
|
|
9090
9242
|
var TaskMessageKind = Type$1.Union([
|
|
9091
9243
|
Type$1.Literal("text_delta"),
|
|
@@ -9178,6 +9330,7 @@ Type$1.Object({
|
|
|
9178
9330
|
imposedByAgentId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
9179
9331
|
imposedByHumanId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
9180
9332
|
acceptedAttemptN: Type$1.Union([Type$1.Number(), Type$1.Null()]),
|
|
9333
|
+
requiredExecutorTrustLevel: ExecutorTrustLevel,
|
|
9181
9334
|
status: TaskStatus,
|
|
9182
9335
|
queuedAt: IsoTimestamp,
|
|
9183
9336
|
completedAt: Type$1.Union([IsoTimestamp, Type$1.Null()]),
|
|
@@ -9201,6 +9354,10 @@ Type$1.Object({
|
|
|
9201
9354
|
status: TaskAttemptStatus,
|
|
9202
9355
|
output: Type$1.Union([Type$1.Record(Type$1.String(), Type$1.Unknown()), Type$1.Null()]),
|
|
9203
9356
|
outputCid: Type$1.Union([Cid, Type$1.Null()]),
|
|
9357
|
+
claimedExecutorFingerprint: Type$1.Union([Cid, Type$1.Null()]),
|
|
9358
|
+
claimedExecutorManifest: Type$1.Union([Type$1.Record(Type$1.String(), Type$1.Unknown()), Type$1.Null()]),
|
|
9359
|
+
completedExecutorFingerprint: Type$1.Union([Cid, Type$1.Null()]),
|
|
9360
|
+
completedExecutorManifest: Type$1.Union([Type$1.Record(Type$1.String(), Type$1.Unknown()), Type$1.Null()]),
|
|
9204
9361
|
error: Type$1.Union([TaskError, Type$1.Null()]),
|
|
9205
9362
|
usage: Type$1.Union([TaskUsage, Type$1.Null()]),
|
|
9206
9363
|
contentSignature: Type$1.Union([Type$1.String(), Type$1.Null()]),
|
|
@@ -9977,7 +10134,15 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
9977
10134
|
const modelHandle = getModel(opts.provider, opts.model);
|
|
9978
10135
|
const resourceLoader = new DefaultResourceLoader({
|
|
9979
10136
|
cwd: mountPath,
|
|
9980
|
-
agentDir: piAuthDir
|
|
10137
|
+
agentDir: piAuthDir,
|
|
10138
|
+
extensionFactories: [createPiOtelExtension({
|
|
10139
|
+
agentName: opts.agentName,
|
|
10140
|
+
spanAttributes: {
|
|
10141
|
+
"moltnet.task.id": task.id,
|
|
10142
|
+
"moltnet.task.attempt": attemptN,
|
|
10143
|
+
"moltnet.task.type": task.taskType
|
|
10144
|
+
}
|
|
10145
|
+
})]
|
|
9981
10146
|
});
|
|
9982
10147
|
await resourceLoader.reload();
|
|
9983
10148
|
session = (await createAgentSession({
|
|
@@ -10094,10 +10259,16 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
10094
10259
|
if (reporterOpen) {
|
|
10095
10260
|
try {
|
|
10096
10261
|
await reporter.finalize(finalUsage);
|
|
10097
|
-
} catch {
|
|
10262
|
+
} catch (err) {
|
|
10263
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
10264
|
+
console.error(`executePiTask: reporter.finalize() failed for task ${task.id} attempt ${attemptN}: ${detail}`);
|
|
10265
|
+
}
|
|
10098
10266
|
try {
|
|
10099
10267
|
await reporter.close();
|
|
10100
|
-
} catch {
|
|
10268
|
+
} catch (err) {
|
|
10269
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
10270
|
+
console.error(`executePiTask: reporter.close() failed for task ${task.id} attempt ${attemptN}: ${detail}`);
|
|
10271
|
+
}
|
|
10101
10272
|
}
|
|
10102
10273
|
await managed.vm.close();
|
|
10103
10274
|
}
|
|
@@ -10415,4 +10586,4 @@ function moltnetExtension(pi) {
|
|
|
10415
10586
|
registerMoltnetReflectCommand(pi, state);
|
|
10416
10587
|
}
|
|
10417
10588
|
//#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 };
|
|
10589
|
+
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.8.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,9 +29,10 @@
|
|
|
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/sdk": "0.
|
|
34
|
+
"@themoltnet/agent-runtime": "0.4.0",
|
|
35
|
+
"@themoltnet/sdk": "0.96.0"
|
|
35
36
|
},
|
|
36
37
|
"peerDependencies": {
|
|
37
38
|
"@mariozechner/pi-coding-agent": ">=0.67.0",
|
|
@@ -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",
|