negotium 0.22.2 → 0.22.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/bin/negotium.mjs +0 -0
  2. package/dist/agent-helpers.js +601 -42
  3. package/dist/agent-helpers.js.map +8 -6
  4. package/dist/{chunk-gzph93xn.js → chunk-0jtan6fd.js} +3 -3
  5. package/dist/{chunk-gzph93xn.js.map → chunk-0jtan6fd.js.map} +2 -2
  6. package/dist/{chunk-kn8y5scr.js → chunk-rwj7x5pg.js} +4 -1
  7. package/dist/{chunk-kn8y5scr.js.map → chunk-rwj7x5pg.js.map} +3 -3
  8. package/dist/{chunk-bn1tt2qw.js → chunk-y4tyc603.js} +1 -1
  9. package/dist/hosted-agent.js +5 -2
  10. package/dist/hosted-agent.js.map +4 -4
  11. package/dist/main.js +1145 -376
  12. package/dist/main.js.map +22 -20
  13. package/dist/mcp-factories.js +620 -61
  14. package/dist/mcp-factories.js.map +8 -6
  15. package/dist/registry.js +3 -3
  16. package/dist/rollout.js +2 -2
  17. package/dist/runtime/src/agents/mcp-tools/request-vault.ts +616 -0
  18. package/dist/runtime/src/application/topic-service.ts +7 -0
  19. package/dist/runtime/src/index.ts +9 -0
  20. package/dist/runtime/src/mcp-runtime-host.ts +1 -0
  21. package/dist/runtime/src/node-host.ts +5 -0
  22. package/dist/runtime/src/runtime/inbox.ts +33 -171
  23. package/dist/runtime/src/runtime/turn-event-stream.ts +2 -0
  24. package/dist/runtime/src/storage/api-messages.ts +8 -2
  25. package/dist/runtime/src/storage/vault-request-gates.ts +359 -0
  26. package/dist/runtime/src/types/api.ts +23 -1
  27. package/dist/runtime/src/version.ts +1 -1
  28. package/dist/runtime-helpers.js +4 -1
  29. package/dist/runtime-helpers.js.map +3 -3
  30. package/dist/storage.js +8 -4
  31. package/dist/storage.js.map +3 -3
  32. package/dist/types/packages/core/src/agents/mcp-tools/request-vault.d.ts +120 -0
  33. package/dist/types/packages/core/src/storage/api-messages.d.ts +1 -0
  34. package/dist/types/packages/core/src/storage/vault-request-gates.d.ts +69 -0
  35. package/dist/types/packages/core/src/types/api.d.ts +15 -1
  36. package/dist/types/packages/core/src/version.d.ts +1 -1
  37. package/package.json +1 -1
  38. /package/dist/{chunk-bn1tt2qw.js.map → chunk-y4tyc603.js.map} +0 -0
@@ -1807,6 +1807,9 @@ function initializeApiMessagesSchema() {
1807
1807
  try {
1808
1808
  db.exec("ALTER TABLE api_messages ADD COLUMN subagent_card TEXT");
1809
1809
  } catch {}
1810
+ try {
1811
+ db.exec("ALTER TABLE api_messages ADD COLUMN vault_secret_request TEXT");
1812
+ } catch {}
1810
1813
  db.exec("CREATE INDEX IF NOT EXISTS idx_api_messages_topic ON api_messages(topic_id)");
1811
1814
  db.exec("CREATE INDEX IF NOT EXISTS idx_api_messages_thread_root ON api_messages(thread_root_id)");
1812
1815
  }
@@ -1843,6 +1846,7 @@ function rowToDto(r) {
1843
1846
  askUserQuestion: r.ask_user_question ? JSON.parse(r.ask_user_question) : undefined,
1844
1847
  tellCard: r.tell_card ? JSON.parse(r.tell_card) : undefined,
1845
1848
  subagentCard: r.subagent_card ? JSON.parse(r.subagent_card) : undefined,
1849
+ vaultSecretRequest: r.vault_secret_request ? JSON.parse(r.vault_secret_request) : undefined,
1846
1850
  mentions: r.mentions ? JSON.parse(r.mentions) : undefined,
1847
1851
  threadRootId: r.thread_root_id ?? undefined,
1848
1852
  createdAt: r.created_at
@@ -1864,9 +1868,9 @@ function appendApiMessage(msg, options = {}) {
1864
1868
  let inserted = false;
1865
1869
  db.transaction(() => {
1866
1870
  const result = db.query(`INSERT INTO api_messages
1867
- (id, topic_id, parent_id, author_id, source_adapter, source_node, source_message_id, text, query_id, agent_type, model, attachments, usage, deleted, edited_at, reactions, kind, ask_user_question, tell_card, subagent_card, mentions, thread_root_id, created_at)
1868
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1869
- ON CONFLICT(id) DO NOTHING`).run(msg.id, msg.topicId, msg.parentId ?? null, msg.authorId, msg.sourceAdapter ?? null, msg.sourceNode ?? null, msg.sourceMessageId ?? null, msg.text, msg.queryId ?? null, msg.agentType ?? null, msg.model ?? null, msg.attachments ? JSON.stringify(msg.attachments) : null, msg.usage ? JSON.stringify(msg.usage) : null, msg.deleted ? 1 : 0, msg.editedAt ?? null, msg.reactions?.length ? JSON.stringify(msg.reactions) : null, msg.kind ?? null, msg.askUserQuestion ? JSON.stringify(msg.askUserQuestion) : null, msg.tellCard ? JSON.stringify(msg.tellCard) : null, msg.subagentCard ? JSON.stringify(msg.subagentCard) : null, msg.mentions?.length ? JSON.stringify(msg.mentions) : null, msg.threadRootId ?? null, msg.createdAt);
1871
+ (id, topic_id, parent_id, author_id, source_adapter, source_node, source_message_id, text, query_id, agent_type, model, attachments, usage, deleted, edited_at, reactions, kind, ask_user_question, tell_card, subagent_card, vault_secret_request, mentions, thread_root_id, created_at)
1872
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1873
+ ON CONFLICT(id) DO NOTHING`).run(msg.id, msg.topicId, msg.parentId ?? null, msg.authorId, msg.sourceAdapter ?? null, msg.sourceNode ?? null, msg.sourceMessageId ?? null, msg.text, msg.queryId ?? null, msg.agentType ?? null, msg.model ?? null, msg.attachments ? JSON.stringify(msg.attachments) : null, msg.usage ? JSON.stringify(msg.usage) : null, msg.deleted ? 1 : 0, msg.editedAt ?? null, msg.reactions?.length ? JSON.stringify(msg.reactions) : null, msg.kind ?? null, msg.askUserQuestion ? JSON.stringify(msg.askUserQuestion) : null, msg.tellCard ? JSON.stringify(msg.tellCard) : null, msg.subagentCard ? JSON.stringify(msg.subagentCard) : null, msg.vaultSecretRequest ? JSON.stringify(msg.vaultSecretRequest) : null, msg.mentions?.length ? JSON.stringify(msg.mentions) : null, msg.threadRootId ?? null, msg.createdAt);
1870
1874
  inserted = Number(result.changes ?? 0) > 0;
1871
1875
  if (inserted && updateTopicLastMessageAt && !msg.deleted) {
1872
1876
  db.query(`UPDATE api_topics
@@ -3926,6 +3930,16 @@ function vaultListDecryptableValues(userId) {
3926
3930
  }
3927
3931
  return entries;
3928
3932
  }
3933
+ function vaultSet(userId, key, value, description = "") {
3934
+ const normalizedKey = normalizeVaultKey(key);
3935
+ activeVaultDatabase().prepare(`INSERT INTO vault (user_id, key, value, description) VALUES (?, ?, ?, ?)
3936
+ ON CONFLICT (user_id, key) DO UPDATE
3937
+ SET value = excluded.value, description = excluded.description`).run(userId, normalizedKey, encryptVaultValue(userId, normalizedKey, value, vaultMasterKey), description);
3938
+ }
3939
+ function vaultHasKey(userId, key) {
3940
+ const row = activeVaultDatabase().prepare("SELECT 1 FROM vault WHERE user_id = ? AND key = ?").get(userId, normalizeVaultKey(key));
3941
+ return row != null;
3942
+ }
3929
3943
  function vaultGetValue(userId, key) {
3930
3944
  const normalizedKey = normalizeVaultKey(key);
3931
3945
  const row = activeVaultDatabase().prepare("SELECT key, value FROM vault WHERE user_id = ? AND key = ?").get(userId, normalizedKey);
@@ -3984,7 +3998,7 @@ function redactVaultSecrets(userId, text) {
3984
3998
  }
3985
3999
  return redacted;
3986
4000
  }
3987
- var vaultDb, vaultMasterKey, VAULT_VALUE_MAX_BYTES;
4001
+ var vaultDb, vaultMasterKey, VAULT_KEY_PATTERN, VAULT_VALUE_MIN_BYTES = 4, VAULT_VALUE_MAX_BYTES, VAULT_DESCRIPTION_MAX_LENGTH = 500;
3988
4002
  var init_vault = __esm(async () => {
3989
4003
  init_config();
3990
4004
  init_vault_crypto();
@@ -3993,6 +4007,7 @@ var init_vault = __esm(async () => {
3993
4007
  init_storage_host()
3994
4008
  ]);
3995
4009
  vaultMasterKey = VAULT_MASTER_KEY;
4010
+ VAULT_KEY_PATTERN = /^[A-Z][A-Z0-9_]{0,127}$/;
3996
4011
  VAULT_VALUE_MAX_BYTES = 64 * 1024;
3997
4012
  });
3998
4013
 
@@ -5215,7 +5230,7 @@ var init_codex = __esm(async () => {
5215
5230
  });
5216
5231
 
5217
5232
  // ../../packages/core/src/version.ts
5218
- var NEGOTIUM_VERSION = "0.22.2";
5233
+ var NEGOTIUM_VERSION = "0.22.4";
5219
5234
 
5220
5235
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
5221
5236
  import { spawn as spawn2 } from "child_process";
@@ -18305,6 +18320,548 @@ var init_topic_config = __esm(async () => {
18305
18320
  await init_api_topic_config();
18306
18321
  });
18307
18322
 
18323
+ // ../../packages/core/src/storage/vault-request-gates.ts
18324
+ function initializeVaultRequestGateSchema() {
18325
+ db.exec(`
18326
+ CREATE TABLE IF NOT EXISTS vault_request_gates (
18327
+ gate_id TEXT PRIMARY KEY,
18328
+ topic_id TEXT NOT NULL REFERENCES api_topics(id) ON DELETE CASCADE,
18329
+ query_id TEXT,
18330
+ idempotency_key TEXT NOT NULL,
18331
+ body_hash TEXT NOT NULL,
18332
+ message_id TEXT NOT NULL UNIQUE,
18333
+ owner_id TEXT NOT NULL,
18334
+ state TEXT NOT NULL,
18335
+ outcome TEXT,
18336
+ answered_by TEXT,
18337
+ claim_source TEXT,
18338
+ created_at TEXT NOT NULL,
18339
+ updated_at TEXT NOT NULL
18340
+ );
18341
+ CREATE INDEX IF NOT EXISTS idx_vault_request_gates_idempotency
18342
+ ON vault_request_gates(topic_id, idempotency_key, created_at);
18343
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_vault_request_gates_active_key
18344
+ ON vault_request_gates(topic_id, idempotency_key)
18345
+ WHERE state IN ('pending', 'claimed');
18346
+ `);
18347
+ }
18348
+ function fromRow2(row) {
18349
+ return {
18350
+ gateId: row.gate_id,
18351
+ topicId: row.topic_id,
18352
+ queryId: row.query_id ?? undefined,
18353
+ idempotencyKey: row.idempotency_key,
18354
+ bodyHash: row.body_hash,
18355
+ messageId: row.message_id,
18356
+ ownerId: row.owner_id,
18357
+ state: row.state,
18358
+ outcome: row.outcome ?? undefined,
18359
+ answeredBy: row.answered_by ?? undefined
18360
+ };
18361
+ }
18362
+ function latestGate2(topicId, idempotencyKey) {
18363
+ const row = db.query(`SELECT * FROM vault_request_gates
18364
+ WHERE topic_id = ? AND idempotency_key = ?
18365
+ ORDER BY rowid DESC LIMIT 1`).get(topicId, idempotencyKey);
18366
+ return row ? fromRow2(row) : null;
18367
+ }
18368
+ function expireVaultCard(topicId, messageId, editedAt) {
18369
+ const row = db.query(`SELECT vault_secret_request FROM api_messages
18370
+ WHERE topic_id = ? AND id = ? AND deleted = 0 AND kind = 'vault_secret_request'`).get(topicId, messageId);
18371
+ if (!row?.vault_secret_request)
18372
+ return null;
18373
+ const request = JSON.parse(row.vault_secret_request);
18374
+ if (request.status !== "pending")
18375
+ return null;
18376
+ const expired = { ...request, status: "expired" };
18377
+ db.query(`UPDATE api_messages SET vault_secret_request = ?, edited_at = ?
18378
+ WHERE topic_id = ? AND id = ? AND deleted = 0 AND kind = 'vault_secret_request'`).run(JSON.stringify(expired), editedAt, topicId, messageId);
18379
+ return { topicId, messageId, vaultSecretRequest: expired, editedAt };
18380
+ }
18381
+ function prepareVaultRequestGate(args) {
18382
+ return db.transaction(() => {
18383
+ const latest = latestGate2(args.topicId, args.idempotencyKey);
18384
+ if (latest?.bodyHash !== undefined && latest.bodyHash !== args.bodyHash) {
18385
+ return { outcome: "conflict", gate: latest };
18386
+ }
18387
+ if (latest?.state === "answered")
18388
+ return { outcome: "replay", gate: latest };
18389
+ if (latest && (latest.state === "pending" || latest.state === "claimed")) {
18390
+ return { outcome: "pending", gate: latest };
18391
+ }
18392
+ db.query(`INSERT INTO vault_request_gates
18393
+ (gate_id, topic_id, query_id, idempotency_key, body_hash, message_id, owner_id, state, created_at, updated_at)
18394
+ VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, ?)`).run(args.gateId, args.topicId, args.queryId ?? null, args.idempotencyKey, args.bodyHash, args.messageId, args.ownerId, args.now, args.now);
18395
+ return {
18396
+ outcome: "created",
18397
+ gate: {
18398
+ gateId: args.gateId,
18399
+ topicId: args.topicId,
18400
+ queryId: args.queryId,
18401
+ idempotencyKey: args.idempotencyKey,
18402
+ bodyHash: args.bodyHash,
18403
+ messageId: args.messageId,
18404
+ ownerId: args.ownerId,
18405
+ state: "pending"
18406
+ }
18407
+ };
18408
+ }).immediate();
18409
+ }
18410
+ function quarantineVaultRequestGate(gateId, ownerId, now = new Date().toISOString()) {
18411
+ return db.transaction(() => {
18412
+ const row = db.query("SELECT * FROM vault_request_gates WHERE gate_id = ?").get(gateId);
18413
+ if (!row || row.owner_id !== ownerId || !["pending", "claimed"].includes(row.state)) {
18414
+ return null;
18415
+ }
18416
+ const result = db.query(`UPDATE vault_request_gates SET state = 'quarantined', updated_at = ?
18417
+ WHERE gate_id = ? AND owner_id = ? AND state IN ('pending', 'claimed')`).run(now, gateId, ownerId);
18418
+ if (Number(result.changes ?? 0) === 0)
18419
+ return null;
18420
+ return expireVaultCard(row.topic_id, row.message_id, now);
18421
+ }).immediate();
18422
+ }
18423
+ function quarantineForeignVaultRequestGates(liveOwnerIds, now = new Date().toISOString()) {
18424
+ return db.transaction(() => {
18425
+ const rows = db.query(`SELECT * FROM vault_request_gates
18426
+ WHERE state IN ('pending', 'claimed')`).all();
18427
+ const updates = [];
18428
+ for (const row of rows) {
18429
+ if (liveOwnerIds.has(row.owner_id))
18430
+ continue;
18431
+ if (row.state === "claimed" && row.outcome && row.answered_by) {
18432
+ db.query(`UPDATE vault_request_gates SET state = 'answered', updated_at = ?
18433
+ WHERE gate_id = ? AND state = 'claimed'`).run(now, row.gate_id);
18434
+ continue;
18435
+ }
18436
+ const result = db.query(`UPDATE vault_request_gates SET state = 'quarantined', updated_at = ?
18437
+ WHERE gate_id = ? AND state IN ('pending', 'claimed')`).run(now, row.gate_id);
18438
+ if (Number(result.changes ?? 0) === 0)
18439
+ continue;
18440
+ const update = expireVaultCard(row.topic_id, row.message_id, now);
18441
+ if (update)
18442
+ updates.push(update);
18443
+ }
18444
+ const legacyCards = db.query(`SELECT m.topic_id, m.id
18445
+ FROM api_messages m
18446
+ LEFT JOIN vault_request_gates g ON g.message_id = m.id
18447
+ WHERE m.kind = 'vault_secret_request'
18448
+ AND m.deleted = 0
18449
+ AND g.gate_id IS NULL`).all();
18450
+ for (const card of legacyCards) {
18451
+ const update = expireVaultCard(card.topic_id, card.id, now);
18452
+ if (update)
18453
+ updates.push(update);
18454
+ }
18455
+ return updates;
18456
+ }).immediate();
18457
+ }
18458
+ function claimVaultRequestGateAndAnswer(args) {
18459
+ return db.transaction(() => {
18460
+ const row = db.query(`SELECT * FROM vault_request_gates
18461
+ WHERE topic_id = ? AND message_id = ?
18462
+ ORDER BY rowid DESC LIMIT 1`).get(args.topicId, args.messageId);
18463
+ if (!row || row.owner_id !== args.ownerId || row.state !== "pending") {
18464
+ return { outcome: "unavailable" };
18465
+ }
18466
+ const message = db.query(`SELECT vault_secret_request FROM api_messages
18467
+ WHERE topic_id = ? AND id = ? AND deleted = 0 AND kind = 'vault_secret_request'`).get(args.topicId, args.messageId);
18468
+ if (!message?.vault_secret_request)
18469
+ return { outcome: "unavailable" };
18470
+ const request = JSON.parse(message.vault_secret_request);
18471
+ if (request.status !== "pending")
18472
+ return { outcome: "unavailable" };
18473
+ const claimed = db.query(`UPDATE vault_request_gates
18474
+ SET state = 'answered', outcome = ?, answered_by = ?, claim_source = ?, updated_at = ?
18475
+ WHERE gate_id = ? AND owner_id = ? AND state = 'pending'`).run(args.outcome, args.userId, args.source, args.now, row.gate_id, args.ownerId);
18476
+ if (Number(claimed.changes ?? 0) === 0)
18477
+ return { outcome: "unavailable" };
18478
+ const answered = {
18479
+ ...request,
18480
+ status: args.outcome,
18481
+ answeredBy: args.userId
18482
+ };
18483
+ db.query(`UPDATE api_messages SET vault_secret_request = ?, edited_at = ?
18484
+ WHERE topic_id = ? AND id = ? AND deleted = 0 AND kind = 'vault_secret_request'`).run(JSON.stringify(answered), args.now, args.topicId, args.messageId);
18485
+ return {
18486
+ outcome: "claimed",
18487
+ gate: fromRow2({
18488
+ ...row,
18489
+ state: "answered",
18490
+ outcome: args.outcome,
18491
+ answered_by: args.userId
18492
+ }),
18493
+ vaultSecretRequest: answered,
18494
+ editedAt: args.now
18495
+ };
18496
+ }).immediate();
18497
+ }
18498
+ function cancelVaultRequestGate(topicId, messageId, ownerId, now = new Date().toISOString()) {
18499
+ return db.transaction(() => {
18500
+ const row = db.query(`SELECT * FROM vault_request_gates
18501
+ WHERE topic_id = ? AND message_id = ? ORDER BY rowid DESC LIMIT 1`).get(topicId, messageId);
18502
+ if (!row || row.owner_id !== ownerId || row.state !== "pending")
18503
+ return null;
18504
+ const result = db.query(`UPDATE vault_request_gates SET state = 'cancelled', updated_at = ?
18505
+ WHERE gate_id = ? AND owner_id = ? AND state = 'pending'`).run(now, row.gate_id, ownerId);
18506
+ if (Number(result.changes ?? 0) === 0)
18507
+ return null;
18508
+ return expireVaultCard(topicId, messageId, now);
18509
+ }).immediate();
18510
+ }
18511
+ var init_vault_request_gates = __esm(async () => {
18512
+ await __promiseAll([
18513
+ init_forum_db(),
18514
+ init_storage_host()
18515
+ ]);
18516
+ registerStorageSchemaInitializer(initializeVaultRequestGateSchema, 32);
18517
+ });
18518
+
18519
+ // ../../packages/core/src/agents/mcp-tools/request-vault.ts
18520
+ import { createHash as createHash12, randomUUID as randomUUID17 } from "crypto";
18521
+ import { z as z4 } from "zod";
18522
+ function requestBodyHash(key, description) {
18523
+ return createHash12("sha256").update(JSON.stringify({ key, description })).digest("hex");
18524
+ }
18525
+ function answerToolResult2(key, outcome) {
18526
+ if (outcome === "no_answer") {
18527
+ return errorResult("The AI turn ended before the user answered.");
18528
+ }
18529
+ if (outcome === "declined") {
18530
+ return errorResult(`User declined to provide a value for ${key}.`);
18531
+ }
18532
+ return textResult(`User stored a value for ${key} in the vault. Reference it as {{${key}}} directly in supported transient tool inputs. Never ask the user to repeat the raw value in chat.`);
18533
+ }
18534
+ function createRequestVaultRuntime(host) {
18535
+ const pendingRequests = new Map;
18536
+ const { ownerId } = host.runtime;
18537
+ const createId = () => host.runtime.createId();
18538
+ const now = () => host.runtime.now();
18539
+ const isProcessLeaseAlive = (lease) => host.durability.processLeases.isAlive(lease);
18540
+ let gateOwnerLease = null;
18541
+ function broadcastGateUpdates(updates) {
18542
+ for (const update of updates)
18543
+ host.messaging.publication.publishCardUpdate(update);
18544
+ }
18545
+ function startGateOwner() {
18546
+ if (gateOwnerLease)
18547
+ return;
18548
+ gateOwnerLease = host.durability.processLeases.acquire(`${VAULT_GATE_LEASE_ROLE_PREFIX}${ownerId}`, ownerId);
18549
+ if (!gateOwnerLease)
18550
+ throw new Error("failed to acquire the vault-request gate owner lease");
18551
+ }
18552
+ function stopGateOwner() {
18553
+ gateOwnerLease?.stop();
18554
+ gateOwnerLease = null;
18555
+ }
18556
+ function reconcilePendingGates() {
18557
+ startGateOwner();
18558
+ host.durability.processLeases.removeDead(VAULT_GATE_LEASE_ROLE_PREFIX);
18559
+ const liveOwners = new Set(host.durability.processLeases.list(VAULT_GATE_LEASE_ROLE_PREFIX).filter(isProcessLeaseAlive).map((lease) => lease.ownerId));
18560
+ const updates = host.durability.gates.quarantineForeign(liveOwners, now());
18561
+ broadcastGateUpdates(updates);
18562
+ return updates.length;
18563
+ }
18564
+ function answerPendingRequest(topicId, messageId, userId, value) {
18565
+ reconcilePendingGates();
18566
+ const topic = host.topics.getTopic(topicId);
18567
+ if (!topic?.participants.some((participant) => participant.userId === userId)) {
18568
+ return { ok: false, error: "User is not a member of this topic." };
18569
+ }
18570
+ const existing = host.messaging.persistence.getMessage(topicId, messageId);
18571
+ const request = existing?.kind === "vault_secret_request" ? existing.vaultSecretRequest : undefined;
18572
+ if (!existing || existing.deleted || !request) {
18573
+ return { ok: false, error: "Vault request card not found." };
18574
+ }
18575
+ if (request.status !== "pending") {
18576
+ return { ok: false, error: "This request is no longer awaiting an answer." };
18577
+ }
18578
+ const pending = pendingRequests.get(messageId);
18579
+ if (!pending || pending.topicId !== topicId) {
18580
+ return { ok: false, error: "This request is no longer awaiting an answer." };
18581
+ }
18582
+ const declining = value === null;
18583
+ if (!declining) {
18584
+ const bytes = Buffer.byteLength(value, "utf8");
18585
+ if (bytes < VAULT_VALUE_MIN_BYTES || bytes > VAULT_VALUE_MAX_BYTES) {
18586
+ return {
18587
+ ok: false,
18588
+ error: `Value must be between ${VAULT_VALUE_MIN_BYTES} and ${VAULT_VALUE_MAX_BYTES} bytes.`
18589
+ };
18590
+ }
18591
+ }
18592
+ if (!declining) {
18593
+ try {
18594
+ host.vault.set(userId, pending.key, value, pending.description);
18595
+ } catch (error) {
18596
+ return {
18597
+ ok: false,
18598
+ error: `Failed to store the value: ${error instanceof Error ? error.message : String(error)}`
18599
+ };
18600
+ }
18601
+ }
18602
+ const outcome = declining ? "declined" : "fulfilled";
18603
+ let claim;
18604
+ try {
18605
+ claim = host.durability.gates.claimAndAnswer({
18606
+ topicId,
18607
+ messageId,
18608
+ outcome,
18609
+ userId,
18610
+ ownerId,
18611
+ source: "host-control",
18612
+ now: now()
18613
+ });
18614
+ } catch {
18615
+ return { ok: false, error: "This request could not be claimed safely. Retry once." };
18616
+ }
18617
+ if (claim.outcome !== "claimed") {
18618
+ return { ok: false, error: "This request is no longer awaiting an answer." };
18619
+ }
18620
+ pendingRequests.delete(messageId);
18621
+ const answerMessage = {
18622
+ id: createId(),
18623
+ topicId,
18624
+ authorId: userId,
18625
+ text: outcome === "fulfilled" ? `Provided a value for ${pending.key}.` : `Declined to provide a value for ${pending.key}.`,
18626
+ parentId: messageId,
18627
+ ...existing.threadRootId ? { threadRootId: existing.threadRootId } : {},
18628
+ createdAt: now()
18629
+ };
18630
+ try {
18631
+ host.messaging.publication.publishCardUpdate({
18632
+ topicId,
18633
+ messageId,
18634
+ vaultSecretRequest: claim.vaultSecretRequest,
18635
+ editedAt: claim.editedAt
18636
+ });
18637
+ host.messaging.persistence.appendMessage(answerMessage);
18638
+ host.messaging.publication.publishMessage(topicId, answerMessage);
18639
+ } catch {}
18640
+ pending.resolve({ outcome, userId });
18641
+ return { ok: true, queryId: pending.queryId, outcome, answerMessage };
18642
+ }
18643
+ function cancelPendingRequests(topicId, queryId) {
18644
+ for (const [messageId, pending] of pendingRequests) {
18645
+ if (pending.topicId === topicId && pending.queryId === queryId) {
18646
+ pendingRequests.delete(messageId);
18647
+ const update = host.durability.gates.cancel(topicId, messageId, ownerId, now());
18648
+ if (update)
18649
+ broadcastGateUpdates([update]);
18650
+ pending.resolve({ outcome: "no_answer", userId: "" });
18651
+ }
18652
+ }
18653
+ }
18654
+ function appendRequestMessage(ctx, key, description, messageId) {
18655
+ const cfg = host.topics.getConfig(ctx.topicId);
18656
+ const vaultSecretRequest = { key, description, status: "pending" };
18657
+ const message = {
18658
+ id: messageId,
18659
+ topicId: ctx.topicId,
18660
+ authorId: "ai",
18661
+ text: `Vault secret requested: ${key}`,
18662
+ queryId: ctx.queryId,
18663
+ agentType: ctx.agent,
18664
+ model: ctx.model ?? cfg?.model ?? "unknown",
18665
+ kind: "vault_secret_request",
18666
+ vaultSecretRequest,
18667
+ ...ctx.threadRootId ? { threadRootId: ctx.threadRootId } : {},
18668
+ createdAt: now()
18669
+ };
18670
+ host.messaging.persistence.appendMessage(message);
18671
+ return message;
18672
+ }
18673
+ async function requestVaultSecret(ctx, input) {
18674
+ if (ctx.unsupportedSurface) {
18675
+ return errorResult("Error: this surface cannot mask secret input safely (there is no password field here, so a typed value would land in plaintext chat history). Ask the user to provide this value from the Terminal app or Otium instead.");
18676
+ }
18677
+ reconcilePendingGates();
18678
+ const topic = host.topics.getTopic(ctx.topicId);
18679
+ if (!topic)
18680
+ return errorResult(`Error: topic '${ctx.topicId}' not found.`);
18681
+ if (!topic.participants.some((participant) => participant.userId === ctx.userId)) {
18682
+ return errorResult("Error: user is not a member of this topic.");
18683
+ }
18684
+ if (typeof input.key !== "string" || !input.key.trim()) {
18685
+ return errorResult("Error: key is required.");
18686
+ }
18687
+ const key = normalizeVaultKey(input.key);
18688
+ if (!VAULT_KEY_PATTERN.test(key)) {
18689
+ return errorResult("Error: key must start with a letter and contain only uppercase letters, digits, and underscores (max 128 chars).");
18690
+ }
18691
+ if (typeof input.description !== "string" || !input.description.trim()) {
18692
+ return errorResult("Error: description is required so the user knows what to type.");
18693
+ }
18694
+ const description = input.description.trim().slice(0, MAX_DESCRIPTION_CHARS);
18695
+ if (input.idempotency_key !== undefined && (typeof input.idempotency_key !== "string" || !input.idempotency_key.trim())) {
18696
+ return errorResult("Error: idempotency_key must be a non-empty string.");
18697
+ }
18698
+ const rawIdempotencyKey = typeof input.idempotency_key === "string" ? input.idempotency_key.trim() : createId();
18699
+ if (rawIdempotencyKey.length > MAX_IDEMPOTENCY_KEY_CHARS2) {
18700
+ return errorResult(`Error: idempotency_key must be at most ${MAX_IDEMPOTENCY_KEY_CHARS2} characters.`);
18701
+ }
18702
+ const bodyHash = requestBodyHash(key, description);
18703
+ const createGate = () => {
18704
+ const suffix = createId();
18705
+ return host.durability.gates.prepare({
18706
+ gateId: `vault-request-gate-${suffix}`,
18707
+ topicId: ctx.topicId,
18708
+ queryId: ctx.queryId,
18709
+ idempotencyKey: rawIdempotencyKey,
18710
+ bodyHash,
18711
+ messageId: `vault-request-${ctx.queryId ?? "runtime"}-${suffix}`,
18712
+ ownerId,
18713
+ now: now()
18714
+ });
18715
+ };
18716
+ let prepared = createGate();
18717
+ if (prepared.outcome === "conflict") {
18718
+ return errorResult("Error: idempotency_conflict. The same idempotency_key was already used with a different key or description.");
18719
+ }
18720
+ if (prepared.outcome === "replay") {
18721
+ if (!prepared.gate.outcome || !prepared.gate.answeredBy) {
18722
+ return errorResult("Error: the stored request_vault_secret replay is incomplete.");
18723
+ }
18724
+ return answerToolResult2(key, prepared.gate.outcome);
18725
+ }
18726
+ if (prepared.outcome === "pending") {
18727
+ if (prepared.gate.ownerId !== ownerId) {
18728
+ return errorResult("Error: this request_vault_secret is pending in another healthy runtime process.");
18729
+ }
18730
+ const existingPending = pendingRequests.get(prepared.gate.messageId);
18731
+ if (existingPending)
18732
+ return answerToolResult2(key, (await existingPending.promise).outcome);
18733
+ if (prepared.gate.state === "claimed") {
18734
+ return errorResult("Error: this request_vault_secret is already being finalized.");
18735
+ }
18736
+ const update = host.durability.gates.quarantine(prepared.gate.gateId, ownerId, now());
18737
+ if (update)
18738
+ broadcastGateUpdates([update]);
18739
+ prepared = createGate();
18740
+ }
18741
+ if (prepared.outcome !== "created") {
18742
+ return errorResult("Error: failed to create a durable request_vault_secret gate.");
18743
+ }
18744
+ if (host.vault.hasKey(ctx.userId, key)) {
18745
+ host.durability.gates.quarantine(prepared.gate.gateId, ownerId, now());
18746
+ return errorResult(`Error: vault key '${key}' already exists. Pick a different key, or ask the user to update it from their Vault manager.`);
18747
+ }
18748
+ let message;
18749
+ try {
18750
+ message = appendRequestMessage(ctx, key, description, prepared.gate.messageId);
18751
+ } catch (error) {
18752
+ host.durability.gates.quarantine(prepared.gate.gateId, ownerId, now());
18753
+ return errorResult(`Error: failed to persist request_vault_secret: ${error instanceof Error ? error.message : String(error)}`);
18754
+ }
18755
+ let resolveAnswer;
18756
+ const promise = new Promise((resolve17) => {
18757
+ resolveAnswer = resolve17;
18758
+ });
18759
+ pendingRequests.set(message.id, {
18760
+ topicId: ctx.topicId,
18761
+ queryId: ctx.queryId,
18762
+ messageId: message.id,
18763
+ key,
18764
+ description,
18765
+ promise,
18766
+ resolve: resolveAnswer
18767
+ });
18768
+ try {
18769
+ host.messaging.publication.publishMessage(ctx.topicId, message);
18770
+ } catch (error) {
18771
+ pendingRequests.delete(message.id);
18772
+ try {
18773
+ host.durability.gates.quarantine(prepared.gate.gateId, ownerId, now());
18774
+ } catch {}
18775
+ return errorResult(`Error: failed to publish request_vault_secret: ${error instanceof Error ? error.message : String(error)}`);
18776
+ }
18777
+ return answerToolResult2(key, (await promise).outcome);
18778
+ }
18779
+ function createToolDefinition(ctx) {
18780
+ return {
18781
+ name: "request_vault_secret",
18782
+ description: "Ask the user to store a brand-new secret value in their Vault. You choose the key name and a short description of what to paste; the user types only the value on a surface that can mask it, and you never see the raw value. Use this instead of asking the user to paste a secret directly into chat. After it succeeds, reference the value as {{KEY}} in supported transient tool inputs.",
18783
+ schema: {
18784
+ key: z4.string().describe("New Vault key name, e.g. STRIPE_API_KEY. Letters/digits/underscores, must start with a letter."),
18785
+ description: z4.string().max(MAX_DESCRIPTION_CHARS).describe("One sentence telling the user what value to paste and where it's from."),
18786
+ idempotency_key: z4.string().max(MAX_IDEMPOTENCY_KEY_CHARS2).optional().describe("Optional retry key. Reusing it with the same key/description replays the existing result; reusing it with different content fails.")
18787
+ },
18788
+ async handler(input) {
18789
+ return requestVaultSecret(ctx, input);
18790
+ }
18791
+ };
18792
+ }
18793
+ return {
18794
+ startGateOwner,
18795
+ stopGateOwner,
18796
+ reconcilePendingGates,
18797
+ answerPendingRequest,
18798
+ cancelPendingRequests,
18799
+ createToolDefinition
18800
+ };
18801
+ }
18802
+ function cancelPendingVaultSecretRequests(topicId, queryId) {
18803
+ defaultRequestVaultRuntime.cancelPendingRequests(topicId, queryId);
18804
+ }
18805
+ var MAX_DESCRIPTION_CHARS, MAX_IDEMPOTENCY_KEY_CHARS2 = 200, VAULT_GATE_OWNER_ID, VAULT_GATE_LEASE_ROLE_PREFIX = "vault-request-gate:", defaultRequestVaultDurabilityHost, defaultRequestVaultRuntime;
18806
+ var init_request_vault = __esm(async () => {
18807
+ init_common();
18808
+ await __promiseAll([
18809
+ init_bus(),
18810
+ init_api_messages(),
18811
+ init_api_topic_config(),
18812
+ init_api_topics(),
18813
+ init_runtime_process_leases(),
18814
+ init_vault(),
18815
+ init_vault_request_gates()
18816
+ ]);
18817
+ MAX_DESCRIPTION_CHARS = VAULT_DESCRIPTION_MAX_LENGTH;
18818
+ VAULT_GATE_OWNER_ID = `request-vault-${process.pid}-${randomUUID17()}`;
18819
+ defaultRequestVaultDurabilityHost = {
18820
+ gates: {
18821
+ prepare: prepareVaultRequestGate,
18822
+ claimAndAnswer: claimVaultRequestGateAndAnswer,
18823
+ cancel: cancelVaultRequestGate,
18824
+ quarantine: quarantineVaultRequestGate,
18825
+ quarantineForeign: quarantineForeignVaultRequestGates
18826
+ },
18827
+ processLeases: {
18828
+ acquire: (role, ownerId) => acquireRuntimeProcessLease(role, { ownerId }),
18829
+ list: listRuntimeProcessLeases,
18830
+ isAlive: isRuntimeProcessLeaseAlive,
18831
+ removeDead: removeDeadRuntimeProcessLeases
18832
+ }
18833
+ };
18834
+ defaultRequestVaultRuntime = createRequestVaultRuntime({
18835
+ messaging: {
18836
+ persistence: {
18837
+ getMessage: getApiMessage,
18838
+ appendMessage: appendApiMessage
18839
+ },
18840
+ publication: {
18841
+ publishMessage: (topicId, message) => WsHub.get().broadcastMessage(topicId, message),
18842
+ publishCardUpdate: (update) => WsHub.get().broadcastMessageUpdated(update.topicId, update.messageId, {
18843
+ vaultSecretRequest: update.vaultSecretRequest,
18844
+ editedAt: update.editedAt
18845
+ })
18846
+ }
18847
+ },
18848
+ topics: {
18849
+ getTopic,
18850
+ getConfig: getApiTopicConfig
18851
+ },
18852
+ vault: {
18853
+ hasKey: vaultHasKey,
18854
+ set: vaultSet
18855
+ },
18856
+ durability: defaultRequestVaultDurabilityHost,
18857
+ runtime: {
18858
+ ownerId: VAULT_GATE_OWNER_ID,
18859
+ createId: randomUUID17,
18860
+ now: () => new Date().toISOString()
18861
+ }
18862
+ });
18863
+ });
18864
+
18308
18865
  // ../../packages/core/src/mcp/peer-bridge.ts
18309
18866
  function flushPeerRuntimeEvents(localTopicId) {
18310
18867
  return activeBridge2?.flushEvents?.(localTopicId) ?? Promise.resolve(true);
@@ -18679,7 +19236,7 @@ var init_visuals = __esm(async () => {
18679
19236
  });
18680
19237
 
18681
19238
  // ../../packages/core/src/runtime/turn-event-stream.ts
18682
- import { randomUUID as randomUUID17 } from "crypto";
19239
+ import { randomUUID as randomUUID18 } from "crypto";
18683
19240
  import { realpathSync as realpathSync6, statSync as statSync9 } from "fs";
18684
19241
  import { isAbsolute as isAbsolute7, resolve as resolve18 } from "path";
18685
19242
  function sessionEventMatchesCurrentExecution(topicId, queryId, agent, model) {
@@ -18786,7 +19343,7 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
18786
19343
  if (silent || !text2.trim())
18787
19344
  return null;
18788
19345
  const message = {
18789
- id: randomUUID17(),
19346
+ id: randomUUID18(),
18790
19347
  topicId,
18791
19348
  authorId: "ai",
18792
19349
  text: text2,
@@ -19266,6 +19823,7 @@ ${JSON.stringify(event.input ?? {})}`);
19266
19823
  const stillCurrent = getRoomQuery(roomId)?.queryId === queryId;
19267
19824
  clearRoomQuery(roomId, queryId);
19268
19825
  cancelPendingAskUserQuestions(topicId, queryId);
19826
+ cancelPendingVaultSecretRequests(topicId, queryId);
19269
19827
  if (stillCurrent && roomId === topicId)
19270
19828
  clearQueryState(userId, topicId, topicTitle);
19271
19829
  if (!silent && stillCurrent)
@@ -19309,6 +19867,7 @@ var init_turn_event_stream = __esm(async () => {
19309
19867
  init_idle_archiver(),
19310
19868
  init_idle_compact(),
19311
19869
  init_ask_user(),
19870
+ init_request_vault(),
19312
19871
  init_spawn_subagent(),
19313
19872
  init_registry(),
19314
19873
  init_bus(),
@@ -19463,7 +20022,7 @@ __export(exports_turn_runner, {
19463
20022
  FALLBACK_ORDER: () => FALLBACK_ORDER,
19464
20023
  AGENT_DISPLAY_NAME: () => AGENT_DISPLAY_NAME
19465
20024
  });
19466
- import { randomUUID as randomUUID18 } from "crypto";
20025
+ import { randomUUID as randomUUID19 } from "crypto";
19467
20026
  import { existsSync as existsSync22, mkdirSync as mkdirSync19, readdirSync as readdirSync5, statSync as statSync10 } from "fs";
19468
20027
  import { join as join31 } from "path";
19469
20028
  function withDefaultPlaywright(configuredMcp, isManager) {
@@ -19488,7 +20047,7 @@ ${reminderPrompt}` };
19488
20047
  }
19489
20048
  function appendSystemMessage(topicId, text2, threadRootId) {
19490
20049
  const message = {
19491
- id: randomUUID18(),
20050
+ id: randomUUID19(),
19492
20051
  topicId,
19493
20052
  authorId: "system",
19494
20053
  text: text2,
@@ -19510,7 +20069,7 @@ function notifyPlaywrightUnavailable(topicId) {
19510
20069
  }
19511
20070
  function appendAskReplyMessage(topicId, text2, sourceLabel, body, kind, agentType, threadRootId) {
19512
20071
  const message = {
19513
- id: randomUUID18(),
20072
+ id: randomUUID19(),
19514
20073
  topicId,
19515
20074
  authorId: "ai",
19516
20075
  text: text2,
@@ -20031,7 +20590,7 @@ function startAiTurn(params) {
20031
20590
  const askReplySources = params.askReplySources;
20032
20591
  const sessionRetried = params._sessionRetried === true;
20033
20592
  const emptyResponseRetried = params._emptyResponseRetried === true;
20034
- const queryId = asTurnId(params._queryId ?? randomUUID18());
20593
+ const queryId = asTurnId(params._queryId ?? randomUUID19());
20035
20594
  const roomId = turnConcurrency === "isolated" ? isolatedTurnRoomId(topicId, queryId) : asTurnSlotKey(topicId);
20036
20595
  const currentRuntimeEpoch = getRuntimeTopicEpoch(topic.id);
20037
20596
  const runtimeEpoch = params._runtimeEpoch ?? currentRuntimeEpoch;
@@ -20802,7 +21361,7 @@ function triggerTopicAiTurn(topicId, userId, prompt, agentType, opts) {
20802
21361
  if (!opts?.silent && !opts?.hideInjectMessage) {
20803
21362
  const now = new Date().toISOString();
20804
21363
  const injectMsg = {
20805
- id: opts?.requestId ? `tell-${opts.requestId}` : `tell-${randomUUID18()}`,
21364
+ id: opts?.requestId ? `tell-${opts.requestId}` : `tell-${randomUUID19()}`,
20806
21365
  topicId,
20807
21366
  authorId: opts?.injectAuthorId ?? userId,
20808
21367
  sourceAdapter: opts?.injectSourceAdapter,
@@ -20924,7 +21483,7 @@ var init_turn_runner = __esm(async () => {
20924
21483
  });
20925
21484
 
20926
21485
  // ../../packages/core/src/agents/mcp-tools/spawn-subagent.ts
20927
- import { z as z4 } from "zod";
21486
+ import { z as z5 } from "zod";
20928
21487
  function activeQueryUserId(topic) {
20929
21488
  return topic?.participants.find((participant) => hasActiveQuery(participant.userId, topic.id))?.userId;
20930
21489
  }
@@ -21491,7 +22050,7 @@ ${card.task}${reportInstruction}`;
21491
22050
  name: "start_subagent",
21492
22051
  description: "Start a prepared descendant subagent task.",
21493
22052
  schema: {
21494
- topic_id: z4.string().describe("Prepared subagent room id.")
22053
+ topic_id: z5.string().describe("Prepared subagent room id.")
21495
22054
  },
21496
22055
  async handler(input) {
21497
22056
  const result = ownedSubagentTree(ctx);
@@ -21511,7 +22070,7 @@ ${card.task}${reportInstruction}`;
21511
22070
  name: "delete_subagent",
21512
22071
  description: "Permanently delete one descendant subagent room managed by the current room. " + "This removes its conversation, workspace, runtime state, and topic. Keep it when follow-up work is likely.",
21513
22072
  schema: {
21514
- topic_id: z4.string().describe("Exact subagent room id returned by list_subagents; names are not accepted.")
22073
+ topic_id: z5.string().describe("Exact subagent room id returned by list_subagents; names are not accepted.")
21515
22074
  },
21516
22075
  async handler(input) {
21517
22076
  const result = ownedSubagentTree(ctx);
@@ -21535,8 +22094,8 @@ ${card.task}${reportInstruction}`;
21535
22094
  name: "grant_subagent_tell",
21536
22095
  description: "Allow one descendant subagent to tell_session another topic in this managed subagent tree. " + "Direct-parent reporting is always allowed and does not need a grant.",
21537
22096
  schema: {
21538
- subagent_topic_id: z4.string().describe("Source descendant subagent topic id."),
21539
- target_topic_id: z4.string().describe("Target topic id: this manager room or another descendant in its tree.")
22097
+ subagent_topic_id: z5.string().describe("Source descendant subagent topic id."),
22098
+ target_topic_id: z5.string().describe("Target topic id: this manager room or another descendant in its tree.")
21540
22099
  },
21541
22100
  async handler(input) {
21542
22101
  const result = ownedSubagentTree(ctx);
@@ -21561,8 +22120,8 @@ ${card.task}${reportInstruction}`;
21561
22120
  name: "revoke_subagent_tell",
21562
22121
  description: "Remove an extra tell_session connection previously granted to a descendant subagent.",
21563
22122
  schema: {
21564
- subagent_topic_id: z4.string().describe("Source descendant subagent topic id."),
21565
- target_topic_id: z4.string().describe("Granted target topic id.")
22123
+ subagent_topic_id: z5.string().describe("Source descendant subagent topic id."),
22124
+ target_topic_id: z5.string().describe("Granted target topic id.")
21566
22125
  },
21567
22126
  async handler(input) {
21568
22127
  const result = ownedSubagentTree(ctx);
@@ -21584,18 +22143,18 @@ ${card.task}${reportInstruction}`;
21584
22143
  }
21585
22144
  function createSpawnSubagentToolDefinition(ctx, options = {}) {
21586
22145
  const executionOverrideSchema = options.executionOverrides === false ? {} : {
21587
- agent: z4.enum(["claude", "codex", "maestro"]).optional().describe("Agent backend override. Defaults to this room's agent."),
21588
- model: z4.string().optional().describe(`Best-fit model override from the system prompt catalog. Omit agent+model to inherit ${ctx.agent}/${ctx.model ?? "default"}; overriding agent without model uses that agent's default.`)
22146
+ agent: z5.enum(["claude", "codex", "maestro"]).optional().describe("Agent backend override. Defaults to this room's agent."),
22147
+ model: z5.string().optional().describe(`Best-fit model override from the system prompt catalog. Omit agent+model to inherit ${ctx.agent}/${ctx.model ?? "default"}; overriding agent without model uses that agent's default.`)
21589
22148
  };
21590
22149
  return {
21591
22150
  name: "spawn_subagent",
21592
22151
  description: "Delegate a self-contained task to a subagent that works in its own new agent room. " + "Returns immediately; the subagent runs in the background and reports according to report_mode. " + "The fresh room has no parent conversation history; it receives the task plus the selected or inherited topic memory. " + "Use for parallelizable or long-running side work. Do not use provider built-in Task/Agent subagents.",
21593
22152
  schema: {
21594
- task: z4.string().describe("Self-contained task brief. The worker has topic memory but no parent conversation history, so include all task-specific context."),
21595
- name: z4.string().optional().describe("Short name for the subagent room. Auto-generated when omitted."),
22153
+ task: z5.string().describe("Self-contained task brief. The worker has topic memory but no parent conversation history, so include all task-specific context."),
22154
+ name: z5.string().optional().describe("Short name for the subagent room. Auto-generated when omitted."),
21596
22155
  ...executionOverrideSchema,
21597
- memory_topic: z4.string().optional().describe("Optional knowledge source: an accessible topic id/title or topic/*.md brief. Defaults to the parent room's effective topic brief."),
21598
- report_mode: z4.enum(["auto", "tell", "status-only"]).optional().describe("Completion reporting: auto injects the final result, tell requires tell_session, status-only updates only the lifecycle card.")
22156
+ memory_topic: z5.string().optional().describe("Optional knowledge source: an accessible topic id/title or topic/*.md brief. Defaults to the parent room's effective topic brief."),
22157
+ report_mode: z5.enum(["auto", "tell", "status-only"]).optional().describe("Completion reporting: auto injects the final result, tell requires tell_session, status-only updates only the lifecycle card.")
21599
22158
  },
21600
22159
  async handler(input) {
21601
22160
  return createSubagent(ctx, input, true);
@@ -21607,14 +22166,14 @@ ${card.task}${reportInstruction}`;
21607
22166
  name: "create_subagent",
21608
22167
  description: "Create a prepared subagent room and task definition without starting its AI turn. " + "Use start_subagent when the team is ready to run it.",
21609
22168
  schema: {
21610
- task: z4.string().describe("Self-contained task brief for the subagent."),
21611
- name: z4.string().optional().describe("Short room name. Auto-generated when omitted."),
22169
+ task: z5.string().describe("Self-contained task brief for the subagent."),
22170
+ name: z5.string().optional().describe("Short room name. Auto-generated when omitted."),
21612
22171
  ...options.executionOverrides === false ? {} : {
21613
- agent: z4.enum(["claude", "codex", "maestro"]).optional(),
21614
- model: z4.string().optional()
22172
+ agent: z5.enum(["claude", "codex", "maestro"]).optional(),
22173
+ model: z5.string().optional()
21615
22174
  },
21616
- memory_topic: z4.string().optional().describe("Optional accessible topic id/title or topic/*.md brief to use as this worker's knowledge source. Defaults to the parent."),
21617
- report_mode: z4.enum(["auto", "tell", "status-only"]).optional()
22175
+ memory_topic: z5.string().optional().describe("Optional accessible topic id/title or topic/*.md brief to use as this worker's knowledge source. Defaults to the parent."),
22176
+ report_mode: z5.enum(["auto", "tell", "status-only"]).optional()
21618
22177
  },
21619
22178
  async handler(input) {
21620
22179
  return createSubagent(ctx, input, false);
@@ -22737,14 +23296,14 @@ await init_spawn_subagent();
22737
23296
 
22738
23297
  // ../../packages/core/src/agents/mcp-tools/visuals.ts
22739
23298
  init_common();
22740
- import { z as z5 } from "zod";
23299
+ import { z as z6 } from "zod";
22741
23300
  var VISUALS_MCP_KEY = "visuals";
22742
23301
  var showHtmlTool = {
22743
23302
  name: "show_html",
22744
23303
  description: "Display charts, tables, or interactive HTML to the user in a sandboxed WebView side panel. Pass a complete, self-contained HTML string with inline CSS/JS. Local DOM interactions are supported. Links (<a href> to http/https URLs) are safe to include: a click opens the URL in the user's system browser. Network requests, external scripts, form posts, popups, and parent-window access are blocked.",
22745
23304
  schema: {
22746
- html: z5.string().describe("Complete, self-contained HTML document or fragment to render."),
22747
- title: z5.string().optional().describe("Optional title shown above the rendered card.")
23305
+ html: z6.string().describe("Complete, self-contained HTML document or fragment to render."),
23306
+ title: z6.string().optional().describe("Optional title shown above the rendered card.")
22748
23307
  },
22749
23308
  async handler() {
22750
23309
  return textResult("HTML card displayed to the user.");
@@ -22754,9 +23313,9 @@ var showMermaidTool = {
22754
23313
  name: "show_mermaid",
22755
23314
  description: "Render a Mermaid diagram in the user's visual side panel. Pass Mermaid DSL only, not markdown fences. Use this for flowcharts, sequence diagrams, class diagrams, state diagrams, ER diagrams, timelines, and architecture diagrams.",
22756
23315
  schema: {
22757
- code: z5.string().describe("Mermaid diagram source without markdown code fences."),
22758
- title: z5.string().optional().describe("Optional title shown above the rendered card."),
22759
- theme: z5.enum(["default", "neutral", "dark", "forest"]).optional().describe("Optional Mermaid theme. Defaults to neutral.")
23316
+ code: z6.string().describe("Mermaid diagram source without markdown code fences."),
23317
+ title: z6.string().optional().describe("Optional title shown above the rendered card."),
23318
+ theme: z6.enum(["default", "neutral", "dark", "forest"]).optional().describe("Optional Mermaid theme. Defaults to neutral.")
22760
23319
  },
22761
23320
  async handler() {
22762
23321
  return textResult("Mermaid diagram displayed to the user.");
@@ -22766,10 +23325,10 @@ var showImageTool = {
22766
23325
  name: "show_image",
22767
23326
  description: "Display an image in the user's visual side panel. Provide either file_path for an image in the topic workspace, or file_id for an uploaded file already attached in this topic.",
22768
23327
  schema: {
22769
- file_path: z5.string().optional().describe("Absolute or workspace-relative path to an image file."),
22770
- file_id: z5.string().optional().describe("Full Otium uploaded file UUID for an image attached in this topic."),
22771
- title: z5.string().optional().describe("Optional title shown above the rendered card."),
22772
- alt: z5.string().optional().describe("Optional alt text for the image.")
23328
+ file_path: z6.string().optional().describe("Absolute or workspace-relative path to an image file."),
23329
+ file_id: z6.string().optional().describe("Full Otium uploaded file UUID for an image attached in this topic."),
23330
+ title: z6.string().optional().describe("Optional title shown above the rendered card."),
23331
+ alt: z6.string().optional().describe("Optional alt text for the image.")
22773
23332
  },
22774
23333
  async handler(input) {
22775
23334
  if (!input?.file_path && !input?.file_id) {
@@ -22855,4 +23414,4 @@ export {
22855
23414
  DEFAULT_SELF_CONFIG_PRODUCT
22856
23415
  };
22857
23416
 
22858
- //# debugId=F428F94DC4F1D10264756E2164756E21
23417
+ //# debugId=A147953DF9A0AA1764756E2164756E21