@themoltnet/pi-runtime 0.15.2 → 0.15.3

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +8 -3
  2. package/dist/index.js +2267 -2184
  3. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -234,1676 +234,193 @@ function computeContentCid(entryType, title, content, tags) {
234
234
  return CID.createV1(raw.code, digest).toString(base32);
235
235
  }
236
236
  //#endregion
237
- //#region src/moltnet/render-phase6.ts
238
- function slugToTitle(value) {
239
- return value.split(/[:/_-]+/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join(" ");
240
- }
241
- function extractScope(tags) {
242
- const scope = tags?.find((tag) => tag.startsWith("scope:"));
243
- return scope ? scope.slice(6) : null;
244
- }
245
- function extractSeverity(tags) {
246
- const severity = tags?.find((tag) => tag.startsWith("severity:"));
247
- return severity ? severity.slice(9) : null;
248
- }
249
- function stripEntryScaffolding(content) {
250
- let out = content;
251
- out = out.replace(/<moltnet-signed>[\s\S]*?<content>([\s\S]*?)<\/content>[\s\S]*?<\/moltnet-signed>/gi, "$1");
252
- out = out.replace(/<metadata>[\s\S]*?<\/metadata>/gi, "");
253
- out = out.replace(/<signature>[\s\S]*?<\/signature>/gi, "");
254
- out = out.replace(/<\/?(?:moltnet-signed|content|signature|metadata)[^>]*>/gi, "");
255
- out = out.replace(/^- Compression:.*$/gim, "");
256
- out = out.replace(/^- Tokens:.*$/gim, "");
257
- return out.trim();
258
- }
259
- function normalizeKey(value) {
260
- return value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
261
- }
262
- function extractRules(content) {
263
- return stripEntryScaffolding(content).split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && (/(^rule:|^watch for:|^must\b|^never\b)/i.test(line) || /\b(MUST|NEVER)\b/.test(line))).slice(0, 5);
264
- }
265
- function renderSourceRefs(entries) {
266
- return entries.map((entry) => {
267
- const shortId = entry.entryId.slice(0, 8);
268
- const fingerprint = entry.entry.creator?.fingerprint?.replaceAll("-", "").slice(0, 4).toLowerCase();
269
- return `[\`e:${shortId}\`](@unknown · ${fingerprint ? `agent:${fingerprint}` : "agent:unkn"})`;
270
- }).join(", ");
271
- }
272
- function renderKeywords(tags) {
273
- const keywords = (tags ?? []).filter((tag) => !tag.startsWith("scope:") && !tag.startsWith("severity:"));
274
- if (keywords.length === 0) return "";
275
- return `Relevant search terms include ${keywords.slice(0, 6).map((tag) => `\`${tag}\``).join(", ")}.`;
276
- }
277
- function renderPhase6Markdown(pack) {
278
- const entries = pack.entries ?? [];
279
- const grouped = /* @__PURE__ */ new Map();
280
- for (const entry of entries) {
281
- const scope = extractScope(entry.entry.tags) ?? "general";
282
- const title = entry.entry.title?.trim() || `Entry ${entry.entryId.slice(0, 8)}`;
283
- const groupKey = normalizeKey(scope);
284
- const topicKey = normalizeKey(title) || entry.entryId;
285
- if (!grouped.has(groupKey)) grouped.set(groupKey, /* @__PURE__ */ new Map());
286
- const topics = grouped.get(groupKey);
287
- const existing = topics.get(topicKey);
288
- if (existing) existing.entries.push(entry);
289
- else topics.set(topicKey, {
290
- title,
291
- scope,
292
- entries: [entry]
293
- });
294
- }
295
- const lines = [];
296
- lines.push("# Rendered Pack");
297
- lines.push("");
298
- lines.push("## Source");
299
- lines.push("");
300
- lines.push("| Pack UUID | Pack CID | Entries |");
301
- lines.push("| --------- | -------- | ------- |");
302
- lines.push(`| \`${pack.id}\` | \`${pack.packCid}\` | ${entries.length} |`);
303
- lines.push("");
304
- for (const [, topics] of grouped) {
305
- const scope = topics.values().next().value?.scope ?? "general";
306
- lines.push(`## ${slugToTitle(scope)}`);
307
- lines.push("");
308
- for (const [, topic] of topics) {
309
- const primary = topic.entries[0];
310
- const mergedContent = topic.entries.map((entry) => stripEntryScaffolding(entry.entry.content)).filter(Boolean).join("\n\n");
311
- const rules = topic.entries.flatMap((entry) => extractRules(entry.entry.content));
312
- const severity = extractSeverity(primary.entry.tags);
313
- lines.push(`### ${topic.title}`);
314
- lines.push("");
315
- lines.push(`**Subsystem:** ${slugToTitle(topic.scope)}`);
316
- if (severity) lines.push(`**Severity:** ${slugToTitle(severity)}`);
317
- lines.push(`**Type:** ${primary.entry.entryType}`);
318
- lines.push("");
319
- if (rules.length > 0) {
320
- lines.push("**Rules**");
321
- lines.push("");
322
- for (const rule of Array.from(new Set(rules))) lines.push(`- ${rule}`);
323
- lines.push("");
324
- }
325
- lines.push(mergedContent);
326
- lines.push("");
327
- const keywords = renderKeywords(primary.entry.tags);
328
- if (keywords) {
329
- lines.push(keywords);
330
- lines.push("");
331
- }
332
- lines.push("Provenance:");
333
- for (const entry of topic.entries) lines.push(`- Entry ID \`${entry.entryId}\`, CID \`${entry.entryCidSnapshot}\``);
334
- lines.push("");
335
- lines.push(`*Sources: ${renderSourceRefs(topic.entries)}*`);
336
- lines.push("");
337
- }
338
- }
339
- if (entries.length === 0) {
340
- lines.push("_This pack has no expanded entries._");
341
- lines.push("");
342
- }
343
- return lines.join("\n").trim();
344
- }
345
- //#endregion
346
- //#region src/moltnet/tools.ts
237
+ //#region ../models/src/credential-scopes.ts
238
+ var CREDENTIAL_SCOPES = {
239
+ AgentProfile: "agent:profile",
240
+ ConnectorInvoke: "connector:invoke",
241
+ CryptoSign: "crypto:sign",
242
+ DiaryManage: "diary:manage",
243
+ DiaryRead: "diary:read",
244
+ DiaryWrite: "diary:write",
245
+ HumanProfile: "human:profile",
246
+ KeyManage: "key:manage",
247
+ PackRead: "pack:read",
248
+ PackWrite: "pack:write",
249
+ RuntimeManage: "runtime:manage",
250
+ RuntimeRead: "runtime:read",
251
+ TaskClaim: "task:claim",
252
+ TaskExecute: "task:execute",
253
+ TaskManage: "task:manage",
254
+ TaskRead: "task:read",
255
+ TaskWrite: "task:write",
256
+ TeamManage: "team:manage",
257
+ TeamRead: "team:read"
258
+ };
259
+ var ALL_CREDENTIAL_SCOPES = Object.freeze(Object.values(CREDENTIAL_SCOPES));
260
+ CREDENTIAL_SCOPES.AgentProfile, CREDENTIAL_SCOPES.CryptoSign, CREDENTIAL_SCOPES.RuntimeRead, CREDENTIAL_SCOPES.TaskRead, CREDENTIAL_SCOPES.TaskClaim, CREDENTIAL_SCOPES.TaskExecute;
261
+ CREDENTIAL_SCOPES.AgentProfile, CREDENTIAL_SCOPES.TaskRead, CREDENTIAL_SCOPES.TaskWrite;
262
+ CREDENTIAL_SCOPES.AgentProfile, CREDENTIAL_SCOPES.DiaryRead, CREDENTIAL_SCOPES.PackRead, CREDENTIAL_SCOPES.RuntimeRead, CREDENTIAL_SCOPES.TaskRead, CREDENTIAL_SCOPES.TeamRead;
263
+ Object.freeze(ALL_CREDENTIAL_SCOPES.filter((scope) => scope !== CREDENTIAL_SCOPES.HumanProfile));
347
264
  /**
348
- * MoltNet custom tools for pi.
265
+ * REST capabilities exercised by the current MCP tool surface.
349
266
  *
350
- * Factory that produces ready-to-register pi tool definitions.
351
- * These tools run on the host (not in the VM) via the MoltNet SDK,
352
- * so agent credentials never touch the VM filesystem.
267
+ * Intentionally excludes connector invocation, key management, runtime
268
+ * management/read, and task claiming because MCP exposes none of those
269
+ * operations.
353
270
  */
354
- var MOLTNET_TOOL_NAMES = [
355
- "moltnet_pack_get",
356
- "moltnet_pack_create",
357
- "moltnet_pack_provenance",
358
- "moltnet_pack_render",
359
- "moltnet_rendered_pack_list",
360
- "moltnet_rendered_pack_get",
361
- "moltnet_diary_tags",
362
- "moltnet_list_entries",
363
- "moltnet_get_entry",
364
- "moltnet_search_entries",
365
- "moltnet_create_entry",
366
- "moltnet_get_task",
367
- "moltnet_list_task_attempts",
368
- "moltnet_list_task_messages",
369
- "moltnet_upload_task_artifact",
370
- "moltnet_list_task_artifacts",
371
- "moltnet_download_task_artifact",
372
- "moltnet_review_session_errors",
373
- "moltnet_host_exec"
271
+ var MCP_CLIENT_SCOPES = [
272
+ CREDENTIAL_SCOPES.AgentProfile,
273
+ CREDENTIAL_SCOPES.CryptoSign,
274
+ CREDENTIAL_SCOPES.DiaryManage,
275
+ CREDENTIAL_SCOPES.DiaryRead,
276
+ CREDENTIAL_SCOPES.DiaryWrite,
277
+ CREDENTIAL_SCOPES.HumanProfile,
278
+ CREDENTIAL_SCOPES.PackRead,
279
+ CREDENTIAL_SCOPES.PackWrite,
280
+ CREDENTIAL_SCOPES.TaskExecute,
281
+ CREDENTIAL_SCOPES.TaskManage,
282
+ CREDENTIAL_SCOPES.TaskRead,
283
+ CREDENTIAL_SCOPES.TaskWrite,
284
+ CREDENTIAL_SCOPES.TeamManage,
285
+ CREDENTIAL_SCOPES.TeamRead
374
286
  ];
375
- var DIARY_TAG_MAX_LENGTH$1 = 128;
376
- /**
377
- * Baseline env keys forwarded to host-exec child processes.
378
- * Callers can extend this set at sandbox startup via `MoltNetToolsConfig.hostExecBaseEnv`.
379
- */
380
- var HOST_EXEC_DEFAULT_BASE_ENV = new Set([
381
- "PATH",
382
- "HOME",
383
- "LANG",
384
- "LC_ALL",
385
- "TMPDIR",
386
- "GIT_CONFIG_GLOBAL",
387
- "MOLTNET_CREDENTIALS_PATH",
388
- "GIT_AUTHOR_NAME",
389
- "GIT_AUTHOR_EMAIL",
390
- "GIT_COMMITTER_NAME",
391
- "GIT_COMMITTER_EMAIL",
392
- "SSH_AUTH_SOCK"
393
- ]);
394
- function ensureConnected(config) {
395
- const agent = config.getAgent();
396
- const diaryId = config.getDiaryId();
397
- if (!agent || !diaryId) throw new Error("MoltNet not connected");
398
- return {
399
- agent,
400
- diaryId,
401
- teamId: config.getTeamId() ?? ""
402
- };
403
- }
404
- function hostExecMatchesAutoApproveRule(params, rule) {
405
- if (params.executable !== rule.executable) return false;
406
- if (rule.argsExcludes?.some((arg) => params.args.includes(arg))) return false;
407
- if (rule.argsPrefix && !rule.argsPrefix.every((arg, index) => params.args[index] === arg)) return false;
408
- if (rule.argsContains && !rule.argsContains.every((arg) => params.args.includes(arg))) return false;
409
- return true;
410
- }
411
- function shouldAutoApproveHostExec(params, config) {
412
- const policy = config.autoApproveHostExec === true ? true : config.hostExecAutoApprove ?? false;
413
- if (policy === true) return true;
414
- if (!Array.isArray(policy)) return false;
415
- return policy.some((rule) => hostExecMatchesAutoApproveRule(params, rule));
416
- }
417
- async function resolveWorkspaceFilePath(cwd, filePath) {
418
- const resolved = path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(cwd, filePath);
419
- const realCwd = await realpath(cwd);
420
- let realResolved;
421
- try {
422
- realResolved = await realpath(resolved);
423
- } catch (err) {
424
- 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.`);
425
- throw err;
426
- }
427
- const rel = path.relative(realCwd, realResolved);
428
- if (rel === "" || rel.startsWith("..") || path.isAbsolute(rel)) throw new Error(`task artifact path escapes workspace: ${filePath}`);
429
- return realResolved;
430
- }
431
- async function openWorkspaceArtifactInput(config, cwd, filePath) {
432
- if (config.openWorkspaceFileForRead) try {
433
- return await config.openWorkspaceFileForRead(filePath);
434
- } catch (err) {
435
- 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.`);
436
- throw err;
437
- }
438
- const resolved = await resolveWorkspaceFilePath(cwd, filePath);
439
- const info = await stat(resolved);
440
- return {
441
- stream: createReadStream(resolved),
442
- isFile: info.isFile(),
443
- sizeBytes: info.size,
444
- displayPath: path.relative(cwd, resolved)
445
- };
446
- }
447
- async function resolveWorkspaceOutputPath(cwd, filePath) {
448
- const resolved = path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(cwd, filePath);
449
- const workspaceRoot = path.resolve(cwd);
450
- const lexicalRel = path.relative(workspaceRoot, resolved);
451
- if (lexicalRel === "" || lexicalRel.startsWith("..") || path.isAbsolute(lexicalRel)) throw new Error(`task artifact output path escapes workspace: ${filePath}`);
452
- const realCwd = await realpath(cwd);
453
- const parent = path.dirname(resolved);
454
- assertPathInsideWorkspace(realCwd, await findExistingAncestor(parent), filePath);
455
- await mkdir(parent, { recursive: true });
456
- assertPathInsideWorkspace(realCwd, await realpath(parent), filePath);
457
- return resolved;
458
- }
459
- async function findExistingAncestor(candidate) {
460
- let current = candidate;
461
- for (;;) {
462
- try {
463
- return await realpath(current);
464
- } catch (err) {
465
- if (!err || typeof err !== "object" || !("code" in err) || err.code !== "ENOENT") throw err;
466
- }
467
- const parent = path.dirname(current);
468
- if (parent === current) throw new Error(`task artifact output has no existing ancestor: ${candidate}`);
469
- current = parent;
470
- }
471
- }
472
- function assertPathInsideWorkspace(realCwd, realPath, displayPath) {
473
- if (!isResolvedPathInsideRoot$1(realPath, realCwd)) throw new Error(`task artifact output path escapes workspace: ${displayPath}`);
474
- }
475
- /**
476
- * Expand the `taskFilter` shorthand on the diary list/search tools into
477
- * the matching `task:*` provenance tags emitted by `moltnet_create_entry`
478
- * during a task. Returning an array (possibly empty) lets callers spread
479
- * it into a larger `tags` AND-filter without conditionals.
480
- */
481
- function compileTaskFilterTags(filter) {
482
- if (!filter) return [];
483
- const tags = [];
484
- if (filter.taskId) tags.push(`task:id:${filter.taskId}`);
485
- if (filter.taskType) tags.push(`task:type:${filter.taskType}`);
486
- if (filter.correlationId) tags.push(`task:correlation:${filter.correlationId}`);
487
- if (typeof filter.attemptN === "number") tags.push(`task:attempt:${filter.attemptN}`);
488
- return tags;
287
+ MCP_CLIENT_SCOPES.filter((scope) => scope !== CREDENTIAL_SCOPES.HumanProfile);
288
+ Object.freeze([...[
289
+ "openid",
290
+ "offline",
291
+ "offline_access"
292
+ ], ...MCP_CLIENT_SCOPES]);
293
+ //#endregion
294
+ //#region ../models/src/preview-sign.ts
295
+ function schemaRef$1(schema, id) {
296
+ return Type.Unsafe(Type.Ref(id));
489
297
  }
298
+ var PreviewSignBase64UrlSchema = Type.String({
299
+ $id: "PreviewSignBase64Url",
300
+ minLength: 1,
301
+ maxLength: 5462,
302
+ pattern: "^[A-Za-z0-9_-]+$"
303
+ });
304
+ var PreviewSignSha256Base64UrlSchema = Type.String({
305
+ $id: "PreviewSignSha256Base64Url",
306
+ minLength: 43,
307
+ maxLength: 43,
308
+ pattern: "^[A-Za-z0-9_-]+$"
309
+ });
310
+ var PreviewSignP256DerSignatureBase64UrlSchema = Type.String({
311
+ $id: "PreviewSignP256DerSignatureBase64Url",
312
+ minLength: 11,
313
+ maxLength: 96,
314
+ pattern: "^[A-Za-z0-9_-]+$"
315
+ });
316
+ var PreviewSignEs256PublicKeySchema = Type.Object({
317
+ kty: Type.Literal(2),
318
+ algorithm: Type.Literal(-7),
319
+ curve: Type.Literal(1),
320
+ x: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url"),
321
+ y: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url")
322
+ }, {
323
+ $id: "PreviewSignEs256PublicKey",
324
+ additionalProperties: false
325
+ });
326
+ var PreviewSignEcdhEsHkdf256PublicKeySchema = Type.Object({
327
+ kty: Type.Literal(2),
328
+ algorithm: Type.Literal(-25),
329
+ curve: Type.Literal(1),
330
+ x: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url"),
331
+ y: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url")
332
+ }, {
333
+ $id: "PreviewSignEcdhEsHkdf256PublicKey",
334
+ additionalProperties: false
335
+ });
336
+ var PreviewSignEsp256PublicKeySchema = Type.Object({
337
+ kty: Type.Literal(2),
338
+ algorithm: Type.Literal(-9),
339
+ curve: Type.Literal(1),
340
+ x: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url"),
341
+ y: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url")
342
+ }, {
343
+ $id: "PreviewSignEsp256PublicKey",
344
+ additionalProperties: false
345
+ });
346
+ var PreviewSignArkgSeedPublicKeySchema = Type.Object({
347
+ kty: Type.Literal(-65537),
348
+ algorithm: Type.Literal(-65700),
349
+ derivedAlgorithm: Type.Literal(-9),
350
+ blindingKey: schemaRef$1(PreviewSignEs256PublicKeySchema, "PreviewSignEs256PublicKey"),
351
+ kemKey: schemaRef$1(PreviewSignEcdhEsHkdf256PublicKeySchema, "PreviewSignEcdhEsHkdf256PublicKey")
352
+ }, {
353
+ $id: "PreviewSignArkgSeedPublicKey",
354
+ additionalProperties: false
355
+ });
356
+ var PreviewSignPublicMaterialSchema = Type.Object({
357
+ version: Type.Literal(1),
358
+ outerCredentialId: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
359
+ outerPublicKey: schemaRef$1(PreviewSignEs256PublicKeySchema, "PreviewSignEs256PublicKey"),
360
+ previewKeyHandle: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
361
+ seedPublicKey: schemaRef$1(PreviewSignArkgSeedPublicKeySchema, "PreviewSignArkgSeedPublicKey")
362
+ }, {
363
+ $id: "PreviewSignPublicMaterial",
364
+ additionalProperties: false
365
+ });
366
+ var PreviewSignChallengeSchema = Type.Object({
367
+ verificationMethod: Type.Literal("human-hardware-previewsign"),
368
+ version: Type.Literal(1),
369
+ envelope: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
370
+ digest: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url"),
371
+ additionalArguments: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
372
+ outerCredentialId: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
373
+ outerPublicKey: schemaRef$1(PreviewSignEs256PublicKeySchema, "PreviewSignEs256PublicKey"),
374
+ previewKeyHandle: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url")
375
+ }, {
376
+ $id: "PreviewSignChallenge",
377
+ additionalProperties: false
378
+ });
379
+ var PreviewSignChallengeValueSchema = Type.Object({
380
+ verificationMethod: Type.Literal("human-hardware-previewsign"),
381
+ value: schemaRef$1(PreviewSignChallengeSchema, "PreviewSignChallenge")
382
+ }, {
383
+ $id: "PreviewSignChallengeValue",
384
+ additionalProperties: false
385
+ });
386
+ var PreviewSignChallengeOperationSchema = Type.Union([Type.Literal("credential-registration"), Type.Literal("signing-request")], { $id: "PreviewSignChallengeOperation" });
387
+ var PreviewSignReceiptSchema = Type.Object({
388
+ version: Type.Literal(1),
389
+ signature: schemaRef$1(PreviewSignP256DerSignatureBase64UrlSchema, "PreviewSignP256DerSignatureBase64Url")
390
+ }, {
391
+ $id: "PreviewSignReceipt",
392
+ additionalProperties: false
393
+ });
394
+ var PreviewSignReceiptValueSchema = Type.Object({
395
+ verificationMethod: Type.Literal("human-hardware-previewsign"),
396
+ value: schemaRef$1(PreviewSignReceiptSchema, "PreviewSignReceipt")
397
+ }, {
398
+ $id: "PreviewSignReceiptValue",
399
+ additionalProperties: false
400
+ });
401
+ var previewSignSchemaContext = {
402
+ PreviewSignBase64Url: PreviewSignBase64UrlSchema,
403
+ PreviewSignSha256Base64Url: PreviewSignSha256Base64UrlSchema,
404
+ PreviewSignP256DerSignatureBase64Url: PreviewSignP256DerSignatureBase64UrlSchema,
405
+ PreviewSignEs256PublicKey: PreviewSignEs256PublicKeySchema,
406
+ PreviewSignEcdhEsHkdf256PublicKey: PreviewSignEcdhEsHkdf256PublicKeySchema,
407
+ PreviewSignEsp256PublicKey: PreviewSignEsp256PublicKeySchema,
408
+ PreviewSignArkgSeedPublicKey: PreviewSignArkgSeedPublicKeySchema,
409
+ PreviewSignPublicMaterial: PreviewSignPublicMaterialSchema,
410
+ PreviewSignChallenge: PreviewSignChallengeSchema,
411
+ PreviewSignChallengeValue: PreviewSignChallengeValueSchema,
412
+ PreviewSignChallengeOperation: PreviewSignChallengeOperationSchema,
413
+ PreviewSignReceipt: PreviewSignReceiptSchema,
414
+ PreviewSignReceiptValue: PreviewSignReceiptValueSchema
415
+ };
416
+ //#endregion
417
+ //#region ../models/src/verification-method.ts
490
418
  /**
491
- * Create all MoltNet tool definitions, ready to pass to `pi.registerTool()`.
492
- */
493
- function createMoltNetTools(config) {
494
- const getPack = defineTool({
495
- name: "moltnet_pack_get",
496
- label: "Get MoltNet Pack",
497
- description: "Get a context pack by ID. Optionally expand included entries.",
498
- parameters: Type$1.Object({
499
- packId: Type$1.String({ description: "Context pack ID" }),
500
- expandEntries: Type$1.Optional(Type$1.Boolean({ description: "Include full expanded entries" }))
501
- }),
502
- async execute(_id, params) {
503
- const { agent } = ensureConnected(config);
504
- const pack = await agent.packs.get(params.packId, { expand: params.expandEntries ? "entries" : void 0 });
505
- return {
506
- content: [{
507
- type: "text",
508
- text: JSON.stringify(pack, null, 2)
509
- }],
510
- details: {}
511
- };
512
- }
513
- });
514
- const createPack = defineTool({
515
- name: "moltnet_pack_create",
516
- label: "Create MoltNet Pack",
517
- description: "Persist a curated context pack. Entries are caller-ranked (lower rank = more prominent). Recipe/prompt/selection_rationale belong in params. Defaults to pinned=false — packs in the attribution pipeline are ephemeral unless the caller explicitly opts in.",
518
- parameters: Type$1.Object({
519
- entries: Type$1.Array(Type$1.Object({
520
- entryId: Type$1.String({ description: "Diary entry UUID" }),
521
- rank: Type$1.Number({ description: "Rank (1..N, lower = more prominent)" })
522
- }), { description: "Selected entries with their ranks" }),
523
- params: Type$1.Optional(Type$1.Record(Type$1.String(), Type$1.Unknown(), { description: "Free-form recipe parameters (recipe name, prompt, selection rationale, etc.)" })),
524
- tokenBudget: Type$1.Optional(Type$1.Number({ description: "Soft token budget recorded on the pack (optional)" })),
525
- pinned: Type$1.Optional(Type$1.Boolean({ description: "Pin the pack against retention policy (default false)" }))
526
- }),
527
- async execute(_id, params) {
528
- const { agent, diaryId } = ensureConnected(config);
529
- const pack = await agent.packs.create(diaryId, {
530
- packType: "custom",
531
- params: params.params ?? {},
532
- entries: params.entries,
533
- tokenBudget: params.tokenBudget,
534
- pinned: params.pinned ?? false
535
- });
536
- return {
537
- content: [{
538
- type: "text",
539
- text: JSON.stringify(pack, null, 2)
540
- }],
541
- details: {}
542
- };
543
- }
544
- });
545
- const getPackProvenance = defineTool({
546
- name: "moltnet_pack_provenance",
547
- label: "Get MoltNet Pack Provenance",
548
- description: "Get the provenance graph for a context pack by ID or CID.",
549
- parameters: Type$1.Object({
550
- packId: Type$1.Optional(Type$1.String({ description: "Context pack ID" })),
551
- packCid: Type$1.Optional(Type$1.String({ description: "Context pack CID" })),
552
- depth: Type$1.Optional(Type$1.Number({ description: "Supersession ancestry depth to include (default 2)" }))
553
- }),
554
- async execute(_id, params) {
555
- const { agent } = ensureConnected(config);
556
- if (!params.packId && !params.packCid) throw new Error("Provide either packId or packCid");
557
- if (params.packId && params.packCid) throw new Error("Provide only one of packId or packCid");
558
- const graph = params.packId ? await agent.packs.getProvenance(params.packId, { depth: params.depth ?? 2 }) : await agent.packs.getProvenanceByCid(params.packCid, { depth: params.depth ?? 2 });
559
- const payload = {
560
- metadata: graph.metadata,
561
- counts: {
562
- nodes: graph.nodes.length,
563
- edges: graph.edges.length
564
- },
565
- graph
566
- };
567
- return {
568
- content: [{
569
- type: "text",
570
- text: JSON.stringify(payload, null, 2)
571
- }],
572
- details: {}
573
- };
574
- }
575
- });
576
- const renderPack = defineTool({
577
- name: "moltnet_pack_render",
578
- label: "Render MoltNet Pack",
579
- description: "Fetch a pack with entries, transform it into docs, then preview or persist the rendered pack.",
580
- parameters: Type$1.Object({
581
- packId: Type$1.String({ description: "Context pack ID" }),
582
- renderMethod: Type$1.Optional(Type$1.String({ description: "Render method label. Defaults to pi:pack-to-docs-v1" })),
583
- markdown: Type$1.Optional(Type$1.String({ description: "Optional caller-authored markdown override" })),
584
- preview: Type$1.Optional(Type$1.Boolean({ description: "Preview without persisting (default false)" })),
585
- pinned: Type$1.Optional(Type$1.Boolean({ description: "Persist the rendered pack as pinned (default false)" }))
586
- }),
587
- async execute(_id, params) {
588
- const { agent } = ensureConnected(config);
589
- const renderMethod = params.renderMethod ?? "pi:pack-to-docs-v1";
590
- let renderedMarkdown = params.markdown;
591
- if (!renderedMarkdown && !renderMethod.startsWith("server:")) renderedMarkdown = renderPhase6Markdown(await agent.packs.get(params.packId, { expand: "entries" }));
592
- const result = params.preview ?? false ? await agent.packs.previewRendered(params.packId, {
593
- renderMethod,
594
- renderedMarkdown
595
- }) : await agent.packs.render(params.packId, {
596
- renderMethod,
597
- renderedMarkdown,
598
- pinned: params.pinned
599
- });
600
- return {
601
- content: [{
602
- type: "text",
603
- text: JSON.stringify(result, null, 2)
604
- }],
605
- details: {}
606
- };
607
- }
608
- });
609
- const listRenderedPacks = defineTool({
610
- name: "moltnet_rendered_pack_list",
611
- label: "List MoltNet Rendered Packs",
612
- description: "List rendered packs for the current MoltNet diary, optionally filtered by source pack or render method.",
613
- parameters: Type$1.Object({
614
- sourcePackId: Type$1.Optional(Type$1.String({ description: "Filter by source pack ID" })),
615
- renderMethod: Type$1.Optional(Type$1.String({ description: "Filter by render method" })),
616
- limit: Type$1.Optional(Type$1.Number({ description: "Max results (default 10)" })),
617
- offset: Type$1.Optional(Type$1.Number({ description: "Offset for pagination (default 0)" }))
618
- }),
619
- async execute(_id, params) {
620
- const { agent, diaryId } = ensureConnected(config);
621
- const rendered = await agent.packs.listRendered(diaryId, {
622
- sourcePackId: params.sourcePackId,
623
- renderMethod: params.renderMethod,
624
- limit: params.limit ?? 10,
625
- offset: params.offset ?? 0
626
- });
627
- return {
628
- content: [{
629
- type: "text",
630
- text: JSON.stringify(rendered, null, 2)
631
- }],
632
- details: {}
633
- };
634
- }
635
- });
636
- const getRenderedPack = defineTool({
637
- name: "moltnet_rendered_pack_get",
638
- label: "Get MoltNet Rendered Pack",
639
- description: "Get a rendered pack by ID.",
640
- parameters: Type$1.Object({ renderedPackId: Type$1.String({ description: "Rendered pack ID" }) }),
641
- async execute(_id, params) {
642
- const { agent } = ensureConnected(config);
643
- const rendered = await agent.packs.getRendered(params.renderedPackId);
644
- return {
645
- content: [{
646
- type: "text",
647
- text: JSON.stringify(rendered, null, 2)
648
- }],
649
- details: {}
650
- };
651
- }
652
- });
653
- const diaryTags = defineTool({
654
- name: "moltnet_diary_tags",
655
- label: "List MoltNet Diary Tags",
656
- description: "Inventory tags on the current diary with entry counts. Cheap reconnaissance before committing to a search or list — use it to discover scope prefixes and cluster sizes. Optional prefix/minCount/entryTypes filters narrow the result.",
657
- parameters: Type$1.Object({
658
- prefix: Type$1.Optional(Type$1.String({ description: "Filter to tags starting with this prefix (e.g. \"scope:\")" })),
659
- minCount: Type$1.Optional(Type$1.Number({ description: "Exclude tags with fewer than this many entries" })),
660
- entryTypes: Type$1.Optional(Type$1.Array(Type$1.Union([
661
- Type$1.Literal("episodic"),
662
- Type$1.Literal("semantic"),
663
- Type$1.Literal("procedural"),
664
- Type$1.Literal("reflection")
665
- ]), { description: "Scope the tag count to these entry types" }))
666
- }),
667
- async execute(_id, params) {
668
- const { agent, diaryId } = ensureConnected(config);
669
- const result = await agent.diaries.tags(diaryId, {
670
- prefix: params.prefix,
671
- minCount: params.minCount,
672
- entryTypes: params.entryTypes
673
- });
674
- return {
675
- content: [{
676
- type: "text",
677
- text: JSON.stringify(result, null, 2)
678
- }],
679
- details: {}
680
- };
681
- }
682
- });
683
- const listEntries = defineTool({
684
- name: "moltnet_list_entries",
685
- label: "List MoltNet Diary Entries",
686
- description: "List entries from the MoltNet diary. When `entryIds` is provided, batch-fetches those specific entries (max 50) and returns full fields including entryType, contentSignature, and contentHash for signature checks. Otherwise returns recent entries with a content preview, filtered by any combination of tags (AND), excludeTags (NONE), entryType, and the taskFilter shorthand which expands into the right `task:*` tags.",
687
- parameters: Type$1.Object({
688
- limit: Type$1.Optional(Type$1.Number({ description: "Max entries to return (default 10)" })),
689
- tags: Type$1.Optional(Type$1.Array(Type$1.String({
690
- minLength: 1,
691
- maxLength: DIARY_TAG_MAX_LENGTH$1
692
- }), {
693
- description: "Tags filter — entry must have ALL listed tags (AND). Max 20.",
694
- maxItems: 20
695
- })),
696
- excludeTags: Type$1.Optional(Type$1.Array(Type$1.String({
697
- minLength: 1,
698
- maxLength: DIARY_TAG_MAX_LENGTH$1
699
- }), {
700
- description: "Tags to exclude — entry must have NONE of these. Max 20.",
701
- maxItems: 20
702
- })),
703
- entryType: Type$1.Optional(Type$1.String({ description: "Filter by entry type (procedural, semantic, episodic, reflection)." })),
704
- taskFilter: Type$1.Optional(Type$1.Object({
705
- taskId: Type$1.Optional(Type$1.String()),
706
- taskType: Type$1.Optional(Type$1.String()),
707
- correlationId: Type$1.Optional(Type$1.String()),
708
- attemptN: Type$1.Optional(Type$1.Number())
709
- }, { description: "Shorthand: any combination compiles to the matching task:* tags (task:id:<id>, task:type:<type>, task:correlation:<id>, task:attempt:<n>) and is merged into the tags filter." })),
710
- entryIds: Type$1.Optional(Type$1.Array(Type$1.String(), {
711
- description: "Batch-fetch specific entries by UUID (max 50). Overrides every other filter.",
712
- maxItems: 50
713
- }))
714
- }),
715
- async execute(_id, params) {
716
- const { agent, diaryId } = ensureConnected(config);
717
- const query = {
718
- orderBy: "createdAt",
719
- order: "desc"
720
- };
721
- const batchMode = !!params.entryIds?.length;
722
- if (batchMode) query.ids = params.entryIds;
723
- else {
724
- query.limit = params.limit ?? 10;
725
- const expandedTags = compileTaskFilterTags(params.taskFilter);
726
- const allTags = [...params.tags ?? [], ...expandedTags];
727
- if (allTags.length) query.tags = allTags;
728
- if (params.excludeTags?.length) query.excludeTags = params.excludeTags;
729
- if (params.entryType) query.entryType = params.entryType;
730
- }
731
- const entries = await agent.entries.list(diaryId, query);
732
- return {
733
- content: [{
734
- type: "text",
735
- text: JSON.stringify(entries.items?.map((e) => batchMode ? {
736
- id: e.id,
737
- title: e.title,
738
- entryType: e.entryType,
739
- tags: e.tags,
740
- importance: e.importance,
741
- contentHash: e.contentHash,
742
- contentSignature: e.contentSignature,
743
- signingNonce: e.signingNonce,
744
- createdAt: e.createdAt
745
- } : {
746
- id: e.id,
747
- title: e.title,
748
- tags: e.tags,
749
- importance: e.importance,
750
- createdAt: e.createdAt,
751
- contentPreview: typeof e.content === "string" ? e.content.slice(0, 200) : void 0
752
- }), null, 2)
753
- }],
754
- details: {}
755
- };
756
- }
757
- });
758
- const getEntry = defineTool({
759
- name: "moltnet_get_entry",
760
- label: "Get MoltNet Diary Entry",
761
- description: "Get the full content of a specific diary entry by ID.",
762
- parameters: Type$1.Object({ entryId: Type$1.String({ description: "The entry ID to fetch" }) }),
763
- async execute(_id, params) {
764
- const { agent } = ensureConnected(config);
765
- const entry = await agent.entries.get(params.entryId);
766
- return {
767
- content: [{
768
- type: "text",
769
- text: JSON.stringify({
770
- id: entry.id,
771
- title: entry.title,
772
- content: entry.content,
773
- tags: entry.tags,
774
- importance: entry.importance,
775
- createdAt: entry.createdAt
776
- }, null, 2)
777
- }],
778
- details: {}
779
- };
780
- }
781
- });
782
- const searchEntries = defineTool({
783
- name: "moltnet_search_entries",
784
- label: "Search MoltNet Diary Entries",
785
- description: "Hybrid (semantic + lexical) search over diary entries. Use proactively before non-trivial investigation, code changes, review, or episodic incident capture so prior decisions and recurring failures surface before you act. Do not search randomly: pass taskFilter for task/correlation-local searches and tags or entryTypes for broader prior-knowledge searches. Optional tags / excludeTags / entryTypes filters AND with the query; the taskFilter shorthand expands into task:* provenance tags so `taskFilter: { taskType: \"fulfill_brief\" }` returns only entries from fulfill_brief attempts. Filters apply server-side before ranking.",
786
- parameters: Type$1.Object({
787
- query: Type$1.String({ description: "Natural language search query" }),
788
- limit: Type$1.Optional(Type$1.Number({ description: "Max results (default 5)" })),
789
- tags: Type$1.Optional(Type$1.Array(Type$1.String({
790
- minLength: 1,
791
- maxLength: DIARY_TAG_MAX_LENGTH$1
792
- }), {
793
- description: "Entry must have ALL listed tags (AND). Max 20.",
794
- maxItems: 20
795
- })),
796
- excludeTags: Type$1.Optional(Type$1.Array(Type$1.String({
797
- minLength: 1,
798
- maxLength: DIARY_TAG_MAX_LENGTH$1
799
- }), {
800
- description: "Entry must have NONE of these tags. Max 20.",
801
- maxItems: 20
802
- })),
803
- entryTypes: Type$1.Optional(Type$1.Array(Type$1.String(), {
804
- description: "Restrict to these entry types (procedural, semantic, episodic, reflection). Max 4.",
805
- maxItems: 4
806
- })),
807
- taskFilter: Type$1.Optional(Type$1.Object({
808
- taskId: Type$1.Optional(Type$1.String()),
809
- taskType: Type$1.Optional(Type$1.String()),
810
- correlationId: Type$1.Optional(Type$1.String()),
811
- attemptN: Type$1.Optional(Type$1.Number())
812
- }, { description: "Shorthand: any combination compiles to the matching task:* tags and is merged into the tags filter." }))
813
- }),
814
- async execute(_id, params) {
815
- const { agent, diaryId } = ensureConnected(config);
816
- const expandedTags = compileTaskFilterTags(params.taskFilter);
817
- const allTags = [...params.tags ?? [], ...expandedTags];
818
- const results = await agent.entries.search({
819
- diaryId,
820
- query: params.query,
821
- limit: params.limit ?? 5,
822
- ...allTags.length ? { tags: allTags } : {},
823
- ...params.excludeTags?.length ? { excludeTags: params.excludeTags } : {},
824
- ...params.entryTypes?.length ? { entryTypes: params.entryTypes } : {}
825
- });
826
- return {
827
- content: [{
828
- type: "text",
829
- text: JSON.stringify(results.results?.map((e) => ({
830
- id: e.id,
831
- title: e.title,
832
- tags: e.tags,
833
- importance: e.importance,
834
- contentPreview: typeof e.content === "string" ? e.content.slice(0, 200) : void 0
835
- })), null, 2)
836
- }],
837
- details: {}
838
- };
839
- }
840
- });
841
- const createEntry = defineTool({
842
- name: "moltnet_create_entry",
843
- label: "Create MoltNet Diary Entry",
844
- description: "Create a new diary entry to record decisions, findings, incidents, or reflections. Before creating an episodic incident entry, first call moltnet_search_entries with the title/root-cause/error/watch-for terms plus taskFilter, tags, or entryTypes filters, then reference close matches instead of creating an isolated duplicate. During an active task, the entry is forced into the task diary and tagged with the task:* provenance namespace (task:id:<id>, task:type:<type>, task:attempt:<n>, plus task:correlation:<id> when set); an explicit diaryId mismatching the task diary is rejected. Use this tool — NOT `moltnet entry create` / `moltnet entry create-signed` via bash. The CLI path bypasses task-tag auto-injection and leaves entries invisible to taskFilter queries.",
845
- parameters: Type$1.Object({
846
- title: Type$1.String({ description: "Entry title (concise, descriptive)" }),
847
- content: Type$1.String({ description: "Entry content (markdown)" }),
848
- tags: Type$1.Optional(Type$1.Array(Type$1.String(), { description: "Tags for categorization" })),
849
- importance: Type$1.Optional(Type$1.Number({ description: "Importance 1-10 (default 5)" })),
850
- entryType: Type$1.Optional(Type$1.Union([
851
- Type$1.Literal("episodic"),
852
- Type$1.Literal("semantic"),
853
- Type$1.Literal("procedural"),
854
- Type$1.Literal("reflection")
855
- ], { description: "Entry type. Use episodic for incidents, workarounds, bugs, or recurrence evidence; defaults to semantic." })),
856
- diaryId: Type$1.Optional(Type$1.String({ description: "Explicit diary id. During an active task, must match the task diary or the call is rejected. Outside a task, overrides the env-derived diary." })),
857
- signed: Type$1.Optional(Type$1.Boolean({ description: "Create a content-signed (immutable) entry. The signature is produced on the trusted host through the agent-signing capability; fails when the runtime does not expose it." }))
858
- }),
859
- async execute(_id, params) {
860
- const { agent, diaryId: envDiaryId } = ensureConnected(config);
861
- const signer = params.signed ? config.getSigner?.() ?? null : null;
862
- if (params.signed && !signer) throw new Error("entries_create: signed entries require the agent-signing capability; create an unsigned entry or run under a runtime that declares it.");
863
- const taskCtx = config.getTaskContext?.() ?? null;
864
- let targetDiaryId;
865
- let autoTags = [];
866
- if (taskCtx) {
867
- if (params.diaryId && params.diaryId !== taskCtx.diaryId) throw new Error(`entries_create: diaryId "${params.diaryId}" does not match the active task diary "${taskCtx.diaryId}". Entries created during a task must land in the task diary.`);
868
- targetDiaryId = taskCtx.diaryId;
869
- autoTags = [
870
- `task:id:${taskCtx.taskId}`,
871
- `task:type:${taskCtx.taskType}`,
872
- `task:attempt:${taskCtx.attemptN}`,
873
- ...taskCtx.correlationId ? [`task:correlation:${taskCtx.correlationId}`] : []
874
- ];
875
- } else targetDiaryId = params.diaryId ?? envDiaryId;
876
- const userTags = params.tags ?? [];
877
- const mergedTags = autoTags.length ? [...autoTags, ...userTags.filter((t) => !autoTags.includes(t))] : userTags;
878
- let entry;
879
- try {
880
- let signingRequestId;
881
- if (signer) {
882
- const contentCid = computeContentCid(params.entryType ?? "semantic", params.title, params.content, mergedTags);
883
- const request = await agent.crypto.signingRequests.create({
884
- message: contentCid,
885
- verificationMethod: "agent-ed25519"
886
- });
887
- await signer.signDiaryEntry({ signingRequestId: request.id });
888
- signingRequestId = request.id;
889
- }
890
- entry = await agent.entries.create(targetDiaryId, {
891
- title: params.title,
892
- content: params.content,
893
- tags: mergedTags,
894
- importance: params.importance ?? 5,
895
- ...params.entryType ? { entryType: params.entryType } : {},
896
- ...signingRequestId ? { signingRequestId } : {}
897
- });
898
- } catch (error) {
899
- const message = error instanceof Error ? error.message : String(error);
900
- if (taskCtx && /\b403\b|forbidden|not authorized|permission/i.test(message)) {
901
- await config.onTaskProvenanceEvent?.("task.provenance.entry_denied", {
902
- taskId: taskCtx.taskId,
903
- diaryId: targetDiaryId,
904
- error: message
905
- });
906
- throw new Error(`entries_create: the task provenance diary denied this entry. The diary may have moved to another team or its grants may have changed. The task and runtime session remain active; ask a diary manager to restore write access, then retry. (${message})`, { cause: error });
907
- }
908
- throw error;
909
- }
910
- return {
911
- content: [{
912
- type: "text",
913
- text: JSON.stringify({
914
- id: entry.id,
915
- title: entry.title,
916
- createdAt: entry.createdAt,
917
- diaryId: targetDiaryId,
918
- entryType: entry.entryType,
919
- importance: entry.importance,
920
- tags: mergedTags,
921
- signed: signer !== null
922
- }, null, 2)
923
- }],
924
- details: {}
925
- };
926
- }
927
- });
928
- const getTask = defineTool({
929
- name: "moltnet_get_task",
930
- label: "Get MoltNet Task",
931
- description: "Fetch a task by ID — the row, including taskType, status, acceptedAttemptN, references, input, timeouts. Use this when you need to inspect another task (e.g. an assess_brief judging a fulfill_brief: fetch the target task here, then list its attempts via moltnet_list_task_attempts to read the producer's output and decide what to investigate).",
932
- parameters: Type$1.Object({ taskId: Type$1.String({ description: "Task ID (UUID)." }) }),
933
- async execute(_id, params) {
934
- const { agent } = ensureConnected(config);
935
- const task = await agent.tasks.get(params.taskId);
936
- return {
937
- content: [{
938
- type: "text",
939
- text: JSON.stringify(task, null, 2)
940
- }],
941
- details: {}
942
- };
943
- }
944
- });
945
- const listTaskAttempts = defineTool({
946
- name: "moltnet_list_task_attempts",
947
- label: "List MoltNet Task Attempts",
948
- description: "List every attempt made on a task, in attempt-number order. Each attempt carries the claimed agent, status, output, outputCid, and timing. The accepted attempt (whose attemptN matches the parent task's acceptedAttemptN) is the canonical one — its `output` is what consumers should reason against. Earlier failed or timed_out attempts are kept for audit but should not drive downstream decisions.",
949
- parameters: Type$1.Object({ taskId: Type$1.String({ description: "Task ID (UUID)." }) }),
950
- async execute(_id, params) {
951
- const { agent } = ensureConnected(config);
952
- const attempts = await agent.tasks.listAttempts(params.taskId);
953
- return {
954
- content: [{
955
- type: "text",
956
- text: JSON.stringify(attempts, null, 2)
957
- }],
958
- details: {}
959
- };
960
- }
961
- });
962
- const listTaskMessages = defineTool({
963
- name: "moltnet_list_task_messages",
964
- label: "List MoltNet Task Attempt Messages",
965
- description: "List messages for a specific task attempt. Use this when you need the turn-by-turn execution record behind an accepted attempt — tool calls, text deltas, and error/info events that do not appear in the attempt output alone.",
966
- parameters: Type$1.Object({
967
- taskId: Type$1.String({ description: "Task ID (UUID)." }),
968
- attemptN: Type$1.Integer({
969
- minimum: 1,
970
- description: "Attempt number to inspect."
971
- }),
972
- afterSeq: Type$1.Optional(Type$1.Integer({
973
- minimum: 0,
974
- description: "Optional cursor: only return messages with seq > afterSeq."
975
- })),
976
- limit: Type$1.Optional(Type$1.Integer({
977
- minimum: 1,
978
- maximum: 500,
979
- description: "Optional maximum messages to return. Defaults to the API value."
980
- }))
981
- }),
982
- async execute(_id, params) {
983
- const { agent } = ensureConnected(config);
984
- const messages = await agent.tasks.listMessages(params.taskId, params.attemptN, {
985
- afterSeq: params.afterSeq,
986
- limit: params.limit
987
- });
988
- return {
989
- content: [{
990
- type: "text",
991
- text: JSON.stringify(messages, null, 2)
992
- }],
993
- details: {}
994
- };
995
- }
996
- });
997
- const uploadTaskArtifact = defineTool({
998
- name: "moltnet_upload_task_artifact",
999
- label: "Upload MoltNet Task Artifact",
1000
- description: "Upload a file from the current task workspace as an immutable task artifact. Only available during an active task attempt; the tool attaches the artifact to the active taskId/attemptN and returns metadata including cid, sizeBytes, kind, and title. Use this for large logs, reports, build outputs, screenshots, generated files, or other bytes that should be referenced by CID instead of pasted into structured task output.",
1001
- parameters: Type$1.Object({
1002
- filePath: Type$1.String({ description: "Path to a file under the current task workspace. Relative paths are resolved from the workspace root." }),
1003
- kind: Type$1.String({ description: "Artifact category, e.g. log, report, patch, screenshot, bundle, dataset, trace." }),
1004
- title: Type$1.String({ description: "Human-readable artifact title, usually the file name." }),
1005
- contentType: Type$1.Optional(Type$1.String({ description: "MIME type. Defaults to application/octet-stream when omitted." })),
1006
- contentEncoding: Type$1.Optional(Type$1.String({ description: "Optional content encoding if the file is already encoded, e.g. gzip." }))
1007
- }),
1008
- async execute(_id, params) {
1009
- const { agent, teamId } = ensureConnected(config);
1010
- if (!teamId) throw new Error("moltnet_upload_task_artifact requires a team context");
1011
- const taskCtx = config.getTaskContext?.() ?? null;
1012
- if (!taskCtx) throw new Error("moltnet_upload_task_artifact is only available during an active task attempt");
1013
- const input = await openWorkspaceArtifactInput(config, config.getHostCwd?.() ?? process.cwd(), params.filePath);
1014
- if (!input.isFile) throw new Error(`task artifact path is not a file: ${params.filePath}`);
1015
- const artifact = await agent.tasks.artifacts.upload({
1016
- taskId: taskCtx.taskId,
1017
- attemptN: taskCtx.attemptN
1018
- }, input.stream, {
1019
- kind: params.kind,
1020
- title: params.title,
1021
- contentType: params.contentType ?? "application/octet-stream",
1022
- contentEncoding: params.contentEncoding
1023
- }, { teamId });
1024
- return {
1025
- content: [{
1026
- type: "text",
1027
- text: JSON.stringify({
1028
- ...artifact,
1029
- filePath: input.displayPath ?? params.filePath,
1030
- localSizeBytes: input.sizeBytes ?? null
1031
- }, null, 2)
1032
- }],
1033
- details: {}
1034
- };
1035
- }
1036
- });
1037
- const listTaskArtifacts = defineTool({
1038
- name: "moltnet_list_task_artifacts",
1039
- label: "List MoltNet Task Artifacts",
1040
- description: "List immutable artifacts attached to a task, including each artifact CID, attempt number, kind, title, content type, size, uploader, and creation time. Use this when judging or continuing work that references task artifacts.",
1041
- parameters: Type$1.Object({
1042
- taskId: Type$1.Optional(Type$1.String({ description: "Task ID. Defaults to the active task when running inside a task attempt." })),
1043
- limit: Type$1.Optional(Type$1.Integer({
1044
- minimum: 1,
1045
- maximum: 100,
1046
- description: "Maximum artifacts to return. Defaults to the server page size."
1047
- })),
1048
- cursor: Type$1.Optional(Type$1.String({ description: "Pagination cursor returned by a previous moltnet_list_task_artifacts call." }))
1049
- }),
1050
- async execute(_id, params) {
1051
- const { agent, teamId } = ensureConnected(config);
1052
- if (!teamId) throw new Error("moltnet_list_task_artifacts requires a team context");
1053
- const taskId = params.taskId ?? config.getTaskContext?.()?.taskId;
1054
- if (!taskId) throw new Error("moltnet_list_task_artifacts requires taskId outside an active task");
1055
- const page = await agent.tasks.artifacts.listPage(taskId, {
1056
- cursor: params.cursor,
1057
- limit: params.limit
1058
- }, { teamId });
1059
- return {
1060
- content: [{
1061
- type: "text",
1062
- text: JSON.stringify(page, null, 2)
1063
- }],
1064
- details: {}
1065
- };
1066
- }
1067
- });
1068
- const downloadTaskArtifact = defineTool({
1069
- name: "moltnet_download_task_artifact",
1070
- label: "Download MoltNet Task Artifact",
1071
- description: "Download immutable task artifact bytes by taskId and CID into a new file in the current task workspace. Use moltnet_list_task_artifacts first to choose the correct CID. Omit attemptN for a bound input artifact; pass it only to require an artifact from one exact task attempt.",
1072
- parameters: Type$1.Object({
1073
- taskId: Type$1.Optional(Type$1.String({ description: "Task ID. Defaults to the active task when running inside a task attempt." })),
1074
- attemptN: Type$1.Optional(Type$1.Integer({
1075
- minimum: 1,
1076
- description: "Attempt number that produced the artifact. Omit for bound input artifacts, which have no producing attempt."
1077
- })),
1078
- cid: Type$1.String({
1079
- minLength: 1,
1080
- description: "Artifact CID returned by moltnet_list_task_artifacts."
1081
- }),
1082
- outputPath: Type$1.String({ description: "New file path under the current task workspace. The tool refuses to overwrite existing files." })
1083
- }),
1084
- async execute(_id, params) {
1085
- const { agent, teamId } = ensureConnected(config);
1086
- if (!teamId) throw new Error("moltnet_download_task_artifact requires a team context");
1087
- const taskId = params.taskId ?? config.getTaskContext?.()?.taskId;
1088
- if (!taskId) throw new Error("moltnet_download_task_artifact requires taskId outside an active task");
1089
- const cwd = config.getHostCwd?.() ?? process.cwd();
1090
- const outputPath = await resolveWorkspaceOutputPath(cwd, params.outputPath);
1091
- const artifactPath = params.attemptN === void 0 ? {
1092
- taskId,
1093
- cid: params.cid
1094
- } : {
1095
- taskId,
1096
- attemptN: params.attemptN,
1097
- cid: params.cid
1098
- };
1099
- const download = await agent.tasks.artifacts.download(artifactPath, { teamId });
1100
- await pipeline(download.stream, createWriteStream(outputPath, { flags: "wx" }));
1101
- const info = await stat(outputPath);
1102
- return {
1103
- content: [{
1104
- type: "text",
1105
- text: JSON.stringify({
1106
- taskId,
1107
- ...params.attemptN === void 0 ? {} : { attemptN: params.attemptN },
1108
- cid: params.cid,
1109
- artifactId: download.artifactId,
1110
- contentType: download.contentType,
1111
- contentEncoding: download.contentEncoding,
1112
- outputPath: path.relative(cwd, outputPath),
1113
- sizeBytes: info.size
1114
- }, null, 2)
1115
- }],
1116
- details: {}
1117
- };
1118
- }
1119
- });
1120
- const reviewSessionErrors = defineTool({
1121
- name: "moltnet_review_session_errors",
1122
- label: "Review Session Tool Errors",
1123
- description: "Review tool failures buffered during this session (isError=true results). Use this to decide whether any failures are worth persisting as a diary entry via moltnet_create_entry. Most failures are transient (denied prompts, empty greps, mid-iteration typecheck errors) and should NOT be written to the diary — only persist incidents that represent a real finding (root cause identified, non-obvious workaround, recurring pattern). Pass clear=true to drop the buffer after reviewing.",
1124
- parameters: Type$1.Object({ clear: Type$1.Optional(Type$1.Boolean({ description: "If true, empty the buffer after returning it. Use once you have decided whether to persist." })) }),
1125
- async execute(_id, params) {
1126
- const errors = config.getSessionErrors();
1127
- const payload = {
1128
- count: errors.length,
1129
- errors: errors.map((e) => ({
1130
- toolName: e.toolName,
1131
- toolCallId: e.toolCallId,
1132
- timestamp: new Date(e.timestamp).toISOString(),
1133
- input: e.input,
1134
- error: e.error
1135
- }))
1136
- };
1137
- if (params.clear) config.clearSessionErrors();
1138
- return {
1139
- content: [{
1140
- type: "text",
1141
- text: JSON.stringify(payload, null, 2)
1142
- }],
1143
- details: {}
1144
- };
1145
- }
1146
- });
1147
- const HOST_EXEC_ALLOWED = new Set([
1148
- "git",
1149
- "gh",
1150
- "moltnet"
1151
- ]);
1152
- const hostExecBaseEnv = config.hostExecBaseEnv ?? HOST_EXEC_DEFAULT_BASE_ENV;
1153
- const HOST_EXEC_TIMEOUT_MS = 6e4;
1154
- return [
1155
- getPack,
1156
- createPack,
1157
- getPackProvenance,
1158
- renderPack,
1159
- listRenderedPacks,
1160
- getRenderedPack,
1161
- diaryTags,
1162
- listEntries,
1163
- getEntry,
1164
- searchEntries,
1165
- createEntry,
1166
- getTask,
1167
- listTaskAttempts,
1168
- listTaskMessages,
1169
- uploadTaskArtifact,
1170
- listTaskArtifacts,
1171
- downloadTaskArtifact,
1172
- reviewSessionErrors,
1173
- defineTool({
1174
- name: "moltnet_host_exec",
1175
- label: "Run command on host (escape hatch — requires user approval)",
1176
- description: "Runs a command on the HOST machine, outside the sandbox VM. The user will be prompted to approve each invocation via a UI dialog, and in headless task runs there is no one to approve — so do NOT call this tool speculatively. Routine git and gh work — pushing branches, opening pull requests, etc. — runs INSIDE the VM via the normal `bash` tool; use that, not this escape hatch. Credentials are not generally injected into the guest. A runtime may expose an opaque HTTP placeholder that the host proxy can use only for declared destinations, and commit signing is brokered through the `agent-signing` host capability when declared; otherwise authenticated operations are unavailable. Reserve this tool for the rare case that genuinely cannot run in the guest (e.g. reaching a host-only resource the VM has no path to).\n\nAllowed executables: git, gh, moltnet. Runs with a minimal env (PATH, HOME, GIT_CONFIG_GLOBAL, …); pass only non-secret additional vars via the `env` parameter. Every invocation is logged as an auditable host execution.",
1177
- parameters: Type$1.Object({
1178
- executable: Type$1.String({ description: "Executable to run (git | gh | moltnet)" }),
1179
- args: Type$1.Array(Type$1.String(), { description: "Arguments to pass to the executable" }),
1180
- env: Type$1.Optional(Type$1.Record(Type$1.String(), Type$1.String(), { description: "Additional non-secret environment variables for this invocation. Merged on top of the minimal base env." }))
1181
- }),
1182
- async execute(_id, params, _signal, _onUpdate, ctx) {
1183
- if (!HOST_EXEC_ALLOWED.has(params.executable)) throw new Error(`host_exec: '${params.executable}' is not in the allowed list (${[...HOST_EXEC_ALLOWED].join(", ")}). Extend HOST_EXEC_ALLOWED only after explicit security review.`);
1184
- if (ctx?.ui && !shouldAutoApproveHostExec(params, config)) {
1185
- const cmdDisplay = [params.executable, ...params.args].join(" ");
1186
- if (!await ctx.ui.confirm("Allow host command?", `The agent wants to run on your machine:\n\n ${cmdDisplay}\n\nAllow?`)) throw new Error(`host_exec: user declined approval for: ${cmdDisplay}`);
1187
- }
1188
- const cwd = config.getHostCwd?.() ?? process.cwd();
1189
- const baseEnv = {};
1190
- for (const key of hostExecBaseEnv) {
1191
- const val = process.env[key];
1192
- if (val !== void 0) baseEnv[key] = val;
1193
- }
1194
- const mergedEnv = {
1195
- ...baseEnv,
1196
- ...params.env ?? {}
1197
- };
1198
- let stdout;
1199
- let stderr = "";
1200
- try {
1201
- stdout = execFileSync(params.executable, params.args, {
1202
- encoding: "utf8",
1203
- cwd,
1204
- env: mergedEnv,
1205
- stdio: [
1206
- "pipe",
1207
- "pipe",
1208
- "pipe"
1209
- ],
1210
- timeout: HOST_EXEC_TIMEOUT_MS
1211
- });
1212
- } catch (err) {
1213
- const e = err;
1214
- stdout = e.stdout ?? "";
1215
- stderr = e.stderr ?? e.message ?? String(err);
1216
- }
1217
- const result = {
1218
- host_exec: true,
1219
- executable: params.executable,
1220
- args: params.args,
1221
- cwd,
1222
- stdout: stdout.trimEnd(),
1223
- stderr: stderr.trimEnd() || void 0
1224
- };
1225
- return {
1226
- content: [{
1227
- type: "text",
1228
- text: JSON.stringify(result, null, 2)
1229
- }],
1230
- details: {}
1231
- };
1232
- }
1233
- })
1234
- ];
1235
- }
1236
- //#endregion
1237
- //#region src/otel/index.ts
1238
- var TRACER_NAME = "@themoltnet/pi-extension/otel";
1239
- function stripReservedAttrs(attrs) {
1240
- const out = {};
1241
- for (const [k, v] of Object.entries(attrs)) {
1242
- if (k.startsWith("gen_ai.")) continue;
1243
- out[k] = v;
1244
- }
1245
- return out;
1246
- }
1247
- function createPiOtelExtension(options = {}) {
1248
- return function piOtelExtension(pi) {
1249
- const tracer = trace.getTracer(TRACER_NAME);
1250
- const extraAttrs = stripReservedAttrs(options.spanAttributes ?? {});
1251
- let sessionSpan;
1252
- let sessionCtx = context.active();
1253
- let turnSpan;
1254
- let turnCtx = context.active();
1255
- let currentModel;
1256
- const toolSpans = /* @__PURE__ */ new Map();
1257
- function drainToolSpans(reason) {
1258
- for (const [, entry] of toolSpans) {
1259
- entry.span.setStatus({
1260
- code: SpanStatusCode.ERROR,
1261
- message: reason
1262
- });
1263
- entry.span.end();
1264
- }
1265
- toolSpans.clear();
1266
- }
1267
- function endTurnSpan() {
1268
- if (!turnSpan) return;
1269
- drainToolSpans("tool span not closed before turn end");
1270
- turnSpan.end();
1271
- turnSpan = void 0;
1272
- turnCtx = sessionCtx;
1273
- }
1274
- function endSessionSpan() {
1275
- drainToolSpans("tool span not closed before session shutdown");
1276
- endTurnSpan();
1277
- if (sessionSpan) {
1278
- sessionSpan.setStatus({ code: SpanStatusCode.OK });
1279
- sessionSpan.end();
1280
- sessionSpan = void 0;
1281
- sessionCtx = context.active();
1282
- options.onSessionContextChange?.(void 0);
1283
- }
1284
- currentModel = void 0;
1285
- }
1286
- pi.on("session_start", (event, ctx) => {
1287
- endSessionSpan();
1288
- const agentName = options.agentName ?? "pi";
1289
- const parentContext = options.sessionParentContext ?? context.active();
1290
- sessionSpan = tracer.startSpan(`invoke_agent ${agentName}`, { attributes: {
1291
- ...extraAttrs,
1292
- "gen_ai.operation.name": "invoke_agent",
1293
- "gen_ai.agent.name": agentName,
1294
- "session.reason": event.reason,
1295
- "session.cwd": ctx.cwd
1296
- } }, parentContext);
1297
- sessionCtx = trace.setSpan(parentContext, sessionSpan);
1298
- options.onSessionContextChange?.(sessionCtx);
1299
- turnCtx = sessionCtx;
1300
- });
1301
- pi.on("session_shutdown", () => {
1302
- endSessionSpan();
1303
- });
1304
- pi.on("model_select", (event) => {
1305
- currentModel = {
1306
- provider: event.model.provider,
1307
- id: event.model.id
1308
- };
1309
- if (sessionSpan) {
1310
- sessionSpan.setAttribute("gen_ai.request.model", event.model.id);
1311
- sessionSpan.setAttribute("gen_ai.provider.name", event.model.provider);
1312
- }
1313
- });
1314
- pi.on("turn_start", (event) => {
1315
- if (!sessionSpan) return;
1316
- const modelLabel = currentModel?.id ?? "unknown";
1317
- const turnParentContext = options.getTurnParentContext?.() ?? sessionCtx;
1318
- turnSpan = tracer.startSpan(`chat ${modelLabel}`, { attributes: {
1319
- ...extraAttrs,
1320
- "gen_ai.operation.name": "chat",
1321
- "gen_ai.request.model": currentModel?.id ?? "unknown",
1322
- "gen_ai.provider.name": currentModel?.provider ?? "unknown",
1323
- "turn.index": event.turnIndex
1324
- } }, turnParentContext);
1325
- turnCtx = trace.setSpan(turnParentContext, turnSpan);
1326
- });
1327
- pi.on("turn_end", (event) => {
1328
- if (!turnSpan) return;
1329
- const usage = extractUsage(event.message);
1330
- if (usage) {
1331
- turnSpan.setAttribute("gen_ai.usage.input_tokens", usage.input);
1332
- turnSpan.setAttribute("gen_ai.usage.output_tokens", usage.output);
1333
- }
1334
- turnSpan.setAttribute("turn.tool_results", event.toolResults?.length ?? 0);
1335
- turnSpan.setStatus({ code: SpanStatusCode.OK });
1336
- endTurnSpan();
1337
- });
1338
- pi.on("tool_execution_start", (event) => {
1339
- const parentCtx = turnSpan ? turnCtx : sessionCtx;
1340
- const span = tracer.startSpan(`execute_tool ${event.toolName}`, { attributes: {
1341
- ...extraAttrs,
1342
- "gen_ai.operation.name": "execute_tool",
1343
- "gen_ai.tool.name": event.toolName,
1344
- "gen_ai.tool.call.id": event.toolCallId
1345
- } }, parentCtx);
1346
- toolSpans.set(event.toolCallId, {
1347
- span,
1348
- startedAt: Date.now()
1349
- });
1350
- });
1351
- pi.on("tool_execution_end", (event) => {
1352
- const entry = toolSpans.get(event.toolCallId);
1353
- if (!entry) return;
1354
- const durationMs = Date.now() - entry.startedAt;
1355
- entry.span.setAttribute("tool.duration_ms", durationMs);
1356
- if (event.isError) {
1357
- entry.span.setAttribute("error.type", "tool_execution_error");
1358
- entry.span.setStatus({
1359
- code: SpanStatusCode.ERROR,
1360
- message: "tool execution failed"
1361
- });
1362
- } else entry.span.setStatus({ code: SpanStatusCode.OK });
1363
- entry.span.end();
1364
- toolSpans.delete(event.toolCallId);
1365
- });
1366
- };
1367
- }
1368
- function extractUsage(message) {
1369
- if (!message || typeof message !== "object" || !("usage" in message) || !("role" in message)) return null;
1370
- const msg = message;
1371
- if (msg.role !== "assistant" || !msg.usage) return null;
1372
- return {
1373
- input: msg.usage.input ?? 0,
1374
- output: msg.usage.output ?? 0
1375
- };
1376
- }
1377
- //#endregion
1378
- //#region src/runtime/model-options-extension.ts
1379
- function hasPiModelOptions(options) {
1380
- return options.temperature !== void 0 && options.temperature !== null || options.topP !== void 0 && options.topP !== null || options.topK !== void 0 && options.topK !== null || options.maxOutputTokens !== void 0 && options.maxOutputTokens !== null;
1381
- }
1382
- function createPiModelOptionsExtension(options) {
1383
- return function piModelOptionsExtension(pi) {
1384
- pi.on("before_provider_request", (event) => {
1385
- return applyPiModelOptions(event.payload, options);
1386
- });
1387
- };
1388
- }
1389
- function applyPiModelOptions(payload, options) {
1390
- if (!isRecord$1(payload)) return void 0;
1391
- if (!hasPiModelOptions(options)) return void 0;
1392
- if (isGooglePayload(payload)) {
1393
- const config = isRecord$1(payload.config) ? payload.config : {};
1394
- return {
1395
- ...payload,
1396
- config: applyConfigOptions(config, options)
1397
- };
1398
- }
1399
- if (isBedrockPayload(payload)) {
1400
- const inferenceConfig = isRecord$1(payload.inferenceConfig) ? payload.inferenceConfig : {};
1401
- return {
1402
- ...payload,
1403
- inferenceConfig: applyBedrockOptions(inferenceConfig, options)
1404
- };
1405
- }
1406
- return applyTopLevelOptions(payload, options);
1407
- }
1408
- function applyConfigOptions(config, options) {
1409
- return {
1410
- ...config,
1411
- ...options.temperature !== void 0 && options.temperature !== null ? { temperature: options.temperature } : {},
1412
- ...options.topP !== void 0 && options.topP !== null ? { topP: options.topP } : {},
1413
- ...options.topK !== void 0 && options.topK !== null ? { topK: options.topK } : {},
1414
- ...options.maxOutputTokens !== void 0 && options.maxOutputTokens !== null ? { maxOutputTokens: options.maxOutputTokens } : {}
1415
- };
1416
- }
1417
- function applyBedrockOptions(inferenceConfig, options) {
1418
- return {
1419
- ...inferenceConfig,
1420
- ...options.temperature !== void 0 && options.temperature !== null ? { temperature: options.temperature } : {},
1421
- ...options.topP !== void 0 && options.topP !== null ? { topP: options.topP } : {},
1422
- ...options.maxOutputTokens !== void 0 && options.maxOutputTokens !== null ? { maxTokens: options.maxOutputTokens } : {}
1423
- };
1424
- }
1425
- function applyTopLevelOptions(payload, options) {
1426
- const reasoningEnabled = hasActiveThinking(payload.thinking) || "reasoning" in payload || "reasoning_effort" in payload;
1427
- const next = { ...payload };
1428
- if (options.temperature !== void 0 && options.temperature !== null && !reasoningEnabled) next.temperature = options.temperature;
1429
- if (options.topP !== void 0 && options.topP !== null && !reasoningEnabled) next.top_p = options.topP;
1430
- if (options.topK !== void 0 && options.topK !== null && !reasoningEnabled && isAnthropicPayload(next)) next.top_k = options.topK;
1431
- if (options.maxOutputTokens !== void 0 && options.maxOutputTokens !== null) {
1432
- const maxOutputTokens = options.maxOutputTokens;
1433
- if ("max_output_tokens" in next || isResponsesPayload(next)) next.max_output_tokens = maxOutputTokens;
1434
- else if ("max_completion_tokens" in next) next.max_completion_tokens = maxOutputTokens;
1435
- else if ("maxTokens" in next) next.maxTokens = maxOutputTokens;
1436
- else next.max_tokens = maxOutputTokens;
1437
- }
1438
- return next;
1439
- }
1440
- function isGooglePayload(payload) {
1441
- return "contents" in payload && ("config" in payload || "model" in payload);
1442
- }
1443
- function isBedrockPayload(payload) {
1444
- return "inferenceConfig" in payload || "additionalModelRequestFields" in payload;
1445
- }
1446
- function isResponsesPayload(payload) {
1447
- return "input" in payload && !("messages" in payload);
1448
- }
1449
- function isAnthropicPayload(payload) {
1450
- return "anthropic_version" in payload;
1451
- }
1452
- function hasActiveThinking(value) {
1453
- if (!isRecord$1(value)) return false;
1454
- const type = value.type;
1455
- return type !== "disabled" && type !== "off" && type !== false;
1456
- }
1457
- function isRecord$1(value) {
1458
- return typeof value === "object" && value !== null && !Array.isArray(value);
1459
- }
1460
- //#endregion
1461
- //#region src/runtime/agent-session-factory.ts
1462
- var NO_SKILLS = () => ({
1463
- skills: [],
1464
- diagnostics: []
1465
- });
1466
- /**
1467
- * Construct an `AgentSession`. By default it is in-memory; callers may opt
1468
- * parent sessions into daemon-owned file persistence via `sessionPersistence`.
1469
- * The caller is responsible for eventually invoking `session.prompt(...)` and
1470
- * for tearing down — the helper does no lifecycle management beyond
1471
- * construction.
1472
- */
1473
- async function buildAgentSession(args) {
1474
- const piOtelExtension = createPiOtelExtension({
1475
- agentName: args.agentName,
1476
- spanAttributes: args.otelSpanAttrs,
1477
- sessionParentContext: args.otelSessionParentContext,
1478
- getTurnParentContext: args.getOtelTurnParentContext,
1479
- onSessionContextChange: args.onOtelSessionContextChange
1480
- });
1481
- const modelOptions = {
1482
- temperature: args.temperature,
1483
- topP: args.topP,
1484
- topK: args.topK,
1485
- maxOutputTokens: args.maxOutputTokens
1486
- };
1487
- const extensionFactories = [
1488
- piOtelExtension,
1489
- ...hasPiModelOptions(modelOptions) ? [createPiModelOptionsExtension(modelOptions)] : [],
1490
- ...args.extraExtensionFactories ?? []
1491
- ];
1492
- const resourceLoader = new DefaultResourceLoader({
1493
- cwd: args.cwdPath,
1494
- agentDir: args.piAuthDir,
1495
- extensionFactories,
1496
- appendSystemPrompt: args.appendSystemPrompt,
1497
- skillsOverride: args.skillsOverride ?? NO_SKILLS
1498
- });
1499
- await resourceLoader.reload();
1500
- const sessionManager = args.sessionPersistence ? await resolvePersistentSessionManager({
1501
- cwd: args.cwdPath,
1502
- sessionDir: args.sessionPersistence.sessionDir,
1503
- forkFromSessionPath: args.sessionPersistence.forkFromSessionPath
1504
- }) : SessionManager.inMemory(args.cwdPath);
1505
- return (await createAgentSession({
1506
- agentDir: args.piAuthDir,
1507
- cwd: args.cwdPath,
1508
- model: args.modelHandle,
1509
- ...args.modelRuntime ? { modelRuntime: args.modelRuntime } : {},
1510
- thinkingLevel: args.thinkingLevel ?? void 0,
1511
- tools: args.tools,
1512
- customTools: args.customTools,
1513
- sessionManager,
1514
- resourceLoader
1515
- })).session;
1516
- }
1517
- async function resolvePersistentSessionManager(args) {
1518
- if (args.forkFromSessionPath) return SessionManager.forkFrom(args.forkFromSessionPath, args.cwd, args.sessionDir);
1519
- await SessionManager.list(args.cwd, args.sessionDir);
1520
- return SessionManager.continueRecent(args.cwd, args.sessionDir);
1521
- }
1522
- //#endregion
1523
- //#region ../crypto-service/src/json-cid.ts
1524
- /**
1525
- * Generic JSON CID — CIDv1 for arbitrary JSON-serialisable values.
1526
- *
1527
- * Uses the dag-json codec and sha2-256, producing a base32lower CIDv1.
1528
- * Suitable for content-addressing task inputs, schema objects, and other
1529
- * JSON payloads that don't need diary-entry canonical normalisation.
1530
- */
1531
- async function computeJsonCid(value) {
1532
- const bytes = json.encode(value);
1533
- const hash = await sha256$1.digest(bytes);
1534
- return CID.create(1, json.code, hash).toString();
1535
- }
1536
- //#endregion
1537
- //#region src/config.ts
1538
- /** Resolve Pi's host-side auth/config directory from process configuration. */
1539
- function resolvePiCodingAgentDir() {
1540
- return process.env["PI_CODING_AGENT_DIR"] ?? path.join(homedir(), ".pi", "agent");
1541
- }
1542
- //#endregion
1543
- //#region ../runtime-profiles/src/context.ts
1544
- /**
1545
- * How an executor delivers a context entry to its underlying LLM.
1546
- * V1 bindings only; Tier-2 (reference_file, mcp_resource, imported_file,
1547
- * tool_response_seed, additional_context_hook) ship in a later slice.
1548
- */
1549
- var CONTEXT_BINDINGS = [
1550
- "skill",
1551
- "context_inline",
1552
- "prompt_prefix",
1553
- "user_inline"
1554
- ];
1555
- /** Maximum UTF-16 code units accepted in one ContextRef content field. */
1556
- var CONTEXT_REF_MAX_CONTENT_LENGTH = 65536;
1557
- var ContextBinding = Type.Unsafe(Type.Union(CONTEXT_BINDINGS.map((binding) => Type.Literal(binding)), { $id: "ContextBinding" }));
1558
- /**
1559
- * One context entry. Bytes are inlined: the proposer chose them, and the
1560
- * task's `inputCid` already pins the entire input — including
1561
- * `context[]` — so we don't need a separate per-entry hash, fetcher, or
1562
- * flagged-content gate. Tasks reference rendered packs (or any other
1563
- * external content) by copying their bytes into `content` at task
1564
- * creation time.
1565
- *
1566
- * - `slug` — short identifier the daemon uses to disambiguate
1567
- * entries. For `skill` binding it becomes the directory
1568
- * name under the runtime's skill discovery path. Must be
1569
- * kebab-case-safe (alphanumeric + dashes/underscores).
1570
- * - `binding` — how the bytes are delivered to the LLM (see above).
1571
- * - `content` — UTF-8 text. Capped at 65,536 UTF-16 code units per
1572
- * entry; total per-task context bytes are bounded by the
1573
- * soft `maxItems` cap and per-binding daemon limits.
1574
- * Raised from 32 KiB in 2026-05 — protocol-heavy operator
1575
- * skills (e.g. `.claude/skills/legreffier/SKILL.md`) ship
1576
- * at ~35 KiB inline, and the original cap was sized for
1577
- * short example skills, not the kind of skill the eval
1578
- * substrate is dogfooded on (#943, #823).
1579
- */
1580
- var ContextRef = Type.Object({
1581
- slug: Type.String({
1582
- minLength: 1,
1583
- maxLength: 64,
1584
- pattern: "^[a-zA-Z0-9_-]+$"
1585
- }),
1586
- binding: ContextBinding,
1587
- content: Type.String({
1588
- minLength: 1,
1589
- maxLength: CONTEXT_REF_MAX_CONTENT_LENGTH
1590
- })
1591
- }, {
1592
- $id: "ContextRef",
1593
- additionalProperties: false
1594
- });
1595
- /** Reusable input fragment for any task type. Soft cap at 5 items. */
1596
- var TaskContext = Type.Array(ContextRef, {
1597
- $id: "TaskContext",
1598
- maxItems: 5
1599
- });
1600
- //#endregion
1601
- //#region ../runtime-profiles/src/runtime-models.ts
1602
- /**
1603
- * Runtime model catalog: a list of supported provider/model couples that
1604
- * MoltNet daemons can target. Backed by the `runtime_models` table.
1605
- *
1606
- * Scope is intrinsic to the row:
1607
- * - `teamId == null` => global entry (MoltNet-seeded, read-only to most callers)
1608
- * - `teamId != null` => team-owned custom entry
1609
- *
1610
- * The REST API exposes a single shape regardless of scope; the team header
1611
- * gates which rows are returned.
1612
- */
1613
- var RuntimeModelProvider = Type.String({
1614
- minLength: 1,
1615
- maxLength: 100,
1616
- pattern: "^[a-zA-Z0-9][a-zA-Z0-9._-]{0,99}$"
1617
- });
1618
- var RuntimeModelName = Type.String({
1619
- minLength: 1,
1620
- maxLength: 200,
1621
- pattern: "^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,199}$"
1622
- });
1623
- var RuntimeModelCapabilities = Type.Record(Type.String({
1624
- minLength: 1,
1625
- maxLength: 64
1626
- }), Type.Union([
1627
- Type.Boolean(),
1628
- Type.Number(),
1629
- Type.String({ maxLength: 256 })
1630
- ]));
1631
- Type.Object({
1632
- id: Type.String({ format: "uuid" }),
1633
- teamId: Type.Union([Type.String({ format: "uuid" }), Type.Null()]),
1634
- provider: RuntimeModelProvider,
1635
- model: RuntimeModelName,
1636
- displayName: Type.Union([Type.String({ maxLength: 200 }), Type.Null()]),
1637
- description: Type.Union([Type.String({ maxLength: 4096 }), Type.Null()]),
1638
- capabilities: RuntimeModelCapabilities,
1639
- isActive: Type.Boolean(),
1640
- createdByAgentId: Type.Union([Type.String({ format: "uuid" }), Type.Null()]),
1641
- createdByHumanId: Type.Union([Type.String({ format: "uuid" }), Type.Null()]),
1642
- createdAt: Type.String({ format: "date-time" }),
1643
- updatedAt: Type.String({ format: "date-time" })
1644
- }, {
1645
- $id: "RuntimeModel",
1646
- additionalProperties: false
1647
- });
1648
- //#endregion
1649
- //#region ../runtime-profiles/src/runtime-profile-context-recipes.ts
1650
- var RUNTIME_PROFILE_CONTEXT_CATALOGUE = {
1651
- version: 1,
1652
- fragments: {
1653
- "artifact-planner-v1": {
1654
- binding: "prompt_prefix",
1655
- content: "# Bounded artifact planner\n\n- The typed task facts, embedded bounded manifest, exact bound artifact references, registered tools, and runtime capability section are the complete contract. Do not search diaries, inspect a mounted repository, enumerate unrelated tasks or artifacts, modify a checkout, commit, branch, push, or contact GitHub.\n- Read only the exact artifact CIDs named by the task, and only when the embedded manifest does not provide enough evidence. Use the registered task-artifact tools for artifact access; never use shell or CLI wrappers to fetch artifacts, paginate, or discover them speculatively.\n- If the effective runtime exposes a local calculator or shell, use it only inside scratch for coverage accounting, budget arithmetic, and JSON validation. The runtime capability section and policy are authoritative; do not assume a static executable list.\n- Perform semantic classification and planning from supplied content and producer/consumer evidence. Do not substitute filename, directory, language, ecosystem, or repository-specific exclusion rules for evidence.\n- Write and upload exactly the requested versioned plan artifact, then reference its returned metadata through the registered submit-output tool. Do not emit a second prose or JSON representation.",
1656
- slug: "artifact-planner-v1"
1657
- },
1658
- "accountable-delivery-v1": {
1659
- binding: "prompt_prefix",
1660
- content: "# Accountable delivery\n\n- Pair every commit made during this task with a task-provenance diary entry created by the `moltnet_create_entry` custom tool. Put the returned id in a `MoltNet-Diary: <id>` commit trailer. The tool does not currently promise a content signature unless you pass `signed: true` while the runtime kernel declares the `agent-signing` host capability; never describe an entry as signed otherwise.\n- When the runtime kernel declares `agent-signing`, sign commits normally with `git commit -S`: the signature is brokered to the trusted host through `SSH_AUTH_SOCK` and no private key exists in the guest. Without that capability commits are unsigned; do not disable signing the runtime provides, and never try to obtain a key from host configuration.\n- Push a branch and open or update a pull request only when the task asks for it. Use a host-brokered GitHub placeholder only when the runtime kernel declares one; if no GitHub credential is active, the authenticated operation is unavailable.\n- Keep changes, commits, and any requested pull request coherent enough to review independently.",
1661
- slug: "accountable-delivery-v1"
1662
- },
1663
- "judgment-diary-v1": {
1664
- binding: "prompt_prefix",
1665
- content: "# Judgment diary discipline\n\n- For an `assess_brief`, `judge_pack`, or `pr_review` task, create a diary entry with the `moltnet_create_entry` custom tool before submitting the structured judgment. Capture the rationale and evidence that support the verdict. Do not claim a content signature unless you created the entry with `signed: true` under a runtime that declares the `agent-signing` host capability.\n- Add the `judgment` tag and the active task type tag (`assess_brief`, `judge_pack`, or `pr_review`). For `judge_pack`, also add `rubric:<rubricId>` from the task facts.\n- Do not use a shell `moltnet entry` command: task provenance is injected only by the custom tool.",
1666
- slug: "judgment-diary-v1"
1667
- },
1668
- "proactive-memory-v1": {
1669
- binding: "prompt_prefix",
1670
- content: "# Proactive memory use\n\n- Before non-trivial investigation, debugging, code changes, or review, check the task diary for relevant prior knowledge instead of waiting for a human to ask. Use `moltnet_diary_tags` for cheap reconnaissance, `moltnet_list_entries` when tags or task provenance are known, and `moltnet_search_entries` for semantic similarity. Do not search randomly: pass `taskFilter` for task-local or correlation-local queries, and pass `tags` / `entryTypes` for broader prior-knowledge queries using known tags such as `incident`, `decision`, or `scope:<area>`. Broaden only after constrained searches miss.\n- Before creating an `episodic` incident entry, search for similar incidents using the proposed title, root cause, error text, affected subsystem, and watch-for terms, filtered by `entryTypes: [\"episodic\", \"semantic\"]` and any known `scope:*` or task-provenance tags. If a close prior match exists, do not create an isolated duplicate: reference the prior entry in your response or diary content, update or link it when the new occurrence adds material evidence, or create a new recurrence entry only when the recurrence itself is important signal.\n- When you create a recurrence entry, include the prior matching entry id(s) in the content and explain what is new about this occurrence.",
1671
- slug: "proactive-memory-v1"
1672
- },
1673
- "run-eval-direct-v1": {
1674
- binding: "prompt_prefix",
1675
- content: "# Direct evaluation run\n\nThe supplied scenario, typed task facts, injected context, and registered submit-output tool are the complete task contract. Do not search diaries, create diary entries, modify a repository, commit, branch, push, or open a pull request unless a task fact explicitly requires it. Submit the agent-authored payload in the first turn; correction turns exist only to recover a rejected or missing submission.",
1676
- slug: "run-eval-direct-v1"
1677
- },
1678
- "task-diary-discipline-v1": {
1679
- binding: "prompt_prefix",
1680
- content: "# Task diary discipline\n\n- During a daemon task, create diary entries only through the `moltnet_create_entry` custom tool. It binds entries to the current task diary and injects task, type, attempt, and correlation provenance tags.\n- Do not shell out to `moltnet entry create`, `moltnet entry create-signed`, or any other `moltnet entry` subcommand from bash while a task is running. For a content-signed entry pass `signed: true` to the custom tool instead; it signs on the trusted host. Those shell paths bypass the custom tool's task-tag injection, so task-filtered diary queries cannot find the entry.\n- You may add useful tags, but do not try to replace task provenance supplied by the runtime.",
1681
- slug: "task-diary-discipline-v1"
1682
- },
1683
- "verification-and-artifacts-v1": {
1684
- binding: "prompt_prefix",
1685
- content: "# Verification and artifacts\n\n- Run relevant verification before submitting. When task facts include `successCriteria`, assess them honestly in the generated verification contract; a fail or skip with evidence is better than a fabricated pass.\n- The registered submit-output tool owns the exact agent submission schema and validation recovery. Use that schema; do not invent a JSON shape in prose.\n- Upload only task-relevant artifacts, and inspect each before uploading. Never upload secrets, credentials, API keys, auth tokens or headers, .env files, or personal or customer data; redact sensitive values, and prefer minimal, sanitized excerpts over whole logs, bundles, or datasets. Include artifact metadata only where the typed submit contract permits it.\n- If the task depends on prior artifacts, list and download the exact referenced artifact before judging or continuing that work.",
1686
- slug: "verification-and-artifacts-v1"
1687
- }
1688
- },
1689
- recipes: {
1690
- "artifact-planner@v1": {
1691
- description: "Minimal artifact-only context for bounded semantic classification and planning.",
1692
- fragments: ["artifact-planner-v1"]
1693
- },
1694
- "run-eval-direct@v1": {
1695
- description: "Minimal direct context for a short, isolated evaluation run.",
1696
- fragments: ["run-eval-direct-v1"]
1697
- },
1698
- "standard-engineering@v1": {
1699
- description: "Full opt-in operating guidance for engineering tasks that need diary research, accountable delivery, and verification discipline.",
1700
- fragments: [
1701
- "proactive-memory-v1",
1702
- "task-diary-discipline-v1",
1703
- "accountable-delivery-v1",
1704
- "judgment-diary-v1",
1705
- "verification-and-artifacts-v1"
1706
- ]
1707
- }
1708
- }
1709
- };
1710
- function deepFreeze(value) {
1711
- if (value && typeof value === "object") {
1712
- for (const key of Object.keys(value)) deepFreeze(value[key]);
1713
- Object.freeze(value);
1714
- }
1715
- return value;
1716
- }
1717
- deepFreeze(RUNTIME_PROFILE_CONTEXT_CATALOGUE);
1718
- Object.freeze(Object.keys(RUNTIME_PROFILE_CONTEXT_CATALOGUE.recipes));
1719
- //#endregion
1720
- //#region ../models/src/credential-scopes.ts
1721
- var CREDENTIAL_SCOPES = {
1722
- AgentProfile: "agent:profile",
1723
- ConnectorInvoke: "connector:invoke",
1724
- CryptoSign: "crypto:sign",
1725
- DiaryManage: "diary:manage",
1726
- DiaryRead: "diary:read",
1727
- DiaryWrite: "diary:write",
1728
- HumanProfile: "human:profile",
1729
- KeyManage: "key:manage",
1730
- PackRead: "pack:read",
1731
- PackWrite: "pack:write",
1732
- RuntimeManage: "runtime:manage",
1733
- RuntimeRead: "runtime:read",
1734
- TaskClaim: "task:claim",
1735
- TaskExecute: "task:execute",
1736
- TaskManage: "task:manage",
1737
- TaskRead: "task:read",
1738
- TaskWrite: "task:write",
1739
- TeamManage: "team:manage",
1740
- TeamRead: "team:read"
1741
- };
1742
- var ALL_CREDENTIAL_SCOPES = Object.freeze(Object.values(CREDENTIAL_SCOPES));
1743
- CREDENTIAL_SCOPES.AgentProfile, CREDENTIAL_SCOPES.CryptoSign, CREDENTIAL_SCOPES.RuntimeRead, CREDENTIAL_SCOPES.TaskRead, CREDENTIAL_SCOPES.TaskClaim, CREDENTIAL_SCOPES.TaskExecute;
1744
- CREDENTIAL_SCOPES.AgentProfile, CREDENTIAL_SCOPES.TaskRead, CREDENTIAL_SCOPES.TaskWrite;
1745
- CREDENTIAL_SCOPES.AgentProfile, CREDENTIAL_SCOPES.DiaryRead, CREDENTIAL_SCOPES.PackRead, CREDENTIAL_SCOPES.RuntimeRead, CREDENTIAL_SCOPES.TaskRead, CREDENTIAL_SCOPES.TeamRead;
1746
- Object.freeze(ALL_CREDENTIAL_SCOPES.filter((scope) => scope !== CREDENTIAL_SCOPES.HumanProfile));
1747
- /**
1748
- * REST capabilities exercised by the current MCP tool surface.
1749
- *
1750
- * Intentionally excludes connector invocation, key management, runtime
1751
- * management/read, and task claiming because MCP exposes none of those
1752
- * operations.
1753
- */
1754
- var MCP_CLIENT_SCOPES = [
1755
- CREDENTIAL_SCOPES.AgentProfile,
1756
- CREDENTIAL_SCOPES.CryptoSign,
1757
- CREDENTIAL_SCOPES.DiaryManage,
1758
- CREDENTIAL_SCOPES.DiaryRead,
1759
- CREDENTIAL_SCOPES.DiaryWrite,
1760
- CREDENTIAL_SCOPES.HumanProfile,
1761
- CREDENTIAL_SCOPES.PackRead,
1762
- CREDENTIAL_SCOPES.PackWrite,
1763
- CREDENTIAL_SCOPES.TaskExecute,
1764
- CREDENTIAL_SCOPES.TaskManage,
1765
- CREDENTIAL_SCOPES.TaskRead,
1766
- CREDENTIAL_SCOPES.TaskWrite,
1767
- CREDENTIAL_SCOPES.TeamManage,
1768
- CREDENTIAL_SCOPES.TeamRead
1769
- ];
1770
- MCP_CLIENT_SCOPES.filter((scope) => scope !== CREDENTIAL_SCOPES.HumanProfile);
1771
- Object.freeze([...[
1772
- "openid",
1773
- "offline",
1774
- "offline_access"
1775
- ], ...MCP_CLIENT_SCOPES]);
1776
- //#endregion
1777
- //#region ../models/src/preview-sign.ts
1778
- function schemaRef$1(schema, id) {
1779
- return Type.Unsafe(Type.Ref(id));
1780
- }
1781
- var PreviewSignBase64UrlSchema = Type.String({
1782
- $id: "PreviewSignBase64Url",
1783
- minLength: 1,
1784
- maxLength: 5462,
1785
- pattern: "^[A-Za-z0-9_-]+$"
1786
- });
1787
- var PreviewSignSha256Base64UrlSchema = Type.String({
1788
- $id: "PreviewSignSha256Base64Url",
1789
- minLength: 43,
1790
- maxLength: 43,
1791
- pattern: "^[A-Za-z0-9_-]+$"
1792
- });
1793
- var PreviewSignP256DerSignatureBase64UrlSchema = Type.String({
1794
- $id: "PreviewSignP256DerSignatureBase64Url",
1795
- minLength: 11,
1796
- maxLength: 96,
1797
- pattern: "^[A-Za-z0-9_-]+$"
1798
- });
1799
- var PreviewSignEs256PublicKeySchema = Type.Object({
1800
- kty: Type.Literal(2),
1801
- algorithm: Type.Literal(-7),
1802
- curve: Type.Literal(1),
1803
- x: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url"),
1804
- y: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url")
1805
- }, {
1806
- $id: "PreviewSignEs256PublicKey",
1807
- additionalProperties: false
1808
- });
1809
- var PreviewSignEcdhEsHkdf256PublicKeySchema = Type.Object({
1810
- kty: Type.Literal(2),
1811
- algorithm: Type.Literal(-25),
1812
- curve: Type.Literal(1),
1813
- x: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url"),
1814
- y: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url")
1815
- }, {
1816
- $id: "PreviewSignEcdhEsHkdf256PublicKey",
1817
- additionalProperties: false
1818
- });
1819
- var PreviewSignEsp256PublicKeySchema = Type.Object({
1820
- kty: Type.Literal(2),
1821
- algorithm: Type.Literal(-9),
1822
- curve: Type.Literal(1),
1823
- x: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url"),
1824
- y: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url")
1825
- }, {
1826
- $id: "PreviewSignEsp256PublicKey",
1827
- additionalProperties: false
1828
- });
1829
- var PreviewSignArkgSeedPublicKeySchema = Type.Object({
1830
- kty: Type.Literal(-65537),
1831
- algorithm: Type.Literal(-65700),
1832
- derivedAlgorithm: Type.Literal(-9),
1833
- blindingKey: schemaRef$1(PreviewSignEs256PublicKeySchema, "PreviewSignEs256PublicKey"),
1834
- kemKey: schemaRef$1(PreviewSignEcdhEsHkdf256PublicKeySchema, "PreviewSignEcdhEsHkdf256PublicKey")
1835
- }, {
1836
- $id: "PreviewSignArkgSeedPublicKey",
1837
- additionalProperties: false
1838
- });
1839
- var PreviewSignPublicMaterialSchema = Type.Object({
1840
- version: Type.Literal(1),
1841
- outerCredentialId: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
1842
- outerPublicKey: schemaRef$1(PreviewSignEs256PublicKeySchema, "PreviewSignEs256PublicKey"),
1843
- previewKeyHandle: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
1844
- seedPublicKey: schemaRef$1(PreviewSignArkgSeedPublicKeySchema, "PreviewSignArkgSeedPublicKey")
1845
- }, {
1846
- $id: "PreviewSignPublicMaterial",
1847
- additionalProperties: false
1848
- });
1849
- var PreviewSignChallengeSchema = Type.Object({
1850
- verificationMethod: Type.Literal("human-hardware-previewsign"),
1851
- version: Type.Literal(1),
1852
- envelope: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
1853
- digest: schemaRef$1(PreviewSignSha256Base64UrlSchema, "PreviewSignSha256Base64Url"),
1854
- additionalArguments: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
1855
- outerCredentialId: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url"),
1856
- outerPublicKey: schemaRef$1(PreviewSignEs256PublicKeySchema, "PreviewSignEs256PublicKey"),
1857
- previewKeyHandle: schemaRef$1(PreviewSignBase64UrlSchema, "PreviewSignBase64Url")
1858
- }, {
1859
- $id: "PreviewSignChallenge",
1860
- additionalProperties: false
1861
- });
1862
- var PreviewSignChallengeValueSchema = Type.Object({
1863
- verificationMethod: Type.Literal("human-hardware-previewsign"),
1864
- value: schemaRef$1(PreviewSignChallengeSchema, "PreviewSignChallenge")
1865
- }, {
1866
- $id: "PreviewSignChallengeValue",
1867
- additionalProperties: false
1868
- });
1869
- var PreviewSignChallengeOperationSchema = Type.Union([Type.Literal("credential-registration"), Type.Literal("signing-request")], { $id: "PreviewSignChallengeOperation" });
1870
- var PreviewSignReceiptSchema = Type.Object({
1871
- version: Type.Literal(1),
1872
- signature: schemaRef$1(PreviewSignP256DerSignatureBase64UrlSchema, "PreviewSignP256DerSignatureBase64Url")
1873
- }, {
1874
- $id: "PreviewSignReceipt",
1875
- additionalProperties: false
1876
- });
1877
- var PreviewSignReceiptValueSchema = Type.Object({
1878
- verificationMethod: Type.Literal("human-hardware-previewsign"),
1879
- value: schemaRef$1(PreviewSignReceiptSchema, "PreviewSignReceipt")
1880
- }, {
1881
- $id: "PreviewSignReceiptValue",
1882
- additionalProperties: false
1883
- });
1884
- var previewSignSchemaContext = {
1885
- PreviewSignBase64Url: PreviewSignBase64UrlSchema,
1886
- PreviewSignSha256Base64Url: PreviewSignSha256Base64UrlSchema,
1887
- PreviewSignP256DerSignatureBase64Url: PreviewSignP256DerSignatureBase64UrlSchema,
1888
- PreviewSignEs256PublicKey: PreviewSignEs256PublicKeySchema,
1889
- PreviewSignEcdhEsHkdf256PublicKey: PreviewSignEcdhEsHkdf256PublicKeySchema,
1890
- PreviewSignEsp256PublicKey: PreviewSignEsp256PublicKeySchema,
1891
- PreviewSignArkgSeedPublicKey: PreviewSignArkgSeedPublicKeySchema,
1892
- PreviewSignPublicMaterial: PreviewSignPublicMaterialSchema,
1893
- PreviewSignChallenge: PreviewSignChallengeSchema,
1894
- PreviewSignChallengeValue: PreviewSignChallengeValueSchema,
1895
- PreviewSignChallengeOperation: PreviewSignChallengeOperationSchema,
1896
- PreviewSignReceipt: PreviewSignReceiptSchema,
1897
- PreviewSignReceiptValue: PreviewSignReceiptValueSchema
1898
- };
1899
- //#endregion
1900
- //#region ../models/src/verification-method.ts
1901
- /**
1902
- * Persisted and wire-level signing verification method identifiers.
1903
- *
1904
- * This vocabulary is append-only. Never rename, remove, or change an existing
1905
- * value: PostgreSQL rows, workflow inputs, and API clients persist these exact
1906
- * strings. Future signing methods must add a new property and value.
419
+ * Persisted and wire-level signing verification method identifiers.
420
+ *
421
+ * This vocabulary is append-only. Never rename, remove, or change an existing
422
+ * value: PostgreSQL rows, workflow inputs, and API clients persist these exact
423
+ * strings. Future signing methods must add a new property and value.
1907
424
  */
1908
425
  var VERIFICATION_METHOD = {
1909
426
  AgentEd25519: "agent-ed25519",
@@ -2139,576 +656,2116 @@ var TeamRoleSchema = Type.Union([
2139
656
  Type.Literal("executor"),
2140
657
  Type.Literal("member")
2141
658
  ]);
2142
- Type.Object({
2143
- id: UuidSchema,
2144
- name: Type.String()
2145
- });
2146
- var DateTimeUnsafe = Type.Unsafe(Type.String({ format: "date-time" }));
2147
- Type.Object({
2148
- id: UuidSchema,
2149
- code: Type.String(),
2150
- role: Type.Union([
2151
- Type.Literal("manager"),
2152
- Type.Literal("executor"),
2153
- Type.Literal("member")
2154
- ]),
2155
- maxUses: Type.Integer(),
2156
- useCount: Type.Integer(),
2157
- expiresAt: DateTimeUnsafe,
2158
- createdAt: DateTimeUnsafe
2159
- });
2160
- var TeamMemberSchema = Type.Object({
2161
- subjectId: UuidSchema,
2162
- subjectType: Type.Union([Type.Literal("agent"), Type.Literal("human")]),
2163
- role: TeamRoleSchema,
2164
- displayName: Type.String(),
2165
- alias: Type.Optional(AgentAliasSchema),
2166
- fingerprint: Type.Optional(Type.String()),
2167
- email: Type.Optional(Type.String())
659
+ Type.Object({
660
+ id: UuidSchema,
661
+ name: Type.String()
662
+ });
663
+ var DateTimeUnsafe = Type.Unsafe(Type.String({ format: "date-time" }));
664
+ Type.Object({
665
+ id: UuidSchema,
666
+ code: Type.String(),
667
+ role: Type.Union([
668
+ Type.Literal("manager"),
669
+ Type.Literal("executor"),
670
+ Type.Literal("member")
671
+ ]),
672
+ maxUses: Type.Integer(),
673
+ useCount: Type.Integer(),
674
+ expiresAt: DateTimeUnsafe,
675
+ createdAt: DateTimeUnsafe
676
+ });
677
+ var TeamMemberSchema = Type.Object({
678
+ subjectId: UuidSchema,
679
+ subjectType: Type.Union([Type.Literal("agent"), Type.Literal("human")]),
680
+ role: TeamRoleSchema,
681
+ displayName: Type.String(),
682
+ alias: Type.Optional(AgentAliasSchema),
683
+ fingerprint: Type.Optional(Type.String()),
684
+ email: Type.Optional(Type.String())
685
+ });
686
+ Type.Object({
687
+ id: UuidSchema,
688
+ name: Type.String(),
689
+ personal: Type.Boolean(),
690
+ status: Type.String(),
691
+ role: TeamRoleSchema
692
+ });
693
+ Type.Object({
694
+ id: UuidSchema,
695
+ name: Type.String(),
696
+ status: Type.String(),
697
+ personal: Type.Boolean(),
698
+ createdAt: DateTimeUnsafe,
699
+ updatedAt: DateTimeUnsafe,
700
+ members: Type.Array(TeamMemberSchema)
701
+ });
702
+ Type.Object({
703
+ teamId: UuidSchema,
704
+ role: Type.Union([
705
+ Type.Literal("manager"),
706
+ Type.Literal("executor"),
707
+ Type.Literal("member")
708
+ ])
709
+ });
710
+ Type.Object({
711
+ updated: Type.Boolean(),
712
+ role: Type.Union([
713
+ Type.Literal("manager"),
714
+ Type.Literal("executor"),
715
+ Type.Literal("member")
716
+ ])
717
+ });
718
+ Type.Object({ deleted: Type.Boolean() });
719
+ Type.Object({ removed: Type.Boolean() });
720
+ var FoundingMemberSchema = Type.Object({
721
+ subjectId: UuidSchema,
722
+ subjectNs: Type.Union([Type.Literal("Agent"), Type.Literal("Human")]),
723
+ role: Type.Union([
724
+ Type.Literal("owner"),
725
+ Type.Literal("manager"),
726
+ Type.Literal("executor"),
727
+ Type.Literal("member")
728
+ ])
729
+ });
730
+ Type.Object({
731
+ name: Type.String({
732
+ minLength: 1,
733
+ maxLength: 255
734
+ }),
735
+ foundingMembers: Type.Optional(Type.Array(FoundingMemberSchema, { minItems: 1 }))
736
+ });
737
+ Type.Object({
738
+ id: UuidSchema,
739
+ name: Type.String(),
740
+ status: Type.String(),
741
+ workflowId: Type.Optional(Type.String())
742
+ });
743
+ Type.Object({});
744
+ Type.Object({
745
+ accepted: Type.Boolean(),
746
+ teamStatus: Type.String()
747
+ });
748
+ Type.Object({ destinationTeamId: UuidSchema });
749
+ Type.Object({ transferId: UuidSchema });
750
+ var TransferResponseSchema = Type.Object({
751
+ id: UuidSchema,
752
+ diaryId: UuidSchema,
753
+ sourceTeamId: UuidSchema,
754
+ destinationTeamId: UuidSchema,
755
+ status: Type.String(),
756
+ initiatedBy: UuidSchema,
757
+ expiresAt: Type.Unsafe(Type.String({ format: "date-time" })),
758
+ createdAt: Type.Unsafe(Type.String({ format: "date-time" }))
759
+ });
760
+ Type.Object({ items: Type.Array(TransferResponseSchema) });
761
+ Type.Object({ groupId: UuidSchema });
762
+ Type.Object({
763
+ groupId: UuidSchema,
764
+ subjectId: UuidSchema
765
+ });
766
+ Type.Object({ name: Type.String({
767
+ minLength: 1,
768
+ maxLength: 255
769
+ }) });
770
+ Type.Object({
771
+ subjectId: UuidSchema,
772
+ subjectNs: Type.Optional(Type.Union([Type.Literal("Agent"), Type.Literal("Human")]))
773
+ });
774
+ Type.Object({
775
+ id: UuidSchema,
776
+ name: Type.String(),
777
+ teamId: UuidSchema
778
+ });
779
+ var GroupMemberResponseSchema = Type.Object({
780
+ subjectId: UuidSchema,
781
+ subjectNs: Type.String()
782
+ });
783
+ Type.Object({
784
+ id: UuidSchema,
785
+ name: Type.String(),
786
+ teamId: UuidSchema,
787
+ createdAt: DateTimeUnsafe,
788
+ members: Type.Array(GroupMemberResponseSchema)
789
+ });
790
+ var DiaryGrantRoleSchema = Type.Union([Type.Literal("writer"), Type.Literal("manager")]);
791
+ var GrantSubjectNsSchema = Type.Union([
792
+ Type.Literal("Agent"),
793
+ Type.Literal("Human"),
794
+ Type.Literal("Group")
795
+ ]);
796
+ Type.Object({
797
+ subjectId: UuidSchema,
798
+ subjectNs: GrantSubjectNsSchema,
799
+ role: DiaryGrantRoleSchema
800
+ });
801
+ Type.Object({
802
+ subjectId: UuidSchema,
803
+ subjectNs: GrantSubjectNsSchema,
804
+ role: DiaryGrantRoleSchema
805
+ });
806
+ var DiaryGrantResponseSchema = Type.Object({
807
+ subjectId: UuidSchema,
808
+ subjectNs: GrantSubjectNsSchema,
809
+ role: DiaryGrantRoleSchema
810
+ });
811
+ Type.Object({ grants: Type.Array(DiaryGrantResponseSchema) });
812
+ Type.Object({ revoked: Type.Boolean() });
813
+ var TaskGrantRoleSchema = Type.Union([Type.Literal("writer"), Type.Literal("manager")]);
814
+ Type.Object({
815
+ subjectId: UuidSchema,
816
+ subjectNs: GrantSubjectNsSchema,
817
+ role: TaskGrantRoleSchema
818
+ });
819
+ Type.Object({
820
+ subjectId: UuidSchema,
821
+ subjectNs: GrantSubjectNsSchema,
822
+ role: TaskGrantRoleSchema
823
+ });
824
+ var TaskGrantResponseSchema = Type.Object({
825
+ subjectId: UuidSchema,
826
+ subjectNs: GrantSubjectNsSchema,
827
+ role: TaskGrantRoleSchema
828
+ });
829
+ Type.Object({ grants: Type.Array(TaskGrantResponseSchema) });
830
+ Type.Object({ "x-moltnet-team-id": Type.String({
831
+ format: "uuid",
832
+ description: "Team ID (UUID) that will own the resource. Required."
833
+ }) });
834
+ Type.Object({ "x-moltnet-team-id": Type.Optional(Type.String({
835
+ format: "uuid",
836
+ description: "Team ID (UUID) for scoping the request. Optional."
837
+ })) });
838
+ Type.Object({
839
+ kind: Type.Literal("agent"),
840
+ agentId: UuidSchema,
841
+ identityId: Type.Union([UuidSchema, Type.Null()]),
842
+ fingerprint: FingerprintSchema,
843
+ publicKey: PublicKeySchema
844
+ }, {
845
+ $id: "AgentPrincipal",
846
+ additionalProperties: false
847
+ });
848
+ Type.Object({
849
+ kind: Type.Literal("human"),
850
+ humanId: UuidSchema,
851
+ identityId: Type.Union([UuidSchema, Type.Null()])
852
+ }, {
853
+ $id: "HumanPrincipal",
854
+ additionalProperties: false
855
+ });
856
+ var principalUnionVariants = [Type.Object({
857
+ kind: Type.Literal("agent"),
858
+ agentId: UuidSchema,
859
+ identityId: Type.Union([UuidSchema, Type.Null()]),
860
+ fingerprint: FingerprintSchema,
861
+ publicKey: PublicKeySchema
862
+ }, { additionalProperties: false }), Type.Object({
863
+ kind: Type.Literal("human"),
864
+ humanId: UuidSchema,
865
+ identityId: Type.Union([UuidSchema, Type.Null()])
866
+ }, { additionalProperties: false })];
867
+ Type.Union(principalUnionVariants, {
868
+ $id: "PrincipalIdentity",
869
+ discriminator: { propertyName: "kind" }
870
+ });
871
+ /**
872
+ * `$id`-less twin of `PrincipalIdentitySchema`. Required anywhere the
873
+ * schema is **embedded** inline into another schema (MCP `outputSchema`
874
+ * — every tool that returns a creator-bearing object embeds its own
875
+ * copy; provenance-graph node `meta.creator`, etc.). Ajv 8 throws
876
+ * `reference "PrincipalIdentity" resolves to more than one schema` if
877
+ * the same `$id` appears twice in the same compilation pass, which is
878
+ * exactly what happens when the MCP server lists tools and Ajv
879
+ * traverses every advertised `outputSchema`.
880
+ *
881
+ * Structurally identical to `PrincipalIdentitySchema` (they share the
882
+ * variants array); change one, change both.
883
+ */
884
+ var PrincipalIdentitySchemaInline = Type.Union(principalUnionVariants, { discriminator: { propertyName: "kind" } });
885
+ //#endregion
886
+ //#region ../models/src/problem-details.ts
887
+ var ProblemCodeSchema = Type.Union([
888
+ Type.Literal("UNAUTHORIZED"),
889
+ Type.Literal("FORBIDDEN"),
890
+ Type.Literal("NOT_FOUND"),
891
+ Type.Literal("CONFLICT"),
892
+ Type.Literal("UNSUPPORTED_MEDIA_TYPE"),
893
+ Type.Literal("VALIDATION_FAILED"),
894
+ Type.Literal("INVALID_CHALLENGE"),
895
+ Type.Literal("INVALID_SIGNATURE"),
896
+ Type.Literal("RATE_LIMIT_EXCEEDED"),
897
+ Type.Literal("SERIALIZATION_EXHAUSTED"),
898
+ Type.Literal("SIGNING_REQUEST_EXPIRED"),
899
+ Type.Literal("SIGNING_REQUEST_ALREADY_COMPLETED"),
900
+ Type.Literal("SIGNING_REQUEST_LIMIT_REACHED"),
901
+ Type.Literal("REGISTRATION_FAILED"),
902
+ Type.Literal("UPSTREAM_ERROR"),
903
+ Type.Literal("SERVICE_UNAVAILABLE"),
904
+ Type.Literal("INTERNAL_SERVER_ERROR"),
905
+ Type.Literal("TEAM_PERSONAL_IMMUTABLE"),
906
+ Type.Literal("TEAM_NOT_ACTIVE"),
907
+ Type.Literal("INVITE_EXPIRED"),
908
+ Type.Literal("INVITE_EXHAUSTED"),
909
+ Type.Literal("TEAM_LAST_OWNER"),
910
+ Type.Literal("TEAM_ALREADY_ACTIVE"),
911
+ Type.Literal("TEAM_NOT_FOUNDING"),
912
+ Type.Literal("FOUNDING_ALREADY_ACCEPTED"),
913
+ Type.Literal("DIARY_TRANSFER_PENDING"),
914
+ Type.Literal("DIARY_TRANSFER_NOT_FOUND"),
915
+ Type.Literal("DIARY_TRANSFER_ALREADY_RESOLVED")
916
+ ]);
917
+ var ProblemDetailsSchema = Type.Object({
918
+ type: Type.String({ format: "uri" }),
919
+ title: Type.String(),
920
+ status: Type.Integer({
921
+ minimum: 100,
922
+ maximum: 599
923
+ }),
924
+ code: ProblemCodeSchema,
925
+ detail: Type.Optional(Type.String()),
926
+ instance: Type.Optional(Type.String()),
927
+ retryAfter: Type.Optional(Type.Integer({
928
+ minimum: 0,
929
+ description: "Non-negative delay in seconds before retrying, matching the Retry-After response header when present."
930
+ }))
931
+ }, {
932
+ $id: "ProblemDetails",
933
+ additionalProperties: true
2168
934
  });
2169
935
  Type.Object({
2170
- id: UuidSchema,
2171
- name: Type.String(),
2172
- personal: Type.Boolean(),
2173
- status: Type.String(),
2174
- role: TeamRoleSchema
936
+ field: Type.String(),
937
+ message: Type.String(),
938
+ code: Type.Optional(Type.String())
939
+ }, {
940
+ $id: "ValidationError",
941
+ additionalProperties: false
2175
942
  });
2176
943
  Type.Object({
2177
- id: UuidSchema,
2178
- name: Type.String(),
2179
- status: Type.String(),
2180
- personal: Type.Boolean(),
2181
- createdAt: DateTimeUnsafe,
2182
- updatedAt: DateTimeUnsafe,
2183
- members: Type.Array(TeamMemberSchema)
944
+ resource: Type.String(),
945
+ id: Type.Optional(Type.String({ format: "uuid" })),
946
+ keys: Type.Optional(Type.Record(Type.String(), Type.String()))
947
+ }, {
948
+ $id: "ConflictTarget",
949
+ additionalProperties: false
2184
950
  });
2185
951
  Type.Object({
2186
- teamId: UuidSchema,
2187
- role: Type.Union([
2188
- Type.Literal("manager"),
2189
- Type.Literal("executor"),
2190
- Type.Literal("member")
2191
- ])
952
+ constraint: Type.Optional(Type.String()),
953
+ target: Type.Optional(Type.Ref("ConflictTarget"))
954
+ }, {
955
+ $id: "ConflictError",
956
+ additionalProperties: false
2192
957
  });
958
+ var ConflictProblemDetailsSchema = Type.Intersect([ProblemDetailsSchema, Type.Object({ conflict: Type.Ref("ConflictError") })], { $id: "ConflictProblemDetails" });
2193
959
  Type.Object({
2194
- updated: Type.Boolean(),
2195
- role: Type.Union([
2196
- Type.Literal("manager"),
2197
- Type.Literal("executor"),
2198
- Type.Literal("member")
2199
- ])
960
+ type: Type.String(),
961
+ severity: Type.Number(),
962
+ match: Type.String()
963
+ }, {
964
+ $id: "InjectionThreat",
965
+ additionalProperties: false
2200
966
  });
2201
- Type.Object({ deleted: Type.Boolean() });
2202
- Type.Object({ removed: Type.Boolean() });
2203
- var FoundingMemberSchema = Type.Object({
2204
- subjectId: UuidSchema,
2205
- subjectNs: Type.Union([Type.Literal("Agent"), Type.Literal("Human")]),
2206
- role: Type.Union([
2207
- Type.Literal("owner"),
2208
- Type.Literal("manager"),
2209
- Type.Literal("executor"),
2210
- Type.Literal("member")
2211
- ])
967
+ Type.Intersect([ConflictProblemDetailsSchema, Type.Object({ flagged: Type.Optional(Type.Array(Type.Object({
968
+ id: Type.String({ format: "uuid" }),
969
+ threats: Type.Array(Type.Ref("InjectionThreat"))
970
+ }, { additionalProperties: false }))) })], { $id: "InjectionConflictProblemDetails" });
971
+ Type.Intersect([ProblemDetailsSchema, Type.Object({ errors: Type.Array(Type.Ref("ValidationError")) })], { $id: "ValidationProblemDetails" });
972
+ Type.Union([
973
+ Type.Literal("pack"),
974
+ Type.Literal("entry"),
975
+ Type.Literal("rendered_pack")
976
+ ]);
977
+ var ProvenanceGraphEdgeKindSchema = Type.Union([
978
+ Type.Literal("includes"),
979
+ Type.Literal("supersedes"),
980
+ Type.Literal("rendered_from")
981
+ ]);
982
+ var ProvenanceGraphPackMetaSchema = Type.Object({
983
+ packId: UuidSchema,
984
+ diaryId: UuidSchema,
985
+ packCid: Type.String(),
986
+ packType: Type.String(),
987
+ packCodec: Type.String(),
988
+ pinned: Type.Boolean(),
989
+ createdAt: TimestampSchema,
990
+ expiresAt: Type.Union([TimestampSchema, Type.Null()]),
991
+ supersedesPackId: Type.Union([UuidSchema, Type.Null()])
2212
992
  });
2213
- Type.Object({
2214
- name: Type.String({
2215
- minLength: 1,
2216
- maxLength: 255
2217
- }),
2218
- foundingMembers: Type.Optional(Type.Array(FoundingMemberSchema, { minItems: 1 }))
993
+ /**
994
+ * Discriminated creator embedded inside provenance-node response
995
+ * payloads. Re-uses the shared `PrincipalIdentitySchemaInline` (the
996
+ * `$id`-less twin) — embedding the named `PrincipalIdentitySchema`
997
+ * here would clash with the top-level registration via @fastify/swagger
998
+ * (`reference "PrincipalIdentity" resolves to more than one schema`).
999
+ */
1000
+ var ProvenanceGraphCreatorSchema = PrincipalIdentitySchemaInline;
1001
+ var ProvenanceGraphEntryMetaSchema = Type.Object({
1002
+ entryId: UuidSchema,
1003
+ diaryId: UuidSchema,
1004
+ entryType: EntryTypeSchema,
1005
+ contentHash: Type.Union([Type.String(), Type.Null()]),
1006
+ createdAt: TimestampSchema,
1007
+ updatedAt: TimestampSchema,
1008
+ signed: Type.Boolean(),
1009
+ title: Type.Union([Type.String(), Type.Null()]),
1010
+ tags: Type.Array(Type.String()),
1011
+ creator: Type.Optional(ProvenanceGraphCreatorSchema)
2219
1012
  });
2220
- Type.Object({
2221
- id: UuidSchema,
2222
- name: Type.String(),
2223
- status: Type.String(),
2224
- workflowId: Type.Optional(Type.String())
1013
+ var ProvenanceGraphPackNodeSchema = Type.Object({
1014
+ id: Type.String(),
1015
+ kind: Type.Literal("pack"),
1016
+ label: Type.String(),
1017
+ cid: Type.Union([Type.String(), Type.Null()]),
1018
+ meta: Type.Intersect([ProvenanceGraphPackMetaSchema, Type.Object({ creator: Type.Optional(ProvenanceGraphCreatorSchema) })])
2225
1019
  });
2226
- Type.Object({});
2227
- Type.Object({
2228
- accepted: Type.Boolean(),
2229
- teamStatus: Type.String()
1020
+ var ProvenanceGraphEntryNodeSchema = Type.Object({
1021
+ id: Type.String(),
1022
+ kind: Type.Literal("entry"),
1023
+ label: Type.String(),
1024
+ cid: Type.Union([Type.String(), Type.Null()]),
1025
+ meta: ProvenanceGraphEntryMetaSchema
2230
1026
  });
2231
- Type.Object({ destinationTeamId: UuidSchema });
2232
- Type.Object({ transferId: UuidSchema });
2233
- var TransferResponseSchema = Type.Object({
2234
- id: UuidSchema,
1027
+ var ProvenanceGraphRenderedPackMetaSchema = Type.Object({
1028
+ renderedPackId: UuidSchema,
1029
+ sourcePackId: UuidSchema,
2235
1030
  diaryId: UuidSchema,
2236
- sourceTeamId: UuidSchema,
2237
- destinationTeamId: UuidSchema,
2238
- status: Type.String(),
2239
- initiatedBy: UuidSchema,
2240
- expiresAt: Type.Unsafe(Type.String({ format: "date-time" })),
2241
- createdAt: Type.Unsafe(Type.String({ format: "date-time" }))
2242
- });
2243
- Type.Object({ items: Type.Array(TransferResponseSchema) });
2244
- Type.Object({ groupId: UuidSchema });
2245
- Type.Object({
2246
- groupId: UuidSchema,
2247
- subjectId: UuidSchema
1031
+ packCid: Type.String(),
1032
+ renderMethod: Type.String(),
1033
+ totalTokens: Type.Number(),
1034
+ pinned: Type.Boolean(),
1035
+ createdAt: TimestampSchema,
1036
+ expiresAt: Type.Union([TimestampSchema, Type.Null()]),
1037
+ creator: Type.Optional(ProvenanceGraphCreatorSchema)
2248
1038
  });
2249
- Type.Object({ name: Type.String({
2250
- minLength: 1,
2251
- maxLength: 255
2252
- }) });
2253
- Type.Object({
2254
- subjectId: UuidSchema,
2255
- subjectNs: Type.Optional(Type.Union([Type.Literal("Agent"), Type.Literal("Human")]))
1039
+ var ProvenanceGraphRenderedPackNodeSchema = Type.Object({
1040
+ id: Type.String(),
1041
+ kind: Type.Literal("rendered_pack"),
1042
+ label: Type.String(),
1043
+ cid: Type.Union([Type.String(), Type.Null()]),
1044
+ meta: ProvenanceGraphRenderedPackMetaSchema
2256
1045
  });
2257
- Type.Object({
2258
- id: UuidSchema,
2259
- name: Type.String(),
2260
- teamId: UuidSchema
1046
+ var ProvenanceGraphNodeSchema = Type.Union([
1047
+ ProvenanceGraphPackNodeSchema,
1048
+ ProvenanceGraphEntryNodeSchema,
1049
+ ProvenanceGraphRenderedPackNodeSchema
1050
+ ]);
1051
+ var ProvenanceGraphEdgeSchema = Type.Object({
1052
+ id: Type.String(),
1053
+ from: Type.String(),
1054
+ to: Type.String(),
1055
+ kind: ProvenanceGraphEdgeKindSchema,
1056
+ label: Type.Optional(Type.String()),
1057
+ meta: Type.Optional(Type.Record(Type.String(), Type.Union([
1058
+ Type.String(),
1059
+ Type.Number(),
1060
+ Type.Boolean(),
1061
+ Type.Null()
1062
+ ])))
2261
1063
  });
2262
- var GroupMemberResponseSchema = Type.Object({
2263
- subjectId: UuidSchema,
2264
- subjectNs: Type.String()
1064
+ var ProvenanceGraphMetadataSchema = Type.Object({
1065
+ format: Type.Literal("moltnet.provenance-graph/v1"),
1066
+ generatedAt: TimestampSchema,
1067
+ rootNodeId: Type.String(),
1068
+ rootPackId: UuidSchema,
1069
+ depth: Type.Number({ minimum: 0 })
2265
1070
  });
2266
1071
  Type.Object({
2267
- id: UuidSchema,
2268
- name: Type.String(),
2269
- teamId: UuidSchema,
2270
- createdAt: DateTimeUnsafe,
2271
- members: Type.Array(GroupMemberResponseSchema)
1072
+ metadata: ProvenanceGraphMetadataSchema,
1073
+ nodes: Type.Array(ProvenanceGraphNodeSchema),
1074
+ edges: Type.Array(ProvenanceGraphEdgeSchema)
1075
+ }, { $id: "ProvenanceGraph" });
1076
+ //#endregion
1077
+ //#region ../models/src/render-method.ts
1078
+ /**
1079
+ * The `renderMethod` convention for rendered packs (#1857).
1080
+ *
1081
+ * `renderedPacks.renderMethod` is a free-text `varchar(100)`. The server
1082
+ * bifurcates on exactly one thing — whether the label starts with `server:`
1083
+ * — and everything else is a caller-authored render whose markdown the
1084
+ * caller must supply. This module is the single owner of that convention:
1085
+ * the service, the API schemas, the runtime default and the console's
1086
+ * trust-tier derivation all read from here.
1087
+ *
1088
+ * The Go CLI (`apps/moltnet-cli/cobra_pack.go`) cannot import this module;
1089
+ * it carries a pointer comment and its default must be kept in sync with
1090
+ * `DEFAULT_SERVER_RENDER_METHOD` by hand.
1091
+ *
1092
+ * Values observed in production data and accepted unchanged:
1093
+ * `server:pack-to-docs-v1`, `agent:pack-to-docs-v1`, `agent-refined`.
1094
+ * `pi:pack-to-docs-v1` is the live pi-runtime default.
1095
+ */
1096
+ /** Labels carrying this prefix are rendered deterministically by the server. */
1097
+ var SERVER_RENDER_PREFIX = "server:";
1098
+ /**
1099
+ * Prefixes that identify caller-authored markdown.
1100
+ *
1101
+ * `agent:` is the canonical documented label, `pi:` is what the pi-runtime
1102
+ * emits by default, and `agent-` covers the `agent-refined` family that is
1103
+ * live in production data.
1104
+ */
1105
+ var CALLER_AUTHORED_PREFIXES = [
1106
+ "agent:",
1107
+ "pi:",
1108
+ "agent-"
1109
+ ];
1110
+ var DEFAULT_SERVER_RENDER_METHOD = "server:pack-to-docs-v1";
1111
+ var DEFAULT_AGENT_RENDER_METHOD = "agent:pack-to-docs-v1";
1112
+ var DEFAULT_PI_RENDER_METHOD = "pi:pack-to-docs-v1";
1113
+ /**
1114
+ * Write-side validation pattern: a known prefix followed by at least one
1115
+ * non-whitespace character. Stored rows are never re-validated against
1116
+ * this; it applies to new writes at the API boundary only.
1117
+ */
1118
+ var RENDER_METHOD_PATTERN = `^(${[SERVER_RENDER_PREFIX, ...CALLER_AUTHORED_PREFIXES].join("|")})\\S+$`;
1119
+ Type.String({
1120
+ minLength: 1,
1121
+ maxLength: 100,
1122
+ pattern: RENDER_METHOD_PATTERN,
1123
+ description: "Render method label. Server render methods start with \"server:\" and must omit renderedMarkdown; caller-authored methods start with \"agent:\", \"pi:\" or \"agent-\" and require it.",
1124
+ examples: [DEFAULT_SERVER_RENDER_METHOD, DEFAULT_AGENT_RENDER_METHOD]
2272
1125
  });
2273
- var DiaryGrantRoleSchema = Type.Union([Type.Literal("writer"), Type.Literal("manager")]);
2274
- var GrantSubjectNsSchema = Type.Union([
2275
- Type.Literal("Agent"),
2276
- Type.Literal("Human"),
2277
- Type.Literal("Group")
1126
+ /**
1127
+ * The server's bifurcation: a server method is rendered from the source
1128
+ * pack; any other method requires caller-supplied markdown.
1129
+ */
1130
+ function isServerRenderMethod(method) {
1131
+ return method.startsWith(SERVER_RENDER_PREFIX);
1132
+ }
1133
+ //#endregion
1134
+ //#region ../models/src/signer-constraint.ts
1135
+ var SIGNER_CONSTRAINT_TYPE = {
1136
+ Human: "human",
1137
+ TeamRole: "team-role",
1138
+ Group: "group"
1139
+ };
1140
+ Type.Union([
1141
+ Type.Object({
1142
+ type: Type.Literal(SIGNER_CONSTRAINT_TYPE.Human),
1143
+ id: Type.String({ format: "uuid" })
1144
+ }),
1145
+ Type.Object({
1146
+ type: Type.Literal(SIGNER_CONSTRAINT_TYPE.TeamRole),
1147
+ id: TeamRoleSchema
1148
+ }),
1149
+ Type.Object({
1150
+ type: Type.Literal(SIGNER_CONSTRAINT_TYPE.Group),
1151
+ id: Type.String({ format: "uuid" })
1152
+ })
2278
1153
  ]);
2279
- Type.Object({
2280
- subjectId: UuidSchema,
2281
- subjectNs: GrantSubjectNsSchema,
2282
- role: DiaryGrantRoleSchema
1154
+ //#endregion
1155
+ //#region ../models/src/signer-protocol.ts
1156
+ function schemaRef(schema) {
1157
+ const id = schemaId(schema);
1158
+ return Type.Ref(id);
1159
+ }
1160
+ function schemaId(schema) {
1161
+ const id = schema.$id;
1162
+ if (typeof id !== "string" || id.length === 0) throw new Error("Signer protocol schemas must have an identifier");
1163
+ return id;
1164
+ }
1165
+ var SignerBase64UrlSchema = PreviewSignBase64UrlSchema;
1166
+ var SignerUuidSchema = Type.String({
1167
+ $id: "SignerUuid",
1168
+ pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
2283
1169
  });
2284
- Type.Object({
2285
- subjectId: UuidSchema,
2286
- subjectNs: GrantSubjectNsSchema,
2287
- role: DiaryGrantRoleSchema
1170
+ var SignerOperationSchema = Type.Union([
1171
+ Type.Literal("credential-enrollment"),
1172
+ Type.Literal("credential-registration"),
1173
+ Type.Literal("signing-request")
1174
+ ], { $id: "SignerOperation" });
1175
+ var SignerChallengeOperationSchema = PreviewSignChallengeOperationSchema;
1176
+ var SignerPreviewSignPublicMaterialSchema = PreviewSignPublicMaterialSchema;
1177
+ var SignerPreviewSignChallengeValueSchema = PreviewSignChallengeValueSchema;
1178
+ var SignerProblemSchema = Type.Object({
1179
+ code: Type.String({ minLength: 1 }),
1180
+ message: Type.String({ minLength: 1 })
1181
+ }, {
1182
+ $id: "SignerProblem",
1183
+ additionalProperties: false
2288
1184
  });
2289
- var DiaryGrantResponseSchema = Type.Object({
2290
- subjectId: UuidSchema,
2291
- subjectNs: GrantSubjectNsSchema,
2292
- role: DiaryGrantRoleSchema
1185
+ var SignerCeremonyParamsSchema = Type.Object({ ceremonyId: Type.Unsafe(schemaRef(SignerBase64UrlSchema)) }, {
1186
+ $id: "SignerCeremonyParams",
1187
+ additionalProperties: false
2293
1188
  });
2294
- Type.Object({ grants: Type.Array(DiaryGrantResponseSchema) });
2295
- Type.Object({ revoked: Type.Boolean() });
2296
- var TaskGrantRoleSchema = Type.Union([Type.Literal("writer"), Type.Literal("manager")]);
2297
- Type.Object({
2298
- subjectId: UuidSchema,
2299
- subjectNs: GrantSubjectNsSchema,
2300
- role: TaskGrantRoleSchema
1189
+ var SignerSessionSchema = Type.Object({
1190
+ version: Type.Literal(1),
1191
+ token: Type.Unsafe(schemaRef(SignerBase64UrlSchema)),
1192
+ expiresAt: Type.String()
1193
+ }, {
1194
+ $id: "SignerSession",
1195
+ additionalProperties: false
2301
1196
  });
2302
- Type.Object({
2303
- subjectId: UuidSchema,
2304
- subjectNs: GrantSubjectNsSchema,
2305
- role: TaskGrantRoleSchema
1197
+ var SignerEnrollmentCeremonyRequestSchema = Type.Object({
1198
+ version: Type.Literal(1),
1199
+ operation: Type.Literal("credential-enrollment"),
1200
+ label: Type.String({
1201
+ minLength: 1,
1202
+ maxLength: 255
1203
+ }),
1204
+ teamId: Type.Unsafe(schemaRef(SignerUuidSchema))
1205
+ }, {
1206
+ $id: "SignerEnrollmentCeremonyRequest",
1207
+ additionalProperties: false
2306
1208
  });
2307
- var TaskGrantResponseSchema = Type.Object({
2308
- subjectId: UuidSchema,
2309
- subjectNs: GrantSubjectNsSchema,
2310
- role: TaskGrantRoleSchema
1209
+ var SignerChallengeCeremonyRequestSchema = Type.Object({
1210
+ version: Type.Literal(1),
1211
+ operation: Type.Unsafe(schemaRef(SignerChallengeOperationSchema)),
1212
+ resourceId: Type.Unsafe(schemaRef(SignerUuidSchema)),
1213
+ challenge: Type.Unsafe(schemaRef(SignerPreviewSignChallengeValueSchema))
1214
+ }, {
1215
+ $id: "SignerChallengeCeremonyRequest",
1216
+ additionalProperties: false
2311
1217
  });
2312
- Type.Object({ grants: Type.Array(TaskGrantResponseSchema) });
2313
- Type.Object({ "x-moltnet-team-id": Type.String({
2314
- format: "uuid",
2315
- description: "Team ID (UUID) that will own the resource. Required."
2316
- }) });
2317
- Type.Object({ "x-moltnet-team-id": Type.Optional(Type.String({
2318
- format: "uuid",
2319
- description: "Team ID (UUID) for scoping the request. Optional."
2320
- })) });
2321
- Type.Object({
2322
- kind: Type.Literal("agent"),
2323
- agentId: UuidSchema,
2324
- identityId: Type.Union([UuidSchema, Type.Null()]),
2325
- fingerprint: FingerprintSchema,
2326
- publicKey: PublicKeySchema
1218
+ var SignerCeremonyRequestSchema = Type.Union([Type.Unsafe(schemaRef(SignerEnrollmentCeremonyRequestSchema)), Type.Unsafe(schemaRef(SignerChallengeCeremonyRequestSchema))], { $id: "SignerCeremonyRequest" });
1219
+ var SignerCeremonySchema = Type.Object({
1220
+ version: Type.Literal(1),
1221
+ id: Type.Unsafe(schemaRef(SignerBase64UrlSchema)),
1222
+ operation: Type.Unsafe(schemaRef(SignerOperationSchema)),
1223
+ approvalUrl: Type.String(),
1224
+ expiresAt: Type.String()
2327
1225
  }, {
2328
- $id: "AgentPrincipal",
1226
+ $id: "SignerCeremony",
2329
1227
  additionalProperties: false
2330
1228
  });
2331
- Type.Object({
2332
- kind: Type.Literal("human"),
2333
- humanId: UuidSchema,
2334
- identityId: Type.Union([UuidSchema, Type.Null()])
1229
+ var SignerPendingResultSchema = Type.Object({
1230
+ version: Type.Literal(1),
1231
+ status: Type.Literal("pending"),
1232
+ operation: Type.Unsafe(schemaRef(SignerOperationSchema))
2335
1233
  }, {
2336
- $id: "HumanPrincipal",
1234
+ $id: "SignerPendingResult",
2337
1235
  additionalProperties: false
2338
1236
  });
2339
- var principalUnionVariants = [Type.Object({
2340
- kind: Type.Literal("agent"),
2341
- agentId: UuidSchema,
2342
- identityId: Type.Union([UuidSchema, Type.Null()]),
2343
- fingerprint: FingerprintSchema,
2344
- publicKey: PublicKeySchema
2345
- }, { additionalProperties: false }), Type.Object({
2346
- kind: Type.Literal("human"),
2347
- humanId: UuidSchema,
2348
- identityId: Type.Union([UuidSchema, Type.Null()])
2349
- }, { additionalProperties: false })];
2350
- Type.Union(principalUnionVariants, {
2351
- $id: "PrincipalIdentity",
2352
- discriminator: { propertyName: "kind" }
1237
+ var SignerEnrollmentResultSchema = Type.Object({
1238
+ version: Type.Literal(1),
1239
+ status: Type.Literal("completed"),
1240
+ operation: Type.Literal("credential-enrollment"),
1241
+ publicMaterial: Type.Unsafe(schemaRef(SignerPreviewSignPublicMaterialSchema))
1242
+ }, {
1243
+ $id: "SignerEnrollmentResult",
1244
+ additionalProperties: false
1245
+ });
1246
+ var SignerReceiptSchema = PreviewSignReceiptValueSchema;
1247
+ var SignerSignatureResultSchema = Type.Object({
1248
+ version: Type.Literal(1),
1249
+ status: Type.Literal("completed"),
1250
+ operation: Type.Unsafe(schemaRef(SignerChallengeOperationSchema)),
1251
+ receipt: Type.Unsafe(schemaRef(SignerReceiptSchema))
1252
+ }, {
1253
+ $id: "SignerSignatureResult",
1254
+ additionalProperties: false
1255
+ });
1256
+ var SignerFailedResultSchema = Type.Object({
1257
+ version: Type.Literal(1),
1258
+ status: Type.Literal("failed"),
1259
+ operation: Type.Unsafe(schemaRef(SignerOperationSchema)),
1260
+ code: Type.String(),
1261
+ message: Type.String()
1262
+ }, {
1263
+ $id: "SignerFailedResult",
1264
+ additionalProperties: false
2353
1265
  });
1266
+ var SignerCeremonyResultSchema = Type.Union([
1267
+ Type.Unsafe(schemaRef(SignerPendingResultSchema)),
1268
+ Type.Unsafe(schemaRef(SignerEnrollmentResultSchema)),
1269
+ Type.Unsafe(schemaRef(SignerSignatureResultSchema)),
1270
+ Type.Unsafe(schemaRef(SignerFailedResultSchema))
1271
+ ], { $id: "SignerCeremonyResult" });
1272
+ ({ ...previewSignSchemaContext }), schemaId(SignerUuidSchema), schemaId(SignerOperationSchema), schemaId(SignerProblemSchema), schemaId(SignerCeremonyParamsSchema), schemaId(SignerSessionSchema), schemaId(SignerEnrollmentCeremonyRequestSchema), schemaId(SignerChallengeCeremonyRequestSchema), schemaId(SignerCeremonyRequestSchema), schemaId(SignerCeremonySchema), schemaId(SignerPendingResultSchema), schemaId(SignerEnrollmentResultSchema), schemaId(SignerSignatureResultSchema), schemaId(SignerFailedResultSchema), schemaId(SignerCeremonyResultSchema);
1273
+ //#endregion
1274
+ //#region ../models/src/tool-enforcement.ts
1275
+ var TOOL_ENFORCEMENT_VALUES = [
1276
+ "off",
1277
+ "watch",
1278
+ "enforce"
1279
+ ];
1280
+ var toolEnforcementLiterals = [
1281
+ Type.Literal(TOOL_ENFORCEMENT_VALUES[0]),
1282
+ Type.Literal(TOOL_ENFORCEMENT_VALUES[1]),
1283
+ Type.Literal(TOOL_ENFORCEMENT_VALUES[2])
1284
+ ];
1285
+ var ToolEnforcementSchema = Type.Union(toolEnforcementLiterals, { description: "Runtime tool-policy enforcement mode: off (inert), watch (audit only), enforce (block disallowed tools, fail-closed)." });
1286
+ //#endregion
1287
+ //#region src/moltnet/render-phase6.ts
1288
+ function slugToTitle(value) {
1289
+ return value.split(/[:/_-]+/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join(" ");
1290
+ }
1291
+ function extractScope(tags) {
1292
+ const scope = tags?.find((tag) => tag.startsWith("scope:"));
1293
+ return scope ? scope.slice(6) : null;
1294
+ }
1295
+ function extractSeverity(tags) {
1296
+ const severity = tags?.find((tag) => tag.startsWith("severity:"));
1297
+ return severity ? severity.slice(9) : null;
1298
+ }
1299
+ function stripEntryScaffolding(content) {
1300
+ let out = content;
1301
+ out = out.replace(/<moltnet-signed>[\s\S]*?<content>([\s\S]*?)<\/content>[\s\S]*?<\/moltnet-signed>/gi, "$1");
1302
+ out = out.replace(/<metadata>[\s\S]*?<\/metadata>/gi, "");
1303
+ out = out.replace(/<signature>[\s\S]*?<\/signature>/gi, "");
1304
+ out = out.replace(/<\/?(?:moltnet-signed|content|signature|metadata)[^>]*>/gi, "");
1305
+ out = out.replace(/^- Compression:.*$/gim, "");
1306
+ out = out.replace(/^- Tokens:.*$/gim, "");
1307
+ return out.trim();
1308
+ }
1309
+ function normalizeKey(value) {
1310
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
1311
+ }
1312
+ function extractRules(content) {
1313
+ return stripEntryScaffolding(content).split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && (/(^rule:|^watch for:|^must\b|^never\b)/i.test(line) || /\b(MUST|NEVER)\b/.test(line))).slice(0, 5);
1314
+ }
1315
+ function renderSourceRefs(entries) {
1316
+ return entries.map((entry) => {
1317
+ const shortId = entry.entryId.slice(0, 8);
1318
+ const fingerprint = entry.entry.creator?.fingerprint?.replaceAll("-", "").slice(0, 4).toLowerCase();
1319
+ return `[\`e:${shortId}\`](@unknown · ${fingerprint ? `agent:${fingerprint}` : "agent:unkn"})`;
1320
+ }).join(", ");
1321
+ }
1322
+ function renderKeywords(tags) {
1323
+ const keywords = (tags ?? []).filter((tag) => !tag.startsWith("scope:") && !tag.startsWith("severity:"));
1324
+ if (keywords.length === 0) return "";
1325
+ return `Relevant search terms include ${keywords.slice(0, 6).map((tag) => `\`${tag}\``).join(", ")}.`;
1326
+ }
1327
+ function renderPhase6Markdown(pack) {
1328
+ const entries = pack.entries ?? [];
1329
+ const grouped = /* @__PURE__ */ new Map();
1330
+ for (const entry of entries) {
1331
+ const scope = extractScope(entry.entry.tags) ?? "general";
1332
+ const title = entry.entry.title?.trim() || `Entry ${entry.entryId.slice(0, 8)}`;
1333
+ const groupKey = normalizeKey(scope);
1334
+ const topicKey = normalizeKey(title) || entry.entryId;
1335
+ if (!grouped.has(groupKey)) grouped.set(groupKey, /* @__PURE__ */ new Map());
1336
+ const topics = grouped.get(groupKey);
1337
+ const existing = topics.get(topicKey);
1338
+ if (existing) existing.entries.push(entry);
1339
+ else topics.set(topicKey, {
1340
+ title,
1341
+ scope,
1342
+ entries: [entry]
1343
+ });
1344
+ }
1345
+ const lines = [];
1346
+ lines.push("# Rendered Pack");
1347
+ lines.push("");
1348
+ lines.push("## Source");
1349
+ lines.push("");
1350
+ lines.push("| Pack UUID | Pack CID | Entries |");
1351
+ lines.push("| --------- | -------- | ------- |");
1352
+ lines.push(`| \`${pack.id}\` | \`${pack.packCid}\` | ${entries.length} |`);
1353
+ lines.push("");
1354
+ for (const [, topics] of grouped) {
1355
+ const scope = topics.values().next().value?.scope ?? "general";
1356
+ lines.push(`## ${slugToTitle(scope)}`);
1357
+ lines.push("");
1358
+ for (const [, topic] of topics) {
1359
+ const primary = topic.entries[0];
1360
+ const mergedContent = topic.entries.map((entry) => stripEntryScaffolding(entry.entry.content)).filter(Boolean).join("\n\n");
1361
+ const rules = topic.entries.flatMap((entry) => extractRules(entry.entry.content));
1362
+ const severity = extractSeverity(primary.entry.tags);
1363
+ lines.push(`### ${topic.title}`);
1364
+ lines.push("");
1365
+ lines.push(`**Subsystem:** ${slugToTitle(topic.scope)}`);
1366
+ if (severity) lines.push(`**Severity:** ${slugToTitle(severity)}`);
1367
+ lines.push(`**Type:** ${primary.entry.entryType}`);
1368
+ lines.push("");
1369
+ if (rules.length > 0) {
1370
+ lines.push("**Rules**");
1371
+ lines.push("");
1372
+ for (const rule of Array.from(new Set(rules))) lines.push(`- ${rule}`);
1373
+ lines.push("");
1374
+ }
1375
+ lines.push(mergedContent);
1376
+ lines.push("");
1377
+ const keywords = renderKeywords(primary.entry.tags);
1378
+ if (keywords) {
1379
+ lines.push(keywords);
1380
+ lines.push("");
1381
+ }
1382
+ lines.push("Provenance:");
1383
+ for (const entry of topic.entries) lines.push(`- Entry ID \`${entry.entryId}\`, CID \`${entry.entryCidSnapshot}\``);
1384
+ lines.push("");
1385
+ lines.push(`*Sources: ${renderSourceRefs(topic.entries)}*`);
1386
+ lines.push("");
1387
+ }
1388
+ }
1389
+ if (entries.length === 0) {
1390
+ lines.push("_This pack has no expanded entries._");
1391
+ lines.push("");
1392
+ }
1393
+ return lines.join("\n").trim();
1394
+ }
1395
+ //#endregion
1396
+ //#region src/moltnet/tools.ts
2354
1397
  /**
2355
- * `$id`-less twin of `PrincipalIdentitySchema`. Required anywhere the
2356
- * schema is **embedded** inline into another schema (MCP `outputSchema`
2357
- * — every tool that returns a creator-bearing object embeds its own
2358
- * copy; provenance-graph node `meta.creator`, etc.). Ajv 8 throws
2359
- * `reference "PrincipalIdentity" resolves to more than one schema` if
2360
- * the same `$id` appears twice in the same compilation pass, which is
2361
- * exactly what happens when the MCP server lists tools and Ajv
2362
- * traverses every advertised `outputSchema`.
1398
+ * MoltNet custom tools for pi.
2363
1399
  *
2364
- * Structurally identical to `PrincipalIdentitySchema` (they share the
2365
- * variants array); change one, change both.
1400
+ * Factory that produces ready-to-register pi tool definitions.
1401
+ * These tools run on the host (not in the VM) via the MoltNet SDK,
1402
+ * so agent credentials never touch the VM filesystem.
2366
1403
  */
2367
- var PrincipalIdentitySchemaInline = Type.Union(principalUnionVariants, { discriminator: { propertyName: "kind" } });
1404
+ var MOLTNET_TOOL_NAMES = [
1405
+ "moltnet_pack_get",
1406
+ "moltnet_pack_create",
1407
+ "moltnet_pack_provenance",
1408
+ "moltnet_pack_render",
1409
+ "moltnet_rendered_pack_list",
1410
+ "moltnet_rendered_pack_get",
1411
+ "moltnet_diary_tags",
1412
+ "moltnet_list_entries",
1413
+ "moltnet_get_entry",
1414
+ "moltnet_search_entries",
1415
+ "moltnet_create_entry",
1416
+ "moltnet_get_task",
1417
+ "moltnet_list_task_attempts",
1418
+ "moltnet_list_task_messages",
1419
+ "moltnet_upload_task_artifact",
1420
+ "moltnet_list_task_artifacts",
1421
+ "moltnet_download_task_artifact",
1422
+ "moltnet_review_session_errors",
1423
+ "moltnet_host_exec"
1424
+ ];
1425
+ var DIARY_TAG_MAX_LENGTH = 128;
1426
+ /**
1427
+ * Baseline env keys forwarded to host-exec child processes.
1428
+ * Callers can extend this set at sandbox startup via `MoltNetToolsConfig.hostExecBaseEnv`.
1429
+ */
1430
+ var HOST_EXEC_DEFAULT_BASE_ENV = new Set([
1431
+ "PATH",
1432
+ "HOME",
1433
+ "LANG",
1434
+ "LC_ALL",
1435
+ "TMPDIR",
1436
+ "GIT_CONFIG_GLOBAL",
1437
+ "MOLTNET_CREDENTIALS_PATH",
1438
+ "GIT_AUTHOR_NAME",
1439
+ "GIT_AUTHOR_EMAIL",
1440
+ "GIT_COMMITTER_NAME",
1441
+ "GIT_COMMITTER_EMAIL",
1442
+ "SSH_AUTH_SOCK"
1443
+ ]);
1444
+ function ensureConnected(config) {
1445
+ const agent = config.getAgent();
1446
+ const diaryId = config.getDiaryId();
1447
+ if (!agent || !diaryId) throw new Error("MoltNet not connected");
1448
+ return {
1449
+ agent,
1450
+ diaryId,
1451
+ teamId: config.getTeamId() ?? ""
1452
+ };
1453
+ }
1454
+ function hostExecMatchesAutoApproveRule(params, rule) {
1455
+ if (params.executable !== rule.executable) return false;
1456
+ if (rule.argsExcludes?.some((arg) => params.args.includes(arg))) return false;
1457
+ if (rule.argsPrefix && !rule.argsPrefix.every((arg, index) => params.args[index] === arg)) return false;
1458
+ if (rule.argsContains && !rule.argsContains.every((arg) => params.args.includes(arg))) return false;
1459
+ return true;
1460
+ }
1461
+ function shouldAutoApproveHostExec(params, config) {
1462
+ const policy = config.autoApproveHostExec === true ? true : config.hostExecAutoApprove ?? false;
1463
+ if (policy === true) return true;
1464
+ if (!Array.isArray(policy)) return false;
1465
+ return policy.some((rule) => hostExecMatchesAutoApproveRule(params, rule));
1466
+ }
1467
+ async function resolveWorkspaceFilePath(cwd, filePath) {
1468
+ const resolved = path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(cwd, filePath);
1469
+ const realCwd = await realpath(cwd);
1470
+ let realResolved;
1471
+ try {
1472
+ realResolved = await realpath(resolved);
1473
+ } catch (err) {
1474
+ 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.`);
1475
+ throw err;
1476
+ }
1477
+ const rel = path.relative(realCwd, realResolved);
1478
+ if (rel === "" || rel.startsWith("..") || path.isAbsolute(rel)) throw new Error(`task artifact path escapes workspace: ${filePath}`);
1479
+ return realResolved;
1480
+ }
1481
+ async function openWorkspaceArtifactInput(config, cwd, filePath) {
1482
+ if (config.openWorkspaceFileForRead) try {
1483
+ return await config.openWorkspaceFileForRead(filePath);
1484
+ } catch (err) {
1485
+ 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.`);
1486
+ throw err;
1487
+ }
1488
+ const resolved = await resolveWorkspaceFilePath(cwd, filePath);
1489
+ const info = await stat(resolved);
1490
+ return {
1491
+ stream: createReadStream(resolved),
1492
+ isFile: info.isFile(),
1493
+ sizeBytes: info.size,
1494
+ displayPath: path.relative(cwd, resolved)
1495
+ };
1496
+ }
1497
+ async function resolveWorkspaceOutputPath(cwd, filePath) {
1498
+ const resolved = path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(cwd, filePath);
1499
+ const workspaceRoot = path.resolve(cwd);
1500
+ const lexicalRel = path.relative(workspaceRoot, resolved);
1501
+ if (lexicalRel === "" || lexicalRel.startsWith("..") || path.isAbsolute(lexicalRel)) throw new Error(`task artifact output path escapes workspace: ${filePath}`);
1502
+ const realCwd = await realpath(cwd);
1503
+ const parent = path.dirname(resolved);
1504
+ assertPathInsideWorkspace(realCwd, await findExistingAncestor(parent), filePath);
1505
+ await mkdir(parent, { recursive: true });
1506
+ assertPathInsideWorkspace(realCwd, await realpath(parent), filePath);
1507
+ return resolved;
1508
+ }
1509
+ async function findExistingAncestor(candidate) {
1510
+ let current = candidate;
1511
+ for (;;) {
1512
+ try {
1513
+ return await realpath(current);
1514
+ } catch (err) {
1515
+ if (!err || typeof err !== "object" || !("code" in err) || err.code !== "ENOENT") throw err;
1516
+ }
1517
+ const parent = path.dirname(current);
1518
+ if (parent === current) throw new Error(`task artifact output has no existing ancestor: ${candidate}`);
1519
+ current = parent;
1520
+ }
1521
+ }
1522
+ function assertPathInsideWorkspace(realCwd, realPath, displayPath) {
1523
+ if (!isResolvedPathInsideRoot$1(realPath, realCwd)) throw new Error(`task artifact output path escapes workspace: ${displayPath}`);
1524
+ }
1525
+ /**
1526
+ * Expand the `taskFilter` shorthand on the diary list/search tools into
1527
+ * the matching `task:*` provenance tags emitted by `moltnet_create_entry`
1528
+ * during a task. Returning an array (possibly empty) lets callers spread
1529
+ * it into a larger `tags` AND-filter without conditionals.
1530
+ */
1531
+ function compileTaskFilterTags(filter) {
1532
+ if (!filter) return [];
1533
+ const tags = [];
1534
+ if (filter.taskId) tags.push(`task:id:${filter.taskId}`);
1535
+ if (filter.taskType) tags.push(`task:type:${filter.taskType}`);
1536
+ if (filter.correlationId) tags.push(`task:correlation:${filter.correlationId}`);
1537
+ if (typeof filter.attemptN === "number") tags.push(`task:attempt:${filter.attemptN}`);
1538
+ return tags;
1539
+ }
1540
+ /**
1541
+ * Create all MoltNet tool definitions, ready to pass to `pi.registerTool()`.
1542
+ */
1543
+ function createMoltNetTools(config) {
1544
+ const getPack = defineTool({
1545
+ name: "moltnet_pack_get",
1546
+ label: "Get MoltNet Pack",
1547
+ description: "Get a context pack by ID. Optionally expand included entries.",
1548
+ parameters: Type$1.Object({
1549
+ packId: Type$1.String({ description: "Context pack ID" }),
1550
+ expandEntries: Type$1.Optional(Type$1.Boolean({ description: "Include full expanded entries" }))
1551
+ }),
1552
+ async execute(_id, params) {
1553
+ const { agent } = ensureConnected(config);
1554
+ const pack = await agent.packs.get(params.packId, { expand: params.expandEntries ? "entries" : void 0 });
1555
+ return {
1556
+ content: [{
1557
+ type: "text",
1558
+ text: JSON.stringify(pack, null, 2)
1559
+ }],
1560
+ details: {}
1561
+ };
1562
+ }
1563
+ });
1564
+ const createPack = defineTool({
1565
+ name: "moltnet_pack_create",
1566
+ label: "Create MoltNet Pack",
1567
+ description: "Persist a curated context pack. Entries are caller-ranked (lower rank = more prominent). Recipe/prompt/selection_rationale belong in params. Defaults to pinned=false — packs in the attribution pipeline are ephemeral unless the caller explicitly opts in.",
1568
+ parameters: Type$1.Object({
1569
+ entries: Type$1.Array(Type$1.Object({
1570
+ entryId: Type$1.String({ description: "Diary entry UUID" }),
1571
+ rank: Type$1.Number({ description: "Rank (1..N, lower = more prominent)" })
1572
+ }), { description: "Selected entries with their ranks" }),
1573
+ params: Type$1.Optional(Type$1.Record(Type$1.String(), Type$1.Unknown(), { description: "Free-form recipe parameters (recipe name, prompt, selection rationale, etc.)" })),
1574
+ tokenBudget: Type$1.Optional(Type$1.Number({ description: "Soft token budget recorded on the pack (optional)" })),
1575
+ pinned: Type$1.Optional(Type$1.Boolean({ description: "Pin the pack against retention policy (default false)" }))
1576
+ }),
1577
+ async execute(_id, params) {
1578
+ const { agent, diaryId } = ensureConnected(config);
1579
+ const pack = await agent.packs.create(diaryId, {
1580
+ packType: "custom",
1581
+ params: params.params ?? {},
1582
+ entries: params.entries,
1583
+ tokenBudget: params.tokenBudget,
1584
+ pinned: params.pinned ?? false
1585
+ });
1586
+ return {
1587
+ content: [{
1588
+ type: "text",
1589
+ text: JSON.stringify(pack, null, 2)
1590
+ }],
1591
+ details: {}
1592
+ };
1593
+ }
1594
+ });
1595
+ const getPackProvenance = defineTool({
1596
+ name: "moltnet_pack_provenance",
1597
+ label: "Get MoltNet Pack Provenance",
1598
+ description: "Get the provenance graph for a context pack by ID or CID.",
1599
+ parameters: Type$1.Object({
1600
+ packId: Type$1.Optional(Type$1.String({ description: "Context pack ID" })),
1601
+ packCid: Type$1.Optional(Type$1.String({ description: "Context pack CID" })),
1602
+ depth: Type$1.Optional(Type$1.Number({ description: "Supersession ancestry depth to include (default 2)" }))
1603
+ }),
1604
+ async execute(_id, params) {
1605
+ const { agent } = ensureConnected(config);
1606
+ if (!params.packId && !params.packCid) throw new Error("Provide either packId or packCid");
1607
+ if (params.packId && params.packCid) throw new Error("Provide only one of packId or packCid");
1608
+ const graph = params.packId ? await agent.packs.getProvenance(params.packId, { depth: params.depth ?? 2 }) : await agent.packs.getProvenanceByCid(params.packCid, { depth: params.depth ?? 2 });
1609
+ const payload = {
1610
+ metadata: graph.metadata,
1611
+ counts: {
1612
+ nodes: graph.nodes.length,
1613
+ edges: graph.edges.length
1614
+ },
1615
+ graph
1616
+ };
1617
+ return {
1618
+ content: [{
1619
+ type: "text",
1620
+ text: JSON.stringify(payload, null, 2)
1621
+ }],
1622
+ details: {}
1623
+ };
1624
+ }
1625
+ });
1626
+ const renderPack = defineTool({
1627
+ name: "moltnet_pack_render",
1628
+ label: "Render MoltNet Pack",
1629
+ description: "Fetch a pack with entries, transform it into docs, then preview or persist the rendered pack.",
1630
+ parameters: Type$1.Object({
1631
+ packId: Type$1.String({ description: "Context pack ID" }),
1632
+ renderMethod: Type$1.Optional(Type$1.String({ description: `Render method label. Defaults to ${DEFAULT_PI_RENDER_METHOD}` })),
1633
+ markdown: Type$1.Optional(Type$1.String({ description: "Optional caller-authored markdown override" })),
1634
+ preview: Type$1.Optional(Type$1.Boolean({ description: "Preview without persisting (default false)" })),
1635
+ pinned: Type$1.Optional(Type$1.Boolean({ description: "Persist the rendered pack as pinned (default false)" }))
1636
+ }),
1637
+ async execute(_id, params) {
1638
+ const { agent } = ensureConnected(config);
1639
+ const renderMethod = params.renderMethod ?? "pi:pack-to-docs-v1";
1640
+ let renderedMarkdown = params.markdown;
1641
+ if (!renderedMarkdown && !isServerRenderMethod(renderMethod)) renderedMarkdown = renderPhase6Markdown(await agent.packs.get(params.packId, { expand: "entries" }));
1642
+ const result = params.preview ?? false ? await agent.packs.previewRendered(params.packId, {
1643
+ renderMethod,
1644
+ renderedMarkdown
1645
+ }) : await agent.packs.render(params.packId, {
1646
+ renderMethod,
1647
+ renderedMarkdown,
1648
+ pinned: params.pinned
1649
+ });
1650
+ return {
1651
+ content: [{
1652
+ type: "text",
1653
+ text: JSON.stringify(result, null, 2)
1654
+ }],
1655
+ details: {}
1656
+ };
1657
+ }
1658
+ });
1659
+ const listRenderedPacks = defineTool({
1660
+ name: "moltnet_rendered_pack_list",
1661
+ label: "List MoltNet Rendered Packs",
1662
+ description: "List rendered packs for the current MoltNet diary, optionally filtered by source pack or render method.",
1663
+ parameters: Type$1.Object({
1664
+ sourcePackId: Type$1.Optional(Type$1.String({ description: "Filter by source pack ID" })),
1665
+ renderMethod: Type$1.Optional(Type$1.String({ description: "Filter by render method" })),
1666
+ limit: Type$1.Optional(Type$1.Number({ description: "Max results (default 10)" })),
1667
+ offset: Type$1.Optional(Type$1.Number({ description: "Offset for pagination (default 0)" }))
1668
+ }),
1669
+ async execute(_id, params) {
1670
+ const { agent, diaryId } = ensureConnected(config);
1671
+ const rendered = await agent.packs.listRendered(diaryId, {
1672
+ sourcePackId: params.sourcePackId,
1673
+ renderMethod: params.renderMethod,
1674
+ limit: params.limit ?? 10,
1675
+ offset: params.offset ?? 0
1676
+ });
1677
+ return {
1678
+ content: [{
1679
+ type: "text",
1680
+ text: JSON.stringify(rendered, null, 2)
1681
+ }],
1682
+ details: {}
1683
+ };
1684
+ }
1685
+ });
1686
+ const getRenderedPack = defineTool({
1687
+ name: "moltnet_rendered_pack_get",
1688
+ label: "Get MoltNet Rendered Pack",
1689
+ description: "Get a rendered pack by ID.",
1690
+ parameters: Type$1.Object({ renderedPackId: Type$1.String({ description: "Rendered pack ID" }) }),
1691
+ async execute(_id, params) {
1692
+ const { agent } = ensureConnected(config);
1693
+ const rendered = await agent.packs.getRendered(params.renderedPackId);
1694
+ return {
1695
+ content: [{
1696
+ type: "text",
1697
+ text: JSON.stringify(rendered, null, 2)
1698
+ }],
1699
+ details: {}
1700
+ };
1701
+ }
1702
+ });
1703
+ const diaryTags = defineTool({
1704
+ name: "moltnet_diary_tags",
1705
+ label: "List MoltNet Diary Tags",
1706
+ description: "Inventory tags on the current diary with entry counts. Cheap reconnaissance before committing to a search or list — use it to discover scope prefixes and cluster sizes. Optional prefix/minCount/entryTypes filters narrow the result.",
1707
+ parameters: Type$1.Object({
1708
+ prefix: Type$1.Optional(Type$1.String({ description: "Filter to tags starting with this prefix (e.g. \"scope:\")" })),
1709
+ minCount: Type$1.Optional(Type$1.Number({ description: "Exclude tags with fewer than this many entries" })),
1710
+ entryTypes: Type$1.Optional(Type$1.Array(Type$1.Union([
1711
+ Type$1.Literal("episodic"),
1712
+ Type$1.Literal("semantic"),
1713
+ Type$1.Literal("procedural"),
1714
+ Type$1.Literal("reflection")
1715
+ ]), { description: "Scope the tag count to these entry types" }))
1716
+ }),
1717
+ async execute(_id, params) {
1718
+ const { agent, diaryId } = ensureConnected(config);
1719
+ const result = await agent.diaries.tags(diaryId, {
1720
+ prefix: params.prefix,
1721
+ minCount: params.minCount,
1722
+ entryTypes: params.entryTypes
1723
+ });
1724
+ return {
1725
+ content: [{
1726
+ type: "text",
1727
+ text: JSON.stringify(result, null, 2)
1728
+ }],
1729
+ details: {}
1730
+ };
1731
+ }
1732
+ });
1733
+ const listEntries = defineTool({
1734
+ name: "moltnet_list_entries",
1735
+ label: "List MoltNet Diary Entries",
1736
+ description: "List entries from the MoltNet diary. When `entryIds` is provided, batch-fetches those specific entries (max 50) and returns full fields including entryType, contentSignature, and contentHash for signature checks. Otherwise returns recent entries with a content preview, filtered by any combination of tags (AND), excludeTags (NONE), entryType, and the taskFilter shorthand which expands into the right `task:*` tags.",
1737
+ parameters: Type$1.Object({
1738
+ limit: Type$1.Optional(Type$1.Number({ description: "Max entries to return (default 10)" })),
1739
+ tags: Type$1.Optional(Type$1.Array(Type$1.String({
1740
+ minLength: 1,
1741
+ maxLength: DIARY_TAG_MAX_LENGTH
1742
+ }), {
1743
+ description: "Tags filter — entry must have ALL listed tags (AND). Max 20.",
1744
+ maxItems: 20
1745
+ })),
1746
+ excludeTags: Type$1.Optional(Type$1.Array(Type$1.String({
1747
+ minLength: 1,
1748
+ maxLength: DIARY_TAG_MAX_LENGTH
1749
+ }), {
1750
+ description: "Tags to exclude — entry must have NONE of these. Max 20.",
1751
+ maxItems: 20
1752
+ })),
1753
+ entryType: Type$1.Optional(Type$1.String({ description: "Filter by entry type (procedural, semantic, episodic, reflection)." })),
1754
+ taskFilter: Type$1.Optional(Type$1.Object({
1755
+ taskId: Type$1.Optional(Type$1.String()),
1756
+ taskType: Type$1.Optional(Type$1.String()),
1757
+ correlationId: Type$1.Optional(Type$1.String()),
1758
+ attemptN: Type$1.Optional(Type$1.Number())
1759
+ }, { description: "Shorthand: any combination compiles to the matching task:* tags (task:id:<id>, task:type:<type>, task:correlation:<id>, task:attempt:<n>) and is merged into the tags filter." })),
1760
+ entryIds: Type$1.Optional(Type$1.Array(Type$1.String(), {
1761
+ description: "Batch-fetch specific entries by UUID (max 50). Overrides every other filter.",
1762
+ maxItems: 50
1763
+ }))
1764
+ }),
1765
+ async execute(_id, params) {
1766
+ const { agent, diaryId } = ensureConnected(config);
1767
+ const query = {
1768
+ orderBy: "createdAt",
1769
+ order: "desc"
1770
+ };
1771
+ const batchMode = !!params.entryIds?.length;
1772
+ if (batchMode) query.ids = params.entryIds;
1773
+ else {
1774
+ query.limit = params.limit ?? 10;
1775
+ const expandedTags = compileTaskFilterTags(params.taskFilter);
1776
+ const allTags = [...params.tags ?? [], ...expandedTags];
1777
+ if (allTags.length) query.tags = allTags;
1778
+ if (params.excludeTags?.length) query.excludeTags = params.excludeTags;
1779
+ if (params.entryType) query.entryType = params.entryType;
1780
+ }
1781
+ const entries = await agent.entries.list(diaryId, query);
1782
+ return {
1783
+ content: [{
1784
+ type: "text",
1785
+ text: JSON.stringify(entries.items?.map((e) => batchMode ? {
1786
+ id: e.id,
1787
+ title: e.title,
1788
+ entryType: e.entryType,
1789
+ tags: e.tags,
1790
+ importance: e.importance,
1791
+ contentHash: e.contentHash,
1792
+ contentSignature: e.contentSignature,
1793
+ signingNonce: e.signingNonce,
1794
+ createdAt: e.createdAt
1795
+ } : {
1796
+ id: e.id,
1797
+ title: e.title,
1798
+ tags: e.tags,
1799
+ importance: e.importance,
1800
+ createdAt: e.createdAt,
1801
+ contentPreview: typeof e.content === "string" ? e.content.slice(0, 200) : void 0
1802
+ }), null, 2)
1803
+ }],
1804
+ details: {}
1805
+ };
1806
+ }
1807
+ });
1808
+ const getEntry = defineTool({
1809
+ name: "moltnet_get_entry",
1810
+ label: "Get MoltNet Diary Entry",
1811
+ description: "Get the full content of a specific diary entry by ID.",
1812
+ parameters: Type$1.Object({ entryId: Type$1.String({ description: "The entry ID to fetch" }) }),
1813
+ async execute(_id, params) {
1814
+ const { agent } = ensureConnected(config);
1815
+ const entry = await agent.entries.get(params.entryId);
1816
+ return {
1817
+ content: [{
1818
+ type: "text",
1819
+ text: JSON.stringify({
1820
+ id: entry.id,
1821
+ title: entry.title,
1822
+ content: entry.content,
1823
+ tags: entry.tags,
1824
+ importance: entry.importance,
1825
+ createdAt: entry.createdAt
1826
+ }, null, 2)
1827
+ }],
1828
+ details: {}
1829
+ };
1830
+ }
1831
+ });
1832
+ const searchEntries = defineTool({
1833
+ name: "moltnet_search_entries",
1834
+ label: "Search MoltNet Diary Entries",
1835
+ description: "Hybrid (semantic + lexical) search over diary entries. Use proactively before non-trivial investigation, code changes, review, or episodic incident capture so prior decisions and recurring failures surface before you act. Do not search randomly: pass taskFilter for task/correlation-local searches and tags or entryTypes for broader prior-knowledge searches. Optional tags / excludeTags / entryTypes filters AND with the query; the taskFilter shorthand expands into task:* provenance tags so `taskFilter: { taskType: \"fulfill_brief\" }` returns only entries from fulfill_brief attempts. Filters apply server-side before ranking.",
1836
+ parameters: Type$1.Object({
1837
+ query: Type$1.String({ description: "Natural language search query" }),
1838
+ limit: Type$1.Optional(Type$1.Number({ description: "Max results (default 5)" })),
1839
+ tags: Type$1.Optional(Type$1.Array(Type$1.String({
1840
+ minLength: 1,
1841
+ maxLength: DIARY_TAG_MAX_LENGTH
1842
+ }), {
1843
+ description: "Entry must have ALL listed tags (AND). Max 20.",
1844
+ maxItems: 20
1845
+ })),
1846
+ excludeTags: Type$1.Optional(Type$1.Array(Type$1.String({
1847
+ minLength: 1,
1848
+ maxLength: DIARY_TAG_MAX_LENGTH
1849
+ }), {
1850
+ description: "Entry must have NONE of these tags. Max 20.",
1851
+ maxItems: 20
1852
+ })),
1853
+ entryTypes: Type$1.Optional(Type$1.Array(Type$1.String(), {
1854
+ description: "Restrict to these entry types (procedural, semantic, episodic, reflection). Max 4.",
1855
+ maxItems: 4
1856
+ })),
1857
+ taskFilter: Type$1.Optional(Type$1.Object({
1858
+ taskId: Type$1.Optional(Type$1.String()),
1859
+ taskType: Type$1.Optional(Type$1.String()),
1860
+ correlationId: Type$1.Optional(Type$1.String()),
1861
+ attemptN: Type$1.Optional(Type$1.Number())
1862
+ }, { description: "Shorthand: any combination compiles to the matching task:* tags and is merged into the tags filter." }))
1863
+ }),
1864
+ async execute(_id, params) {
1865
+ const { agent, diaryId } = ensureConnected(config);
1866
+ const expandedTags = compileTaskFilterTags(params.taskFilter);
1867
+ const allTags = [...params.tags ?? [], ...expandedTags];
1868
+ const results = await agent.entries.search({
1869
+ diaryId,
1870
+ query: params.query,
1871
+ limit: params.limit ?? 5,
1872
+ ...allTags.length ? { tags: allTags } : {},
1873
+ ...params.excludeTags?.length ? { excludeTags: params.excludeTags } : {},
1874
+ ...params.entryTypes?.length ? { entryTypes: params.entryTypes } : {}
1875
+ });
1876
+ return {
1877
+ content: [{
1878
+ type: "text",
1879
+ text: JSON.stringify(results.results?.map((e) => ({
1880
+ id: e.id,
1881
+ title: e.title,
1882
+ tags: e.tags,
1883
+ importance: e.importance,
1884
+ contentPreview: typeof e.content === "string" ? e.content.slice(0, 200) : void 0
1885
+ })), null, 2)
1886
+ }],
1887
+ details: {}
1888
+ };
1889
+ }
1890
+ });
1891
+ const createEntry = defineTool({
1892
+ name: "moltnet_create_entry",
1893
+ label: "Create MoltNet Diary Entry",
1894
+ description: "Create a new diary entry to record decisions, findings, incidents, or reflections. Before creating an episodic incident entry, first call moltnet_search_entries with the title/root-cause/error/watch-for terms plus taskFilter, tags, or entryTypes filters, then reference close matches instead of creating an isolated duplicate. During an active task, the entry is forced into the task diary and tagged with the task:* provenance namespace (task:id:<id>, task:type:<type>, task:attempt:<n>, plus task:correlation:<id> when set); an explicit diaryId mismatching the task diary is rejected. Use this tool — NOT `moltnet entry create` / `moltnet entry create-signed` via bash. The CLI path bypasses task-tag auto-injection and leaves entries invisible to taskFilter queries.",
1895
+ parameters: Type$1.Object({
1896
+ title: Type$1.String({ description: "Entry title (concise, descriptive)" }),
1897
+ content: Type$1.String({ description: "Entry content (markdown)" }),
1898
+ tags: Type$1.Optional(Type$1.Array(Type$1.String(), { description: "Tags for categorization" })),
1899
+ importance: Type$1.Optional(Type$1.Number({ description: "Importance 1-10 (default 5)" })),
1900
+ entryType: Type$1.Optional(Type$1.Union([
1901
+ Type$1.Literal("episodic"),
1902
+ Type$1.Literal("semantic"),
1903
+ Type$1.Literal("procedural"),
1904
+ Type$1.Literal("reflection")
1905
+ ], { description: "Entry type. Use episodic for incidents, workarounds, bugs, or recurrence evidence; defaults to semantic." })),
1906
+ diaryId: Type$1.Optional(Type$1.String({ description: "Explicit diary id. During an active task, must match the task diary or the call is rejected. Outside a task, overrides the env-derived diary." })),
1907
+ signed: Type$1.Optional(Type$1.Boolean({ description: "Create a content-signed (immutable) entry. The signature is produced on the trusted host through the agent-signing capability; fails when the runtime does not expose it." }))
1908
+ }),
1909
+ async execute(_id, params) {
1910
+ const { agent, diaryId: envDiaryId } = ensureConnected(config);
1911
+ const signer = params.signed ? config.getSigner?.() ?? null : null;
1912
+ if (params.signed && !signer) throw new Error("entries_create: signed entries require the agent-signing capability; create an unsigned entry or run under a runtime that declares it.");
1913
+ const taskCtx = config.getTaskContext?.() ?? null;
1914
+ let targetDiaryId;
1915
+ let autoTags = [];
1916
+ if (taskCtx) {
1917
+ if (params.diaryId && params.diaryId !== taskCtx.diaryId) throw new Error(`entries_create: diaryId "${params.diaryId}" does not match the active task diary "${taskCtx.diaryId}". Entries created during a task must land in the task diary.`);
1918
+ targetDiaryId = taskCtx.diaryId;
1919
+ autoTags = [
1920
+ `task:id:${taskCtx.taskId}`,
1921
+ `task:type:${taskCtx.taskType}`,
1922
+ `task:attempt:${taskCtx.attemptN}`,
1923
+ ...taskCtx.correlationId ? [`task:correlation:${taskCtx.correlationId}`] : []
1924
+ ];
1925
+ } else targetDiaryId = params.diaryId ?? envDiaryId;
1926
+ const userTags = params.tags ?? [];
1927
+ const mergedTags = autoTags.length ? [...autoTags, ...userTags.filter((t) => !autoTags.includes(t))] : userTags;
1928
+ let entry;
1929
+ try {
1930
+ let signingRequestId;
1931
+ if (signer) {
1932
+ const contentCid = computeContentCid(params.entryType ?? "semantic", params.title, params.content, mergedTags);
1933
+ const request = await agent.crypto.signingRequests.create({
1934
+ message: contentCid,
1935
+ verificationMethod: "agent-ed25519"
1936
+ });
1937
+ await signer.signDiaryEntry({ signingRequestId: request.id });
1938
+ signingRequestId = request.id;
1939
+ }
1940
+ entry = await agent.entries.create(targetDiaryId, {
1941
+ title: params.title,
1942
+ content: params.content,
1943
+ tags: mergedTags,
1944
+ importance: params.importance ?? 5,
1945
+ ...params.entryType ? { entryType: params.entryType } : {},
1946
+ ...signingRequestId ? { signingRequestId } : {}
1947
+ });
1948
+ } catch (error) {
1949
+ const message = error instanceof Error ? error.message : String(error);
1950
+ if (taskCtx && /\b403\b|forbidden|not authorized|permission/i.test(message)) {
1951
+ await config.onTaskProvenanceEvent?.("task.provenance.entry_denied", {
1952
+ taskId: taskCtx.taskId,
1953
+ diaryId: targetDiaryId,
1954
+ error: message
1955
+ });
1956
+ throw new Error(`entries_create: the task provenance diary denied this entry. The diary may have moved to another team or its grants may have changed. The task and runtime session remain active; ask a diary manager to restore write access, then retry. (${message})`, { cause: error });
1957
+ }
1958
+ throw error;
1959
+ }
1960
+ return {
1961
+ content: [{
1962
+ type: "text",
1963
+ text: JSON.stringify({
1964
+ id: entry.id,
1965
+ title: entry.title,
1966
+ createdAt: entry.createdAt,
1967
+ diaryId: targetDiaryId,
1968
+ entryType: entry.entryType,
1969
+ importance: entry.importance,
1970
+ tags: mergedTags,
1971
+ signed: signer !== null
1972
+ }, null, 2)
1973
+ }],
1974
+ details: {}
1975
+ };
1976
+ }
1977
+ });
1978
+ const getTask = defineTool({
1979
+ name: "moltnet_get_task",
1980
+ label: "Get MoltNet Task",
1981
+ description: "Fetch a task by ID — the row, including taskType, status, acceptedAttemptN, references, input, timeouts. Use this when you need to inspect another task (e.g. an assess_brief judging a fulfill_brief: fetch the target task here, then list its attempts via moltnet_list_task_attempts to read the producer's output and decide what to investigate).",
1982
+ parameters: Type$1.Object({ taskId: Type$1.String({ description: "Task ID (UUID)." }) }),
1983
+ async execute(_id, params) {
1984
+ const { agent } = ensureConnected(config);
1985
+ const task = await agent.tasks.get(params.taskId);
1986
+ return {
1987
+ content: [{
1988
+ type: "text",
1989
+ text: JSON.stringify(task, null, 2)
1990
+ }],
1991
+ details: {}
1992
+ };
1993
+ }
1994
+ });
1995
+ const listTaskAttempts = defineTool({
1996
+ name: "moltnet_list_task_attempts",
1997
+ label: "List MoltNet Task Attempts",
1998
+ description: "List every attempt made on a task, in attempt-number order. Each attempt carries the claimed agent, status, output, outputCid, and timing. The accepted attempt (whose attemptN matches the parent task's acceptedAttemptN) is the canonical one — its `output` is what consumers should reason against. Earlier failed or timed_out attempts are kept for audit but should not drive downstream decisions.",
1999
+ parameters: Type$1.Object({ taskId: Type$1.String({ description: "Task ID (UUID)." }) }),
2000
+ async execute(_id, params) {
2001
+ const { agent } = ensureConnected(config);
2002
+ const attempts = await agent.tasks.listAttempts(params.taskId);
2003
+ return {
2004
+ content: [{
2005
+ type: "text",
2006
+ text: JSON.stringify(attempts, null, 2)
2007
+ }],
2008
+ details: {}
2009
+ };
2010
+ }
2011
+ });
2012
+ const listTaskMessages = defineTool({
2013
+ name: "moltnet_list_task_messages",
2014
+ label: "List MoltNet Task Attempt Messages",
2015
+ description: "List messages for a specific task attempt. Use this when you need the turn-by-turn execution record behind an accepted attempt — tool calls, text deltas, and error/info events that do not appear in the attempt output alone.",
2016
+ parameters: Type$1.Object({
2017
+ taskId: Type$1.String({ description: "Task ID (UUID)." }),
2018
+ attemptN: Type$1.Integer({
2019
+ minimum: 1,
2020
+ description: "Attempt number to inspect."
2021
+ }),
2022
+ afterSeq: Type$1.Optional(Type$1.Integer({
2023
+ minimum: 0,
2024
+ description: "Optional cursor: only return messages with seq > afterSeq."
2025
+ })),
2026
+ limit: Type$1.Optional(Type$1.Integer({
2027
+ minimum: 1,
2028
+ maximum: 500,
2029
+ description: "Optional maximum messages to return. Defaults to the API value."
2030
+ }))
2031
+ }),
2032
+ async execute(_id, params) {
2033
+ const { agent } = ensureConnected(config);
2034
+ const messages = await agent.tasks.listMessages(params.taskId, params.attemptN, {
2035
+ afterSeq: params.afterSeq,
2036
+ limit: params.limit
2037
+ });
2038
+ return {
2039
+ content: [{
2040
+ type: "text",
2041
+ text: JSON.stringify(messages, null, 2)
2042
+ }],
2043
+ details: {}
2044
+ };
2045
+ }
2046
+ });
2047
+ const uploadTaskArtifact = defineTool({
2048
+ name: "moltnet_upload_task_artifact",
2049
+ label: "Upload MoltNet Task Artifact",
2050
+ description: "Upload a file from the current task workspace as an immutable task artifact. Only available during an active task attempt; the tool attaches the artifact to the active taskId/attemptN and returns metadata including cid, sizeBytes, kind, and title. Use this for large logs, reports, build outputs, screenshots, generated files, or other bytes that should be referenced by CID instead of pasted into structured task output.",
2051
+ parameters: Type$1.Object({
2052
+ filePath: Type$1.String({ description: "Path to a file under the current task workspace. Relative paths are resolved from the workspace root." }),
2053
+ kind: Type$1.String({ description: "Artifact category, e.g. log, report, patch, screenshot, bundle, dataset, trace." }),
2054
+ title: Type$1.String({ description: "Human-readable artifact title, usually the file name." }),
2055
+ contentType: Type$1.Optional(Type$1.String({ description: "MIME type. Defaults to application/octet-stream when omitted." })),
2056
+ contentEncoding: Type$1.Optional(Type$1.String({ description: "Optional content encoding if the file is already encoded, e.g. gzip." }))
2057
+ }),
2058
+ async execute(_id, params) {
2059
+ const { agent, teamId } = ensureConnected(config);
2060
+ if (!teamId) throw new Error("moltnet_upload_task_artifact requires a team context");
2061
+ const taskCtx = config.getTaskContext?.() ?? null;
2062
+ if (!taskCtx) throw new Error("moltnet_upload_task_artifact is only available during an active task attempt");
2063
+ const input = await openWorkspaceArtifactInput(config, config.getHostCwd?.() ?? process.cwd(), params.filePath);
2064
+ if (!input.isFile) throw new Error(`task artifact path is not a file: ${params.filePath}`);
2065
+ const artifact = await agent.tasks.artifacts.upload({
2066
+ taskId: taskCtx.taskId,
2067
+ attemptN: taskCtx.attemptN
2068
+ }, input.stream, {
2069
+ kind: params.kind,
2070
+ title: params.title,
2071
+ contentType: params.contentType ?? "application/octet-stream",
2072
+ contentEncoding: params.contentEncoding
2073
+ }, { teamId });
2074
+ return {
2075
+ content: [{
2076
+ type: "text",
2077
+ text: JSON.stringify({
2078
+ ...artifact,
2079
+ filePath: input.displayPath ?? params.filePath,
2080
+ localSizeBytes: input.sizeBytes ?? null
2081
+ }, null, 2)
2082
+ }],
2083
+ details: {}
2084
+ };
2085
+ }
2086
+ });
2087
+ const listTaskArtifacts = defineTool({
2088
+ name: "moltnet_list_task_artifacts",
2089
+ label: "List MoltNet Task Artifacts",
2090
+ description: "List immutable artifacts attached to a task, including each artifact CID, attempt number, kind, title, content type, size, uploader, and creation time. Use this when judging or continuing work that references task artifacts.",
2091
+ parameters: Type$1.Object({
2092
+ taskId: Type$1.Optional(Type$1.String({ description: "Task ID. Defaults to the active task when running inside a task attempt." })),
2093
+ limit: Type$1.Optional(Type$1.Integer({
2094
+ minimum: 1,
2095
+ maximum: 100,
2096
+ description: "Maximum artifacts to return. Defaults to the server page size."
2097
+ })),
2098
+ cursor: Type$1.Optional(Type$1.String({ description: "Pagination cursor returned by a previous moltnet_list_task_artifacts call." }))
2099
+ }),
2100
+ async execute(_id, params) {
2101
+ const { agent, teamId } = ensureConnected(config);
2102
+ if (!teamId) throw new Error("moltnet_list_task_artifacts requires a team context");
2103
+ const taskId = params.taskId ?? config.getTaskContext?.()?.taskId;
2104
+ if (!taskId) throw new Error("moltnet_list_task_artifacts requires taskId outside an active task");
2105
+ const page = await agent.tasks.artifacts.listPage(taskId, {
2106
+ cursor: params.cursor,
2107
+ limit: params.limit
2108
+ }, { teamId });
2109
+ return {
2110
+ content: [{
2111
+ type: "text",
2112
+ text: JSON.stringify(page, null, 2)
2113
+ }],
2114
+ details: {}
2115
+ };
2116
+ }
2117
+ });
2118
+ const downloadTaskArtifact = defineTool({
2119
+ name: "moltnet_download_task_artifact",
2120
+ label: "Download MoltNet Task Artifact",
2121
+ description: "Download immutable task artifact bytes by taskId and CID into a new file in the current task workspace. Use moltnet_list_task_artifacts first to choose the correct CID. Omit attemptN for a bound input artifact; pass it only to require an artifact from one exact task attempt.",
2122
+ parameters: Type$1.Object({
2123
+ taskId: Type$1.Optional(Type$1.String({ description: "Task ID. Defaults to the active task when running inside a task attempt." })),
2124
+ attemptN: Type$1.Optional(Type$1.Integer({
2125
+ minimum: 1,
2126
+ description: "Attempt number that produced the artifact. Omit for bound input artifacts, which have no producing attempt."
2127
+ })),
2128
+ cid: Type$1.String({
2129
+ minLength: 1,
2130
+ description: "Artifact CID returned by moltnet_list_task_artifacts."
2131
+ }),
2132
+ outputPath: Type$1.String({ description: "New file path under the current task workspace. The tool refuses to overwrite existing files." })
2133
+ }),
2134
+ async execute(_id, params) {
2135
+ const { agent, teamId } = ensureConnected(config);
2136
+ if (!teamId) throw new Error("moltnet_download_task_artifact requires a team context");
2137
+ const taskId = params.taskId ?? config.getTaskContext?.()?.taskId;
2138
+ if (!taskId) throw new Error("moltnet_download_task_artifact requires taskId outside an active task");
2139
+ const cwd = config.getHostCwd?.() ?? process.cwd();
2140
+ const outputPath = await resolveWorkspaceOutputPath(cwd, params.outputPath);
2141
+ const artifactPath = params.attemptN === void 0 ? {
2142
+ taskId,
2143
+ cid: params.cid
2144
+ } : {
2145
+ taskId,
2146
+ attemptN: params.attemptN,
2147
+ cid: params.cid
2148
+ };
2149
+ const download = await agent.tasks.artifacts.download(artifactPath, { teamId });
2150
+ await pipeline(download.stream, createWriteStream(outputPath, { flags: "wx" }));
2151
+ const info = await stat(outputPath);
2152
+ return {
2153
+ content: [{
2154
+ type: "text",
2155
+ text: JSON.stringify({
2156
+ taskId,
2157
+ ...params.attemptN === void 0 ? {} : { attemptN: params.attemptN },
2158
+ cid: params.cid,
2159
+ artifactId: download.artifactId,
2160
+ contentType: download.contentType,
2161
+ contentEncoding: download.contentEncoding,
2162
+ outputPath: path.relative(cwd, outputPath),
2163
+ sizeBytes: info.size
2164
+ }, null, 2)
2165
+ }],
2166
+ details: {}
2167
+ };
2168
+ }
2169
+ });
2170
+ const reviewSessionErrors = defineTool({
2171
+ name: "moltnet_review_session_errors",
2172
+ label: "Review Session Tool Errors",
2173
+ description: "Review tool failures buffered during this session (isError=true results). Use this to decide whether any failures are worth persisting as a diary entry via moltnet_create_entry. Most failures are transient (denied prompts, empty greps, mid-iteration typecheck errors) and should NOT be written to the diary — only persist incidents that represent a real finding (root cause identified, non-obvious workaround, recurring pattern). Pass clear=true to drop the buffer after reviewing.",
2174
+ parameters: Type$1.Object({ clear: Type$1.Optional(Type$1.Boolean({ description: "If true, empty the buffer after returning it. Use once you have decided whether to persist." })) }),
2175
+ async execute(_id, params) {
2176
+ const errors = config.getSessionErrors();
2177
+ const payload = {
2178
+ count: errors.length,
2179
+ errors: errors.map((e) => ({
2180
+ toolName: e.toolName,
2181
+ toolCallId: e.toolCallId,
2182
+ timestamp: new Date(e.timestamp).toISOString(),
2183
+ input: e.input,
2184
+ error: e.error
2185
+ }))
2186
+ };
2187
+ if (params.clear) config.clearSessionErrors();
2188
+ return {
2189
+ content: [{
2190
+ type: "text",
2191
+ text: JSON.stringify(payload, null, 2)
2192
+ }],
2193
+ details: {}
2194
+ };
2195
+ }
2196
+ });
2197
+ const HOST_EXEC_ALLOWED = new Set([
2198
+ "git",
2199
+ "gh",
2200
+ "moltnet"
2201
+ ]);
2202
+ const hostExecBaseEnv = config.hostExecBaseEnv ?? HOST_EXEC_DEFAULT_BASE_ENV;
2203
+ const HOST_EXEC_TIMEOUT_MS = 6e4;
2204
+ return [
2205
+ getPack,
2206
+ createPack,
2207
+ getPackProvenance,
2208
+ renderPack,
2209
+ listRenderedPacks,
2210
+ getRenderedPack,
2211
+ diaryTags,
2212
+ listEntries,
2213
+ getEntry,
2214
+ searchEntries,
2215
+ createEntry,
2216
+ getTask,
2217
+ listTaskAttempts,
2218
+ listTaskMessages,
2219
+ uploadTaskArtifact,
2220
+ listTaskArtifacts,
2221
+ downloadTaskArtifact,
2222
+ reviewSessionErrors,
2223
+ defineTool({
2224
+ name: "moltnet_host_exec",
2225
+ label: "Run command on host (escape hatch — requires user approval)",
2226
+ description: "Runs a command on the HOST machine, outside the sandbox VM. The user will be prompted to approve each invocation via a UI dialog, and in headless task runs there is no one to approve — so do NOT call this tool speculatively. Routine git and gh work — pushing branches, opening pull requests, etc. — runs INSIDE the VM via the normal `bash` tool; use that, not this escape hatch. Credentials are not generally injected into the guest. A runtime may expose an opaque HTTP placeholder that the host proxy can use only for declared destinations, and commit signing is brokered through the `agent-signing` host capability when declared; otherwise authenticated operations are unavailable. Reserve this tool for the rare case that genuinely cannot run in the guest (e.g. reaching a host-only resource the VM has no path to).\n\nAllowed executables: git, gh, moltnet. Runs with a minimal env (PATH, HOME, GIT_CONFIG_GLOBAL, …); pass only non-secret additional vars via the `env` parameter. Every invocation is logged as an auditable host execution.",
2227
+ parameters: Type$1.Object({
2228
+ executable: Type$1.String({ description: "Executable to run (git | gh | moltnet)" }),
2229
+ args: Type$1.Array(Type$1.String(), { description: "Arguments to pass to the executable" }),
2230
+ env: Type$1.Optional(Type$1.Record(Type$1.String(), Type$1.String(), { description: "Additional non-secret environment variables for this invocation. Merged on top of the minimal base env." }))
2231
+ }),
2232
+ async execute(_id, params, _signal, _onUpdate, ctx) {
2233
+ if (!HOST_EXEC_ALLOWED.has(params.executable)) throw new Error(`host_exec: '${params.executable}' is not in the allowed list (${[...HOST_EXEC_ALLOWED].join(", ")}). Extend HOST_EXEC_ALLOWED only after explicit security review.`);
2234
+ if (ctx?.ui && !shouldAutoApproveHostExec(params, config)) {
2235
+ const cmdDisplay = [params.executable, ...params.args].join(" ");
2236
+ if (!await ctx.ui.confirm("Allow host command?", `The agent wants to run on your machine:\n\n ${cmdDisplay}\n\nAllow?`)) throw new Error(`host_exec: user declined approval for: ${cmdDisplay}`);
2237
+ }
2238
+ const cwd = config.getHostCwd?.() ?? process.cwd();
2239
+ const baseEnv = {};
2240
+ for (const key of hostExecBaseEnv) {
2241
+ const val = process.env[key];
2242
+ if (val !== void 0) baseEnv[key] = val;
2243
+ }
2244
+ const mergedEnv = {
2245
+ ...baseEnv,
2246
+ ...params.env ?? {}
2247
+ };
2248
+ let stdout;
2249
+ let stderr = "";
2250
+ try {
2251
+ stdout = execFileSync(params.executable, params.args, {
2252
+ encoding: "utf8",
2253
+ cwd,
2254
+ env: mergedEnv,
2255
+ stdio: [
2256
+ "pipe",
2257
+ "pipe",
2258
+ "pipe"
2259
+ ],
2260
+ timeout: HOST_EXEC_TIMEOUT_MS
2261
+ });
2262
+ } catch (err) {
2263
+ const e = err;
2264
+ stdout = e.stdout ?? "";
2265
+ stderr = e.stderr ?? e.message ?? String(err);
2266
+ }
2267
+ const result = {
2268
+ host_exec: true,
2269
+ executable: params.executable,
2270
+ args: params.args,
2271
+ cwd,
2272
+ stdout: stdout.trimEnd(),
2273
+ stderr: stderr.trimEnd() || void 0
2274
+ };
2275
+ return {
2276
+ content: [{
2277
+ type: "text",
2278
+ text: JSON.stringify(result, null, 2)
2279
+ }],
2280
+ details: {}
2281
+ };
2282
+ }
2283
+ })
2284
+ ];
2285
+ }
2286
+ //#endregion
2287
+ //#region src/otel/index.ts
2288
+ var TRACER_NAME = "@themoltnet/pi-extension/otel";
2289
+ function stripReservedAttrs(attrs) {
2290
+ const out = {};
2291
+ for (const [k, v] of Object.entries(attrs)) {
2292
+ if (k.startsWith("gen_ai.")) continue;
2293
+ out[k] = v;
2294
+ }
2295
+ return out;
2296
+ }
2297
+ function createPiOtelExtension(options = {}) {
2298
+ return function piOtelExtension(pi) {
2299
+ const tracer = trace.getTracer(TRACER_NAME);
2300
+ const extraAttrs = stripReservedAttrs(options.spanAttributes ?? {});
2301
+ let sessionSpan;
2302
+ let sessionCtx = context.active();
2303
+ let turnSpan;
2304
+ let turnCtx = context.active();
2305
+ let currentModel;
2306
+ const toolSpans = /* @__PURE__ */ new Map();
2307
+ function drainToolSpans(reason) {
2308
+ for (const [, entry] of toolSpans) {
2309
+ entry.span.setStatus({
2310
+ code: SpanStatusCode.ERROR,
2311
+ message: reason
2312
+ });
2313
+ entry.span.end();
2314
+ }
2315
+ toolSpans.clear();
2316
+ }
2317
+ function endTurnSpan() {
2318
+ if (!turnSpan) return;
2319
+ drainToolSpans("tool span not closed before turn end");
2320
+ turnSpan.end();
2321
+ turnSpan = void 0;
2322
+ turnCtx = sessionCtx;
2323
+ }
2324
+ function endSessionSpan() {
2325
+ drainToolSpans("tool span not closed before session shutdown");
2326
+ endTurnSpan();
2327
+ if (sessionSpan) {
2328
+ sessionSpan.setStatus({ code: SpanStatusCode.OK });
2329
+ sessionSpan.end();
2330
+ sessionSpan = void 0;
2331
+ sessionCtx = context.active();
2332
+ options.onSessionContextChange?.(void 0);
2333
+ }
2334
+ currentModel = void 0;
2335
+ }
2336
+ pi.on("session_start", (event, ctx) => {
2337
+ endSessionSpan();
2338
+ const agentName = options.agentName ?? "pi";
2339
+ const parentContext = options.sessionParentContext ?? context.active();
2340
+ sessionSpan = tracer.startSpan(`invoke_agent ${agentName}`, { attributes: {
2341
+ ...extraAttrs,
2342
+ "gen_ai.operation.name": "invoke_agent",
2343
+ "gen_ai.agent.name": agentName,
2344
+ "session.reason": event.reason,
2345
+ "session.cwd": ctx.cwd
2346
+ } }, parentContext);
2347
+ sessionCtx = trace.setSpan(parentContext, sessionSpan);
2348
+ options.onSessionContextChange?.(sessionCtx);
2349
+ turnCtx = sessionCtx;
2350
+ });
2351
+ pi.on("session_shutdown", () => {
2352
+ endSessionSpan();
2353
+ });
2354
+ pi.on("model_select", (event) => {
2355
+ currentModel = {
2356
+ provider: event.model.provider,
2357
+ id: event.model.id
2358
+ };
2359
+ if (sessionSpan) {
2360
+ sessionSpan.setAttribute("gen_ai.request.model", event.model.id);
2361
+ sessionSpan.setAttribute("gen_ai.provider.name", event.model.provider);
2362
+ }
2363
+ });
2364
+ pi.on("turn_start", (event) => {
2365
+ if (!sessionSpan) return;
2366
+ const modelLabel = currentModel?.id ?? "unknown";
2367
+ const turnParentContext = options.getTurnParentContext?.() ?? sessionCtx;
2368
+ turnSpan = tracer.startSpan(`chat ${modelLabel}`, { attributes: {
2369
+ ...extraAttrs,
2370
+ "gen_ai.operation.name": "chat",
2371
+ "gen_ai.request.model": currentModel?.id ?? "unknown",
2372
+ "gen_ai.provider.name": currentModel?.provider ?? "unknown",
2373
+ "turn.index": event.turnIndex
2374
+ } }, turnParentContext);
2375
+ turnCtx = trace.setSpan(turnParentContext, turnSpan);
2376
+ });
2377
+ pi.on("turn_end", (event) => {
2378
+ if (!turnSpan) return;
2379
+ const usage = extractUsage(event.message);
2380
+ if (usage) {
2381
+ turnSpan.setAttribute("gen_ai.usage.input_tokens", usage.input);
2382
+ turnSpan.setAttribute("gen_ai.usage.output_tokens", usage.output);
2383
+ }
2384
+ turnSpan.setAttribute("turn.tool_results", event.toolResults?.length ?? 0);
2385
+ turnSpan.setStatus({ code: SpanStatusCode.OK });
2386
+ endTurnSpan();
2387
+ });
2388
+ pi.on("tool_execution_start", (event) => {
2389
+ const parentCtx = turnSpan ? turnCtx : sessionCtx;
2390
+ const span = tracer.startSpan(`execute_tool ${event.toolName}`, { attributes: {
2391
+ ...extraAttrs,
2392
+ "gen_ai.operation.name": "execute_tool",
2393
+ "gen_ai.tool.name": event.toolName,
2394
+ "gen_ai.tool.call.id": event.toolCallId
2395
+ } }, parentCtx);
2396
+ toolSpans.set(event.toolCallId, {
2397
+ span,
2398
+ startedAt: Date.now()
2399
+ });
2400
+ });
2401
+ pi.on("tool_execution_end", (event) => {
2402
+ const entry = toolSpans.get(event.toolCallId);
2403
+ if (!entry) return;
2404
+ const durationMs = Date.now() - entry.startedAt;
2405
+ entry.span.setAttribute("tool.duration_ms", durationMs);
2406
+ if (event.isError) {
2407
+ entry.span.setAttribute("error.type", "tool_execution_error");
2408
+ entry.span.setStatus({
2409
+ code: SpanStatusCode.ERROR,
2410
+ message: "tool execution failed"
2411
+ });
2412
+ } else entry.span.setStatus({ code: SpanStatusCode.OK });
2413
+ entry.span.end();
2414
+ toolSpans.delete(event.toolCallId);
2415
+ });
2416
+ };
2417
+ }
2418
+ function extractUsage(message) {
2419
+ if (!message || typeof message !== "object" || !("usage" in message) || !("role" in message)) return null;
2420
+ const msg = message;
2421
+ if (msg.role !== "assistant" || !msg.usage) return null;
2422
+ return {
2423
+ input: msg.usage.input ?? 0,
2424
+ output: msg.usage.output ?? 0
2425
+ };
2426
+ }
2368
2427
  //#endregion
2369
- //#region ../models/src/problem-details.ts
2370
- var ProblemCodeSchema = Type.Union([
2371
- Type.Literal("UNAUTHORIZED"),
2372
- Type.Literal("FORBIDDEN"),
2373
- Type.Literal("NOT_FOUND"),
2374
- Type.Literal("CONFLICT"),
2375
- Type.Literal("UNSUPPORTED_MEDIA_TYPE"),
2376
- Type.Literal("VALIDATION_FAILED"),
2377
- Type.Literal("INVALID_CHALLENGE"),
2378
- Type.Literal("INVALID_SIGNATURE"),
2379
- Type.Literal("RATE_LIMIT_EXCEEDED"),
2380
- Type.Literal("SERIALIZATION_EXHAUSTED"),
2381
- Type.Literal("SIGNING_REQUEST_EXPIRED"),
2382
- Type.Literal("SIGNING_REQUEST_ALREADY_COMPLETED"),
2383
- Type.Literal("SIGNING_REQUEST_LIMIT_REACHED"),
2384
- Type.Literal("REGISTRATION_FAILED"),
2385
- Type.Literal("UPSTREAM_ERROR"),
2386
- Type.Literal("SERVICE_UNAVAILABLE"),
2387
- Type.Literal("INTERNAL_SERVER_ERROR"),
2388
- Type.Literal("TEAM_PERSONAL_IMMUTABLE"),
2389
- Type.Literal("TEAM_NOT_ACTIVE"),
2390
- Type.Literal("INVITE_EXPIRED"),
2391
- Type.Literal("INVITE_EXHAUSTED"),
2392
- Type.Literal("TEAM_LAST_OWNER"),
2393
- Type.Literal("TEAM_ALREADY_ACTIVE"),
2394
- Type.Literal("TEAM_NOT_FOUNDING"),
2395
- Type.Literal("FOUNDING_ALREADY_ACCEPTED"),
2396
- Type.Literal("DIARY_TRANSFER_PENDING"),
2397
- Type.Literal("DIARY_TRANSFER_NOT_FOUND"),
2398
- Type.Literal("DIARY_TRANSFER_ALREADY_RESOLVED")
2399
- ]);
2400
- var ProblemDetailsSchema = Type.Object({
2401
- type: Type.String({ format: "uri" }),
2402
- title: Type.String(),
2403
- status: Type.Integer({
2404
- minimum: 100,
2405
- maximum: 599
2406
- }),
2407
- code: ProblemCodeSchema,
2408
- detail: Type.Optional(Type.String()),
2409
- instance: Type.Optional(Type.String()),
2410
- retryAfter: Type.Optional(Type.Integer({
2411
- minimum: 0,
2412
- description: "Non-negative delay in seconds before retrying, matching the Retry-After response header when present."
2413
- }))
2414
- }, {
2415
- $id: "ProblemDetails",
2416
- additionalProperties: true
2417
- });
2418
- Type.Object({
2419
- field: Type.String(),
2420
- message: Type.String(),
2421
- code: Type.Optional(Type.String())
2422
- }, {
2423
- $id: "ValidationError",
2424
- additionalProperties: false
2425
- });
2426
- Type.Object({
2427
- resource: Type.String(),
2428
- id: Type.Optional(Type.String({ format: "uuid" })),
2429
- keys: Type.Optional(Type.Record(Type.String(), Type.String()))
2430
- }, {
2431
- $id: "ConflictTarget",
2432
- additionalProperties: false
2433
- });
2434
- Type.Object({
2435
- constraint: Type.Optional(Type.String()),
2436
- target: Type.Optional(Type.Ref("ConflictTarget"))
2437
- }, {
2438
- $id: "ConflictError",
2439
- additionalProperties: false
2440
- });
2441
- var ConflictProblemDetailsSchema = Type.Intersect([ProblemDetailsSchema, Type.Object({ conflict: Type.Ref("ConflictError") })], { $id: "ConflictProblemDetails" });
2442
- Type.Object({
2443
- type: Type.String(),
2444
- severity: Type.Number(),
2445
- match: Type.String()
2446
- }, {
2447
- $id: "InjectionThreat",
2448
- additionalProperties: false
2449
- });
2450
- Type.Intersect([ConflictProblemDetailsSchema, Type.Object({ flagged: Type.Optional(Type.Array(Type.Object({
2451
- id: Type.String({ format: "uuid" }),
2452
- threats: Type.Array(Type.Ref("InjectionThreat"))
2453
- }, { additionalProperties: false }))) })], { $id: "InjectionConflictProblemDetails" });
2454
- Type.Intersect([ProblemDetailsSchema, Type.Object({ errors: Type.Array(Type.Ref("ValidationError")) })], { $id: "ValidationProblemDetails" });
2455
- Type.Union([
2456
- Type.Literal("pack"),
2457
- Type.Literal("entry"),
2458
- Type.Literal("rendered_pack")
2459
- ]);
2460
- var ProvenanceGraphEdgeKindSchema = Type.Union([
2461
- Type.Literal("includes"),
2462
- Type.Literal("supersedes"),
2463
- Type.Literal("rendered_from")
2464
- ]);
2465
- var ProvenanceGraphPackMetaSchema = Type.Object({
2466
- packId: UuidSchema,
2467
- diaryId: UuidSchema,
2468
- packCid: Type.String(),
2469
- packType: Type.String(),
2470
- packCodec: Type.String(),
2471
- pinned: Type.Boolean(),
2472
- createdAt: TimestampSchema,
2473
- expiresAt: Type.Union([TimestampSchema, Type.Null()]),
2474
- supersedesPackId: Type.Union([UuidSchema, Type.Null()])
2428
+ //#region src/runtime/model-options-extension.ts
2429
+ function hasPiModelOptions(options) {
2430
+ return options.temperature !== void 0 && options.temperature !== null || options.topP !== void 0 && options.topP !== null || options.topK !== void 0 && options.topK !== null || options.maxOutputTokens !== void 0 && options.maxOutputTokens !== null;
2431
+ }
2432
+ function createPiModelOptionsExtension(options) {
2433
+ return function piModelOptionsExtension(pi) {
2434
+ pi.on("before_provider_request", (event) => {
2435
+ return applyPiModelOptions(event.payload, options);
2436
+ });
2437
+ };
2438
+ }
2439
+ function applyPiModelOptions(payload, options) {
2440
+ if (!isRecord$1(payload)) return void 0;
2441
+ if (!hasPiModelOptions(options)) return void 0;
2442
+ if (isGooglePayload(payload)) {
2443
+ const config = isRecord$1(payload.config) ? payload.config : {};
2444
+ return {
2445
+ ...payload,
2446
+ config: applyConfigOptions(config, options)
2447
+ };
2448
+ }
2449
+ if (isBedrockPayload(payload)) {
2450
+ const inferenceConfig = isRecord$1(payload.inferenceConfig) ? payload.inferenceConfig : {};
2451
+ return {
2452
+ ...payload,
2453
+ inferenceConfig: applyBedrockOptions(inferenceConfig, options)
2454
+ };
2455
+ }
2456
+ return applyTopLevelOptions(payload, options);
2457
+ }
2458
+ function applyConfigOptions(config, options) {
2459
+ return {
2460
+ ...config,
2461
+ ...options.temperature !== void 0 && options.temperature !== null ? { temperature: options.temperature } : {},
2462
+ ...options.topP !== void 0 && options.topP !== null ? { topP: options.topP } : {},
2463
+ ...options.topK !== void 0 && options.topK !== null ? { topK: options.topK } : {},
2464
+ ...options.maxOutputTokens !== void 0 && options.maxOutputTokens !== null ? { maxOutputTokens: options.maxOutputTokens } : {}
2465
+ };
2466
+ }
2467
+ function applyBedrockOptions(inferenceConfig, options) {
2468
+ return {
2469
+ ...inferenceConfig,
2470
+ ...options.temperature !== void 0 && options.temperature !== null ? { temperature: options.temperature } : {},
2471
+ ...options.topP !== void 0 && options.topP !== null ? { topP: options.topP } : {},
2472
+ ...options.maxOutputTokens !== void 0 && options.maxOutputTokens !== null ? { maxTokens: options.maxOutputTokens } : {}
2473
+ };
2474
+ }
2475
+ function applyTopLevelOptions(payload, options) {
2476
+ const reasoningEnabled = hasActiveThinking(payload.thinking) || "reasoning" in payload || "reasoning_effort" in payload;
2477
+ const next = { ...payload };
2478
+ if (options.temperature !== void 0 && options.temperature !== null && !reasoningEnabled) next.temperature = options.temperature;
2479
+ if (options.topP !== void 0 && options.topP !== null && !reasoningEnabled) next.top_p = options.topP;
2480
+ if (options.topK !== void 0 && options.topK !== null && !reasoningEnabled && isAnthropicPayload(next)) next.top_k = options.topK;
2481
+ if (options.maxOutputTokens !== void 0 && options.maxOutputTokens !== null) {
2482
+ const maxOutputTokens = options.maxOutputTokens;
2483
+ if ("max_output_tokens" in next || isResponsesPayload(next)) next.max_output_tokens = maxOutputTokens;
2484
+ else if ("max_completion_tokens" in next) next.max_completion_tokens = maxOutputTokens;
2485
+ else if ("maxTokens" in next) next.maxTokens = maxOutputTokens;
2486
+ else next.max_tokens = maxOutputTokens;
2487
+ }
2488
+ return next;
2489
+ }
2490
+ function isGooglePayload(payload) {
2491
+ return "contents" in payload && ("config" in payload || "model" in payload);
2492
+ }
2493
+ function isBedrockPayload(payload) {
2494
+ return "inferenceConfig" in payload || "additionalModelRequestFields" in payload;
2495
+ }
2496
+ function isResponsesPayload(payload) {
2497
+ return "input" in payload && !("messages" in payload);
2498
+ }
2499
+ function isAnthropicPayload(payload) {
2500
+ return "anthropic_version" in payload;
2501
+ }
2502
+ function hasActiveThinking(value) {
2503
+ if (!isRecord$1(value)) return false;
2504
+ const type = value.type;
2505
+ return type !== "disabled" && type !== "off" && type !== false;
2506
+ }
2507
+ function isRecord$1(value) {
2508
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2509
+ }
2510
+ //#endregion
2511
+ //#region src/runtime/agent-session-factory.ts
2512
+ var NO_SKILLS = () => ({
2513
+ skills: [],
2514
+ diagnostics: []
2475
2515
  });
2476
2516
  /**
2477
- * Discriminated creator embedded inside provenance-node response
2478
- * payloads. Re-uses the shared `PrincipalIdentitySchemaInline` (the
2479
- * `$id`-less twin) embedding the named `PrincipalIdentitySchema`
2480
- * here would clash with the top-level registration via @fastify/swagger
2481
- * (`reference "PrincipalIdentity" resolves to more than one schema`).
2517
+ * Construct an `AgentSession`. By default it is in-memory; callers may opt
2518
+ * parent sessions into daemon-owned file persistence via `sessionPersistence`.
2519
+ * The caller is responsible for eventually invoking `session.prompt(...)` and
2520
+ * for tearing down the helper does no lifecycle management beyond
2521
+ * construction.
2482
2522
  */
2483
- var ProvenanceGraphCreatorSchema = PrincipalIdentitySchemaInline;
2484
- var ProvenanceGraphEntryMetaSchema = Type.Object({
2485
- entryId: UuidSchema,
2486
- diaryId: UuidSchema,
2487
- entryType: EntryTypeSchema,
2488
- contentHash: Type.Union([Type.String(), Type.Null()]),
2489
- createdAt: TimestampSchema,
2490
- updatedAt: TimestampSchema,
2491
- signed: Type.Boolean(),
2492
- title: Type.Union([Type.String(), Type.Null()]),
2493
- tags: Type.Array(Type.String()),
2494
- creator: Type.Optional(ProvenanceGraphCreatorSchema)
2495
- });
2496
- var ProvenanceGraphPackNodeSchema = Type.Object({
2497
- id: Type.String(),
2498
- kind: Type.Literal("pack"),
2499
- label: Type.String(),
2500
- cid: Type.Union([Type.String(), Type.Null()]),
2501
- meta: Type.Intersect([ProvenanceGraphPackMetaSchema, Type.Object({ creator: Type.Optional(ProvenanceGraphCreatorSchema) })])
2502
- });
2503
- var ProvenanceGraphEntryNodeSchema = Type.Object({
2504
- id: Type.String(),
2505
- kind: Type.Literal("entry"),
2506
- label: Type.String(),
2507
- cid: Type.Union([Type.String(), Type.Null()]),
2508
- meta: ProvenanceGraphEntryMetaSchema
2509
- });
2510
- var ProvenanceGraphRenderedPackMetaSchema = Type.Object({
2511
- renderedPackId: UuidSchema,
2512
- sourcePackId: UuidSchema,
2513
- diaryId: UuidSchema,
2514
- packCid: Type.String(),
2515
- renderMethod: Type.String(),
2516
- totalTokens: Type.Number(),
2517
- pinned: Type.Boolean(),
2518
- createdAt: TimestampSchema,
2519
- expiresAt: Type.Union([TimestampSchema, Type.Null()]),
2520
- creator: Type.Optional(ProvenanceGraphCreatorSchema)
2521
- });
2522
- var ProvenanceGraphRenderedPackNodeSchema = Type.Object({
2523
- id: Type.String(),
2524
- kind: Type.Literal("rendered_pack"),
2525
- label: Type.String(),
2526
- cid: Type.Union([Type.String(), Type.Null()]),
2527
- meta: ProvenanceGraphRenderedPackMetaSchema
2528
- });
2529
- var ProvenanceGraphNodeSchema = Type.Union([
2530
- ProvenanceGraphPackNodeSchema,
2531
- ProvenanceGraphEntryNodeSchema,
2532
- ProvenanceGraphRenderedPackNodeSchema
2533
- ]);
2534
- var ProvenanceGraphEdgeSchema = Type.Object({
2535
- id: Type.String(),
2536
- from: Type.String(),
2537
- to: Type.String(),
2538
- kind: ProvenanceGraphEdgeKindSchema,
2539
- label: Type.Optional(Type.String()),
2540
- meta: Type.Optional(Type.Record(Type.String(), Type.Union([
2541
- Type.String(),
2542
- Type.Number(),
2543
- Type.Boolean(),
2544
- Type.Null()
2545
- ])))
2546
- });
2547
- var ProvenanceGraphMetadataSchema = Type.Object({
2548
- format: Type.Literal("moltnet.provenance-graph/v1"),
2549
- generatedAt: TimestampSchema,
2550
- rootNodeId: Type.String(),
2551
- rootPackId: UuidSchema,
2552
- depth: Type.Number({ minimum: 0 })
2553
- });
2554
- Type.Object({
2555
- metadata: ProvenanceGraphMetadataSchema,
2556
- nodes: Type.Array(ProvenanceGraphNodeSchema),
2557
- edges: Type.Array(ProvenanceGraphEdgeSchema)
2558
- }, { $id: "ProvenanceGraph" });
2559
- //#endregion
2560
- //#region ../models/src/signer-constraint.ts
2561
- var SIGNER_CONSTRAINT_TYPE = {
2562
- Human: "human",
2563
- TeamRole: "team-role",
2564
- Group: "group"
2565
- };
2566
- Type.Union([
2567
- Type.Object({
2568
- type: Type.Literal(SIGNER_CONSTRAINT_TYPE.Human),
2569
- id: Type.String({ format: "uuid" })
2570
- }),
2571
- Type.Object({
2572
- type: Type.Literal(SIGNER_CONSTRAINT_TYPE.TeamRole),
2573
- id: TeamRoleSchema
2574
- }),
2575
- Type.Object({
2576
- type: Type.Literal(SIGNER_CONSTRAINT_TYPE.Group),
2577
- id: Type.String({ format: "uuid" })
2578
- })
2579
- ]);
2580
- //#endregion
2581
- //#region ../models/src/signer-protocol.ts
2582
- function schemaRef(schema) {
2583
- const id = schemaId(schema);
2584
- return Type.Ref(id);
2523
+ async function buildAgentSession(args) {
2524
+ const piOtelExtension = createPiOtelExtension({
2525
+ agentName: args.agentName,
2526
+ spanAttributes: args.otelSpanAttrs,
2527
+ sessionParentContext: args.otelSessionParentContext,
2528
+ getTurnParentContext: args.getOtelTurnParentContext,
2529
+ onSessionContextChange: args.onOtelSessionContextChange
2530
+ });
2531
+ const modelOptions = {
2532
+ temperature: args.temperature,
2533
+ topP: args.topP,
2534
+ topK: args.topK,
2535
+ maxOutputTokens: args.maxOutputTokens
2536
+ };
2537
+ const extensionFactories = [
2538
+ piOtelExtension,
2539
+ ...hasPiModelOptions(modelOptions) ? [createPiModelOptionsExtension(modelOptions)] : [],
2540
+ ...args.extraExtensionFactories ?? []
2541
+ ];
2542
+ const resourceLoader = new DefaultResourceLoader({
2543
+ cwd: args.cwdPath,
2544
+ agentDir: args.piAuthDir,
2545
+ extensionFactories,
2546
+ appendSystemPrompt: args.appendSystemPrompt,
2547
+ skillsOverride: args.skillsOverride ?? NO_SKILLS
2548
+ });
2549
+ await resourceLoader.reload();
2550
+ const sessionManager = args.sessionPersistence ? await resolvePersistentSessionManager({
2551
+ cwd: args.cwdPath,
2552
+ sessionDir: args.sessionPersistence.sessionDir,
2553
+ forkFromSessionPath: args.sessionPersistence.forkFromSessionPath
2554
+ }) : SessionManager.inMemory(args.cwdPath);
2555
+ return (await createAgentSession({
2556
+ agentDir: args.piAuthDir,
2557
+ cwd: args.cwdPath,
2558
+ model: args.modelHandle,
2559
+ ...args.modelRuntime ? { modelRuntime: args.modelRuntime } : {},
2560
+ thinkingLevel: args.thinkingLevel ?? void 0,
2561
+ tools: args.tools,
2562
+ customTools: args.customTools,
2563
+ sessionManager,
2564
+ resourceLoader
2565
+ })).session;
2585
2566
  }
2586
- function schemaId(schema) {
2587
- const id = schema.$id;
2588
- if (typeof id !== "string" || id.length === 0) throw new Error("Signer protocol schemas must have an identifier");
2589
- return id;
2567
+ async function resolvePersistentSessionManager(args) {
2568
+ if (args.forkFromSessionPath) return SessionManager.forkFrom(args.forkFromSessionPath, args.cwd, args.sessionDir);
2569
+ await SessionManager.list(args.cwd, args.sessionDir);
2570
+ return SessionManager.continueRecent(args.cwd, args.sessionDir);
2590
2571
  }
2591
- var SignerBase64UrlSchema = PreviewSignBase64UrlSchema;
2592
- var SignerUuidSchema = Type.String({
2593
- $id: "SignerUuid",
2594
- pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
2595
- });
2596
- var SignerOperationSchema = Type.Union([
2597
- Type.Literal("credential-enrollment"),
2598
- Type.Literal("credential-registration"),
2599
- Type.Literal("signing-request")
2600
- ], { $id: "SignerOperation" });
2601
- var SignerChallengeOperationSchema = PreviewSignChallengeOperationSchema;
2602
- var SignerPreviewSignPublicMaterialSchema = PreviewSignPublicMaterialSchema;
2603
- var SignerPreviewSignChallengeValueSchema = PreviewSignChallengeValueSchema;
2604
- var SignerProblemSchema = Type.Object({
2605
- code: Type.String({ minLength: 1 }),
2606
- message: Type.String({ minLength: 1 })
2607
- }, {
2608
- $id: "SignerProblem",
2609
- additionalProperties: false
2610
- });
2611
- var SignerCeremonyParamsSchema = Type.Object({ ceremonyId: Type.Unsafe(schemaRef(SignerBase64UrlSchema)) }, {
2612
- $id: "SignerCeremonyParams",
2613
- additionalProperties: false
2614
- });
2615
- var SignerSessionSchema = Type.Object({
2616
- version: Type.Literal(1),
2617
- token: Type.Unsafe(schemaRef(SignerBase64UrlSchema)),
2618
- expiresAt: Type.String()
2619
- }, {
2620
- $id: "SignerSession",
2621
- additionalProperties: false
2622
- });
2623
- var SignerEnrollmentCeremonyRequestSchema = Type.Object({
2624
- version: Type.Literal(1),
2625
- operation: Type.Literal("credential-enrollment"),
2626
- label: Type.String({
2572
+ //#endregion
2573
+ //#region ../crypto-service/src/json-cid.ts
2574
+ /**
2575
+ * Generic JSON CID — CIDv1 for arbitrary JSON-serialisable values.
2576
+ *
2577
+ * Uses the dag-json codec and sha2-256, producing a base32lower CIDv1.
2578
+ * Suitable for content-addressing task inputs, schema objects, and other
2579
+ * JSON payloads that don't need diary-entry canonical normalisation.
2580
+ */
2581
+ async function computeJsonCid(value) {
2582
+ const bytes = json.encode(value);
2583
+ const hash = await sha256$1.digest(bytes);
2584
+ return CID.create(1, json.code, hash).toString();
2585
+ }
2586
+ //#endregion
2587
+ //#region src/config.ts
2588
+ /** Resolve Pi's host-side auth/config directory from process configuration. */
2589
+ function resolvePiCodingAgentDir() {
2590
+ return process.env["PI_CODING_AGENT_DIR"] ?? path.join(homedir(), ".pi", "agent");
2591
+ }
2592
+ //#endregion
2593
+ //#region ../runtime-profiles/src/context.ts
2594
+ /**
2595
+ * How an executor delivers a context entry to its underlying LLM.
2596
+ * V1 bindings only; Tier-2 (reference_file, mcp_resource, imported_file,
2597
+ * tool_response_seed, additional_context_hook) ship in a later slice.
2598
+ */
2599
+ var CONTEXT_BINDINGS = [
2600
+ "skill",
2601
+ "context_inline",
2602
+ "prompt_prefix",
2603
+ "user_inline"
2604
+ ];
2605
+ /** Maximum UTF-16 code units accepted in one ContextRef content field. */
2606
+ var CONTEXT_REF_MAX_CONTENT_LENGTH = 65536;
2607
+ var ContextBinding = Type.Unsafe(Type.Union(CONTEXT_BINDINGS.map((binding) => Type.Literal(binding)), { $id: "ContextBinding" }));
2608
+ /**
2609
+ * One context entry. Bytes are inlined: the proposer chose them, and the
2610
+ * task's `inputCid` already pins the entire input — including
2611
+ * `context[]` — so we don't need a separate per-entry hash, fetcher, or
2612
+ * flagged-content gate. Tasks reference rendered packs (or any other
2613
+ * external content) by copying their bytes into `content` at task
2614
+ * creation time.
2615
+ *
2616
+ * - `slug` — short identifier the daemon uses to disambiguate
2617
+ * entries. For `skill` binding it becomes the directory
2618
+ * name under the runtime's skill discovery path. Must be
2619
+ * kebab-case-safe (alphanumeric + dashes/underscores).
2620
+ * - `binding` — how the bytes are delivered to the LLM (see above).
2621
+ * - `content` — UTF-8 text. Capped at 65,536 UTF-16 code units per
2622
+ * entry; total per-task context bytes are bounded by the
2623
+ * soft `maxItems` cap and per-binding daemon limits.
2624
+ * Raised from 32 KiB in 2026-05 — protocol-heavy operator
2625
+ * skills (e.g. `.claude/skills/legreffier/SKILL.md`) ship
2626
+ * at ~35 KiB inline, and the original cap was sized for
2627
+ * short example skills, not the kind of skill the eval
2628
+ * substrate is dogfooded on (#943, #823).
2629
+ */
2630
+ var ContextRef = Type.Object({
2631
+ slug: Type.String({
2627
2632
  minLength: 1,
2628
- maxLength: 255
2633
+ maxLength: 64,
2634
+ pattern: "^[a-zA-Z0-9_-]+$"
2629
2635
  }),
2630
- teamId: Type.Unsafe(schemaRef(SignerUuidSchema))
2631
- }, {
2632
- $id: "SignerEnrollmentCeremonyRequest",
2633
- additionalProperties: false
2634
- });
2635
- var SignerChallengeCeremonyRequestSchema = Type.Object({
2636
- version: Type.Literal(1),
2637
- operation: Type.Unsafe(schemaRef(SignerChallengeOperationSchema)),
2638
- resourceId: Type.Unsafe(schemaRef(SignerUuidSchema)),
2639
- challenge: Type.Unsafe(schemaRef(SignerPreviewSignChallengeValueSchema))
2640
- }, {
2641
- $id: "SignerChallengeCeremonyRequest",
2642
- additionalProperties: false
2643
- });
2644
- var SignerCeremonyRequestSchema = Type.Union([Type.Unsafe(schemaRef(SignerEnrollmentCeremonyRequestSchema)), Type.Unsafe(schemaRef(SignerChallengeCeremonyRequestSchema))], { $id: "SignerCeremonyRequest" });
2645
- var SignerCeremonySchema = Type.Object({
2646
- version: Type.Literal(1),
2647
- id: Type.Unsafe(schemaRef(SignerBase64UrlSchema)),
2648
- operation: Type.Unsafe(schemaRef(SignerOperationSchema)),
2649
- approvalUrl: Type.String(),
2650
- expiresAt: Type.String()
2636
+ binding: ContextBinding,
2637
+ content: Type.String({
2638
+ minLength: 1,
2639
+ maxLength: CONTEXT_REF_MAX_CONTENT_LENGTH
2640
+ })
2651
2641
  }, {
2652
- $id: "SignerCeremony",
2642
+ $id: "ContextRef",
2653
2643
  additionalProperties: false
2654
2644
  });
2655
- var SignerPendingResultSchema = Type.Object({
2656
- version: Type.Literal(1),
2657
- status: Type.Literal("pending"),
2658
- operation: Type.Unsafe(schemaRef(SignerOperationSchema))
2659
- }, {
2660
- $id: "SignerPendingResult",
2661
- additionalProperties: false
2645
+ /** Reusable input fragment for any task type. Soft cap at 5 items. */
2646
+ var TaskContext = Type.Array(ContextRef, {
2647
+ $id: "TaskContext",
2648
+ maxItems: 5
2662
2649
  });
2663
- var SignerEnrollmentResultSchema = Type.Object({
2664
- version: Type.Literal(1),
2665
- status: Type.Literal("completed"),
2666
- operation: Type.Literal("credential-enrollment"),
2667
- publicMaterial: Type.Unsafe(schemaRef(SignerPreviewSignPublicMaterialSchema))
2668
- }, {
2669
- $id: "SignerEnrollmentResult",
2670
- additionalProperties: false
2650
+ //#endregion
2651
+ //#region ../runtime-profiles/src/runtime-models.ts
2652
+ /**
2653
+ * Runtime model catalog: a list of supported provider/model couples that
2654
+ * MoltNet daemons can target. Backed by the `runtime_models` table.
2655
+ *
2656
+ * Scope is intrinsic to the row:
2657
+ * - `teamId == null` => global entry (MoltNet-seeded, read-only to most callers)
2658
+ * - `teamId != null` => team-owned custom entry
2659
+ *
2660
+ * The REST API exposes a single shape regardless of scope; the team header
2661
+ * gates which rows are returned.
2662
+ */
2663
+ var RuntimeModelProvider = Type.String({
2664
+ minLength: 1,
2665
+ maxLength: 100,
2666
+ pattern: "^[a-zA-Z0-9][a-zA-Z0-9._-]{0,99}$"
2671
2667
  });
2672
- var SignerReceiptSchema = PreviewSignReceiptValueSchema;
2673
- var SignerSignatureResultSchema = Type.Object({
2674
- version: Type.Literal(1),
2675
- status: Type.Literal("completed"),
2676
- operation: Type.Unsafe(schemaRef(SignerChallengeOperationSchema)),
2677
- receipt: Type.Unsafe(schemaRef(SignerReceiptSchema))
2678
- }, {
2679
- $id: "SignerSignatureResult",
2680
- additionalProperties: false
2668
+ var RuntimeModelName = Type.String({
2669
+ minLength: 1,
2670
+ maxLength: 200,
2671
+ pattern: "^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,199}$"
2681
2672
  });
2682
- var SignerFailedResultSchema = Type.Object({
2683
- version: Type.Literal(1),
2684
- status: Type.Literal("failed"),
2685
- operation: Type.Unsafe(schemaRef(SignerOperationSchema)),
2686
- code: Type.String(),
2687
- message: Type.String()
2673
+ var RuntimeModelCapabilities = Type.Record(Type.String({
2674
+ minLength: 1,
2675
+ maxLength: 64
2676
+ }), Type.Union([
2677
+ Type.Boolean(),
2678
+ Type.Number(),
2679
+ Type.String({ maxLength: 256 })
2680
+ ]));
2681
+ Type.Object({
2682
+ id: Type.String({ format: "uuid" }),
2683
+ teamId: Type.Union([Type.String({ format: "uuid" }), Type.Null()]),
2684
+ provider: RuntimeModelProvider,
2685
+ model: RuntimeModelName,
2686
+ displayName: Type.Union([Type.String({ maxLength: 200 }), Type.Null()]),
2687
+ description: Type.Union([Type.String({ maxLength: 4096 }), Type.Null()]),
2688
+ capabilities: RuntimeModelCapabilities,
2689
+ isActive: Type.Boolean(),
2690
+ createdByAgentId: Type.Union([Type.String({ format: "uuid" }), Type.Null()]),
2691
+ createdByHumanId: Type.Union([Type.String({ format: "uuid" }), Type.Null()]),
2692
+ createdAt: Type.String({ format: "date-time" }),
2693
+ updatedAt: Type.String({ format: "date-time" })
2688
2694
  }, {
2689
- $id: "SignerFailedResult",
2695
+ $id: "RuntimeModel",
2690
2696
  additionalProperties: false
2691
2697
  });
2692
- var SignerCeremonyResultSchema = Type.Union([
2693
- Type.Unsafe(schemaRef(SignerPendingResultSchema)),
2694
- Type.Unsafe(schemaRef(SignerEnrollmentResultSchema)),
2695
- Type.Unsafe(schemaRef(SignerSignatureResultSchema)),
2696
- Type.Unsafe(schemaRef(SignerFailedResultSchema))
2697
- ], { $id: "SignerCeremonyResult" });
2698
- ({ ...previewSignSchemaContext }), schemaId(SignerUuidSchema), schemaId(SignerOperationSchema), schemaId(SignerProblemSchema), schemaId(SignerCeremonyParamsSchema), schemaId(SignerSessionSchema), schemaId(SignerEnrollmentCeremonyRequestSchema), schemaId(SignerChallengeCeremonyRequestSchema), schemaId(SignerCeremonyRequestSchema), schemaId(SignerCeremonySchema), schemaId(SignerPendingResultSchema), schemaId(SignerEnrollmentResultSchema), schemaId(SignerSignatureResultSchema), schemaId(SignerFailedResultSchema), schemaId(SignerCeremonyResultSchema);
2699
2698
  //#endregion
2700
- //#region ../models/src/tool-enforcement.ts
2701
- var TOOL_ENFORCEMENT_VALUES = [
2702
- "off",
2703
- "watch",
2704
- "enforce"
2705
- ];
2706
- var toolEnforcementLiterals = [
2707
- Type.Literal(TOOL_ENFORCEMENT_VALUES[0]),
2708
- Type.Literal(TOOL_ENFORCEMENT_VALUES[1]),
2709
- Type.Literal(TOOL_ENFORCEMENT_VALUES[2])
2710
- ];
2711
- var ToolEnforcementSchema = Type.Union(toolEnforcementLiterals, { description: "Runtime tool-policy enforcement mode: off (inert), watch (audit only), enforce (block disallowed tools, fail-closed)." });
2699
+ //#region ../runtime-profiles/src/runtime-profile-context-recipes.ts
2700
+ var RUNTIME_PROFILE_CONTEXT_CATALOGUE = {
2701
+ version: 1,
2702
+ fragments: {
2703
+ "artifact-planner-v1": {
2704
+ binding: "prompt_prefix",
2705
+ content: "# Bounded artifact planner\n\n- The typed task facts, embedded bounded manifest, exact bound artifact references, registered tools, and runtime capability section are the complete contract. Do not search diaries, inspect a mounted repository, enumerate unrelated tasks or artifacts, modify a checkout, commit, branch, push, or contact GitHub.\n- Read only the exact artifact CIDs named by the task, and only when the embedded manifest does not provide enough evidence. Use the registered task-artifact tools for artifact access; never use shell or CLI wrappers to fetch artifacts, paginate, or discover them speculatively.\n- If the effective runtime exposes a local calculator or shell, use it only inside scratch for coverage accounting, budget arithmetic, and JSON validation. The runtime capability section and policy are authoritative; do not assume a static executable list.\n- Perform semantic classification and planning from supplied content and producer/consumer evidence. Do not substitute filename, directory, language, ecosystem, or repository-specific exclusion rules for evidence.\n- Write and upload exactly the requested versioned plan artifact, then reference its returned metadata through the registered submit-output tool. Do not emit a second prose or JSON representation.",
2706
+ slug: "artifact-planner-v1"
2707
+ },
2708
+ "accountable-delivery-v1": {
2709
+ binding: "prompt_prefix",
2710
+ content: "# Accountable delivery\n\n- Pair every commit made during this task with a task-provenance diary entry created by the `moltnet_create_entry` custom tool. Put the returned id in a `MoltNet-Diary: <id>` commit trailer. The tool does not currently promise a content signature unless you pass `signed: true` while the runtime kernel declares the `agent-signing` host capability; never describe an entry as signed otherwise.\n- When the runtime kernel declares `agent-signing`, sign commits normally with `git commit -S`: the signature is brokered to the trusted host through `SSH_AUTH_SOCK` and no private key exists in the guest. Without that capability commits are unsigned; do not disable signing the runtime provides, and never try to obtain a key from host configuration.\n- Push a branch and open or update a pull request only when the task asks for it. Use a host-brokered GitHub placeholder only when the runtime kernel declares one; if no GitHub credential is active, the authenticated operation is unavailable.\n- Keep changes, commits, and any requested pull request coherent enough to review independently.",
2711
+ slug: "accountable-delivery-v1"
2712
+ },
2713
+ "judgment-diary-v1": {
2714
+ binding: "prompt_prefix",
2715
+ content: "# Judgment diary discipline\n\n- For an `assess_brief`, `judge_pack`, or `pr_review` task, create a diary entry with the `moltnet_create_entry` custom tool before submitting the structured judgment. Capture the rationale and evidence that support the verdict. Do not claim a content signature unless you created the entry with `signed: true` under a runtime that declares the `agent-signing` host capability.\n- Add the `judgment` tag and the active task type tag (`assess_brief`, `judge_pack`, or `pr_review`). For `judge_pack`, also add `rubric:<rubricId>` from the task facts.\n- Do not use a shell `moltnet entry` command: task provenance is injected only by the custom tool.",
2716
+ slug: "judgment-diary-v1"
2717
+ },
2718
+ "proactive-memory-v1": {
2719
+ binding: "prompt_prefix",
2720
+ content: "# Proactive memory use\n\n- Before non-trivial investigation, debugging, code changes, or review, check the task diary for relevant prior knowledge instead of waiting for a human to ask. Use `moltnet_diary_tags` for cheap reconnaissance, `moltnet_list_entries` when tags or task provenance are known, and `moltnet_search_entries` for semantic similarity. Do not search randomly: pass `taskFilter` for task-local or correlation-local queries, and pass `tags` / `entryTypes` for broader prior-knowledge queries using known tags such as `incident`, `decision`, or `scope:<area>`. Broaden only after constrained searches miss.\n- Before creating an `episodic` incident entry, search for similar incidents using the proposed title, root cause, error text, affected subsystem, and watch-for terms, filtered by `entryTypes: [\"episodic\", \"semantic\"]` and any known `scope:*` or task-provenance tags. If a close prior match exists, do not create an isolated duplicate: reference the prior entry in your response or diary content, update or link it when the new occurrence adds material evidence, or create a new recurrence entry only when the recurrence itself is important signal.\n- When you create a recurrence entry, include the prior matching entry id(s) in the content and explain what is new about this occurrence.",
2721
+ slug: "proactive-memory-v1"
2722
+ },
2723
+ "run-eval-direct-v1": {
2724
+ binding: "prompt_prefix",
2725
+ content: "# Direct evaluation run\n\nThe supplied scenario, typed task facts, injected context, and registered submit-output tool are the complete task contract. Do not search diaries, create diary entries, modify a repository, commit, branch, push, or open a pull request unless a task fact explicitly requires it. Submit the agent-authored payload in the first turn; correction turns exist only to recover a rejected or missing submission.",
2726
+ slug: "run-eval-direct-v1"
2727
+ },
2728
+ "task-diary-discipline-v1": {
2729
+ binding: "prompt_prefix",
2730
+ content: "# Task diary discipline\n\n- During a daemon task, create diary entries only through the `moltnet_create_entry` custom tool. It binds entries to the current task diary and injects task, type, attempt, and correlation provenance tags.\n- Do not shell out to `moltnet entry create`, `moltnet entry create-signed`, or any other `moltnet entry` subcommand from bash while a task is running. For a content-signed entry pass `signed: true` to the custom tool instead; it signs on the trusted host. Those shell paths bypass the custom tool's task-tag injection, so task-filtered diary queries cannot find the entry.\n- You may add useful tags, but do not try to replace task provenance supplied by the runtime.",
2731
+ slug: "task-diary-discipline-v1"
2732
+ },
2733
+ "verification-and-artifacts-v1": {
2734
+ binding: "prompt_prefix",
2735
+ content: "# Verification and artifacts\n\n- Run relevant verification before submitting. When task facts include `successCriteria`, assess them honestly in the generated verification contract; a fail or skip with evidence is better than a fabricated pass.\n- The registered submit-output tool owns the exact agent submission schema and validation recovery. Use that schema; do not invent a JSON shape in prose.\n- Upload only task-relevant artifacts, and inspect each before uploading. Never upload secrets, credentials, API keys, auth tokens or headers, .env files, or personal or customer data; redact sensitive values, and prefer minimal, sanitized excerpts over whole logs, bundles, or datasets. Include artifact metadata only where the typed submit contract permits it.\n- If the task depends on prior artifacts, list and download the exact referenced artifact before judging or continuing that work.",
2736
+ slug: "verification-and-artifacts-v1"
2737
+ }
2738
+ },
2739
+ recipes: {
2740
+ "artifact-planner@v1": {
2741
+ description: "Minimal artifact-only context for bounded semantic classification and planning.",
2742
+ fragments: ["artifact-planner-v1"]
2743
+ },
2744
+ "run-eval-direct@v1": {
2745
+ description: "Minimal direct context for a short, isolated evaluation run.",
2746
+ fragments: ["run-eval-direct-v1"]
2747
+ },
2748
+ "standard-engineering@v1": {
2749
+ description: "Full opt-in operating guidance for engineering tasks that need diary research, accountable delivery, and verification discipline.",
2750
+ fragments: [
2751
+ "proactive-memory-v1",
2752
+ "task-diary-discipline-v1",
2753
+ "accountable-delivery-v1",
2754
+ "judgment-diary-v1",
2755
+ "verification-and-artifacts-v1"
2756
+ ]
2757
+ }
2758
+ }
2759
+ };
2760
+ function deepFreeze(value) {
2761
+ if (value && typeof value === "object") {
2762
+ for (const key of Object.keys(value)) deepFreeze(value[key]);
2763
+ Object.freeze(value);
2764
+ }
2765
+ return value;
2766
+ }
2767
+ deepFreeze(RUNTIME_PROFILE_CONTEXT_CATALOGUE);
2768
+ Object.freeze(Object.keys(RUNTIME_PROFILE_CONTEXT_CATALOGUE.recipes));
2712
2769
  //#endregion
2713
2770
  //#region ../runtime-profiles/src/runtime-profiles.ts
2714
2771
  var RuntimeProfileName = Type.String({
@@ -3921,7 +3978,8 @@ function createGondolinBashOps(vm, localCwd, guestWorkspace, config) {
3921
3978
  * allow-set can't bound it. This is the interim conservative stance for
3922
3979
  * issue #1348 — an operator who lists `bash` still cannot smuggle
3923
3980
  * `bash -c "curl … | sh"` past `enforce`.
3924
- * 3. **Unlisted executables** — any resolved executable not in `allowedTools`.
3981
+ * 3. **Unauthorized executables** — any resolved executable without either a
3982
+ * non-colliding broad grant or a matching scoped shell-command rule.
3925
3983
  *
3926
3984
  * KNOWN LIMITATION (follow-up): the `escapable` risk tier (GTFOBins binaries
3927
3985
  * like `find`, `tar`, `awk` that document shell-spawn / file-write techniques)
@@ -3953,8 +4011,9 @@ function decideToolCall(input) {
3953
4011
  return fenced(input.enforcement, "tool_not_permitted", `not permitted by tool policy: ${missing.join(", ")}`, `would block (watch): ${missing.join(", ")}`, missing);
3954
4012
  }
3955
4013
  const matchedShellCommands = [];
4014
+ const structuredToolNames = input.structuredToolNames ?? DEFAULT_PI_STRUCTURED_TOOL_NAMES;
3956
4015
  const missingShellCommands = resolved.tools.filter((tool) => {
3957
- if (input.allowedTools.has(tool.name)) return false;
4016
+ if (input.allowedTools.has(tool.name) && !structuredToolNames.has(tool.name)) return false;
3958
4017
  const matched = input.allowedShellCommands.find((rule) => matchesArgvPrefix(tool.argv, rule.argvPrefix));
3959
4018
  if (!matched) return true;
3960
4019
  matchedShellCommands.push(toMatchedShellCommand(tool.name, matched.argvPrefix));
@@ -3977,6 +4036,20 @@ function decideToolCall(input) {
3977
4036
  const missing = [...new Set(missingShellCommands.map(({ executable }) => executable))];
3978
4037
  return fenced(input.enforcement, "tool_not_permitted", `not permitted by tool policy: ${missing.join(", ")}`, `would block (watch): ${missing.join(", ")}`, missing, missingShellCommands);
3979
4038
  }
4039
+ /**
4040
+ * Protect direct gate callers that predate active-runtime tool projection.
4041
+ * The session integration supplies the complete registered set, including
4042
+ * custom tools, while these are Pi's built-in structured tool names.
4043
+ */
4044
+ var DEFAULT_PI_STRUCTURED_TOOL_NAMES = new Set([
4045
+ "read",
4046
+ "write",
4047
+ "edit",
4048
+ "bash",
4049
+ "grep",
4050
+ "ls",
4051
+ "find"
4052
+ ]);
3980
4053
  function fingerprintArgv(argv) {
3981
4054
  return `sha256:${createHash("sha256").update(JSON.stringify(argv.map((token) => token === null ? { dynamic: true } : token))).digest("hex").slice(0, 16)}`;
3982
4055
  }
@@ -4139,13 +4212,14 @@ async function withTimeout$1(promise, timeoutMs) {
4139
4212
  * Map a pi `tool_call` event to a gate decision, extracting the shell command
4140
4213
  * for `bash` and delegating to {@link decideToolCall}.
4141
4214
  */
4142
- function decideForEvent(event, policy, analyze) {
4215
+ function decideForEvent(event, policy, analyze, structuredToolNames) {
4143
4216
  const command = event.toolName === "bash" ? event.input.command : void 0;
4144
4217
  return decideToolCall({
4145
4218
  toolName: event.toolName,
4146
4219
  command,
4147
4220
  enforcement: policy.enforcement,
4148
4221
  allowedTools: policy.allowedTools,
4222
+ structuredToolNames,
4149
4223
  allowedShellCommands: policy.allowedShellCommands,
4150
4224
  analyze
4151
4225
  });
@@ -4164,7 +4238,7 @@ function createToolPolicyExtension(deps) {
4164
4238
  return (pi) => {
4165
4239
  if (deps.policy.enforcement === "off") return;
4166
4240
  pi.on("tool_call", (event) => {
4167
- const decision = decideForEvent(event, deps.policy, (command) => deps.analyzer.analyze(command));
4241
+ const decision = decideForEvent(event, deps.policy, (command) => deps.analyzer.analyze(command), deps.structuredToolNames);
4168
4242
  if ("allow" in decision && decision.allow) {
4169
4243
  deps.logger.info({
4170
4244
  ...decisionContext(deps),
@@ -6408,6 +6482,15 @@ async function executePiTask(claimedTask, reporter, opts) {
6408
6482
  }))
6409
6483
  };
6410
6484
  const toolPolicyDecisionContext = buildToolPolicyDecisionContext(claimedTask, opts.runtimeProfileId);
6485
+ const taskHasSubagents = taskTypeUsesSubagents(task.taskType);
6486
+ const structuredToolNames = new Set([
6487
+ ...gondolinCustomTools.map((tool) => tool.name),
6488
+ ...moltnetTools.map((tool) => tool.name),
6489
+ ...submitTools.map((tool) => tool.name),
6490
+ ...opts.runtimeDefinition?.tools.map((tool) => tool.descriptor.name) ?? [],
6491
+ ...opts.runtimeDefinition?.extensions.flatMap((extension) => extension.declaredTools) ?? [],
6492
+ ...taskHasSubagents ? ["subagent"] : []
6493
+ ]);
6411
6494
  if (opts.runtimeProfileId && opts.toolEnforcement) {
6412
6495
  const policy = await resolveSessionToolPolicy({
6413
6496
  agent: moltnetAgent,
@@ -6432,6 +6515,7 @@ async function executePiTask(claimedTask, reporter, opts) {
6432
6515
  policy: resolvedToolPolicy,
6433
6516
  analyzer,
6434
6517
  logger: toolPolicyLogger,
6518
+ structuredToolNames,
6435
6519
  context: toolPolicyDecisionContext
6436
6520
  }));
6437
6521
  }
@@ -6476,7 +6560,6 @@ async function executePiTask(claimedTask, reporter, opts) {
6476
6560
  policy: resolvedToolPolicy
6477
6561
  }), gondolinLifecycle) : [];
6478
6562
  const visibleBaseTools = filterModelVisibleTools([...gondolinCustomTools, ...moltnetTools], resolvedToolPolicy);
6479
- const taskHasSubagents = taskTypeUsesSubagents(task.taskType);
6480
6563
  const visibleParentToolNames = modelVisiblePiToolNames({
6481
6564
  tools: [
6482
6565
  ...visibleBaseTools,