negotium 0.22.3 → 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 (37) 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-6c637pwe.js → chunk-0jtan6fd.js} +3 -3
  5. package/dist/{chunk-6c637pwe.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 +1138 -252
  12. package/dist/main.js.map +21 -19
  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/turn-event-stream.ts +2 -0
  23. package/dist/runtime/src/storage/api-messages.ts +8 -2
  24. package/dist/runtime/src/storage/vault-request-gates.ts +359 -0
  25. package/dist/runtime/src/types/api.ts +23 -1
  26. package/dist/runtime/src/version.ts +1 -1
  27. package/dist/runtime-helpers.js +4 -1
  28. package/dist/runtime-helpers.js.map +3 -3
  29. package/dist/storage.js +8 -4
  30. package/dist/storage.js.map +3 -3
  31. package/dist/types/packages/core/src/agents/mcp-tools/request-vault.d.ts +120 -0
  32. package/dist/types/packages/core/src/storage/api-messages.d.ts +1 -0
  33. package/dist/types/packages/core/src/storage/vault-request-gates.d.ts +69 -0
  34. package/dist/types/packages/core/src/types/api.d.ts +15 -1
  35. package/dist/types/packages/core/src/version.d.ts +1 -1
  36. package/package.json +1 -1
  37. /package/dist/{chunk-bn1tt2qw.js.map → chunk-y4tyc603.js.map} +0 -0
@@ -2457,6 +2457,9 @@ function initializeApiMessagesSchema() {
2457
2457
  try {
2458
2458
  db.exec("ALTER TABLE api_messages ADD COLUMN subagent_card TEXT");
2459
2459
  } catch {}
2460
+ try {
2461
+ db.exec("ALTER TABLE api_messages ADD COLUMN vault_secret_request TEXT");
2462
+ } catch {}
2460
2463
  db.exec("CREATE INDEX IF NOT EXISTS idx_api_messages_topic ON api_messages(topic_id)");
2461
2464
  db.exec("CREATE INDEX IF NOT EXISTS idx_api_messages_thread_root ON api_messages(thread_root_id)");
2462
2465
  }
@@ -2493,6 +2496,7 @@ function rowToDto(r) {
2493
2496
  askUserQuestion: r.ask_user_question ? JSON.parse(r.ask_user_question) : undefined,
2494
2497
  tellCard: r.tell_card ? JSON.parse(r.tell_card) : undefined,
2495
2498
  subagentCard: r.subagent_card ? JSON.parse(r.subagent_card) : undefined,
2499
+ vaultSecretRequest: r.vault_secret_request ? JSON.parse(r.vault_secret_request) : undefined,
2496
2500
  mentions: r.mentions ? JSON.parse(r.mentions) : undefined,
2497
2501
  threadRootId: r.thread_root_id ?? undefined,
2498
2502
  createdAt: r.created_at
@@ -2514,9 +2518,9 @@ function appendApiMessage(msg, options = {}) {
2514
2518
  let inserted = false;
2515
2519
  db.transaction(() => {
2516
2520
  const result = db.query(`INSERT INTO api_messages
2517
- (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)
2518
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2519
- 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);
2521
+ (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)
2522
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2523
+ 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);
2520
2524
  inserted = Number(result.changes ?? 0) > 0;
2521
2525
  if (inserted && updateTopicLastMessageAt && !msg.deleted) {
2522
2526
  db.query(`UPDATE api_topics
@@ -4573,6 +4577,16 @@ function vaultListDecryptableValues(userId) {
4573
4577
  }
4574
4578
  return entries;
4575
4579
  }
4580
+ function vaultSet(userId, key, value2, description = "") {
4581
+ const normalizedKey = normalizeVaultKey(key);
4582
+ activeVaultDatabase().prepare(`INSERT INTO vault (user_id, key, value, description) VALUES (?, ?, ?, ?)
4583
+ ON CONFLICT (user_id, key) DO UPDATE
4584
+ SET value = excluded.value, description = excluded.description`).run(userId, normalizedKey, encryptVaultValue(userId, normalizedKey, value2, vaultMasterKey), description);
4585
+ }
4586
+ function vaultHasKey(userId, key) {
4587
+ const row = activeVaultDatabase().prepare("SELECT 1 FROM vault WHERE user_id = ? AND key = ?").get(userId, normalizeVaultKey(key));
4588
+ return row != null;
4589
+ }
4576
4590
  function vaultGetValue(userId, key) {
4577
4591
  const normalizedKey = normalizeVaultKey(key);
4578
4592
  const row = activeVaultDatabase().prepare("SELECT key, value FROM vault WHERE user_id = ? AND key = ?").get(userId, normalizedKey);
@@ -4631,7 +4645,7 @@ function redactVaultSecrets(userId, text) {
4631
4645
  }
4632
4646
  return redacted;
4633
4647
  }
4634
- var vaultDb, vaultMasterKey, VAULT_VALUE_MAX_BYTES;
4648
+ var vaultDb, vaultMasterKey, VAULT_KEY_PATTERN, VAULT_VALUE_MIN_BYTES = 4, VAULT_VALUE_MAX_BYTES, VAULT_DESCRIPTION_MAX_LENGTH = 500;
4635
4649
  var init_vault = __esm(async () => {
4636
4650
  init_config();
4637
4651
  init_vault_crypto();
@@ -4640,6 +4654,7 @@ var init_vault = __esm(async () => {
4640
4654
  init_storage_host()
4641
4655
  ]);
4642
4656
  vaultMasterKey = VAULT_MASTER_KEY;
4657
+ VAULT_KEY_PATTERN = /^[A-Z][A-Z0-9_]{0,127}$/;
4643
4658
  VAULT_VALUE_MAX_BYTES = 64 * 1024;
4644
4659
  });
4645
4660
 
@@ -5253,7 +5268,7 @@ var init_codex = __esm(async () => {
5253
5268
  });
5254
5269
 
5255
5270
  // ../../packages/core/src/version.ts
5256
- var NEGOTIUM_VERSION = "0.22.3";
5271
+ var NEGOTIUM_VERSION = "0.22.4";
5257
5272
 
5258
5273
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
5259
5274
  import { spawn as spawn2 } from "child_process";
@@ -18295,6 +18310,548 @@ var init_ask_user = __esm(async () => {
18295
18310
  });
18296
18311
  });
18297
18312
 
18313
+ // ../../packages/core/src/storage/vault-request-gates.ts
18314
+ function initializeVaultRequestGateSchema() {
18315
+ db.exec(`
18316
+ CREATE TABLE IF NOT EXISTS vault_request_gates (
18317
+ gate_id TEXT PRIMARY KEY,
18318
+ topic_id TEXT NOT NULL REFERENCES api_topics(id) ON DELETE CASCADE,
18319
+ query_id TEXT,
18320
+ idempotency_key TEXT NOT NULL,
18321
+ body_hash TEXT NOT NULL,
18322
+ message_id TEXT NOT NULL UNIQUE,
18323
+ owner_id TEXT NOT NULL,
18324
+ state TEXT NOT NULL,
18325
+ outcome TEXT,
18326
+ answered_by TEXT,
18327
+ claim_source TEXT,
18328
+ created_at TEXT NOT NULL,
18329
+ updated_at TEXT NOT NULL
18330
+ );
18331
+ CREATE INDEX IF NOT EXISTS idx_vault_request_gates_idempotency
18332
+ ON vault_request_gates(topic_id, idempotency_key, created_at);
18333
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_vault_request_gates_active_key
18334
+ ON vault_request_gates(topic_id, idempotency_key)
18335
+ WHERE state IN ('pending', 'claimed');
18336
+ `);
18337
+ }
18338
+ function fromRow2(row) {
18339
+ return {
18340
+ gateId: row.gate_id,
18341
+ topicId: row.topic_id,
18342
+ queryId: row.query_id ?? undefined,
18343
+ idempotencyKey: row.idempotency_key,
18344
+ bodyHash: row.body_hash,
18345
+ messageId: row.message_id,
18346
+ ownerId: row.owner_id,
18347
+ state: row.state,
18348
+ outcome: row.outcome ?? undefined,
18349
+ answeredBy: row.answered_by ?? undefined
18350
+ };
18351
+ }
18352
+ function latestGate2(topicId, idempotencyKey) {
18353
+ const row = db.query(`SELECT * FROM vault_request_gates
18354
+ WHERE topic_id = ? AND idempotency_key = ?
18355
+ ORDER BY rowid DESC LIMIT 1`).get(topicId, idempotencyKey);
18356
+ return row ? fromRow2(row) : null;
18357
+ }
18358
+ function expireVaultCard(topicId, messageId, editedAt) {
18359
+ const row = db.query(`SELECT vault_secret_request FROM api_messages
18360
+ WHERE topic_id = ? AND id = ? AND deleted = 0 AND kind = 'vault_secret_request'`).get(topicId, messageId);
18361
+ if (!row?.vault_secret_request)
18362
+ return null;
18363
+ const request = JSON.parse(row.vault_secret_request);
18364
+ if (request.status !== "pending")
18365
+ return null;
18366
+ const expired = { ...request, status: "expired" };
18367
+ db.query(`UPDATE api_messages SET vault_secret_request = ?, edited_at = ?
18368
+ WHERE topic_id = ? AND id = ? AND deleted = 0 AND kind = 'vault_secret_request'`).run(JSON.stringify(expired), editedAt, topicId, messageId);
18369
+ return { topicId, messageId, vaultSecretRequest: expired, editedAt };
18370
+ }
18371
+ function prepareVaultRequestGate(args) {
18372
+ return db.transaction(() => {
18373
+ const latest = latestGate2(args.topicId, args.idempotencyKey);
18374
+ if (latest?.bodyHash !== undefined && latest.bodyHash !== args.bodyHash) {
18375
+ return { outcome: "conflict", gate: latest };
18376
+ }
18377
+ if (latest?.state === "answered")
18378
+ return { outcome: "replay", gate: latest };
18379
+ if (latest && (latest.state === "pending" || latest.state === "claimed")) {
18380
+ return { outcome: "pending", gate: latest };
18381
+ }
18382
+ db.query(`INSERT INTO vault_request_gates
18383
+ (gate_id, topic_id, query_id, idempotency_key, body_hash, message_id, owner_id, state, created_at, updated_at)
18384
+ VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, ?)`).run(args.gateId, args.topicId, args.queryId ?? null, args.idempotencyKey, args.bodyHash, args.messageId, args.ownerId, args.now, args.now);
18385
+ return {
18386
+ outcome: "created",
18387
+ gate: {
18388
+ gateId: args.gateId,
18389
+ topicId: args.topicId,
18390
+ queryId: args.queryId,
18391
+ idempotencyKey: args.idempotencyKey,
18392
+ bodyHash: args.bodyHash,
18393
+ messageId: args.messageId,
18394
+ ownerId: args.ownerId,
18395
+ state: "pending"
18396
+ }
18397
+ };
18398
+ }).immediate();
18399
+ }
18400
+ function quarantineVaultRequestGate(gateId, ownerId, now = new Date().toISOString()) {
18401
+ return db.transaction(() => {
18402
+ const row = db.query("SELECT * FROM vault_request_gates WHERE gate_id = ?").get(gateId);
18403
+ if (!row || row.owner_id !== ownerId || !["pending", "claimed"].includes(row.state)) {
18404
+ return null;
18405
+ }
18406
+ const result = db.query(`UPDATE vault_request_gates SET state = 'quarantined', updated_at = ?
18407
+ WHERE gate_id = ? AND owner_id = ? AND state IN ('pending', 'claimed')`).run(now, gateId, ownerId);
18408
+ if (Number(result.changes ?? 0) === 0)
18409
+ return null;
18410
+ return expireVaultCard(row.topic_id, row.message_id, now);
18411
+ }).immediate();
18412
+ }
18413
+ function quarantineForeignVaultRequestGates(liveOwnerIds, now = new Date().toISOString()) {
18414
+ return db.transaction(() => {
18415
+ const rows = db.query(`SELECT * FROM vault_request_gates
18416
+ WHERE state IN ('pending', 'claimed')`).all();
18417
+ const updates = [];
18418
+ for (const row of rows) {
18419
+ if (liveOwnerIds.has(row.owner_id))
18420
+ continue;
18421
+ if (row.state === "claimed" && row.outcome && row.answered_by) {
18422
+ db.query(`UPDATE vault_request_gates SET state = 'answered', updated_at = ?
18423
+ WHERE gate_id = ? AND state = 'claimed'`).run(now, row.gate_id);
18424
+ continue;
18425
+ }
18426
+ const result = db.query(`UPDATE vault_request_gates SET state = 'quarantined', updated_at = ?
18427
+ WHERE gate_id = ? AND state IN ('pending', 'claimed')`).run(now, row.gate_id);
18428
+ if (Number(result.changes ?? 0) === 0)
18429
+ continue;
18430
+ const update = expireVaultCard(row.topic_id, row.message_id, now);
18431
+ if (update)
18432
+ updates.push(update);
18433
+ }
18434
+ const legacyCards = db.query(`SELECT m.topic_id, m.id
18435
+ FROM api_messages m
18436
+ LEFT JOIN vault_request_gates g ON g.message_id = m.id
18437
+ WHERE m.kind = 'vault_secret_request'
18438
+ AND m.deleted = 0
18439
+ AND g.gate_id IS NULL`).all();
18440
+ for (const card of legacyCards) {
18441
+ const update = expireVaultCard(card.topic_id, card.id, now);
18442
+ if (update)
18443
+ updates.push(update);
18444
+ }
18445
+ return updates;
18446
+ }).immediate();
18447
+ }
18448
+ function claimVaultRequestGateAndAnswer(args) {
18449
+ return db.transaction(() => {
18450
+ const row = db.query(`SELECT * FROM vault_request_gates
18451
+ WHERE topic_id = ? AND message_id = ?
18452
+ ORDER BY rowid DESC LIMIT 1`).get(args.topicId, args.messageId);
18453
+ if (!row || row.owner_id !== args.ownerId || row.state !== "pending") {
18454
+ return { outcome: "unavailable" };
18455
+ }
18456
+ const message = db.query(`SELECT vault_secret_request FROM api_messages
18457
+ WHERE topic_id = ? AND id = ? AND deleted = 0 AND kind = 'vault_secret_request'`).get(args.topicId, args.messageId);
18458
+ if (!message?.vault_secret_request)
18459
+ return { outcome: "unavailable" };
18460
+ const request = JSON.parse(message.vault_secret_request);
18461
+ if (request.status !== "pending")
18462
+ return { outcome: "unavailable" };
18463
+ const claimed = db.query(`UPDATE vault_request_gates
18464
+ SET state = 'answered', outcome = ?, answered_by = ?, claim_source = ?, updated_at = ?
18465
+ WHERE gate_id = ? AND owner_id = ? AND state = 'pending'`).run(args.outcome, args.userId, args.source, args.now, row.gate_id, args.ownerId);
18466
+ if (Number(claimed.changes ?? 0) === 0)
18467
+ return { outcome: "unavailable" };
18468
+ const answered = {
18469
+ ...request,
18470
+ status: args.outcome,
18471
+ answeredBy: args.userId
18472
+ };
18473
+ db.query(`UPDATE api_messages SET vault_secret_request = ?, edited_at = ?
18474
+ WHERE topic_id = ? AND id = ? AND deleted = 0 AND kind = 'vault_secret_request'`).run(JSON.stringify(answered), args.now, args.topicId, args.messageId);
18475
+ return {
18476
+ outcome: "claimed",
18477
+ gate: fromRow2({
18478
+ ...row,
18479
+ state: "answered",
18480
+ outcome: args.outcome,
18481
+ answered_by: args.userId
18482
+ }),
18483
+ vaultSecretRequest: answered,
18484
+ editedAt: args.now
18485
+ };
18486
+ }).immediate();
18487
+ }
18488
+ function cancelVaultRequestGate(topicId, messageId, ownerId, now = new Date().toISOString()) {
18489
+ return db.transaction(() => {
18490
+ const row = db.query(`SELECT * FROM vault_request_gates
18491
+ WHERE topic_id = ? AND message_id = ? ORDER BY rowid DESC LIMIT 1`).get(topicId, messageId);
18492
+ if (!row || row.owner_id !== ownerId || row.state !== "pending")
18493
+ return null;
18494
+ const result = db.query(`UPDATE vault_request_gates SET state = 'cancelled', updated_at = ?
18495
+ WHERE gate_id = ? AND owner_id = ? AND state = 'pending'`).run(now, row.gate_id, ownerId);
18496
+ if (Number(result.changes ?? 0) === 0)
18497
+ return null;
18498
+ return expireVaultCard(topicId, messageId, now);
18499
+ }).immediate();
18500
+ }
18501
+ var init_vault_request_gates = __esm(async () => {
18502
+ await __promiseAll([
18503
+ init_forum_db(),
18504
+ init_storage_host()
18505
+ ]);
18506
+ registerStorageSchemaInitializer(initializeVaultRequestGateSchema, 32);
18507
+ });
18508
+
18509
+ // ../../packages/core/src/agents/mcp-tools/request-vault.ts
18510
+ import { createHash as createHash12, randomUUID as randomUUID17 } from "crypto";
18511
+ import { z as z5 } from "zod";
18512
+ function requestBodyHash(key, description) {
18513
+ return createHash12("sha256").update(JSON.stringify({ key, description })).digest("hex");
18514
+ }
18515
+ function answerToolResult2(key, outcome) {
18516
+ if (outcome === "no_answer") {
18517
+ return errorResult("The AI turn ended before the user answered.");
18518
+ }
18519
+ if (outcome === "declined") {
18520
+ return errorResult(`User declined to provide a value for ${key}.`);
18521
+ }
18522
+ 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.`);
18523
+ }
18524
+ function createRequestVaultRuntime(host) {
18525
+ const pendingRequests = new Map;
18526
+ const { ownerId } = host.runtime;
18527
+ const createId = () => host.runtime.createId();
18528
+ const now = () => host.runtime.now();
18529
+ const isProcessLeaseAlive = (lease) => host.durability.processLeases.isAlive(lease);
18530
+ let gateOwnerLease = null;
18531
+ function broadcastGateUpdates(updates) {
18532
+ for (const update of updates)
18533
+ host.messaging.publication.publishCardUpdate(update);
18534
+ }
18535
+ function startGateOwner() {
18536
+ if (gateOwnerLease)
18537
+ return;
18538
+ gateOwnerLease = host.durability.processLeases.acquire(`${VAULT_GATE_LEASE_ROLE_PREFIX}${ownerId}`, ownerId);
18539
+ if (!gateOwnerLease)
18540
+ throw new Error("failed to acquire the vault-request gate owner lease");
18541
+ }
18542
+ function stopGateOwner() {
18543
+ gateOwnerLease?.stop();
18544
+ gateOwnerLease = null;
18545
+ }
18546
+ function reconcilePendingGates() {
18547
+ startGateOwner();
18548
+ host.durability.processLeases.removeDead(VAULT_GATE_LEASE_ROLE_PREFIX);
18549
+ const liveOwners = new Set(host.durability.processLeases.list(VAULT_GATE_LEASE_ROLE_PREFIX).filter(isProcessLeaseAlive).map((lease) => lease.ownerId));
18550
+ const updates = host.durability.gates.quarantineForeign(liveOwners, now());
18551
+ broadcastGateUpdates(updates);
18552
+ return updates.length;
18553
+ }
18554
+ function answerPendingRequest(topicId, messageId, userId, value2) {
18555
+ reconcilePendingGates();
18556
+ const topic = host.topics.getTopic(topicId);
18557
+ if (!topic?.participants.some((participant) => participant.userId === userId)) {
18558
+ return { ok: false, error: "User is not a member of this topic." };
18559
+ }
18560
+ const existing = host.messaging.persistence.getMessage(topicId, messageId);
18561
+ const request = existing?.kind === "vault_secret_request" ? existing.vaultSecretRequest : undefined;
18562
+ if (!existing || existing.deleted || !request) {
18563
+ return { ok: false, error: "Vault request card not found." };
18564
+ }
18565
+ if (request.status !== "pending") {
18566
+ return { ok: false, error: "This request is no longer awaiting an answer." };
18567
+ }
18568
+ const pending = pendingRequests.get(messageId);
18569
+ if (!pending || pending.topicId !== topicId) {
18570
+ return { ok: false, error: "This request is no longer awaiting an answer." };
18571
+ }
18572
+ const declining = value2 === null;
18573
+ if (!declining) {
18574
+ const bytes = Buffer.byteLength(value2, "utf8");
18575
+ if (bytes < VAULT_VALUE_MIN_BYTES || bytes > VAULT_VALUE_MAX_BYTES) {
18576
+ return {
18577
+ ok: false,
18578
+ error: `Value must be between ${VAULT_VALUE_MIN_BYTES} and ${VAULT_VALUE_MAX_BYTES} bytes.`
18579
+ };
18580
+ }
18581
+ }
18582
+ if (!declining) {
18583
+ try {
18584
+ host.vault.set(userId, pending.key, value2, pending.description);
18585
+ } catch (error) {
18586
+ return {
18587
+ ok: false,
18588
+ error: `Failed to store the value: ${error instanceof Error ? error.message : String(error)}`
18589
+ };
18590
+ }
18591
+ }
18592
+ const outcome = declining ? "declined" : "fulfilled";
18593
+ let claim;
18594
+ try {
18595
+ claim = host.durability.gates.claimAndAnswer({
18596
+ topicId,
18597
+ messageId,
18598
+ outcome,
18599
+ userId,
18600
+ ownerId,
18601
+ source: "host-control",
18602
+ now: now()
18603
+ });
18604
+ } catch {
18605
+ return { ok: false, error: "This request could not be claimed safely. Retry once." };
18606
+ }
18607
+ if (claim.outcome !== "claimed") {
18608
+ return { ok: false, error: "This request is no longer awaiting an answer." };
18609
+ }
18610
+ pendingRequests.delete(messageId);
18611
+ const answerMessage = {
18612
+ id: createId(),
18613
+ topicId,
18614
+ authorId: userId,
18615
+ text: outcome === "fulfilled" ? `Provided a value for ${pending.key}.` : `Declined to provide a value for ${pending.key}.`,
18616
+ parentId: messageId,
18617
+ ...existing.threadRootId ? { threadRootId: existing.threadRootId } : {},
18618
+ createdAt: now()
18619
+ };
18620
+ try {
18621
+ host.messaging.publication.publishCardUpdate({
18622
+ topicId,
18623
+ messageId,
18624
+ vaultSecretRequest: claim.vaultSecretRequest,
18625
+ editedAt: claim.editedAt
18626
+ });
18627
+ host.messaging.persistence.appendMessage(answerMessage);
18628
+ host.messaging.publication.publishMessage(topicId, answerMessage);
18629
+ } catch {}
18630
+ pending.resolve({ outcome, userId });
18631
+ return { ok: true, queryId: pending.queryId, outcome, answerMessage };
18632
+ }
18633
+ function cancelPendingRequests(topicId, queryId) {
18634
+ for (const [messageId, pending] of pendingRequests) {
18635
+ if (pending.topicId === topicId && pending.queryId === queryId) {
18636
+ pendingRequests.delete(messageId);
18637
+ const update = host.durability.gates.cancel(topicId, messageId, ownerId, now());
18638
+ if (update)
18639
+ broadcastGateUpdates([update]);
18640
+ pending.resolve({ outcome: "no_answer", userId: "" });
18641
+ }
18642
+ }
18643
+ }
18644
+ function appendRequestMessage(ctx, key, description, messageId) {
18645
+ const cfg = host.topics.getConfig(ctx.topicId);
18646
+ const vaultSecretRequest = { key, description, status: "pending" };
18647
+ const message = {
18648
+ id: messageId,
18649
+ topicId: ctx.topicId,
18650
+ authorId: "ai",
18651
+ text: `Vault secret requested: ${key}`,
18652
+ queryId: ctx.queryId,
18653
+ agentType: ctx.agent,
18654
+ model: ctx.model ?? cfg?.model ?? "unknown",
18655
+ kind: "vault_secret_request",
18656
+ vaultSecretRequest,
18657
+ ...ctx.threadRootId ? { threadRootId: ctx.threadRootId } : {},
18658
+ createdAt: now()
18659
+ };
18660
+ host.messaging.persistence.appendMessage(message);
18661
+ return message;
18662
+ }
18663
+ async function requestVaultSecret(ctx, input) {
18664
+ if (ctx.unsupportedSurface) {
18665
+ 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.");
18666
+ }
18667
+ reconcilePendingGates();
18668
+ const topic = host.topics.getTopic(ctx.topicId);
18669
+ if (!topic)
18670
+ return errorResult(`Error: topic '${ctx.topicId}' not found.`);
18671
+ if (!topic.participants.some((participant) => participant.userId === ctx.userId)) {
18672
+ return errorResult("Error: user is not a member of this topic.");
18673
+ }
18674
+ if (typeof input.key !== "string" || !input.key.trim()) {
18675
+ return errorResult("Error: key is required.");
18676
+ }
18677
+ const key = normalizeVaultKey(input.key);
18678
+ if (!VAULT_KEY_PATTERN.test(key)) {
18679
+ return errorResult("Error: key must start with a letter and contain only uppercase letters, digits, and underscores (max 128 chars).");
18680
+ }
18681
+ if (typeof input.description !== "string" || !input.description.trim()) {
18682
+ return errorResult("Error: description is required so the user knows what to type.");
18683
+ }
18684
+ const description = input.description.trim().slice(0, MAX_DESCRIPTION_CHARS);
18685
+ if (input.idempotency_key !== undefined && (typeof input.idempotency_key !== "string" || !input.idempotency_key.trim())) {
18686
+ return errorResult("Error: idempotency_key must be a non-empty string.");
18687
+ }
18688
+ const rawIdempotencyKey = typeof input.idempotency_key === "string" ? input.idempotency_key.trim() : createId();
18689
+ if (rawIdempotencyKey.length > MAX_IDEMPOTENCY_KEY_CHARS2) {
18690
+ return errorResult(`Error: idempotency_key must be at most ${MAX_IDEMPOTENCY_KEY_CHARS2} characters.`);
18691
+ }
18692
+ const bodyHash = requestBodyHash(key, description);
18693
+ const createGate = () => {
18694
+ const suffix = createId();
18695
+ return host.durability.gates.prepare({
18696
+ gateId: `vault-request-gate-${suffix}`,
18697
+ topicId: ctx.topicId,
18698
+ queryId: ctx.queryId,
18699
+ idempotencyKey: rawIdempotencyKey,
18700
+ bodyHash,
18701
+ messageId: `vault-request-${ctx.queryId ?? "runtime"}-${suffix}`,
18702
+ ownerId,
18703
+ now: now()
18704
+ });
18705
+ };
18706
+ let prepared = createGate();
18707
+ if (prepared.outcome === "conflict") {
18708
+ return errorResult("Error: idempotency_conflict. The same idempotency_key was already used with a different key or description.");
18709
+ }
18710
+ if (prepared.outcome === "replay") {
18711
+ if (!prepared.gate.outcome || !prepared.gate.answeredBy) {
18712
+ return errorResult("Error: the stored request_vault_secret replay is incomplete.");
18713
+ }
18714
+ return answerToolResult2(key, prepared.gate.outcome);
18715
+ }
18716
+ if (prepared.outcome === "pending") {
18717
+ if (prepared.gate.ownerId !== ownerId) {
18718
+ return errorResult("Error: this request_vault_secret is pending in another healthy runtime process.");
18719
+ }
18720
+ const existingPending = pendingRequests.get(prepared.gate.messageId);
18721
+ if (existingPending)
18722
+ return answerToolResult2(key, (await existingPending.promise).outcome);
18723
+ if (prepared.gate.state === "claimed") {
18724
+ return errorResult("Error: this request_vault_secret is already being finalized.");
18725
+ }
18726
+ const update = host.durability.gates.quarantine(prepared.gate.gateId, ownerId, now());
18727
+ if (update)
18728
+ broadcastGateUpdates([update]);
18729
+ prepared = createGate();
18730
+ }
18731
+ if (prepared.outcome !== "created") {
18732
+ return errorResult("Error: failed to create a durable request_vault_secret gate.");
18733
+ }
18734
+ if (host.vault.hasKey(ctx.userId, key)) {
18735
+ host.durability.gates.quarantine(prepared.gate.gateId, ownerId, now());
18736
+ return errorResult(`Error: vault key '${key}' already exists. Pick a different key, or ask the user to update it from their Vault manager.`);
18737
+ }
18738
+ let message;
18739
+ try {
18740
+ message = appendRequestMessage(ctx, key, description, prepared.gate.messageId);
18741
+ } catch (error) {
18742
+ host.durability.gates.quarantine(prepared.gate.gateId, ownerId, now());
18743
+ return errorResult(`Error: failed to persist request_vault_secret: ${error instanceof Error ? error.message : String(error)}`);
18744
+ }
18745
+ let resolveAnswer;
18746
+ const promise = new Promise((resolve17) => {
18747
+ resolveAnswer = resolve17;
18748
+ });
18749
+ pendingRequests.set(message.id, {
18750
+ topicId: ctx.topicId,
18751
+ queryId: ctx.queryId,
18752
+ messageId: message.id,
18753
+ key,
18754
+ description,
18755
+ promise,
18756
+ resolve: resolveAnswer
18757
+ });
18758
+ try {
18759
+ host.messaging.publication.publishMessage(ctx.topicId, message);
18760
+ } catch (error) {
18761
+ pendingRequests.delete(message.id);
18762
+ try {
18763
+ host.durability.gates.quarantine(prepared.gate.gateId, ownerId, now());
18764
+ } catch {}
18765
+ return errorResult(`Error: failed to publish request_vault_secret: ${error instanceof Error ? error.message : String(error)}`);
18766
+ }
18767
+ return answerToolResult2(key, (await promise).outcome);
18768
+ }
18769
+ function createToolDefinition(ctx) {
18770
+ return {
18771
+ name: "request_vault_secret",
18772
+ 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.",
18773
+ schema: {
18774
+ key: z5.string().describe("New Vault key name, e.g. STRIPE_API_KEY. Letters/digits/underscores, must start with a letter."),
18775
+ description: z5.string().max(MAX_DESCRIPTION_CHARS).describe("One sentence telling the user what value to paste and where it's from."),
18776
+ idempotency_key: z5.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.")
18777
+ },
18778
+ async handler(input) {
18779
+ return requestVaultSecret(ctx, input);
18780
+ }
18781
+ };
18782
+ }
18783
+ return {
18784
+ startGateOwner,
18785
+ stopGateOwner,
18786
+ reconcilePendingGates,
18787
+ answerPendingRequest,
18788
+ cancelPendingRequests,
18789
+ createToolDefinition
18790
+ };
18791
+ }
18792
+ function cancelPendingVaultSecretRequests(topicId, queryId) {
18793
+ defaultRequestVaultRuntime.cancelPendingRequests(topicId, queryId);
18794
+ }
18795
+ var MAX_DESCRIPTION_CHARS, MAX_IDEMPOTENCY_KEY_CHARS2 = 200, VAULT_GATE_OWNER_ID, VAULT_GATE_LEASE_ROLE_PREFIX = "vault-request-gate:", defaultRequestVaultDurabilityHost, defaultRequestVaultRuntime;
18796
+ var init_request_vault = __esm(async () => {
18797
+ init_common();
18798
+ await __promiseAll([
18799
+ init_bus(),
18800
+ init_api_messages(),
18801
+ init_api_topic_config(),
18802
+ init_api_topics(),
18803
+ init_runtime_process_leases(),
18804
+ init_vault(),
18805
+ init_vault_request_gates()
18806
+ ]);
18807
+ MAX_DESCRIPTION_CHARS = VAULT_DESCRIPTION_MAX_LENGTH;
18808
+ VAULT_GATE_OWNER_ID = `request-vault-${process.pid}-${randomUUID17()}`;
18809
+ defaultRequestVaultDurabilityHost = {
18810
+ gates: {
18811
+ prepare: prepareVaultRequestGate,
18812
+ claimAndAnswer: claimVaultRequestGateAndAnswer,
18813
+ cancel: cancelVaultRequestGate,
18814
+ quarantine: quarantineVaultRequestGate,
18815
+ quarantineForeign: quarantineForeignVaultRequestGates
18816
+ },
18817
+ processLeases: {
18818
+ acquire: (role, ownerId) => acquireRuntimeProcessLease(role, { ownerId }),
18819
+ list: listRuntimeProcessLeases,
18820
+ isAlive: isRuntimeProcessLeaseAlive,
18821
+ removeDead: removeDeadRuntimeProcessLeases
18822
+ }
18823
+ };
18824
+ defaultRequestVaultRuntime = createRequestVaultRuntime({
18825
+ messaging: {
18826
+ persistence: {
18827
+ getMessage: getApiMessage,
18828
+ appendMessage: appendApiMessage
18829
+ },
18830
+ publication: {
18831
+ publishMessage: (topicId, message) => WsHub.get().broadcastMessage(topicId, message),
18832
+ publishCardUpdate: (update) => WsHub.get().broadcastMessageUpdated(update.topicId, update.messageId, {
18833
+ vaultSecretRequest: update.vaultSecretRequest,
18834
+ editedAt: update.editedAt
18835
+ })
18836
+ }
18837
+ },
18838
+ topics: {
18839
+ getTopic,
18840
+ getConfig: getApiTopicConfig
18841
+ },
18842
+ vault: {
18843
+ hasKey: vaultHasKey,
18844
+ set: vaultSet
18845
+ },
18846
+ durability: defaultRequestVaultDurabilityHost,
18847
+ runtime: {
18848
+ ownerId: VAULT_GATE_OWNER_ID,
18849
+ createId: randomUUID17,
18850
+ now: () => new Date().toISOString()
18851
+ }
18852
+ });
18853
+ });
18854
+
18298
18855
  // ../../packages/core/src/mcp/peer-bridge.ts
18299
18856
  function flushPeerRuntimeEvents(localTopicId) {
18300
18857
  return activeBridge2?.flushEvents?.(localTopicId) ?? Promise.resolve(true);
@@ -18669,7 +19226,7 @@ var init_visuals = __esm(async () => {
18669
19226
  });
18670
19227
 
18671
19228
  // ../../packages/core/src/runtime/turn-event-stream.ts
18672
- import { randomUUID as randomUUID17 } from "crypto";
19229
+ import { randomUUID as randomUUID18 } from "crypto";
18673
19230
  import { realpathSync as realpathSync7, statSync as statSync9 } from "fs";
18674
19231
  import { isAbsolute as isAbsolute6, resolve as resolve18 } from "path";
18675
19232
  function sessionEventMatchesCurrentExecution(topicId, queryId, agent, model) {
@@ -18776,7 +19333,7 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
18776
19333
  if (silent || !text2.trim())
18777
19334
  return null;
18778
19335
  const message = {
18779
- id: randomUUID17(),
19336
+ id: randomUUID18(),
18780
19337
  topicId,
18781
19338
  authorId: "ai",
18782
19339
  text: text2,
@@ -19256,6 +19813,7 @@ ${JSON.stringify(event.input ?? {})}`);
19256
19813
  const stillCurrent = getRoomQuery(roomId)?.queryId === queryId;
19257
19814
  clearRoomQuery(roomId, queryId);
19258
19815
  cancelPendingAskUserQuestions(topicId, queryId);
19816
+ cancelPendingVaultSecretRequests(topicId, queryId);
19259
19817
  if (stillCurrent && roomId === topicId)
19260
19818
  clearQueryState(userId, topicId, topicTitle);
19261
19819
  if (!silent && stillCurrent)
@@ -19299,6 +19857,7 @@ var init_turn_event_stream = __esm(async () => {
19299
19857
  init_idle_archiver(),
19300
19858
  init_idle_compact(),
19301
19859
  init_ask_user(),
19860
+ init_request_vault(),
19302
19861
  init_spawn_subagent(),
19303
19862
  init_registry(),
19304
19863
  init_bus(),
@@ -19453,7 +20012,7 @@ __export(exports_turn_runner, {
19453
20012
  FALLBACK_ORDER: () => FALLBACK_ORDER,
19454
20013
  AGENT_DISPLAY_NAME: () => AGENT_DISPLAY_NAME
19455
20014
  });
19456
- import { randomUUID as randomUUID18 } from "crypto";
20015
+ import { randomUUID as randomUUID19 } from "crypto";
19457
20016
  import { existsSync as existsSync24, mkdirSync as mkdirSync21, readdirSync as readdirSync7, statSync as statSync10 } from "fs";
19458
20017
  import { join as join33 } from "path";
19459
20018
  function withDefaultPlaywright(configuredMcp, isManager) {
@@ -19478,7 +20037,7 @@ ${reminderPrompt}` };
19478
20037
  }
19479
20038
  function appendSystemMessage(topicId, text2, threadRootId) {
19480
20039
  const message = {
19481
- id: randomUUID18(),
20040
+ id: randomUUID19(),
19482
20041
  topicId,
19483
20042
  authorId: "system",
19484
20043
  text: text2,
@@ -19500,7 +20059,7 @@ function notifyPlaywrightUnavailable(topicId) {
19500
20059
  }
19501
20060
  function appendAskReplyMessage(topicId, text2, sourceLabel, body, kind, agentType, threadRootId) {
19502
20061
  const message = {
19503
- id: randomUUID18(),
20062
+ id: randomUUID19(),
19504
20063
  topicId,
19505
20064
  authorId: "ai",
19506
20065
  text: text2,
@@ -20021,7 +20580,7 @@ function startAiTurn(params) {
20021
20580
  const askReplySources = params.askReplySources;
20022
20581
  const sessionRetried = params._sessionRetried === true;
20023
20582
  const emptyResponseRetried = params._emptyResponseRetried === true;
20024
- const queryId = asTurnId(params._queryId ?? randomUUID18());
20583
+ const queryId = asTurnId(params._queryId ?? randomUUID19());
20025
20584
  const roomId = turnConcurrency === "isolated" ? isolatedTurnRoomId(topicId, queryId) : asTurnSlotKey(topicId);
20026
20585
  const currentRuntimeEpoch = getRuntimeTopicEpoch(topic.id);
20027
20586
  const runtimeEpoch = params._runtimeEpoch ?? currentRuntimeEpoch;
@@ -20792,7 +21351,7 @@ function triggerTopicAiTurn(topicId, userId, prompt, agentType, opts) {
20792
21351
  if (!opts?.silent && !opts?.hideInjectMessage) {
20793
21352
  const now = new Date().toISOString();
20794
21353
  const injectMsg = {
20795
- id: opts?.requestId ? `tell-${opts.requestId}` : `tell-${randomUUID18()}`,
21354
+ id: opts?.requestId ? `tell-${opts.requestId}` : `tell-${randomUUID19()}`,
20796
21355
  topicId,
20797
21356
  authorId: opts?.injectAuthorId ?? userId,
20798
21357
  sourceAdapter: opts?.injectSourceAdapter,
@@ -21016,7 +21575,7 @@ var init_auth_check = __esm(async () => {
21016
21575
  });
21017
21576
 
21018
21577
  // ../../packages/core/src/agents/mcp-tools/spawn-subagent.ts
21019
- import { z as z5 } from "zod";
21578
+ import { z as z6 } from "zod";
21020
21579
  function activeQueryUserId(topic) {
21021
21580
  return topic?.participants.find((participant) => hasActiveQuery(participant.userId, topic.id))?.userId;
21022
21581
  }
@@ -21583,7 +22142,7 @@ ${card.task}${reportInstruction}`;
21583
22142
  name: "start_subagent",
21584
22143
  description: "Start a prepared descendant subagent task.",
21585
22144
  schema: {
21586
- topic_id: z5.string().describe("Prepared subagent room id.")
22145
+ topic_id: z6.string().describe("Prepared subagent room id.")
21587
22146
  },
21588
22147
  async handler(input) {
21589
22148
  const result = ownedSubagentTree(ctx);
@@ -21603,7 +22162,7 @@ ${card.task}${reportInstruction}`;
21603
22162
  name: "delete_subagent",
21604
22163
  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.",
21605
22164
  schema: {
21606
- topic_id: z5.string().describe("Exact subagent room id returned by list_subagents; names are not accepted.")
22165
+ topic_id: z6.string().describe("Exact subagent room id returned by list_subagents; names are not accepted.")
21607
22166
  },
21608
22167
  async handler(input) {
21609
22168
  const result = ownedSubagentTree(ctx);
@@ -21627,8 +22186,8 @@ ${card.task}${reportInstruction}`;
21627
22186
  name: "grant_subagent_tell",
21628
22187
  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.",
21629
22188
  schema: {
21630
- subagent_topic_id: z5.string().describe("Source descendant subagent topic id."),
21631
- target_topic_id: z5.string().describe("Target topic id: this manager room or another descendant in its tree.")
22189
+ subagent_topic_id: z6.string().describe("Source descendant subagent topic id."),
22190
+ target_topic_id: z6.string().describe("Target topic id: this manager room or another descendant in its tree.")
21632
22191
  },
21633
22192
  async handler(input) {
21634
22193
  const result = ownedSubagentTree(ctx);
@@ -21653,8 +22212,8 @@ ${card.task}${reportInstruction}`;
21653
22212
  name: "revoke_subagent_tell",
21654
22213
  description: "Remove an extra tell_session connection previously granted to a descendant subagent.",
21655
22214
  schema: {
21656
- subagent_topic_id: z5.string().describe("Source descendant subagent topic id."),
21657
- target_topic_id: z5.string().describe("Granted target topic id.")
22215
+ subagent_topic_id: z6.string().describe("Source descendant subagent topic id."),
22216
+ target_topic_id: z6.string().describe("Granted target topic id.")
21658
22217
  },
21659
22218
  async handler(input) {
21660
22219
  const result = ownedSubagentTree(ctx);
@@ -21676,18 +22235,18 @@ ${card.task}${reportInstruction}`;
21676
22235
  }
21677
22236
  function createSpawnSubagentToolDefinition(ctx, options = {}) {
21678
22237
  const executionOverrideSchema = options.executionOverrides === false ? {} : {
21679
- agent: z5.enum(["claude", "codex", "maestro"]).optional().describe("Agent backend override. Defaults to this room's agent."),
21680
- 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.`)
22238
+ agent: z6.enum(["claude", "codex", "maestro"]).optional().describe("Agent backend override. Defaults to this room's agent."),
22239
+ model: z6.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.`)
21681
22240
  };
21682
22241
  return {
21683
22242
  name: "spawn_subagent",
21684
22243
  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.",
21685
22244
  schema: {
21686
- task: z5.string().describe("Self-contained task brief. The worker has topic memory but no parent conversation history, so include all task-specific context."),
21687
- name: z5.string().optional().describe("Short name for the subagent room. Auto-generated when omitted."),
22245
+ task: z6.string().describe("Self-contained task brief. The worker has topic memory but no parent conversation history, so include all task-specific context."),
22246
+ name: z6.string().optional().describe("Short name for the subagent room. Auto-generated when omitted."),
21688
22247
  ...executionOverrideSchema,
21689
- 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."),
21690
- 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.")
22248
+ memory_topic: z6.string().optional().describe("Optional knowledge source: an accessible topic id/title or topic/*.md brief. Defaults to the parent room's effective topic brief."),
22249
+ report_mode: z6.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.")
21691
22250
  },
21692
22251
  async handler(input) {
21693
22252
  return createSubagent(ctx, input, true);
@@ -21699,14 +22258,14 @@ ${card.task}${reportInstruction}`;
21699
22258
  name: "create_subagent",
21700
22259
  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.",
21701
22260
  schema: {
21702
- task: z5.string().describe("Self-contained task brief for the subagent."),
21703
- name: z5.string().optional().describe("Short room name. Auto-generated when omitted."),
22261
+ task: z6.string().describe("Self-contained task brief for the subagent."),
22262
+ name: z6.string().optional().describe("Short room name. Auto-generated when omitted."),
21704
22263
  ...options.executionOverrides === false ? {} : {
21705
- agent: z5.enum(["claude", "codex", "maestro"]).optional(),
21706
- model: z5.string().optional()
22264
+ agent: z6.enum(["claude", "codex", "maestro"]).optional(),
22265
+ model: z6.string().optional()
21707
22266
  },
21708
- 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."),
21709
- report_mode: z5.enum(["auto", "tell", "status-only"]).optional()
22267
+ memory_topic: z6.string().optional().describe("Optional accessible topic id/title or topic/*.md brief to use as this worker's knowledge source. Defaults to the parent."),
22268
+ report_mode: z6.enum(["auto", "tell", "status-only"]).optional()
21710
22269
  },
21711
22270
  async handler(input) {
21712
22271
  return createSubagent(ctx, input, false);
@@ -24636,7 +25195,7 @@ ${renderDecisionList(decisions)}`);
24636
25195
  }
24637
25196
  // ../../packages/core/src/mcp/factories/session-comm.ts
24638
25197
  import { McpServer as McpServer4 } from "@modelcontextprotocol/sdk/server/mcp.js";
24639
- import { z as z6 } from "zod";
25198
+ import { z as z7 } from "zod";
24640
25199
 
24641
25200
  // ../../packages/core/src/mcp/session-comm/tell-permissions.ts
24642
25201
  await __promiseAll([
@@ -24677,29 +25236,29 @@ function createSessionCommMcpServer(context, host, options = {}) {
24677
25236
  const subagentIdentity = resolveSubagentTellIdentity(context.currentTopicId, context.subagentParentTopicId);
24678
25237
  server.tool("list_sessions", "List available sessions and remote peer sessions for inter-session communication.", {}, async () => host.listSessions(context));
24679
25238
  server.tool("configure_mcp", `Configure optional MCP servers for the current topic. Required: ${required}. Optional: ${optional}.`, {
24680
- enabled: z6.array(z6.string()).nullable().optional()
25239
+ enabled: z7.array(z7.string()).nullable().optional()
24681
25240
  }, async ({ enabled }) => host.configureMcp(context, enabled));
24682
25241
  server.tool("get_mcp_config", "Get the current topic MCP configuration.", {}, async () => host.getMcpConfig(context));
24683
25242
  if (host.getBrowserProfile) {
24684
25243
  server.tool("get_browser_profile", "Get this topic's browser profile and the profiles owned by the same user.", {}, async () => host.getBrowserProfile(context));
24685
25244
  }
24686
25245
  if (host.setBrowserProfile) {
24687
- server.tool("set_browser_profile", "Assign this topic to a named shared browser profile. Takes effect next turn.", { profile: z6.string() }, async ({ profile }) => host.setBrowserProfile(context, profile));
25246
+ server.tool("set_browser_profile", "Assign this topic to a named shared browser profile. Takes effect next turn.", { profile: z7.string() }, async ({ profile }) => host.setBrowserProfile(context, profile));
24688
25247
  }
24689
25248
  if (host.deleteBrowserProfile) {
24690
- server.tool("delete_browser_profile", "Delete an unused named browser profile and its on-disk data. The default or an assigned profile cannot be deleted.", { profile: z6.string() }, async ({ profile }) => host.deleteBrowserProfile(context, profile));
25249
+ server.tool("delete_browser_profile", "Delete an unused named browser profile and its on-disk data. The default or an assigned profile cannot be deleted.", { profile: z7.string() }, async ({ profile }) => host.deleteBrowserProfile(context, profile));
24691
25250
  }
24692
25251
  server.tool("peek_session", "Inspect running and idle sessions, including pending ask_session calls.", {}, async () => host.peekSession(context));
24693
- server.tool("set_description", "Set the current session description used as a routing hint.", { description: z6.string() }, async ({ description }) => host.setDescription(context, description));
25252
+ server.tool("set_description", "Set the current session description used as a routing hint.", { description: z7.string() }, async ({ description }) => host.setDescription(context, description));
24694
25253
  if (!context.replyOnly) {
24695
25254
  if (!subagentIdentity.restricted) {
24696
- server.tool("ask_session", "Ask another local or remote session a question and wait for its answer.", { to: z6.string(), message: z6.string() }, async (input) => host.askSession(context, input));
24697
- server.tool("ask_cron", "Ask this topic's shared Cron session a question. The answer is injected asynchronously into the current conversation.", { message: z6.string() }, async ({ message }) => host.askCron(context, message));
25255
+ server.tool("ask_session", "Ask another local or remote session a question and wait for its answer.", { to: z7.string(), message: z7.string() }, async (input) => host.askSession(context, input));
25256
+ server.tool("ask_cron", "Ask this topic's shared Cron session a question. The answer is injected asynchronously into the current conversation.", { message: z7.string() }, async ({ message }) => host.askCron(context, message));
24698
25257
  }
24699
25258
  if (!subagentIdentity.restricted) {
24700
- server.tool("abort_session", "Abort the active query in another session.", { to: z6.string() }, async ({ to }) => host.abortSession(context, to));
25259
+ server.tool("abort_session", "Abort the active query in another session.", { to: z7.string() }, async ({ to }) => host.abortSession(context, to));
24701
25260
  }
24702
- server.tool("tell_session", "Send a one-way message to another local or remote session.", { to: z6.string(), message: z6.string() }, async (input) => {
25261
+ server.tool("tell_session", "Send a one-way message to another local or remote session.", { to: z7.string(), message: z7.string() }, async (input) => {
24703
25262
  if (subagentIdentity.restricted && !canSubagentTellTargetByName(subagentIdentity, input.to)) {
24704
25263
  return {
24705
25264
  content: [
@@ -24916,7 +25475,7 @@ function createSystemHealthMcpServer(host = defaultSystemHealthMcpHost) {
24916
25475
  init_mcp_helpers();
24917
25476
  await init_tasks();
24918
25477
  import { McpServer as McpServer6 } from "@modelcontextprotocol/sdk/server/mcp.js";
24919
- import { z as z7 } from "zod";
25478
+ import { z as z8 } from "zod";
24920
25479
  var defaultTaskMcpHost = {
24921
25480
  readTasks,
24922
25481
  writeTasks
@@ -24930,13 +25489,13 @@ function createTaskMcpServer(context, host = defaultTaskMcpHost) {
24930
25489
  }
24931
25490
  return null;
24932
25491
  };
24933
- const statusEnum = z7.enum(TASK_STATUS_VALUES);
25492
+ const statusEnum = z8.enum(TASK_STATUS_VALUES);
24934
25493
  server.tool("task_create", "Add one or more tasks to this topic's shared Otium task list. Use this instead of provider-native todo/task tools.", {
24935
- tasks: z7.array(z7.object({
24936
- subject: z7.string().min(1).describe("Short imperative task description"),
24937
- active_form: z7.string().optional().describe("Label to show while the task is in progress"),
24938
- blocked_by: z7.array(z7.string()).optional().describe("Task ids blocking this task"),
24939
- owner: z7.string().optional().describe("Owner/agent label for multi-agent work")
25494
+ tasks: z8.array(z8.object({
25495
+ subject: z8.string().min(1).describe("Short imperative task description"),
25496
+ active_form: z8.string().optional().describe("Label to show while the task is in progress"),
25497
+ blocked_by: z8.array(z8.string()).optional().describe("Task ids blocking this task"),
25498
+ owner: z8.string().optional().describe("Owner/agent label for multi-agent work")
24940
25499
  })).min(1)
24941
25500
  }, async ({ tasks: inputs }) => {
24942
25501
  const guard = requireContext();
@@ -24960,13 +25519,13 @@ ${renderTaskList(tasks2)}`);
24960
25519
  }
24961
25520
  });
24962
25521
  server.tool("task_update", "Update task status/content in this topic's shared Otium task list. Batch related status changes in one call.", {
24963
- updates: z7.array(z7.object({
24964
- id: z7.string().describe("Target task id"),
25522
+ updates: z8.array(z8.object({
25523
+ id: z8.string().describe("Target task id"),
24965
25524
  status: statusEnum.optional().describe("New status"),
24966
- subject: z7.string().min(1).optional().describe("Replacement task description"),
24967
- active_form: z7.string().optional().describe("Replacement in-progress label; empty string clears it"),
24968
- blocked_by: z7.array(z7.string()).optional().describe("Replacement blocker id list"),
24969
- owner: z7.string().optional().describe("Replacement owner; empty string clears it")
25525
+ subject: z8.string().min(1).optional().describe("Replacement task description"),
25526
+ active_form: z8.string().optional().describe("Replacement in-progress label; empty string clears it"),
25527
+ blocked_by: z8.array(z8.string()).optional().describe("Replacement blocker id list"),
25528
+ owner: z8.string().optional().describe("Replacement owner; empty string clears it")
24970
25529
  })).min(1)
24971
25530
  }, async ({ updates }) => {
24972
25531
  const guard = requireContext();
@@ -25000,7 +25559,7 @@ Missing ids ignored: ${missing.join(", ")}` : "";
25000
25559
  return mcpError(`task_list failed: ${errMsg(error)}`);
25001
25560
  }
25002
25561
  });
25003
- server.tool("task_get", "Read one task from this topic's shared Otium task list as JSON.", { id: z7.string().describe("Task id") }, async ({ id }) => {
25562
+ server.tool("task_get", "Read one task from this topic's shared Otium task list as JSON.", { id: z8.string().describe("Task id") }, async ({ id }) => {
25004
25563
  const guard = requireContext();
25005
25564
  if (guard)
25006
25565
  return guard;
@@ -25014,8 +25573,8 @@ Missing ids ignored: ${missing.join(", ")}` : "";
25014
25573
  }
25015
25574
  });
25016
25575
  server.tool("task_delete", "Delete tasks from this topic's shared Otium task list. Use all=true only when starting a new plan.", {
25017
- ids: z7.array(z7.string()).optional().describe("Task ids to delete"),
25018
- all: z7.boolean().optional().describe("Delete the whole list")
25576
+ ids: z8.array(z8.string()).optional().describe("Task ids to delete"),
25577
+ all: z8.boolean().optional().describe("Delete the whole list")
25019
25578
  }, async ({ ids, all }) => {
25020
25579
  const guard = requireContext();
25021
25580
  if (guard)
@@ -25038,7 +25597,7 @@ ${renderTaskList(tasks2)}`);
25038
25597
  // ../../packages/core/src/mcp/factories/token-stats.ts
25039
25598
  await init_token_stats();
25040
25599
  import { McpServer as McpServer7 } from "@modelcontextprotocol/sdk/server/mcp.js";
25041
- import { z as z8 } from "zod";
25600
+ import { z as z9 } from "zod";
25042
25601
  var defaultTokenStatsMcpHost = {
25043
25602
  getStats,
25044
25603
  calcCost
@@ -25058,9 +25617,9 @@ function createTokenStatsMcpServer(context, host = defaultTokenStatsMcpHost) {
25058
25617
  "groupBy: 'session'\uC73C\uB85C \uC124\uC815\uD558\uBA74 \uC138\uC158\uBCC4 \uD1A0\uD070 \uC0AC\uC6A9\uB7C9 \uB7AD\uD0B9\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4."
25059
25618
  ].join(`
25060
25619
  `), {
25061
- from: z8.string().optional().describe("\uC2DC\uC791 \uC2DC\uAC01 ISO 8601. \uC608: '2026-03-28T09:00:00Z'"),
25062
- to: z8.string().optional().describe("\uC885\uB8CC \uC2DC\uAC01 ISO 8601. \uC0DD\uB7B5 \uC2DC \uD604\uC7AC"),
25063
- groupBy: z8.enum(["hour", "session"]).optional().describe("\uC9D1\uACC4 \uAE30\uC900. \uAE30\uBCF8\uAC12: hour")
25620
+ from: z9.string().optional().describe("\uC2DC\uC791 \uC2DC\uAC01 ISO 8601. \uC608: '2026-03-28T09:00:00Z'"),
25621
+ to: z9.string().optional().describe("\uC885\uB8CC \uC2DC\uAC01 ISO 8601. \uC0DD\uB7B5 \uC2DC \uD604\uC7AC"),
25622
+ groupBy: z9.enum(["hour", "session"]).optional().describe("\uC9D1\uACC4 \uAE30\uC900. \uAE30\uBCF8\uAC12: hour")
25064
25623
  }, async ({ from, to, groupBy = "hour" }) => {
25065
25624
  const snapshot = host.getStats(context.userId, from, to);
25066
25625
  const {
@@ -25170,4 +25729,4 @@ export {
25170
25729
  createAgentHealthMcpServer
25171
25730
  };
25172
25731
 
25173
- //# debugId=1F80C21C052F8C6A64756E2164756E21
25732
+ //# debugId=0C619D7E527DC93B64756E2164756E21