@themoltnet/pi-runtime 0.15.2 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +17 -6
- package/dist/index.js +2265 -2217
- 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/
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
-
*
|
|
265
|
+
* REST capabilities exercised by the current MCP tool surface.
|
|
349
266
|
*
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
*
|
|
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
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
-
*
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
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
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
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
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
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
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
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
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
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({
|
|
2202
|
-
Type.
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
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
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
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:
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
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.
|
|
2228
|
-
|
|
2229
|
-
|
|
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({
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
id: UuidSchema,
|
|
1027
|
+
var ProvenanceGraphRenderedPackMetaSchema = Type.Object({
|
|
1028
|
+
renderedPackId: UuidSchema,
|
|
1029
|
+
sourcePackId: UuidSchema,
|
|
2235
1030
|
diaryId: UuidSchema,
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
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
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
Type.
|
|
2254
|
-
|
|
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.
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
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
|
|
2263
|
-
|
|
2264
|
-
|
|
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
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
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
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
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
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
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.
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
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
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
role: DiaryGrantRoleSchema
|
|
1185
|
+
var SignerCeremonyParamsSchema = Type.Object({ ceremonyId: Type.Unsafe(schemaRef(SignerBase64UrlSchema)) }, {
|
|
1186
|
+
$id: "SignerCeremonyParams",
|
|
1187
|
+
additionalProperties: false
|
|
2293
1188
|
});
|
|
2294
|
-
Type.Object({
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
Type.
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
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
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
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
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
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.
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
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: "
|
|
1226
|
+
$id: "SignerCeremony",
|
|
2329
1227
|
additionalProperties: false
|
|
2330
1228
|
});
|
|
2331
|
-
Type.Object({
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
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: "
|
|
1234
|
+
$id: "SignerPendingResult",
|
|
2337
1235
|
additionalProperties: false
|
|
2338
1236
|
});
|
|
2339
|
-
var
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
Type.
|
|
2351
|
-
|
|
2352
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
2365
|
-
*
|
|
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
|
|
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
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
}
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
}
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
}
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
}
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
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
|
-
*
|
|
2478
|
-
*
|
|
2479
|
-
*
|
|
2480
|
-
*
|
|
2481
|
-
*
|
|
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
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
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
|
-
]);
|
|
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;
|
|
2566
|
+
}
|
|
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);
|
|
2571
|
+
}
|
|
2580
2572
|
//#endregion
|
|
2581
|
-
//#region ../
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
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
2585
|
}
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
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");
|
|
2590
2591
|
}
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
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:
|
|
2633
|
+
maxLength: 64,
|
|
2634
|
+
pattern: "^[a-zA-Z0-9_-]+$"
|
|
2629
2635
|
}),
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
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: "
|
|
2642
|
+
$id: "ContextRef",
|
|
2653
2643
|
additionalProperties: false
|
|
2654
2644
|
});
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
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
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
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
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
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
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
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: "
|
|
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 ../
|
|
2701
|
-
var
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
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({
|
|
@@ -2833,18 +2890,6 @@ Type.Object({ profileId: Type.String({ format: "uuid" }) }, {
|
|
|
2833
2890
|
$id: "RuntimeProfileRef",
|
|
2834
2891
|
additionalProperties: false
|
|
2835
2892
|
});
|
|
2836
|
-
var RuntimeProfileLeaseTtlSec = Type.Integer({
|
|
2837
|
-
minimum: 1,
|
|
2838
|
-
maximum: 86400
|
|
2839
|
-
});
|
|
2840
|
-
var RuntimeProfileHeartbeatIntervalMs = Type.Integer({
|
|
2841
|
-
minimum: 0,
|
|
2842
|
-
maximum: 36e5
|
|
2843
|
-
});
|
|
2844
|
-
var RuntimeProfileMaxBatchSize = Type.Integer({
|
|
2845
|
-
minimum: 1,
|
|
2846
|
-
maximum: 1e3
|
|
2847
|
-
});
|
|
2848
2893
|
var RuntimeProfileMaxTurns = Type.Integer({
|
|
2849
2894
|
minimum: 0,
|
|
2850
2895
|
maximum: 1e4
|
|
@@ -2873,21 +2918,8 @@ Type.Object({
|
|
|
2873
2918
|
maxOutputTokens: RuntimeProfileNullableMaxOutputTokens,
|
|
2874
2919
|
runtimeKind: RuntimeProfileRuntimeKind,
|
|
2875
2920
|
sandbox: RuntimeProfileSandbox,
|
|
2876
|
-
sessionStorageMode: Type.Literal("local"),
|
|
2877
|
-
workspaceStorageMode: Type.Literal("local"),
|
|
2878
2921
|
defaultWorkspaceMode: Type.Union([RuntimeProfileWorkspaceMode, Type.Null()]),
|
|
2879
2922
|
allowedWorkspaceModes: RuntimeProfileAllowedWorkspaceModes,
|
|
2880
|
-
sessionTtlSec: Type.Integer({
|
|
2881
|
-
minimum: 1,
|
|
2882
|
-
maximum: 86400
|
|
2883
|
-
}),
|
|
2884
|
-
workspaceTtlSec: Type.Integer({
|
|
2885
|
-
minimum: 1,
|
|
2886
|
-
maximum: 86400
|
|
2887
|
-
}),
|
|
2888
|
-
leaseTtlSec: RuntimeProfileLeaseTtlSec,
|
|
2889
|
-
heartbeatIntervalMs: RuntimeProfileHeartbeatIntervalMs,
|
|
2890
|
-
maxBatchSize: RuntimeProfileMaxBatchSize,
|
|
2891
2923
|
maxTurns: RuntimeProfileMaxTurns,
|
|
2892
2924
|
maxBashTimeouts: RuntimeProfileMaxBashTimeouts,
|
|
2893
2925
|
toolEnforcement: RuntimeProfileToolEnforcement,
|
|
@@ -3020,6 +3052,7 @@ var ResolvedRuntimeSlot = Type.Object({
|
|
|
3020
3052
|
workspace: Type.Union([RuntimeWorkspace, Type.Null()])
|
|
3021
3053
|
}, { $id: "ResolvedRuntimeSlot" });
|
|
3022
3054
|
Type.Object({ items: Type.Array(ResolvedRuntimeSlot) }, { $id: "RuntimeSlotListResponse" });
|
|
3055
|
+
var MAX_RUNTIME_WARM_RETENTION_SEC = 86400;
|
|
3023
3056
|
Type.Object({
|
|
3024
3057
|
agentName: Type.String({
|
|
3025
3058
|
minLength: 1,
|
|
@@ -3046,7 +3079,11 @@ Type.Object({
|
|
|
3046
3079
|
worktreeBranch: Type.Optional(Type.String({ minLength: 1 })),
|
|
3047
3080
|
workspaceKind: Type.Optional(RuntimeWorkspaceKind),
|
|
3048
3081
|
lastTaskId: Type.String({ format: "uuid" }),
|
|
3049
|
-
lastAttemptN: Type.Integer({ minimum: 1 })
|
|
3082
|
+
lastAttemptN: Type.Integer({ minimum: 1 }),
|
|
3083
|
+
warmRetentionSec: Type.Integer({
|
|
3084
|
+
minimum: 0,
|
|
3085
|
+
maximum: MAX_RUNTIME_WARM_RETENTION_SEC
|
|
3086
|
+
})
|
|
3050
3087
|
}, {
|
|
3051
3088
|
$id: "BeginRuntimeSlotBody",
|
|
3052
3089
|
additionalProperties: false
|
|
@@ -3068,7 +3105,11 @@ Type.Object({
|
|
|
3068
3105
|
slotKey: Type.String({ minLength: 1 }),
|
|
3069
3106
|
taskId: Type.String({ format: "uuid" }),
|
|
3070
3107
|
attemptN: Type.Integer({ minimum: 1 }),
|
|
3071
|
-
sessionPath: Type.Optional(Type.String({ minLength: 1 }))
|
|
3108
|
+
sessionPath: Type.Optional(Type.String({ minLength: 1 })),
|
|
3109
|
+
warmRetentionSec: Type.Integer({
|
|
3110
|
+
minimum: 0,
|
|
3111
|
+
maximum: MAX_RUNTIME_WARM_RETENTION_SEC
|
|
3112
|
+
})
|
|
3072
3113
|
}, {
|
|
3073
3114
|
$id: "FinishRuntimeSlotBody",
|
|
3074
3115
|
additionalProperties: false
|
|
@@ -3921,14 +3962,19 @@ function createGondolinBashOps(vm, localCwd, guestWorkspace, config) {
|
|
|
3921
3962
|
* allow-set can't bound it. This is the interim conservative stance for
|
|
3922
3963
|
* issue #1348 — an operator who lists `bash` still cannot smuggle
|
|
3923
3964
|
* `bash -c "curl … | sh"` past `enforce`.
|
|
3924
|
-
* 3. **
|
|
3965
|
+
* 3. **Unauthorized executables** — any resolved executable without a matching
|
|
3966
|
+
* shell-command rule. Tool names in `allowedTools` never authorize a shell
|
|
3967
|
+
* invocation.
|
|
3968
|
+
* 4. **Output redirection** — a `bash` command that redirects output (`>`,
|
|
3969
|
+
* `2>`, `>>`, `&>`, …). No shell-command rule authorizes it; file writes go
|
|
3970
|
+
* through structured tools.
|
|
3925
3971
|
*
|
|
3926
3972
|
* KNOWN LIMITATION (follow-up): the `escapable` risk tier (GTFOBins binaries
|
|
3927
3973
|
* like `find`, `tar`, `awk` that document shell-spawn / file-write techniques)
|
|
3928
3974
|
* is NOT blocked on the tier alone. The analyzer already re-analyzes the
|
|
3929
3975
|
* sub-commands it can see through documented escape flags (`find -exec`,
|
|
3930
3976
|
* `tar --to-command`, …), but techniques it cannot parse statically could still
|
|
3931
|
-
* escape
|
|
3977
|
+
* escape an argv-prefix rule. Tightening `escapable` (e.g. an LLM judge or a
|
|
3932
3978
|
* capability-aware allow-set) is tracked as future work.
|
|
3933
3979
|
*/
|
|
3934
3980
|
function decideToolCall(input) {
|
|
@@ -3954,18 +4000,20 @@ function decideToolCall(input) {
|
|
|
3954
4000
|
}
|
|
3955
4001
|
const matchedShellCommands = [];
|
|
3956
4002
|
const missingShellCommands = resolved.tools.filter((tool) => {
|
|
3957
|
-
if (input.allowedTools.has(tool.name)) return false;
|
|
3958
4003
|
const matched = input.allowedShellCommands.find((rule) => matchesArgvPrefix(tool.argv, rule.argvPrefix));
|
|
3959
4004
|
if (!matched) return true;
|
|
3960
4005
|
matchedShellCommands.push(toMatchedShellCommand(tool.name, matched.argvPrefix));
|
|
3961
4006
|
return false;
|
|
3962
4007
|
}).map(toMissingShellCommand);
|
|
3963
|
-
if (missingShellCommands.length === 0 && resolved.hasOutputRedirection
|
|
3964
|
-
executable
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
4008
|
+
if (missingShellCommands.length === 0 && resolved.hasOutputRedirection) {
|
|
4009
|
+
const executables = [...new Set(matchedShellCommands.map(({ executable }) => executable))];
|
|
4010
|
+
return fenced(input.enforcement, "shell_output_redirection_not_permitted", "shell output redirection is not permitted by tool policy", "would block shell output redirection (watch)", executables.length > 0 ? executables : void 0, matchedShellCommands.map(({ executable, argvPrefixFingerprint, argvPrefixLength }) => ({
|
|
4011
|
+
executable,
|
|
4012
|
+
argvFingerprint: argvPrefixFingerprint,
|
|
4013
|
+
argvLength: argvPrefixLength,
|
|
4014
|
+
dynamicTokenCount: 0
|
|
4015
|
+
})));
|
|
4016
|
+
}
|
|
3969
4017
|
if (missingShellCommands.length === 0) return matchedShellCommands.length > 0 ? {
|
|
3970
4018
|
allow: true,
|
|
3971
4019
|
reasonCode: "shell_command_prefix_allowed",
|
|
@@ -4079,7 +4127,7 @@ async function resolveSessionToolPolicy(input) {
|
|
|
4079
4127
|
enforcement: resolved.enforcement,
|
|
4080
4128
|
allowedTools: new Set(resolved.allowedTools),
|
|
4081
4129
|
allowedShellCommands: shellCommands.map((rule) => {
|
|
4082
|
-
if (rule.argvPrefix.length <
|
|
4130
|
+
if (rule.argvPrefix.length < 1 || rule.argvPrefix.length > 8 || rule.argvPrefix.some((token) => !token)) throw new Error("runtime returned an invalid shell command rule");
|
|
4083
4131
|
return { argvPrefix: rule.argvPrefix };
|
|
4084
4132
|
}),
|
|
4085
4133
|
executionPolicySnapshotHash: resolved.policySnapshotHash,
|
|
@@ -4686,7 +4734,7 @@ function buildToolPolicyInstructions(policy) {
|
|
|
4686
4734
|
lines.push(policy.allowedTools.length > 0 ? "- The visible structured-tool definitions are the authorized surface." : policy.enforcement === "enforce" ? "- No optional structured tools are authorized." : "- No optional structured tools are registered.");
|
|
4687
4735
|
if (policy.enforcement === "watch") lines.push("- Watch mode records policy decisions but does not block tool calls.");
|
|
4688
4736
|
else if (policy.allowedShellCommands.length === 0) lines.push("- No shell commands are authorized. `bash` is not available; do not", " attempt shell, filesystem, git, GitHub CLI, or MoltNet CLI commands.");
|
|
4689
|
-
else lines.push("- Shell commands are restricted to these authorized argv prefixes:", ...renderShellCommandPrefixes(policy.allowedShellCommands), "- A visible `bash` tool does not grant broader shell authority. Do not", " attempt commands outside those prefixes.");
|
|
4737
|
+
else lines.push("- Shell commands are restricted to these authorized argv prefixes:", ...renderShellCommandPrefixes(policy.allowedShellCommands), "- A visible `bash` tool does not grant broader shell authority. Do not", " attempt commands outside those prefixes.", "- Output redirection (`>`, `2>`, `>>`, `&>`) is never authorized, even", " for an authorized prefix. Write files with the structured file tools.");
|
|
4690
4738
|
lines.push("- Tools and commands absent from this effective policy are unavailable,", " even if advisory context mentions them.");
|
|
4691
4739
|
return lines.join("\n");
|
|
4692
4740
|
}
|
|
@@ -6408,6 +6456,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
6408
6456
|
}))
|
|
6409
6457
|
};
|
|
6410
6458
|
const toolPolicyDecisionContext = buildToolPolicyDecisionContext(claimedTask, opts.runtimeProfileId);
|
|
6459
|
+
const taskHasSubagents = taskTypeUsesSubagents(task.taskType);
|
|
6411
6460
|
if (opts.runtimeProfileId && opts.toolEnforcement) {
|
|
6412
6461
|
const policy = await resolveSessionToolPolicy({
|
|
6413
6462
|
agent: moltnetAgent,
|
|
@@ -6476,7 +6525,6 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
6476
6525
|
policy: resolvedToolPolicy
|
|
6477
6526
|
}), gondolinLifecycle) : [];
|
|
6478
6527
|
const visibleBaseTools = filterModelVisibleTools([...gondolinCustomTools, ...moltnetTools], resolvedToolPolicy);
|
|
6479
|
-
const taskHasSubagents = taskTypeUsesSubagents(task.taskType);
|
|
6480
6528
|
const visibleParentToolNames = modelVisiblePiToolNames({
|
|
6481
6529
|
tools: [
|
|
6482
6530
|
...visibleBaseTools,
|