negotium 0.3.0 → 0.3.2

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 (61) hide show
  1. package/dist/agent-helpers.js +107 -6
  2. package/dist/agent-helpers.js.map +11 -11
  3. package/dist/background-bash.js +3 -2
  4. package/dist/background-bash.js.map +4 -4
  5. package/dist/browser-runtime.js +3 -2
  6. package/dist/browser-runtime.js.map +4 -4
  7. package/dist/chunk-s2gez3wg.js.map +1 -1
  8. package/dist/{chunk-1y2xw0xe.js → chunk-vvfwqxnh.js} +33 -3
  9. package/dist/{chunk-1y2xw0xe.js.map → chunk-vvfwqxnh.js.map} +8 -8
  10. package/dist/hosted-agent.js +34 -4
  11. package/dist/hosted-agent.js.map +8 -8
  12. package/dist/main.js +1179 -366
  13. package/dist/main.js.map +24 -21
  14. package/dist/mcp-catalog.js +2 -1
  15. package/dist/mcp-catalog.js.map +3 -3
  16. package/dist/mcp-factories.js +635 -256
  17. package/dist/mcp-factories.js.map +15 -13
  18. package/dist/mcp-servers.js +3 -1
  19. package/dist/mcp-servers.js.map +3 -3
  20. package/dist/prompts.js +8 -2
  21. package/dist/prompts.js.map +5 -5
  22. package/dist/query-runtime.js +3 -2
  23. package/dist/query-runtime.js.map +4 -4
  24. package/dist/registry.js +3 -3
  25. package/dist/registry.js.map +2 -2
  26. package/dist/rollout.js +1 -1
  27. package/dist/runtime/src/mcp/canonical-bridge-config.ts +1 -1
  28. package/dist/runtime/src/mcp/canonical-proxy-server.ts +74 -1
  29. package/dist/runtime/src/mcp/decision-server.ts +21 -0
  30. package/dist/runtime/src/mcp/factories/decision.ts +178 -0
  31. package/dist/runtime/src/mcp/factories/index.ts +6 -0
  32. package/dist/runtime/src/mcp/runtime-spec.ts +1 -0
  33. package/dist/runtime/src/node-host.ts +1 -0
  34. package/dist/runtime/src/platform/config.ts +2 -1
  35. package/dist/runtime/src/platform/mcp-catalog-policy.ts +1 -0
  36. package/dist/runtime/src/platform/mcp-config.ts +31 -1
  37. package/dist/runtime/src/prompts/builders.ts +5 -0
  38. package/dist/runtime/src/runtime/visual-html.ts +68 -2
  39. package/dist/runtime/src/storage/decisions.ts +231 -0
  40. package/dist/runtime/src/storage/storage-public.ts +2 -0
  41. package/dist/runtime/src/types.ts +13 -0
  42. package/dist/runtime/src/version.ts +1 -1
  43. package/dist/runtime-helpers.js +71 -4
  44. package/dist/runtime-helpers.js.map +5 -5
  45. package/dist/storage.js +261 -56
  46. package/dist/storage.js.map +5 -4
  47. package/dist/types/apps/negotium/src/mcp-servers.d.ts +1 -1
  48. package/dist/types/packages/core/src/mcp/canonical-bridge-config.d.ts +1 -1
  49. package/dist/types/packages/core/src/mcp/factories/decision.d.ts +16 -0
  50. package/dist/types/packages/core/src/mcp/factories/index.d.ts +1 -0
  51. package/dist/types/packages/core/src/mcp/runtime-spec.d.ts +1 -1
  52. package/dist/types/packages/core/src/platform/config.d.ts +2 -1
  53. package/dist/types/packages/core/src/platform/mcp-catalog-policy.d.ts +4 -0
  54. package/dist/types/packages/core/src/storage/decisions.d.ts +45 -0
  55. package/dist/types/packages/core/src/storage/storage-public.d.ts +2 -0
  56. package/dist/types/packages/core/src/types.d.ts +12 -0
  57. package/dist/types/packages/core/src/version.d.ts +1 -1
  58. package/dist/vault.js +3 -2
  59. package/dist/vault.js.map +4 -4
  60. package/install-browser-rs.mjs +3 -3
  61. package/package.json +2 -2
@@ -313,7 +313,7 @@ function resolveDefaultModel(agent, registryDefaultModel) {
313
313
  function codexAuthFilePath() {
314
314
  return process.env.NEGOTIUM_CODEX_AUTH_FILE || join(process.env.CODEX_HOME || join(homedir(), ".codex"), "auth.json");
315
315
  }
316
- var HOME, PROJECT_ROOT, STATE_DIR_ENV, STATE_DIR, WORKSPACE_DIR, TOPIC_WORKSPACE_DIR, SHARED_WIKI_DIR, CRON_WORKSPACE_DIR, BROWSER_DIR, BROWSER_PROFILES_DIR, BINARIES_DIR, SECRETS_DIR, DM_WORKSPACE_DIR, SESSION_WORKSPACE_DIR, CLAUDE_EXECUTABLE_ENV, CLAUDE_EXECUTABLE, DEFAULT_OUTPUT_LANGUAGE = "English", BROWSER_RS_VERSION = "v0.1.19", BROWSER_RS_MIN_SECURE_VERSION = "0.1.15", BROWSER_RS_VERSION_PROBE_TIMEOUT_MS = 15000, SNIPPETS_API_URL, BROWSER_RS_BIN, BASH_RS_VERSION = "v0.1.5", BASH_RS_BIN, PLAYWRIGHT_MCP_BIN, TSX_BIN, TSX_LOADER, TSCONFIG_PATH, SESSION_COMM_SERVER, TASK_SERVER, BROWSER_MCP_SSE_PROXY_SERVER, CANONICAL_MCP_PROXY_SERVER, WIKI_SERVER, TOKEN_STATS_SERVER, COMPACTION_LOG_SERVER, SYSTEM_HEALTH_SERVER, AGENT_HEALTH_SERVER, VAULT_SERVER, BG_BASH_BASE_PORT, BG_BASH_MAX_PORT, RUNTIME_MCP_SECRET, NODE_CONTROL_TOKEN, NODE_ID, VAULT_MASTER_KEY, NEGOTIUM_PORT, hostname, DATA_DIR, LOG_DIR, UPLOADS_DIR, VAULT_DIR, SESSIONS_DB, DEBUG_FILE, USERS_LOG_DIR, RUN_DIR, PROGRESS_DIR, DM_CMD_DIR, DM_RESP_DIR, SESSION_INBOX_DIR, SESSION_ASKS_DIR, BASHRS_SPILL_ROOT, PLAYWRIGHT_BASE_PORT, PLAYWRIGHT_MAX_PORT, PLAYWRIGHT_PORTS_DIR, ACTIVE_QUERY_STALE_MS, AGENTS_PROMPTS_DIR, RESOURCES_DIR, FILE_TAG_REGEX, MODEL_SONNET = "claude-sonnet-5", MODEL_OPUS = "claude-opus-5", MODEL_FABLE = "claude-fable-5", FALLBACK_AGENT, SESSION_AGENT, GATEWAY_AGENT, FALLBACK_MODEL, SESSION_MODEL, GATEWAY_MODEL, FFMPEG_BIN, FFPROBE_BIN, PYTHON_BIN, FASTER_WHISPER_WRAPPER, WHISPER_MODEL, TESSERACT_BIN, PDFTOTEXT_BIN, _envMaxTellDepth, MAX_TELL_DEPTH;
316
+ var HOME, PROJECT_ROOT, STATE_DIR_ENV, STATE_DIR, WORKSPACE_DIR, TOPIC_WORKSPACE_DIR, SHARED_WIKI_DIR, CRON_WORKSPACE_DIR, BROWSER_DIR, BROWSER_PROFILES_DIR, BINARIES_DIR, SECRETS_DIR, DM_WORKSPACE_DIR, SESSION_WORKSPACE_DIR, CLAUDE_EXECUTABLE_ENV, CLAUDE_EXECUTABLE, DEFAULT_OUTPUT_LANGUAGE = "English", BROWSER_RS_VERSION = "v0.1.21", BROWSER_RS_MIN_SECURE_VERSION = "0.1.15", BROWSER_RS_VERSION_PROBE_TIMEOUT_MS = 15000, SNIPPETS_API_URL, BROWSER_RS_BIN, BASH_RS_VERSION = "v0.1.5", BASH_RS_BIN, PLAYWRIGHT_MCP_BIN, TSX_BIN, TSX_LOADER, TSCONFIG_PATH, SESSION_COMM_SERVER, TASK_SERVER, DECISION_SERVER, BROWSER_MCP_SSE_PROXY_SERVER, CANONICAL_MCP_PROXY_SERVER, WIKI_SERVER, TOKEN_STATS_SERVER, COMPACTION_LOG_SERVER, SYSTEM_HEALTH_SERVER, AGENT_HEALTH_SERVER, VAULT_SERVER, BG_BASH_BASE_PORT, BG_BASH_MAX_PORT, RUNTIME_MCP_SECRET, NODE_CONTROL_TOKEN, NODE_ID, VAULT_MASTER_KEY, NEGOTIUM_PORT, hostname, DATA_DIR, LOG_DIR, UPLOADS_DIR, VAULT_DIR, SESSIONS_DB, DEBUG_FILE, USERS_LOG_DIR, RUN_DIR, PROGRESS_DIR, DM_CMD_DIR, DM_RESP_DIR, SESSION_INBOX_DIR, SESSION_ASKS_DIR, BASHRS_SPILL_ROOT, PLAYWRIGHT_BASE_PORT, PLAYWRIGHT_MAX_PORT, PLAYWRIGHT_PORTS_DIR, ACTIVE_QUERY_STALE_MS, AGENTS_PROMPTS_DIR, RESOURCES_DIR, FILE_TAG_REGEX, MODEL_SONNET = "claude-sonnet-5", MODEL_OPUS = "claude-opus-5", MODEL_FABLE = "claude-fable-5", FALLBACK_AGENT, SESSION_AGENT, GATEWAY_AGENT, FALLBACK_MODEL, SESSION_MODEL, GATEWAY_MODEL, FFMPEG_BIN, FFPROBE_BIN, PYTHON_BIN, FASTER_WHISPER_WRAPPER, WHISPER_MODEL, TESSERACT_BIN, PDFTOTEXT_BIN, _envMaxTellDepth, MAX_TELL_DEPTH;
317
317
  var init_config = __esm(() => {
318
318
  init_config_helpers();
319
319
  init_logger();
@@ -343,6 +343,7 @@ var init_config = __esm(() => {
343
343
  TSCONFIG_PATH = resolve(PROJECT_ROOT, "tsconfig.json");
344
344
  SESSION_COMM_SERVER = resolve(PROJECT_ROOT, "src/mcp/session-comm/server.ts");
345
345
  TASK_SERVER = resolve(PROJECT_ROOT, "src/mcp/task-server.ts");
346
+ DECISION_SERVER = resolve(PROJECT_ROOT, "src/mcp/decision-server.ts");
346
347
  BROWSER_MCP_SSE_PROXY_SERVER = resolve(PROJECT_ROOT, "src/mcp/browser-sse-proxy-server.ts");
347
348
  CANONICAL_MCP_PROXY_SERVER = resolve(PROJECT_ROOT, "src/mcp/canonical-proxy-server.ts");
348
349
  WIKI_SERVER = resolve(PROJECT_ROOT, "src/mcp/wiki-server.ts");
@@ -1016,7 +1017,7 @@ var init_claude_registry = __esm(() => {
1016
1017
  });
1017
1018
 
1018
1019
  // ../../packages/core/src/version.ts
1019
- var NEGOTIUM_VERSION = "0.3.0";
1020
+ var NEGOTIUM_VERSION = "0.3.2";
1020
1021
 
1021
1022
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
1022
1023
  import { spawn as spawn2 } from "child_process";
@@ -1707,6 +1708,7 @@ var init_mcp_catalog_policy = __esm(() => {
1707
1708
  runtime: { scopes: ["forum", "manager", "fork", "cron"], forumRequired: true },
1708
1709
  "token-stats": { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
1709
1710
  task: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
1711
+ decision: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
1710
1712
  "session-comm": { scopes: ["forum", "fork", "manager"], forumRequired: true },
1711
1713
  wiki: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
1712
1714
  skills: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
@@ -1930,7 +1932,7 @@ function getForumMcpServers(opts) {
1930
1932
  peerBridge
1931
1933
  } = opts;
1932
1934
  const filter = (name) => {
1933
- if (silent && name === "task")
1935
+ if (silent && (name === "task" || name === "decision"))
1934
1936
  return false;
1935
1937
  if (enabled === null)
1936
1938
  return true;
@@ -2148,6 +2150,34 @@ var init_mcp_config = __esm(() => {
2148
2150
  return buildBuiltinMcpServer("task", ctx, () => buildStdioMcpServer(agent, TASK_SERVER, args));
2149
2151
  }
2150
2152
  },
2153
+ decision: {
2154
+ ...commonRuntimeMcpPolicy("decision"),
2155
+ build(ctx) {
2156
+ const { userId, session, topicId, queryId, agent, model, peerBridge } = ctx;
2157
+ if (peerBridge) {
2158
+ if (!topicId || !queryId)
2159
+ return null;
2160
+ const env = canonicalMcpBridgeEnv({
2161
+ surface: "decision",
2162
+ userId,
2163
+ topicId,
2164
+ queryId,
2165
+ peerBridge
2166
+ });
2167
+ return env ? buildStdioMcpServer(agent, CANONICAL_MCP_PROXY_SERVER, ["--surface=decision"], env) : null;
2168
+ }
2169
+ const args = [
2170
+ `--user-id=${userId}`,
2171
+ `--topic=${session}`,
2172
+ `--agent=${agent ?? FALLBACK_AGENT}`
2173
+ ];
2174
+ if (topicId)
2175
+ args.push(`--topic-id=${topicId}`);
2176
+ if (model)
2177
+ args.push(`--model=${model}`);
2178
+ return buildBuiltinMcpServer("decision", ctx, () => buildStdioMcpServer(agent, DECISION_SERVER, args));
2179
+ }
2180
+ },
2151
2181
  "session-comm": {
2152
2182
  ...commonRuntimeMcpPolicy("session-comm"),
2153
2183
  build(ctx) {
@@ -3549,6 +3579,21 @@ function mcpError(text) {
3549
3579
  }
3550
3580
  var init_mcp_helpers = () => {};
3551
3581
 
3582
+ // ../../packages/core/src/security/sanitize.ts
3583
+ function sanitizeTopicName(name, lowercase = false) {
3584
+ const safe = name.replace(/[^a-zA-Z0-9\uAC00-\uD7A3_-]/g, "_") || "_";
3585
+ return lowercase ? safe.toLowerCase() : safe;
3586
+ }
3587
+ function sanitizeFileName(name) {
3588
+ const safe = name.replace(/[^a-zA-Z0-9._-]/g, "_") || "_";
3589
+ if (safe === "." || safe === "..")
3590
+ return "_";
3591
+ return safe;
3592
+ }
3593
+ function sanitizeId(id) {
3594
+ return id.replace(/[^a-zA-Z0-9_-]/g, "_") || "_";
3595
+ }
3596
+
3552
3597
  // ../../packages/core/src/agents/mcp-tools/common.ts
3553
3598
  function textResult(text) {
3554
3599
  return mcpOk(text);
@@ -5227,7 +5272,7 @@ var init_registry = __esm(async () => {
5227
5272
  });
5228
5273
 
5229
5274
  // ../../packages/core/src/agents/fork.ts
5230
- import { existsSync as existsSync11, unlinkSync as unlinkSync9 } from "fs";
5275
+ import { existsSync as existsSync12, unlinkSync as unlinkSync9 } from "fs";
5231
5276
  function createAgentForkHelpers(host) {
5232
5277
  return {
5233
5278
  async forkAgentSession(options) {
@@ -5264,7 +5309,7 @@ var init_fork = __esm(async () => {
5264
5309
  init_logger();
5265
5310
  defaultForkHelpers = createAgentForkHelpers({
5266
5311
  forkSession: (agent, options) => getRegistryOperations(agent).forkSession(options),
5267
- exists: existsSync11,
5312
+ exists: existsSync12,
5268
5313
  unlink: unlinkSync9,
5269
5314
  warn: (details, message) => logger.warn(details, message)
5270
5315
  });
@@ -5935,7 +5980,7 @@ import { randomBytes as randomBytes5, timingSafeEqual as timingSafeEqual2 } from
5935
5980
  import { chmodSync as chmodSync5 } from "fs";
5936
5981
  import { createServer as createServer2 } from "net";
5937
5982
  import { tmpdir as tmpdir2 } from "os";
5938
- import { join as join12 } from "path";
5983
+ import { join as join13 } from "path";
5939
5984
  function deepMapStrings(value2, transform) {
5940
5985
  if (typeof value2 === "string")
5941
5986
  return transform(value2);
@@ -6022,7 +6067,7 @@ function authorized(actual, expected) {
6022
6067
  }
6023
6068
  async function createBrowserVaultBroker(userId) {
6024
6069
  const token = randomBytes5(32).toString("hex");
6025
- const socketPath = join12(process.platform === "win32" ? tmpdir2() : "/tmp", `negotium-browser-vault-${process.pid}-${randomBytes5(8).toString("hex")}.sock`);
6070
+ const socketPath = join13(process.platform === "win32" ? tmpdir2() : "/tmp", `negotium-browser-vault-${process.pid}-${randomBytes5(8).toString("hex")}.sock`);
6026
6071
  const retainedForms = new Map;
6027
6072
  const leases = new Map;
6028
6073
  const sockets = new Set;
@@ -6159,21 +6204,6 @@ var init_vault_broker = __esm(async () => {
6159
6204
  };
6160
6205
  });
6161
6206
 
6162
- // ../../packages/core/src/security/sanitize.ts
6163
- function sanitizeTopicName(name, lowercase = false) {
6164
- const safe = name.replace(/[^a-zA-Z0-9\uAC00-\uD7A3_-]/g, "_") || "_";
6165
- return lowercase ? safe.toLowerCase() : safe;
6166
- }
6167
- function sanitizeFileName(name) {
6168
- const safe = name.replace(/[^a-zA-Z0-9._-]/g, "_") || "_";
6169
- if (safe === "." || safe === "..")
6170
- return "_";
6171
- return safe;
6172
- }
6173
- function sanitizeId(id) {
6174
- return id.replace(/[^a-zA-Z0-9_-]/g, "_") || "_";
6175
- }
6176
-
6177
6207
  // ../../packages/core/src/storage/browser-profiles.ts
6178
6208
  function normalizeBrowserProfileName(name) {
6179
6209
  const value2 = name.trim().toLowerCase();
@@ -6239,21 +6269,21 @@ import { execFileSync as execFileSync4, spawn as spawn6 } from "child_process";
6239
6269
  import { createHash as createHash3, randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual3 } from "crypto";
6240
6270
  import {
6241
6271
  cpSync,
6242
- existsSync as existsSync12,
6243
- mkdirSync as mkdirSync9,
6244
- readFileSync as readFileSync10,
6272
+ existsSync as existsSync13,
6273
+ mkdirSync as mkdirSync10,
6274
+ readFileSync as readFileSync11,
6245
6275
  rmSync as rmSync2,
6246
6276
  unlinkSync as unlinkSync11,
6247
- writeFileSync as writeFileSync7
6277
+ writeFileSync as writeFileSync8
6248
6278
  } from "fs";
6249
- import { dirname as dirname8, join as join13, resolve as resolve10 } from "path";
6279
+ import { dirname as dirname9, join as join14, resolve as resolve10 } from "path";
6250
6280
  function removeDefaultProfileDataDir(userDataDir) {
6251
6281
  const root = resolve10(BROWSER_PROFILES_DIR);
6252
6282
  const target = resolve10(userDataDir);
6253
- if (dirname8(target) !== root) {
6283
+ if (dirname9(target) !== root) {
6254
6284
  throw new Error(`Refusing to delete browser profile outside managed root: ${target}`);
6255
6285
  }
6256
- const existed = existsSync12(target);
6286
+ const existed = existsSync13(target);
6257
6287
  rmSync2(target, { recursive: true, force: true });
6258
6288
  return existed;
6259
6289
  }
@@ -6335,15 +6365,15 @@ function portFileName(instanceKey) {
6335
6365
  }
6336
6366
  function writePortFile(instanceKey, port) {
6337
6367
  try {
6338
- mkdirSync9(managerHost.portsDir, { recursive: true });
6339
- writeFileSync7(join13(managerHost.portsDir, portFileName(instanceKey)), String(port));
6368
+ mkdirSync10(managerHost.portsDir, { recursive: true });
6369
+ writeFileSync8(join14(managerHost.portsDir, portFileName(instanceKey)), String(port));
6340
6370
  } catch (e) {
6341
6371
  logger.warn({ err: e, instanceKey, port }, "Failed to save playwright port file");
6342
6372
  }
6343
6373
  }
6344
6374
  function deletePortFile(instanceKey) {
6345
6375
  try {
6346
- unlinkSync11(join13(managerHost.portsDir, portFileName(instanceKey)));
6376
+ unlinkSync11(join14(managerHost.portsDir, portFileName(instanceKey)));
6347
6377
  } catch (e) {
6348
6378
  if (e.code === "ENOENT")
6349
6379
  return;
@@ -6544,7 +6574,7 @@ async function spawnPlaywright(instanceKey, ownerId, reservedPort, browserBin =
6544
6574
  }
6545
6575
  const userDataDir = resolveUserDataDir(instanceKey);
6546
6576
  const port = reservedPort ?? await allocatePort();
6547
- mkdirSync9(userDataDir, { recursive: true });
6577
+ mkdirSync10(userDataDir, { recursive: true });
6548
6578
  const mcpArgs = [
6549
6579
  "--port",
6550
6580
  String(port),
@@ -6828,14 +6858,14 @@ async function cloneProfileForChild(opts) {
6828
6858
  killInstance(dstKey);
6829
6859
  await delay(500);
6830
6860
  }
6831
- if (!existsSync12(srcDir)) {
6861
+ if (!existsSync13(srcDir)) {
6832
6862
  return { copied: false, srcDir, dstDir, reason: "src-missing" };
6833
6863
  }
6834
6864
  try {
6835
- if (existsSync12(dstDir)) {
6865
+ if (existsSync13(dstDir)) {
6836
6866
  rmSync2(dstDir, { recursive: true, force: true });
6837
6867
  }
6838
- mkdirSync9(dirname8(dstDir), { recursive: true });
6868
+ mkdirSync10(dirname9(dstDir), { recursive: true });
6839
6869
  if (process.platform === "darwin") {
6840
6870
  try {
6841
6871
  execFileSync4("cp", ["-cR", srcDir, dstDir], { stdio: "pipe" });
@@ -7397,30 +7427,30 @@ var init_api_topic_config = __esm(async () => {
7397
7427
 
7398
7428
  // ../../packages/core/src/storage/conversations.ts
7399
7429
  import {
7400
- existsSync as existsSync13,
7401
- mkdirSync as mkdirSync10,
7402
- readFileSync as readFileSync11,
7403
- renameSync as renameSync5,
7430
+ existsSync as existsSync14,
7431
+ mkdirSync as mkdirSync11,
7432
+ readFileSync as readFileSync12,
7433
+ renameSync as renameSync6,
7404
7434
  unlinkSync as unlinkSync12,
7405
- writeFileSync as writeFileSync8
7435
+ writeFileSync as writeFileSync9
7406
7436
  } from "fs";
7407
- import { dirname as dirname9, join as join14 } from "path";
7437
+ import { dirname as dirname10, join as join15 } from "path";
7408
7438
  function conversationDir(_userId) {
7409
- return join14(resolveStorageDataDir(), "conversations");
7439
+ return join15(resolveStorageDataDir(), "conversations");
7410
7440
  }
7411
7441
  function topicFilename(topicName) {
7412
7442
  const t = sanitizeTopicName(topicName, true);
7413
7443
  return `${t}.jsonl`;
7414
7444
  }
7415
7445
  function getConversationPath(userId, topicName) {
7416
- return join14(conversationDir(userId), topicFilename(topicName));
7446
+ return join15(conversationDir(userId), topicFilename(topicName));
7417
7447
  }
7418
7448
  function getActiveConversationPath(userId, topicName) {
7419
7449
  const rawPath = getConversationPath(userId, topicName);
7420
7450
  return rawPath.endsWith(".jsonl") ? `${rawPath.slice(0, -".jsonl".length)}.active.jsonl` : `${rawPath}.active`;
7421
7451
  }
7422
7452
  function hasActiveConversation(userId, topicName) {
7423
- return existsSync13(getActiveConversationPath(userId, topicName));
7453
+ return existsSync14(getActiveConversationPath(userId, topicName));
7424
7454
  }
7425
7455
  function appendConversationEvent(userId, topicName, agent, event) {
7426
7456
  try {
@@ -7439,10 +7469,10 @@ function appendConversationEventStrict(userId, topicName, agent, event) {
7439
7469
  event
7440
7470
  };
7441
7471
  const line = JSON.stringify(entry);
7442
- mkdirSync10(dirname9(path), { recursive: true });
7472
+ mkdirSync11(dirname10(path), { recursive: true });
7443
7473
  appendJsonlLine(path, line);
7444
7474
  const activePath = getActiveConversationPath(userId, topicName);
7445
- if (existsSync13(activePath)) {
7475
+ if (existsSync14(activePath)) {
7446
7476
  try {
7447
7477
  appendJsonlLine(activePath, line);
7448
7478
  } catch (cause) {
@@ -7452,11 +7482,11 @@ function appendConversationEventStrict(userId, topicName, agent, event) {
7452
7482
  }
7453
7483
  function readConversationPath(path) {
7454
7484
  const out = [];
7455
- if (!existsSync13(path))
7485
+ if (!existsSync14(path))
7456
7486
  return out;
7457
7487
  let raw;
7458
7488
  try {
7459
- raw = readFileSync11(path, "utf8");
7489
+ raw = readFileSync12(path, "utf8");
7460
7490
  } catch (err) {
7461
7491
  logger.warn({ err, path }, "readConversation: read failed");
7462
7492
  return out;
@@ -7475,7 +7505,7 @@ function readConversationPath(path) {
7475
7505
  }
7476
7506
  function readConversation(userId, topicName) {
7477
7507
  const activePath = getActiveConversationPath(userId, topicName);
7478
- return readConversationPath(existsSync13(activePath) ? activePath : getConversationPath(userId, topicName));
7508
+ return readConversationPath(existsSync14(activePath) ? activePath : getConversationPath(userId, topicName));
7479
7509
  }
7480
7510
  function readRawConversation(userId, topicName) {
7481
7511
  return readConversationPath(getConversationPath(userId, topicName));
@@ -7488,12 +7518,12 @@ function replaceRawConversationStrict(userId, topicName, entries) {
7488
7518
  }
7489
7519
  function replaceConversationPathStrict(path, entries) {
7490
7520
  const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
7491
- mkdirSync10(dirname9(path), { recursive: true });
7521
+ mkdirSync11(dirname10(path), { recursive: true });
7492
7522
  try {
7493
- writeFileSync8(tempPath, entries.length > 0 ? `${entries.map((entry) => JSON.stringify(entry)).join(`
7523
+ writeFileSync9(tempPath, entries.length > 0 ? `${entries.map((entry) => JSON.stringify(entry)).join(`
7494
7524
  `)}
7495
7525
  ` : "", { flag: "wx" });
7496
- renameSync5(tempPath, path);
7526
+ renameSync6(tempPath, path);
7497
7527
  } catch (error) {
7498
7528
  try {
7499
7529
  unlinkSync12(tempPath);
@@ -7635,8 +7665,8 @@ RULES:
7635
7665
  - Keep the entire summary under 1500 words.`, CHARS_PER_TEXT_TOKEN = 3.5, CHARS_PER_CJK_TOKEN = 0.9;
7636
7666
 
7637
7667
  // ../../packages/core/src/storage/tasks.ts
7638
- import { existsSync as existsSync14, mkdirSync as mkdirSync11, readFileSync as readFileSync12, renameSync as renameSync6, statSync as statSync4, writeFileSync as writeFileSync9 } from "fs";
7639
- import { dirname as dirname10, join as join15 } from "path";
7668
+ import { existsSync as existsSync15, mkdirSync as mkdirSync12, readFileSync as readFileSync13, renameSync as renameSync7, statSync as statSync4, writeFileSync as writeFileSync10 } from "fs";
7669
+ import { dirname as dirname11, join as join16 } from "path";
7640
7670
  function safeTaskScopeKey(scopeKey) {
7641
7671
  const safe = sanitizeFileName(scopeKey);
7642
7672
  if (!safe || safe === "." || safe === "..") {
@@ -7648,14 +7678,14 @@ function taskScopeKey(opts) {
7648
7678
  return opts.topicId?.trim() || opts.session || "default";
7649
7679
  }
7650
7680
  function getTaskFilePath(userId, scopeKey) {
7651
- return join15(resolveStorageDataDir(), "tasks", `${safeTaskScopeKey(scopeKey)}.json`);
7681
+ return join16(resolveStorageDataDir(), "tasks", `${safeTaskScopeKey(scopeKey)}.json`);
7652
7682
  }
7653
7683
  function readTasks(userId, scopeKey) {
7654
7684
  const path = getTaskFilePath(userId, scopeKey);
7655
- if (!existsSync14(path))
7685
+ if (!existsSync15(path))
7656
7686
  return [];
7657
7687
  try {
7658
- const parsed = JSON.parse(readFileSync12(path, "utf-8"));
7688
+ const parsed = JSON.parse(readFileSync13(path, "utf-8"));
7659
7689
  return Array.isArray(parsed?.tasks) ? parsed.tasks : [];
7660
7690
  } catch (e) {
7661
7691
  logger.warn({ err: e, path }, "tasks: failed to read task store");
@@ -7664,11 +7694,11 @@ function readTasks(userId, scopeKey) {
7664
7694
  }
7665
7695
  function writeTasks(userId, scopeKey, tasks) {
7666
7696
  const path = getTaskFilePath(userId, scopeKey);
7667
- mkdirSync11(dirname10(path), { recursive: true });
7697
+ mkdirSync12(dirname11(path), { recursive: true });
7668
7698
  const payload = { version: 1, tasks };
7669
7699
  const tmp = `${path}.${process.pid}.tmp`;
7670
- writeFileSync9(tmp, JSON.stringify(payload, null, 2), "utf-8");
7671
- renameSync6(tmp, path);
7700
+ writeFileSync10(tmp, JSON.stringify(payload, null, 2), "utf-8");
7701
+ renameSync7(tmp, path);
7672
7702
  }
7673
7703
  function taskFileMtimeNs(userId, scopeKey) {
7674
7704
  try {
@@ -7910,7 +7940,7 @@ __export(exports_claude_provider, {
7910
7940
  buildClaudeDisallowedTools: () => buildClaudeDisallowedTools
7911
7941
  });
7912
7942
  import { spawn as spawn7 } from "child_process";
7913
- import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
7943
+ import { existsSync as existsSync16, readFileSync as readFileSync14 } from "fs";
7914
7944
  import { query } from "@anthropic-ai/claude-agent-sdk";
7915
7945
  function claudeBuiltInTools(opts) {
7916
7946
  return opts.toolPolicy ? [] : undefined;
@@ -7963,7 +7993,7 @@ function buildClaudePrompt(opts) {
7963
7993
  source: {
7964
7994
  type: "base64",
7965
7995
  media_type: attachment.mimeType,
7966
- data: readFileSync13(attachment.path).toString("base64")
7996
+ data: readFileSync14(attachment.path).toString("base64")
7967
7997
  }
7968
7998
  });
7969
7999
  } catch (err) {
@@ -8091,7 +8121,7 @@ function isAbortError(err) {
8091
8121
  }
8092
8122
  async function* claudeProvider(opts) {
8093
8123
  const claudeExecutable = hostedClaudeCodeExecutablePath();
8094
- if (claudeExecutable && !existsSync15(claudeExecutable)) {
8124
+ if (claudeExecutable && !existsSync16(claudeExecutable)) {
8095
8125
  yield {
8096
8126
  type: "error",
8097
8127
  content: `Claude CLI not found at ${claudeExecutable}. Install Claude Code or configure the executable path.`
@@ -8611,7 +8641,7 @@ var init_codex_tree_kill = __esm(() => {
8611
8641
  });
8612
8642
 
8613
8643
  // ../../packages/core/src/agents/tool-format.ts
8614
- import { readFileSync as readFileSync14, statSync as statSync5 } from "fs";
8644
+ import { readFileSync as readFileSync15, statSync as statSync5 } from "fs";
8615
8645
  import { isAbsolute as isAbsolute2, resolve as resolve11 } from "path";
8616
8646
  import { diffLines as computeLineDiff } from "diff";
8617
8647
  function summarizeDisplayText(value2) {
@@ -8940,7 +8970,7 @@ function sourceStartLine(input, before, after, cwd) {
8940
8970
  try {
8941
8971
  if (statSync5(path).size > TOOL_DIFF_SOURCE_MAX_BYTES)
8942
8972
  return 1;
8943
- const source = readFileSync14(path, "utf8").replace(/\r\n?/g, `
8973
+ const source = readFileSync15(path, "utf8").replace(/\r\n?/g, `
8944
8974
  `);
8945
8975
  for (const candidate of [before, after].filter(Boolean)) {
8946
8976
  const index = source.indexOf(candidate);
@@ -9134,9 +9164,9 @@ __export(exports_codex_provider, {
9134
9164
  codexProvider: () => codexProvider
9135
9165
  });
9136
9166
  import { execFileSync as execFileSync6 } from "child_process";
9137
- import { existsSync as existsSync16, readFileSync as readFileSync15, realpathSync as realpathSync4, statSync as statSync6 } from "fs";
9167
+ import { existsSync as existsSync17, readFileSync as readFileSync16, realpathSync as realpathSync4, statSync as statSync6 } from "fs";
9138
9168
  import { homedir as homedir6 } from "os";
9139
- import { dirname as dirname11, isAbsolute as isAbsolute3, join as join16, relative as relative2, resolve as resolve12 } from "path";
9169
+ import { dirname as dirname12, isAbsolute as isAbsolute3, join as join17, relative as relative2, resolve as resolve12 } from "path";
9140
9170
  import { Codex } from "@openai/codex-sdk";
9141
9171
  function sameCodexUsage(usage, total) {
9142
9172
  return usage.input_tokens === total.inputTokens && usage.output_tokens === total.outputTokens && (usage.cached_input_tokens ?? 0) === total.cachedInputTokens && (usage.cache_write_input_tokens ?? 0) === total.cacheWriteInputTokens;
@@ -9164,12 +9194,12 @@ function codexMcpServerName(name) {
9164
9194
  return CODEX_MCP_SERVER_NAME_OVERRIDES[name] ?? name;
9165
9195
  }
9166
9196
  function globalCodexMcpServerNames(authFilePath) {
9167
- const configPath = join16(dirname11(authFilePath), "config.toml");
9168
- if (!existsSync16(configPath))
9197
+ const configPath = join17(dirname12(authFilePath), "config.toml");
9198
+ if (!existsSync17(configPath))
9169
9199
  return [];
9170
9200
  try {
9171
9201
  const names = new Set;
9172
- for (const line of readFileSync15(configPath, "utf8").split(`
9202
+ for (const line of readFileSync16(configPath, "utf8").split(`
9173
9203
  `)) {
9174
9204
  const match = line.match(/^\s*\[\s*mcp_servers\.((?:"(?:\\.|[^"])*")|(?:'[^']*')|[^.\]\s]+)(?:[.\]])/);
9175
9205
  const raw = match?.[1];
@@ -9266,13 +9296,13 @@ function canonicalPath(path) {
9266
9296
  }
9267
9297
  }
9268
9298
  function textFile(path, maxBytes = CODEX_DIFF_FILE_LIMIT) {
9269
- if (!existsSync16(path))
9299
+ if (!existsSync17(path))
9270
9300
  return null;
9271
9301
  try {
9272
9302
  const byteLimit = Math.min(CODEX_DIFF_FILE_LIMIT, Math.max(0, maxBytes));
9273
9303
  if (statSync6(path).size > byteLimit)
9274
9304
  return;
9275
- const content = readFileSync15(path);
9305
+ const content = readFileSync16(path);
9276
9306
  if (content.byteLength > byteLimit || content.includes(0))
9277
9307
  return;
9278
9308
  return content.toString("utf8");
@@ -9478,7 +9508,7 @@ async function* codexProvider(opts) {
9478
9508
  const filePreviews = new CodexFilePreviewTracker(opts.cwd);
9479
9509
  const consumedPatchCallIds = new Set(opts.sessionId ? readCodexPatchCallIds(opts.sessionId) : []);
9480
9510
  const codexAuthPath = hostedCodexAuthFilePath();
9481
- if (!existsSync16(codexAuthPath)) {
9511
+ if (!existsSync17(codexAuthPath)) {
9482
9512
  yield {
9483
9513
  type: "error",
9484
9514
  content: `Codex auth file not found at ${codexAuthPath}. Run \`codex login\` to authenticate.`
@@ -9517,12 +9547,12 @@ async function* codexProvider(opts) {
9517
9547
  model: opts.model ?? "(sdk default)",
9518
9548
  effort: opts.effort ?? "(off)",
9519
9549
  cwd: opts.cwd,
9520
- cwdExists: existsSync16(opts.cwd),
9550
+ cwdExists: existsSync17(opts.cwd),
9521
9551
  resume: Boolean(opts.sessionId),
9522
9552
  mcpServerCount: Object.keys(codexMcpServers).length
9523
9553
  }, "codexProvider: starting turn");
9524
9554
  const hostedCodexHome = hostedCodexHomePath();
9525
- const inheritedCodexHome = process.env.CODEX_HOME || join16(homedir6(), ".codex");
9555
+ const inheritedCodexHome = process.env.CODEX_HOME || join17(homedir6(), ".codex");
9526
9556
  const codexEnvironment = scopedBrowserCapability || resolve12(hostedCodexHome) !== resolve12(inheritedCodexHome) ? {
9527
9557
  ...Object.fromEntries(Object.entries(process.env).filter((entry) => typeof entry[1] === "string")),
9528
9558
  CODEX_HOME: hostedCodexHome,
@@ -9965,9 +9995,9 @@ var init_maestro_provider = __esm(async () => {
9965
9995
  });
9966
9996
 
9967
9997
  // ../../packages/core/src/agents/index.ts
9968
- import { existsSync as existsSync17 } from "fs";
9998
+ import { existsSync as existsSync18 } from "fs";
9969
9999
  import { homedir as homedir7 } from "os";
9970
- import { join as join17 } from "path";
10000
+ import { join as join18 } from "path";
9971
10001
  async function* dispatchAgent(opts) {
9972
10002
  switch (opts.agent) {
9973
10003
  case "claude": {
@@ -10001,11 +10031,11 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
10001
10031
  switch (agent) {
10002
10032
  case "claude": {
10003
10033
  const encodedCwd = encodeClaudeCwd(cwd);
10004
- const path = join17(homedir7(), ".claude", "projects", encodedCwd, `${sessionId}.jsonl`);
10005
- return !existsSync17(path);
10034
+ const path = join18(homedir7(), ".claude", "projects", encodedCwd, `${sessionId}.jsonl`);
10035
+ return !existsSync18(path);
10006
10036
  }
10007
10037
  case "codex": {
10008
- const sessionsDir = join17(hostedCodexHomePath(), "sessions");
10038
+ const sessionsDir = join18(hostedCodexHomePath(), "sessions");
10009
10039
  const glob = new Bun.Glob(`**/rollout-*-${sessionId}.jsonl`);
10010
10040
  for await (const _rel of glob.scan({ cwd: sessionsDir, onlyFiles: true })) {
10011
10041
  return false;
@@ -10014,7 +10044,7 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
10014
10044
  }
10015
10045
  case "maestro": {
10016
10046
  const { hasActiveMaestroSession, maestroSessionPath: maestroSessionPath2 } = await import("maestro-agent-sdk");
10017
- return !existsSync17(maestroSessionPath2(sessionId)) && !hasActiveMaestroSession(sessionId);
10047
+ return !existsSync18(maestroSessionPath2(sessionId)) && !hasActiveMaestroSession(sessionId);
10018
10048
  }
10019
10049
  default: {
10020
10050
  const _exhaustive = agent;
@@ -10085,10 +10115,10 @@ var init_agents = __esm(async () => {
10085
10115
  });
10086
10116
 
10087
10117
  // ../../packages/core/src/prompts/builders.ts
10088
- import { readFileSync as readFileSync16 } from "fs";
10118
+ import { readFileSync as readFileSync17 } from "fs";
10089
10119
  import { resolve as resolve14 } from "path";
10090
10120
  function loadPrompt(filename, dir = SESSIONS_DIR) {
10091
- const raw = readFileSync16(resolve14(dir, filename), "utf-8");
10121
+ const raw = readFileSync17(resolve14(dir, filename), "utf-8");
10092
10122
  return raw.replace(/\{\{RESOURCES_DIR\}\}/g, RESOURCES_DIR);
10093
10123
  }
10094
10124
  function replaceVars(template, vars) {
@@ -10137,7 +10167,7 @@ function visualDesignGuide() {
10137
10167
  return _visualDesignGuide;
10138
10168
  }
10139
10169
  function loadAgentPrompt(filename) {
10140
- const raw = readFileSync16(resolve14(AGENTS_PROMPTS_DIR, filename), "utf-8");
10170
+ const raw = readFileSync17(resolve14(AGENTS_PROMPTS_DIR, filename), "utf-8");
10141
10171
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
10142
10172
  if (!match)
10143
10173
  throw new Error(`Agent prompt ${filename} is missing frontmatter`);
@@ -10183,6 +10213,7 @@ function buildRuntimeToolSection(opts, extensions) {
10183
10213
  } = opts;
10184
10214
  const runtimeNamespace = "mcp__runtime";
10185
10215
  const taskNamespace = "mcp__task";
10216
+ const decisionNamespace = "mcp__decision";
10186
10217
  const visualToolLine = agentKind === "codex" ? `To display charts, tables, or interactive HTML results to the user, call the \`show_html\` function in the \`${runtimeNamespace}\` namespace with { html: "<complete HTML string>", title?: "optional title" }.` : `To display charts, tables, or interactive HTML results to the user, call the MCP tool "${runtimeNamespace}__show_html" with { html: "<complete HTML string>", title?: "optional title" }.`;
10187
10218
  const mermaidToolLine = agentKind === "codex" ? `For diagrams that Mermaid supports, prefer the \`show_mermaid\` function in the \`${runtimeNamespace}\` namespace with { code: "<Mermaid DSL without markdown fences>", title?: "...", theme?: "neutral" }.` : `For diagrams that Mermaid supports, prefer the MCP tool "${runtimeNamespace}__show_mermaid" with { code: "<Mermaid DSL without markdown fences>", title?: "...", theme?: "neutral" }.`;
10188
10219
  const mediaToolLine = agentKind === "codex" ? `To display an existing image or video in the visual panel, use \`show_image\` or \`show_video\` in the \`${runtimeNamespace}\` namespace with either { file_path: "...", title?: "..." } for a topic-workspace file or { file_id: "...", title?: "..." } for an uploaded file already attached in this topic.` : `To display an existing image or video in the visual panel, use MCP tool "${runtimeNamespace}__show_image" or "${runtimeNamespace}__show_video" with either { file_path: "...", title?: "..." } for a topic-workspace file or { file_id: "...", title?: "..." } for an uploaded file already attached in this topic.`;
@@ -10190,6 +10221,7 @@ function buildRuntimeToolSection(opts, extensions) {
10190
10221
  const askUserToolLine = agentKind === "codex" ? `When you need a blocking user choice, call the \`ask_user_question\` function in the \`${runtimeNamespace}\` namespace with { question: "...", choices: [{ label: "...", description?: "..." }] }.` : `When you need a blocking user choice, call the MCP tool "${runtimeNamespace}__ask_user_question" with { question: "...", choices: [{ label: "...", description?: "..." }] }.`;
10191
10222
  const scheduleSelfToolLine = agentKind === "codex" ? `For a one-shot delayed continuation within 24 hours, call the \`schedule_self\` function in the \`${runtimeNamespace}\` namespace with { delay_seconds: number, message: "self-contained future instruction" }. Only one pending self-schedule is allowed per topic; use \`get_self_schedule\`, \`update_self_schedule\`, or \`cancel_self_schedule\` in that namespace to manage it. Use cron-manager for recurring schedules.` : `For a one-shot delayed continuation within 24 hours, call the MCP tool "${runtimeNamespace}__schedule_self" with { delay_seconds: number, message: "self-contained future instruction" }. Only one pending self-schedule is allowed per topic; manage it with "${runtimeNamespace}__get_self_schedule", "${runtimeNamespace}__update_self_schedule", or "${runtimeNamespace}__cancel_self_schedule". Use cron-manager for recurring schedules.`;
10192
10223
  const taskToolLine = agentKind === "codex" ? `For task tracking, use \`task_create\`, \`task_update\`, \`task_list\`, \`task_get\`, and \`task_delete\` functions in the \`${taskNamespace}\` namespace.` : `For task tracking, use MCP tools "${taskNamespace}__task_create", "${taskNamespace}__task_update", "${taskNamespace}__task_list", "${taskNamespace}__task_get", and "${taskNamespace}__task_delete".`;
10224
+ const decisionToolLine = `Use the shared Decision tools in the \`${decisionNamespace}\` namespace when an architectural, product, or operational choice establishes or changes a durable direction or constraint. Do not record routine task progress or temporary implementation details; link causal predecessors when relevant.`;
10193
10225
  const runtimeToolRef = (name) => agentKind === "codex" ? `\`${name}\`` : `"${runtimeNamespace}__${name}"`;
10194
10226
  const spawnSubagentToolLine = `Use ${runtimeToolRef("spawn_subagent")} for self-contained parallel or long-running background work; keep quick work inline.`;
10195
10227
  const lifecycleToolLine = `For staged work, call ${runtimeToolRef("create_subagent")} then ${runtimeToolRef("start_subagent")}. Create fixes \`task\` and \`report_mode\`; start takes only the room ID, so create after inputs are known unless preparing a \`tell_session\` receiver. Manage descendants with ${runtimeToolRef("list_subagents")} and ${runtimeToolRef("delete_subagent")}, and non-parent tell routes with ${runtimeToolRef("grant_subagent_tell")} and ${runtimeToolRef("revoke_subagent_tell")}. Direct-parent reporting needs no grant. Use ${runtimeToolRef("list_memory_topics")} to select \`memory_topic\`.`;
@@ -10236,6 +10268,9 @@ function buildRuntimeToolSection(opts, extensions) {
10236
10268
  taskToolLine,
10237
10269
  "Use this shared task store for plans, progress, and checklist updates; it is visible across claude/codex/maestro turns.",
10238
10270
  nativeTaskPolicyLine,
10271
+ "",
10272
+ "## Shared Decisions",
10273
+ decisionToolLine,
10239
10274
  ...extensions.render("after-shared-tasks"),
10240
10275
  ...fileDeliverySection,
10241
10276
  ...extensions.render("before-session-communication"),
@@ -10524,9 +10559,9 @@ var init_api_topic_brief = __esm(async () => {
10524
10559
  });
10525
10560
 
10526
10561
  // ../../packages/core/src/storage/wiki.ts
10527
- import { basename as basename3, dirname as dirname12, join as join18 } from "path";
10562
+ import { basename as basename3, dirname as dirname13, join as join19 } from "path";
10528
10563
  function getSharedWikiDir(workspaceDir = resolveStorageWorkspaceDir()) {
10529
- return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join18(workspaceDir, "wiki");
10564
+ return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join19(workspaceDir, "wiki");
10530
10565
  }
10531
10566
  var init_wiki = __esm(async () => {
10532
10567
  await init_storage_host();
@@ -10534,8 +10569,8 @@ var init_wiki = __esm(async () => {
10534
10569
 
10535
10570
  // ../../packages/core/src/agents/archiver.ts
10536
10571
  import { randomUUID as randomUUID9 } from "crypto";
10537
- import { existsSync as existsSync18, readdirSync as readdirSync5, readFileSync as readFileSync17, statSync as statSync7 } from "fs";
10538
- import { join as join19 } from "path";
10572
+ import { existsSync as existsSync19, readdirSync as readdirSync5, readFileSync as readFileSync18, statSync as statSync7 } from "fs";
10573
+ import { join as join20 } from "path";
10539
10574
  function resolveMemoryLanguage() {
10540
10575
  const override = process.env.NEGOTIUM_MEMORY_LANG?.trim();
10541
10576
  return override && override.length > 0 ? override : resolveOutputLanguage();
@@ -10786,10 +10821,10 @@ function distillOneLine(summaryMd) {
10786
10821
  return "";
10787
10822
  }
10788
10823
  function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
10789
- const dir = join19(storage.getWikiDir(), "summaries");
10824
+ const dir = join20(storage.getWikiDir(), "summaries");
10790
10825
  if (!storage.fileExists(dir))
10791
10826
  return null;
10792
- const predicted = join19(dir, wikiSummaryFilename(date, topicTitle, topicId));
10827
+ const predicted = join20(dir, wikiSummaryFilename(date, topicTitle, topicId));
10793
10828
  if (storage.fileExists(predicted) && storage.fileModifiedAt(predicted) >= sinceMs)
10794
10829
  return predicted;
10795
10830
  let best = null;
@@ -10797,7 +10832,7 @@ function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
10797
10832
  if (!f.startsWith(`${date}-`) || !isTopicSummaryFile(f, topicId ?? "", topicTitle)) {
10798
10833
  continue;
10799
10834
  }
10800
- const p = join19(dir, f);
10835
+ const p = join20(dir, f);
10801
10836
  try {
10802
10837
  const m = storage.fileModifiedAt(p);
10803
10838
  if (m >= sinceMs && (!best || m > best.mtime))
@@ -10878,9 +10913,9 @@ var init_archiver = __esm(async () => {
10878
10913
  defaultArchiverRuntime = createArchiverRuntime({
10879
10914
  storage: {
10880
10915
  getWikiDir: getSharedWikiDir,
10881
- fileExists: existsSync18,
10916
+ fileExists: existsSync19,
10882
10917
  listDirectory: readdirSync5,
10883
- readTextFile: (path) => readFileSync17(path, "utf-8"),
10918
+ readTextFile: (path) => readFileSync18(path, "utf-8"),
10884
10919
  fileSize: (path) => statSync7(path).size,
10885
10920
  fileModifiedAt: (path) => statSync7(path).mtimeMs,
10886
10921
  getGeneralTopicId: (userId) => ensurePersonalGeneral(userId).id,
@@ -11000,15 +11035,15 @@ function formatTopicArchiveTranscriptRecord(row, topicTitle, index) {
11000
11035
  }
11001
11036
 
11002
11037
  // ../../packages/core/src/storage/topic-archive.ts
11003
- import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync10 } from "fs";
11004
- import { join as join20 } from "path";
11038
+ import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync11 } from "fs";
11039
+ import { join as join21 } from "path";
11005
11040
  function archiveTopicMessages(topicId, topicTitle, options = {}) {
11006
11041
  const rows = options.afterRowid !== undefined ? getMessagesForTopicAfterRowid(topicId, options.afterRowid) : getAllMessagesForTopic(topicId);
11007
11042
  if (rows.length === 0)
11008
11043
  return null;
11009
11044
  const safeTopic = sanitizeTopicName(topicTitle, true);
11010
- const archiveDir = join20(getSharedWikiDir(), "archive");
11011
- mkdirSync12(archiveDir, { recursive: true });
11045
+ const archiveDir = join21(getSharedWikiDir(), "archive");
11046
+ mkdirSync13(archiveDir, { recursive: true });
11012
11047
  const date = new Date().toISOString().slice(0, 10);
11013
11048
  const reasonSuffix = options.reason && options.reason !== "delete" ? `_${options.reason}` : "";
11014
11049
  let filename;
@@ -11020,9 +11055,9 @@ function archiveTopicMessages(topicId, topicTitle, options = {}) {
11020
11055
  let path;
11021
11056
  while (true) {
11022
11057
  filename = `${safeTopic}_${date}${reasonSuffix}${counter === 1 ? "" : `_${counter}`}.jsonl`;
11023
- path = join20(archiveDir, filename);
11058
+ path = join21(archiveDir, filename);
11024
11059
  try {
11025
- writeFileSync10(path, body, { flag: "wx" });
11060
+ writeFileSync11(path, body, { flag: "wx" });
11026
11061
  break;
11027
11062
  } catch (err) {
11028
11063
  if (err.code !== "EEXIST")
@@ -11065,8 +11100,8 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
11065
11100
  const entries = readRawConversation(userId, topicTitle);
11066
11101
  if (entries.length === 0)
11067
11102
  return null;
11068
- const archiveDir = join20(getSharedWikiDir(), "archive");
11069
- mkdirSync12(archiveDir, { recursive: true });
11103
+ const archiveDir = join21(getSharedWikiDir(), "archive");
11104
+ mkdirSync13(archiveDir, { recursive: true });
11070
11105
  const safeTopic = sanitizeTopicName(topicTitle, true);
11071
11106
  const date = new Date().toISOString().slice(0, 10);
11072
11107
  const reasonSuffix = options.reason && options.reason !== "delete" ? `_${options.reason}` : "";
@@ -11093,9 +11128,9 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
11093
11128
  let counter = 1;
11094
11129
  while (true) {
11095
11130
  const suffix = counter === 1 ? "" : `_${counter}`;
11096
- const path = join20(archiveDir, `${safeTopic}_${date}${reasonSuffix}_events${suffix}.jsonl`);
11131
+ const path = join21(archiveDir, `${safeTopic}_${date}${reasonSuffix}_events${suffix}.jsonl`);
11097
11132
  try {
11098
- writeFileSync10(path, body, { flag: "wx" });
11133
+ writeFileSync11(path, body, { flag: "wx" });
11099
11134
  logger.info({ topicId, topicTitle, archive: path, eventCount: entries.length }, "archiveConversationEvents: archived raw conversation events");
11100
11135
  return { path, eventCount: entries.length };
11101
11136
  } catch (error) {
@@ -11411,8 +11446,8 @@ var init_idle_archiver = __esm(async () => {
11411
11446
  });
11412
11447
 
11413
11448
  // ../../packages/core/src/agents/topic-cleanup.ts
11414
- import { mkdirSync as mkdirSync13, renameSync as renameSync7, unlinkSync as unlinkSync13, writeFileSync as writeFileSync11 } from "fs";
11415
- import { dirname as dirname13 } from "path";
11449
+ import { mkdirSync as mkdirSync14, renameSync as renameSync8, unlinkSync as unlinkSync13, writeFileSync as writeFileSync12 } from "fs";
11450
+ import { dirname as dirname14 } from "path";
11416
11451
  function collectSessionIdsByAgent(entries, extraSessions = []) {
11417
11452
  const out = new Map;
11418
11453
  for (const e of entries) {
@@ -11478,11 +11513,11 @@ function createTopicLogMaintenance(host) {
11478
11513
  const path = runtimeHost.activeConversationPath(opts.userId, opts.topicName);
11479
11514
  const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
11480
11515
  try {
11481
- mkdirSync13(dirname13(path), { recursive: true });
11482
- writeFileSync11(tempPath, retained.length > 0 ? `${retained.map((entry) => JSON.stringify(entry)).join(`
11516
+ mkdirSync14(dirname14(path), { recursive: true });
11517
+ writeFileSync12(tempPath, retained.length > 0 ? `${retained.map((entry) => JSON.stringify(entry)).join(`
11483
11518
  `)}
11484
11519
  ` : "", { flag: "wx" });
11485
- renameSync7(tempPath, path);
11520
+ renameSync8(tempPath, path);
11486
11521
  } catch (e) {
11487
11522
  try {
11488
11523
  unlinkSync13(tempPath);
@@ -11640,9 +11675,9 @@ var init_usage_alert = __esm(() => {
11640
11675
 
11641
11676
  // ../../packages/core/src/topics/session.ts
11642
11677
  import { randomUUID as randomUUID10 } from "crypto";
11643
- import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, writeFileSync as writeFileSync12 } from "fs";
11678
+ import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, writeFileSync as writeFileSync13 } from "fs";
11644
11679
  import { tmpdir as tmpdir3 } from "os";
11645
- import { join as join21 } from "path";
11680
+ import { join as join22 } from "path";
11646
11681
  function previousCompactedSummary(entries) {
11647
11682
  for (let index = entries.length - 2;index >= 0; index -= 1) {
11648
11683
  const request = entries[index]?.event;
@@ -11772,7 +11807,7 @@ function formatCompactElapsed(startedAt) {
11772
11807
  async function summarizeTopicContext(request) {
11773
11808
  const startedAt = Date.now();
11774
11809
  const sessionIds = [];
11775
- const compactCwd = mkdtempSync2(join21(tmpdir3(), "negotium-compact-"));
11810
+ const compactCwd = mkdtempSync2(join22(tmpdir3(), "negotium-compact-"));
11776
11811
  const abortController = new AbortController;
11777
11812
  const relayAbort = () => abortController.abort(request.signal?.reason);
11778
11813
  if (request.signal?.aborted)
@@ -11799,7 +11834,7 @@ async function summarizeTopicContext(request) {
11799
11834
  let error = "";
11800
11835
  let toolViolation = false;
11801
11836
  let compactionLogCalls = 0;
11802
- const compactionLogPath = join21(compactCwd, "conversation.log");
11837
+ const compactionLogPath = join22(compactCwd, "conversation.log");
11803
11838
  try {
11804
11839
  const compactionMcp = useCompactionLog ? {
11805
11840
  compact_log: {
@@ -11813,7 +11848,7 @@ async function summarizeTopicContext(request) {
11813
11848
  }
11814
11849
  } : undefined;
11815
11850
  if (useCompactionLog)
11816
- writeFileSync12(compactionLogPath, request.source, { mode: 384 });
11851
+ writeFileSync13(compactionLogPath, request.source, { mode: 384 });
11817
11852
  for await (const event of runAgent({
11818
11853
  agent: request.agent,
11819
11854
  prompt: useCompactionLog ? [
@@ -12062,7 +12097,7 @@ __export(exports_derive, {
12062
12097
  TopicDeriveBusyError: () => TopicDeriveBusyError
12063
12098
  });
12064
12099
  import { createHash as createHash4, randomUUID as randomUUID11 } from "crypto";
12065
- import { mkdirSync as mkdirSync14, rmSync as rmSync5, unlinkSync as unlinkSync14 } from "fs";
12100
+ import { mkdirSync as mkdirSync15, rmSync as rmSync5, unlinkSync as unlinkSync14 } from "fs";
12066
12101
  function getTopics(opts = {}) {
12067
12102
  return listTopics(opts).filter((topic) => !isLegacySharedGeneral(topic.id));
12068
12103
  }
@@ -12195,7 +12230,7 @@ async function createDerivedTopicImpl(topic, sourceTopicId, userId, copyHistory,
12195
12230
  try {
12196
12231
  if (agent) {
12197
12232
  const cwd = derivedWorkspace;
12198
- mkdirSync14(cwd, { recursive: true });
12233
+ mkdirSync15(cwd, { recursive: true });
12199
12234
  const registry = getRegistry(agent);
12200
12235
  const requestedRolloutModel = subagentModel ?? (subagent?.agent ? undefined : sourceConfig?.model) ?? derived.defaultModel;
12201
12236
  const rolloutModel = resolveModelForAgent(agent, requestedRolloutModel, registry);
@@ -12418,14 +12453,14 @@ var init_derive = __esm(async () => {
12418
12453
  });
12419
12454
 
12420
12455
  // ../../packages/core/src/query/session-inbox-path.ts
12421
- import { join as join22 } from "path";
12456
+ import { join as join23 } from "path";
12422
12457
  function sessionInboxPath(userId, topicId) {
12423
12458
  const key = Buffer.from(topicId, "utf8").toString("base64url");
12424
- return join22(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${JSONL_SUFFIX}`);
12459
+ return join23(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${JSONL_SUFFIX}`);
12425
12460
  }
12426
12461
  function scheduledSessionInboxPath(userId, topicId) {
12427
12462
  const key = Buffer.from(topicId, "utf8").toString("base64url");
12428
- return join22(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${SCHEDULE_SUFFIX}`);
12463
+ return join23(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${SCHEDULE_SUFFIX}`);
12429
12464
  }
12430
12465
  var TOPIC_ID_FILE_PREFIX = "topic-id-", JSONL_SUFFIX = ".jsonl", SCHEDULE_SUFFIX = ".schedule";
12431
12466
  var init_session_inbox_path = __esm(() => {
@@ -12434,13 +12469,13 @@ var init_session_inbox_path = __esm(() => {
12434
12469
 
12435
12470
  // ../../packages/core/src/query/session-inbox-cleanup.ts
12436
12471
  import { unlinkSync as unlinkSync15 } from "fs";
12437
- import { basename as basename4, join as join23 } from "path";
12472
+ import { basename as basename4, join as join24 } from "path";
12438
12473
  function cleanupSessionInboxFiles(userId, topicId, legacyTopicTitle) {
12439
12474
  const live = sessionInboxPath(userId, topicId);
12440
12475
  const scheduled = scheduledSessionInboxPath(userId, topicId);
12441
12476
  const candidates = new Set([live, `${live}.processing`, scheduled, `${scheduled}.processing`]);
12442
12477
  if (legacyTopicTitle && legacyTopicTitle !== "." && legacyTopicTitle !== ".." && basename4(legacyTopicTitle) === legacyTopicTitle) {
12443
- const legacyBase = join23(SESSION_INBOX_DIR, userId, legacyTopicTitle);
12478
+ const legacyBase = join24(SESSION_INBOX_DIR, userId, legacyTopicTitle);
12444
12479
  for (const suffix of [".jsonl", ".jsonl.processing", ".schedule", ".schedule.processing"]) {
12445
12480
  candidates.add(`${legacyBase}${suffix}`);
12446
12481
  }
@@ -12465,29 +12500,29 @@ var init_session_inbox_cleanup = __esm(() => {
12465
12500
  });
12466
12501
 
12467
12502
  // ../../packages/core/src/query/state.ts
12468
- import { mkdirSync as mkdirSync15, renameSync as renameSync8, unlinkSync as unlinkSync16, writeFileSync as writeFileSync13 } from "fs";
12469
- import { basename as basename5, join as join24 } from "path";
12503
+ import { mkdirSync as mkdirSync16, renameSync as renameSync9, unlinkSync as unlinkSync16, writeFileSync as writeFileSync14 } from "fs";
12504
+ import { basename as basename5, join as join25 } from "path";
12470
12505
  function createQueryStateStore(options) {
12471
12506
  const sanitize = options.sanitizeTopicId ?? sanitizeId;
12472
- const queryStateDirPath = (userId) => join24(options.usersLogDir, String(userId), "active-queries");
12473
- const queryStateFile = (userId, topicId) => join24(queryStateDirPath(userId), `${sanitize(topicId)}.json`);
12507
+ const queryStateDirPath = (userId) => join25(options.usersLogDir, String(userId), "active-queries");
12508
+ const queryStateFile = (userId, topicId) => join25(queryStateDirPath(userId), `${sanitize(topicId)}.json`);
12474
12509
  const legacyQueryStateFile = (userId, topicName) => {
12475
12510
  if (!topicName || topicName === "." || topicName === ".." || basename5(topicName) !== topicName) {
12476
12511
  return null;
12477
12512
  }
12478
- return join24(queryStateDirPath(userId), `${topicName}.json`);
12513
+ return join25(queryStateDirPath(userId), `${topicName}.json`);
12479
12514
  };
12480
12515
  return {
12481
12516
  write(userId, topicId, topicName, task) {
12482
12517
  const dir = queryStateDirPath(userId);
12483
- mkdirSync15(dir, { recursive: true });
12518
+ mkdirSync16(dir, { recursive: true });
12484
12519
  const state = { topicId, topicName, since: new Date().toISOString() };
12485
12520
  if (task)
12486
12521
  state.task = [...task.replace(/\n+/g, " ").trim()].slice(0, 100).join("");
12487
12522
  const target = queryStateFile(userId, topicId);
12488
12523
  const tmp = `${target}.${process.pid}.${Date.now()}.tmp`;
12489
- writeFileSync13(tmp, JSON.stringify(state));
12490
- renameSync8(tmp, target);
12524
+ writeFileSync14(tmp, JSON.stringify(state));
12525
+ renameSync9(tmp, target);
12491
12526
  },
12492
12527
  clear(userId, topicId, legacyTopicName) {
12493
12528
  const paths = [
@@ -12628,38 +12663,38 @@ __export(exports_session_asks, {
12628
12663
  import { createHash as createHash5 } from "crypto";
12629
12664
  import {
12630
12665
  closeSync as closeSync3,
12631
- mkdirSync as mkdirSync16,
12666
+ mkdirSync as mkdirSync17,
12632
12667
  openSync as openSync3,
12633
12668
  readdirSync as readdirSync6,
12634
- readFileSync as readFileSync18,
12669
+ readFileSync as readFileSync19,
12635
12670
  statSync as statSync8,
12636
12671
  unlinkSync as unlinkSync17,
12637
- writeFileSync as writeFileSync14
12672
+ writeFileSync as writeFileSync15
12638
12673
  } from "fs";
12639
- import { dirname as dirname14, join as join25 } from "path";
12674
+ import { dirname as dirname15, join as join26 } from "path";
12640
12675
  function pendingAskDir(userId) {
12641
12676
  const rawUserId = String(userId);
12642
12677
  const safeUserId = /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${createHash5("sha256").update(rawUserId).digest("hex")}`;
12643
- return join25(resolveStorageSessionAsksDir(), safeUserId);
12678
+ return join26(resolveStorageSessionAsksDir(), safeUserId);
12644
12679
  }
12645
12680
  function encodeAskKey(key) {
12646
12681
  return JSON.stringify([key.from, key.to]);
12647
12682
  }
12648
12683
  function pendingAskPath(key) {
12649
12684
  const digest = createHash5("sha256").update(encodeAskKey(key)).digest("hex");
12650
- return join25(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
12685
+ return join26(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
12651
12686
  }
12652
12687
  function v2PendingAskPath(key) {
12653
12688
  const encoded = Buffer.from(encodeAskKey(key), "utf8").toString("base64url");
12654
- return join25(pendingAskDir(key.userId), `${V2_ASK_FILENAME_PREFIX}${encoded}.pending`);
12689
+ return join26(pendingAskDir(key.userId), `${V2_ASK_FILENAME_PREFIX}${encoded}.pending`);
12655
12690
  }
12656
12691
  function legacyPendingAskPath(key) {
12657
12692
  if (key.from.includes("/") || key.from.includes("\\") || key.to.includes("/") || key.to.includes("\\") || key.from.includes("\x00") || key.to.includes("\x00")) {
12658
12693
  return null;
12659
12694
  }
12660
12695
  const dir = pendingAskDir(key.userId);
12661
- const candidate = join25(dir, `${key.from}___${key.to}.pending`);
12662
- return dirname14(candidate) === dir ? candidate : null;
12696
+ const candidate = join26(dir, `${key.from}___${key.to}.pending`);
12697
+ return dirname15(candidate) === dir ? candidate : null;
12663
12698
  }
12664
12699
  function parsePendingAskFilename(fileName) {
12665
12700
  if (!fileName.endsWith(".pending"))
@@ -12680,7 +12715,7 @@ function parsePendingAskFilename(fileName) {
12680
12715
  }
12681
12716
  function readPendingAskFile(path, fallback) {
12682
12717
  try {
12683
- const raw = readFileSync18(path, "utf8").trim();
12718
+ const raw = readFileSync19(path, "utf8").trim();
12684
12719
  if (!raw)
12685
12720
  return null;
12686
12721
  if (!raw.startsWith("{")) {
@@ -12731,17 +12766,17 @@ function isStale(record, path) {
12731
12766
  }
12732
12767
  function writePendingAsk(record) {
12733
12768
  const path = pendingAskPath(record);
12734
- mkdirSync16(pendingAskDir(record.userId), { recursive: true });
12735
- writeFileSync14(path, `${JSON.stringify(record)}
12769
+ mkdirSync17(pendingAskDir(record.userId), { recursive: true });
12770
+ writeFileSync15(path, `${JSON.stringify(record)}
12736
12771
  `);
12737
12772
  }
12738
12773
  function writePendingAskIfAbsent(record) {
12739
12774
  const path = pendingAskPath(record);
12740
- mkdirSync16(pendingAskDir(record.userId), { recursive: true });
12775
+ mkdirSync17(pendingAskDir(record.userId), { recursive: true });
12741
12776
  let fd = null;
12742
12777
  try {
12743
12778
  fd = openSync3(path, "wx");
12744
- writeFileSync14(fd, `${JSON.stringify(record)}
12779
+ writeFileSync15(fd, `${JSON.stringify(record)}
12745
12780
  `);
12746
12781
  return true;
12747
12782
  } catch (error) {
@@ -12801,12 +12836,12 @@ function createPendingAsk(args) {
12801
12836
  createdAt: now,
12802
12837
  updatedAt: now
12803
12838
  };
12804
- mkdirSync16(pendingAskDir(args.userId), { recursive: true });
12839
+ mkdirSync17(pendingAskDir(args.userId), { recursive: true });
12805
12840
  for (let attempt = 0;attempt < 2; attempt++) {
12806
12841
  let fd = null;
12807
12842
  try {
12808
12843
  fd = openSync3(path, "wx");
12809
- writeFileSync14(fd, `${JSON.stringify(record)}
12844
+ writeFileSync15(fd, `${JSON.stringify(record)}
12810
12845
  `);
12811
12846
  return { ok: true, record };
12812
12847
  } catch (err) {
@@ -12899,7 +12934,7 @@ function listPendingAsksForCaller(args) {
12899
12934
  const parsed = isV3 ? { from: args.from, to: "" } : parsePendingAskFilename(fileName);
12900
12935
  if (!parsed)
12901
12936
  continue;
12902
- const path = join25(dir, fileName);
12937
+ const path = join26(dir, fileName);
12903
12938
  const record = readPendingAskFile(path, {
12904
12939
  userId: args.userId,
12905
12940
  from: parsed.from,
@@ -12941,7 +12976,7 @@ function deletePendingAsksForTopic(args) {
12941
12976
  }
12942
12977
  let deleted = 0;
12943
12978
  for (const fileName of files) {
12944
- const path = join25(dir, fileName);
12979
+ const path = join26(dir, fileName);
12945
12980
  const parsed = parsePendingAskFilename(fileName);
12946
12981
  const record = readPendingAskFile(path, {
12947
12982
  userId: args.userId,
@@ -13378,8 +13413,8 @@ var init_self_schedules = __esm(async () => {
13378
13413
 
13379
13414
  // ../../packages/core/src/storage/token-stats.ts
13380
13415
  import { createHash as createHash6 } from "crypto";
13381
- import { mkdirSync as mkdirSync17, writeFileSync as writeFileSync15 } from "fs";
13382
- import { join as join26 } from "path";
13416
+ import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
13417
+ import { join as join27 } from "path";
13383
13418
  function emptyBucket() {
13384
13419
  return {
13385
13420
  inputTokens: 0,
@@ -13397,8 +13432,8 @@ function tokenStatsFileId(userId) {
13397
13432
  function queriesPath(userId) {
13398
13433
  const fileId = tokenStatsFileId(userId);
13399
13434
  const logDir = resolveStorageLogDir();
13400
- mkdirSync17(logDir, { recursive: true });
13401
- return join26(logDir, `token-queries-${fileId}.jsonl`);
13435
+ mkdirSync18(logDir, { recursive: true });
13436
+ return join27(logDir, `token-queries-${fileId}.jsonl`);
13402
13437
  }
13403
13438
  function loadRecords(userId) {
13404
13439
  try {
@@ -13467,7 +13502,7 @@ function deleteTopicStats(userId, topicId) {
13467
13502
  return true;
13468
13503
  }
13469
13504
  });
13470
- writeFileSync15(path, kept.length > 0 ? `${kept.join(`
13505
+ writeFileSync16(path, kept.length > 0 ? `${kept.join(`
13471
13506
  `)}
13472
13507
  ` : "", "utf-8");
13473
13508
  } catch (e) {
@@ -13799,8 +13834,8 @@ var init_lifecycle = __esm(async () => {
13799
13834
 
13800
13835
  // ../../packages/core/src/runtime/attachments.ts
13801
13836
  import { randomUUID as randomUUID13 } from "crypto";
13802
- import { copyFileSync as copyFileSync2, mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
13803
- import { basename as basename6, join as join27 } from "path";
13837
+ import { copyFileSync as copyFileSync2, mkdirSync as mkdirSync19, writeFileSync as writeFileSync17 } from "fs";
13838
+ import { basename as basename6, join as join28 } from "path";
13804
13839
  function workspaceCwdFor(topicId) {
13805
13840
  return resolveTopicWorkspaceDir(topicId);
13806
13841
  }
@@ -13813,7 +13848,7 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
13813
13848
  if (!attachmentIds?.length)
13814
13849
  return [];
13815
13850
  const out = [];
13816
- const destDir = join27(workspaceCwdFor(topicId), "attachments", queryId);
13851
+ const destDir = join28(workspaceCwdFor(topicId), "attachments", queryId);
13817
13852
  for (const rawId of attachmentIds) {
13818
13853
  if (typeof rawId !== "string")
13819
13854
  continue;
@@ -13827,10 +13862,10 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
13827
13862
  continue;
13828
13863
  }
13829
13864
  try {
13830
- mkdirSync18(destDir, { recursive: true });
13865
+ mkdirSync19(destDir, { recursive: true });
13831
13866
  const index = String(out.length + 1).padStart(2, "0");
13832
13867
  const safeName = safeAttachmentFilename(attachment.filename, fileId);
13833
- const destPath = join27(destDir, `${index}-${fileId.slice(0, 8)}-${safeName}`);
13868
+ const destPath = join28(destDir, `${index}-${fileId.slice(0, 8)}-${safeName}`);
13834
13869
  copyFileSync2(sourcePath, destPath);
13835
13870
  out.push({
13836
13871
  id: attachment.id,
@@ -13860,14 +13895,14 @@ function promptWithAttachments(prompt, attachments) {
13860
13895
  return composeAttachmentPrompt(prompt, attachments.map(({ filename, path }) => attachmentPromptLine(filename, path)));
13861
13896
  }
13862
13897
  function ingestAttachment(args) {
13863
- const destDir = join27(UPLOADS_DIR, args.topicId);
13864
- mkdirSync18(destDir, { recursive: true });
13898
+ const destDir = join28(UPLOADS_DIR, args.topicId);
13899
+ mkdirSync19(destDir, { recursive: true });
13865
13900
  const safeName = safeAttachmentFilename(args.filename, "upload");
13866
- const destPath = join27(destDir, `${Date.now()}-${randomUUID13().slice(0, 8)}-${safeName}`);
13901
+ const destPath = join28(destDir, `${Date.now()}-${randomUUID13().slice(0, 8)}-${safeName}`);
13867
13902
  if (args.sourcePath !== undefined) {
13868
13903
  copyFileSync2(args.sourcePath, destPath);
13869
13904
  } else if (args.bytes !== undefined) {
13870
- writeFileSync16(destPath, args.bytes);
13905
+ writeFileSync17(destPath, args.bytes);
13871
13906
  } else {
13872
13907
  throw new Error("ingestAttachment: provide sourcePath or bytes");
13873
13908
  }
@@ -14336,7 +14371,7 @@ var init_ask_user_gates = __esm(async () => {
14336
14371
 
14337
14372
  // ../../packages/core/src/agents/mcp-tools/ask-user.ts
14338
14373
  import { createHash as createHash7, randomUUID as randomUUID14 } from "crypto";
14339
- import { z as z3 } from "zod";
14374
+ import { z as z4 } from "zod";
14340
14375
  function normalizeAskText(value2, maxChars) {
14341
14376
  if (typeof value2 !== "string")
14342
14377
  return;
@@ -14609,12 +14644,12 @@ function createAskUserRuntime(host) {
14609
14644
  name: "ask_user_question",
14610
14645
  description: "Ask the user a blocking multiple-choice question in the chat and wait for their selection. Use this instead of provider built-in AskUserQuestion. Use only when you cannot proceed safely without the user's choice.",
14611
14646
  schema: {
14612
- question: z3.string().describe("The concise question to show to the user."),
14613
- choices: z3.array(z3.object({
14614
- label: z3.string().describe("Choice label shown on the button."),
14615
- description: z3.string().optional().describe("Optional one-sentence consequence.")
14647
+ question: z4.string().describe("The concise question to show to the user."),
14648
+ choices: z4.array(z4.object({
14649
+ label: z4.string().describe("Choice label shown on the button."),
14650
+ description: z4.string().optional().describe("Optional one-sentence consequence.")
14616
14651
  })).min(1).max(MAX_CHOICES).describe("Choices the user can select."),
14617
- idempotency_key: z3.string().max(MAX_IDEMPOTENCY_KEY_CHARS).optional().describe("Optional retry key. Reusing it with the same question replays the existing result; reusing it with different content fails.")
14652
+ idempotency_key: z4.string().max(MAX_IDEMPOTENCY_KEY_CHARS).optional().describe("Optional retry key. Reusing it with the same question replays the existing result; reusing it with different content fails.")
14618
14653
  },
14619
14654
  async handler(input) {
14620
14655
  return askUserQuestion(ctx, input);
@@ -14784,6 +14819,10 @@ function buildMermaidHtml(code, theme, scriptUrl = MERMAID_BROWSER_ASSET_RELATIV
14784
14819
  .controls button:focus-visible{outline:2px solid var(--celadon);outline-offset:1px}
14785
14820
  .zoom-value{min-width:46px;color:var(--graphite);font:500 11px/1 Geist,system-ui,sans-serif;text-align:center;font-variant-numeric:tabular-nums}
14786
14821
  .error{margin:0;white-space:pre-wrap;color:#7D2E2E;background:#FFF5F2;border:1px solid #E4B9B1;border-radius:6px;padding:14px;font:13px ui-monospace,SFMono-Regular,Menlo,monospace}
14822
+ .failure{max-width:56ch;margin:0 auto}
14823
+ .failure p{margin:0 0 12px;color:var(--graphite);font:14px/1.6 Geist,system-ui,sans-serif}
14824
+ .failure details{color:var(--graphite);font:12px/1.5 Geist,system-ui,sans-serif}
14825
+ .failure summary{cursor:pointer;padding:4px 0}
14787
14826
  @media(max-width:600px){.viewport{padding:54px 14px 18px}.controls{top:10px;right:10px}}
14788
14827
  </style>
14789
14828
  </head>
@@ -14798,11 +14837,58 @@ function buildMermaidHtml(code, theme, scriptUrl = MERMAID_BROWSER_ASSET_RELATIV
14798
14837
  <script data-otium-mermaid-runtime src="${safeScriptUrl}"></script>
14799
14838
  <script>
14800
14839
  (async () => {
14840
+ // An unrendered document reports itself in more than one voice: Mermaid's
14841
+ // own 0x0 guard, and the browser refusing geometry on a path that was
14842
+ // never laid out. Both mean the same thing, so both are worth one retry
14843
+ // and, if it still fails, the same explanation.
14844
+ const unrendered = (error) => {
14845
+ const message = String(error && error.message ? error.message : error);
14846
+ return message.indexOf("not in render tree") !== -1 || message.indexOf("path is empty") !== -1;
14847
+ };
14801
14848
  try {
14802
14849
  const runtime = globalThis.mermaid;
14803
14850
  if (!runtime) throw new Error("Mermaid renderer failed to load.");
14804
14851
  runtime.initialize({ startOnLoad: false, securityLevel: "strict", theme: ${safeTheme} });
14805
- await runtime.run({ querySelector: ".mermaid" });
14852
+ const host = document.querySelector(".mermaid");
14853
+ // Mermaid sizes every label by appending a probe <svg> to the body and
14854
+ // reading getBBox(), and it throws "svg element not in render tree" the
14855
+ // moment that comes back 0x0. That is what a hidden panel looks like from
14856
+ // in here: the document exists but nothing is in the render tree, so the
14857
+ // measurement has no geometry to report. Ask the same question Mermaid
14858
+ // will ask, and only start once it has an answer.
14859
+ const measurable = () => {
14860
+ const probe = document.createElementNS("http://www.w3.org/2000/svg", "svg");
14861
+ const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
14862
+ text.textContent = "M";
14863
+ probe.appendChild(text);
14864
+ document.body.appendChild(probe);
14865
+ let box = { width: 0, height: 0 };
14866
+ try { box = text.getBBox(); } catch (ignored) {}
14867
+ probe.remove();
14868
+ return box.width > 0 || box.height > 0;
14869
+ };
14870
+ // requestAnimationFrame is the right clock here: a hidden document stops
14871
+ // being animated, so this waits without spinning and resumes on the frame
14872
+ // the panel is shown. The cap counts rendered frames, not wall time.
14873
+ const waitUntilMeasurable = async (maxFrames) => {
14874
+ for (let frame = 0; frame < maxFrames; frame += 1) {
14875
+ if (measurable()) return true;
14876
+ await new Promise((next) => requestAnimationFrame(next));
14877
+ }
14878
+ return measurable();
14879
+ };
14880
+ await waitUntilMeasurable(600);
14881
+ try {
14882
+ await runtime.run({ querySelector: ".mermaid" });
14883
+ } catch (firstAttempt) {
14884
+ if (!unrendered(firstAttempt)) throw firstAttempt;
14885
+ // The panel can be hidden again between the probe and the real measure.
14886
+ // Clear the marker Mermaid leaves behind so the retry is not skipped as
14887
+ // already done, then wait for the render tree once more.
14888
+ host.removeAttribute("data-processed");
14889
+ await waitUntilMeasurable(600);
14890
+ await runtime.run({ querySelector: ".mermaid" });
14891
+ }
14806
14892
  const viewport = document.querySelector(".viewport");
14807
14893
  const svg = document.querySelector(".mermaid svg");
14808
14894
  const value = document.querySelector(".zoom-value");
@@ -14829,7 +14915,22 @@ function buildMermaidHtml(code, theme, scriptUrl = MERMAID_BROWSER_ASSET_RELATIV
14829
14915
  applyScale(scale, false);
14830
14916
  } catch (error) {
14831
14917
  document.querySelector(".controls")?.remove();
14832
- document.querySelector(".viewport").innerHTML = '<pre class="error">' + String(error && error.message ? error.message : error).replace(/[&<>]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;'}[c])) + '</pre>';
14918
+ const raw = String(error && error.message ? error.message : error);
14919
+ const escape = (value) => value.replace(/[&<>]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;'}[c]));
14920
+ // Mermaid aborts the whole render when it cannot place an edge label:
14921
+ // cardinality markers ask for a point a fixed distance along the edge,
14922
+ // and a relation laid out shorter than that walks off the end. Nothing
14923
+ // is wrong with the diagram, so the raw message sends authors looking
14924
+ // for a syntax error that does not exist. Say what actually moves it.
14925
+ const note = raw.indexOf("Could not find a suitable point") !== -1
14926
+ ? "Two nodes ended up too close together for Mermaid to fit a label on the edge between them. Renaming a node, adding another, or setting an explicit direction usually spreads the layout enough to render."
14927
+ : unrendered(error)
14928
+ ? "The panel stayed hidden long enough that there was never a laid-out page to measure the diagram against. Reopening the panel renders it."
14929
+ : "This diagram could not be rendered.";
14930
+ document.querySelector(".viewport").innerHTML =
14931
+ '<div class="failure"><p>' + escape(note) +
14932
+ '</p><details><summary>Technical detail</summary><pre class="error">' +
14933
+ escape(raw) + '</pre></details></div>';
14833
14934
  }
14834
14935
  })();
14835
14936
  </script>
@@ -15631,10 +15732,10 @@ var init_turn_session = __esm(async () => {
15631
15732
  });
15632
15733
 
15633
15734
  // ../../packages/core/src/storage/app-settings.ts
15634
- import { existsSync as existsSync19, mkdirSync as mkdirSync19, readFileSync as readFileSync19, writeFileSync as writeFileSync17 } from "fs";
15635
- import { dirname as dirname15, join as join28 } from "path";
15735
+ import { existsSync as existsSync20, mkdirSync as mkdirSync20, readFileSync as readFileSync20, writeFileSync as writeFileSync18 } from "fs";
15736
+ import { dirname as dirname16, join as join29 } from "path";
15636
15737
  function settingsFile() {
15637
- return join28(resolveStorageDataDir(), "otium-settings.json");
15738
+ return join29(resolveStorageDataDir(), "otium-settings.json");
15638
15739
  }
15639
15740
  function ensureSettingsLoaded() {
15640
15741
  const path = settingsFile();
@@ -15643,8 +15744,8 @@ function ensureSettingsLoaded() {
15643
15744
  loadedSettingsFile = path;
15644
15745
  aiName = DEFAULT_AI_NAME;
15645
15746
  try {
15646
- if (existsSync19(path)) {
15647
- const data = JSON.parse(readFileSync19(path, "utf8"));
15747
+ if (existsSync20(path)) {
15748
+ const data = JSON.parse(readFileSync20(path, "utf8"));
15648
15749
  if (typeof data.aiName === "string" && data.aiName.trim()) {
15649
15750
  aiName = data.aiName.trim();
15650
15751
  }
@@ -15737,8 +15838,8 @@ __export(exports_turn_runner, {
15737
15838
  AGENT_DISPLAY_NAME: () => AGENT_DISPLAY_NAME
15738
15839
  });
15739
15840
  import { randomUUID as randomUUID16 } from "crypto";
15740
- import { existsSync as existsSync20, mkdirSync as mkdirSync20, readdirSync as readdirSync7, statSync as statSync10 } from "fs";
15741
- import { join as join29 } from "path";
15841
+ import { existsSync as existsSync21, mkdirSync as mkdirSync21, readdirSync as readdirSync7, statSync as statSync10 } from "fs";
15842
+ import { join as join30 } from "path";
15742
15843
  function withDefaultPlaywright(configuredMcp, isManager) {
15743
15844
  if (isManager)
15744
15845
  return configuredMcp;
@@ -15793,8 +15894,8 @@ function appendAskReplyMessage(topicId, text2, agentType) {
15793
15894
  return message;
15794
15895
  }
15795
15896
  function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, matchesTitle, preferExact = true) {
15796
- const preferred = join29(directory, preferredFilename);
15797
- if (preferExact && existsSync20(preferred))
15897
+ const preferred = join30(directory, preferredFilename);
15898
+ if (preferExact && existsSync21(preferred))
15798
15899
  return preferred;
15799
15900
  let newestLegacyId = null;
15800
15901
  let newestTitle = null;
@@ -15804,7 +15905,7 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
15804
15905
  const legacyIdMatch = !titleMatch && matchesLegacyId(filename);
15805
15906
  if (!titleMatch && !legacyIdMatch)
15806
15907
  continue;
15807
- const path = join29(directory, filename);
15908
+ const path = join30(directory, filename);
15808
15909
  const mtimeMs = statSync10(path).mtimeMs;
15809
15910
  if (titleMatch) {
15810
15911
  if (!newestTitle || mtimeMs > newestTitle.mtimeMs) {
@@ -15818,10 +15919,10 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
15818
15919
  return newestTitle?.path ?? newestLegacyId?.path ?? preferred;
15819
15920
  }
15820
15921
  function resolveWikiMemoryMirror(wikiDir, topicId, topicTitle) {
15821
- const briefFile = resolveWikiMirrorPath(join29(wikiDir, "topic"), `${wikiBriefStorageKey(topicTitle, topicId)}.md`, (filename) => isTopicBriefFile(filename, topicId), (filename) => isTopicBriefFile(filename, topicId, topicTitle));
15822
- const hasBriefFile = existsSync20(briefFile) && statSync10(briefFile).isFile();
15823
- const latestSummaryCandidate = resolveWikiMirrorPath(join29(wikiDir, "summaries"), `__missing__-${wikiSummaryFilename("0000-00-00", topicTitle, topicId)}`, (filename) => isTopicSummaryFile(filename, topicId), (filename) => isTopicSummaryFile(filename, topicId, topicTitle), false);
15824
- const latestSummaryFile = existsSync20(latestSummaryCandidate) && statSync10(latestSummaryCandidate).isFile() ? latestSummaryCandidate : null;
15922
+ const briefFile = resolveWikiMirrorPath(join30(wikiDir, "topic"), `${wikiBriefStorageKey(topicTitle, topicId)}.md`, (filename) => isTopicBriefFile(filename, topicId), (filename) => isTopicBriefFile(filename, topicId, topicTitle));
15923
+ const hasBriefFile = existsSync21(briefFile) && statSync10(briefFile).isFile();
15924
+ const latestSummaryCandidate = resolveWikiMirrorPath(join30(wikiDir, "summaries"), `__missing__-${wikiSummaryFilename("0000-00-00", topicTitle, topicId)}`, (filename) => isTopicSummaryFile(filename, topicId), (filename) => isTopicSummaryFile(filename, topicId, topicTitle), false);
15925
+ const latestSummaryFile = existsSync21(latestSummaryCandidate) && statSync10(latestSummaryCandidate).isFile() ? latestSummaryCandidate : null;
15825
15926
  return { briefFile, hasBriefFile, latestSummaryFile };
15826
15927
  }
15827
15928
  async function streamAgentEvents(topicId, topicTitle, queryId, events, control, agentType, model, effort, userId, retryableSessionExpired = true, onSessionId, execution) {
@@ -16496,7 +16597,7 @@ function startAiTurn(params) {
16496
16597
  const resolvedModel = execution.model;
16497
16598
  const resolvedEffort = execution.effort;
16498
16599
  const workspaceCwd = cwd ?? workspaceCwdFor(topicId);
16499
- mkdirSync20(workspaceCwd, { recursive: true });
16600
+ mkdirSync21(workspaceCwd, { recursive: true });
16500
16601
  let historyBridgeError;
16501
16602
  if (bridgeSessionFromHistory && !sessionId) {
16502
16603
  const bridge = tryReconstructTopicRollout({
@@ -17017,9 +17118,9 @@ __export(exports_auth_check, {
17017
17118
  checkAgentAuth: () => checkAgentAuth
17018
17119
  });
17019
17120
  import { execFileSync as execFileSync7 } from "child_process";
17020
- import { existsSync as existsSync21 } from "fs";
17121
+ import { existsSync as existsSync22 } from "fs";
17021
17122
  import { homedir as homedir8, platform } from "os";
17022
- import { join as join30 } from "path";
17123
+ import { join as join31 } from "path";
17023
17124
  function hasMaestroCredential(host, key, userId) {
17024
17125
  if (userId && host.getVaultValue(userId, key)?.trim())
17025
17126
  return true;
@@ -17039,7 +17140,7 @@ function checkAgentAuth(agent, host = defaultAgentAuthHost, userId) {
17039
17140
  case "claude": {
17040
17141
  if (host.environment.ANTHROPIC_API_KEY)
17041
17142
  return { ok: true };
17042
- const path = join30(host.homeDirectory(), ".claude", ".credentials.json");
17143
+ const path = join31(host.homeDirectory(), ".claude", ".credentials.json");
17043
17144
  if (host.operatingSystem() === "darwin") {
17044
17145
  if (host.hasMacOsCredential("Claude Code-credentials") || host.exists(path)) {
17045
17146
  return { ok: true };
@@ -17090,7 +17191,7 @@ var init_auth_check = __esm(async () => {
17090
17191
  await init_vault();
17091
17192
  defaultAgentAuthHost = {
17092
17193
  codexAuthFilePath,
17093
- exists: existsSync21,
17194
+ exists: existsSync22,
17094
17195
  environment: process.env,
17095
17196
  homeDirectory: homedir8,
17096
17197
  operatingSystem: platform,
@@ -17107,7 +17208,7 @@ var init_auth_check = __esm(async () => {
17107
17208
  });
17108
17209
 
17109
17210
  // ../../packages/core/src/agents/mcp-tools/spawn-subagent.ts
17110
- import { z as z4 } from "zod";
17211
+ import { z as z5 } from "zod";
17111
17212
  function createSubagentLifecycle(host) {
17112
17213
  const runtimeOwnerId = host.runtime.ownerId;
17113
17214
  const limits = Object.freeze({
@@ -17642,7 +17743,7 @@ ${card.task}${reportInstruction}`;
17642
17743
  name: "start_subagent",
17643
17744
  description: "Start a prepared descendant subagent task.",
17644
17745
  schema: {
17645
- topic_id: z4.string().describe("Prepared subagent room id.")
17746
+ topic_id: z5.string().describe("Prepared subagent room id.")
17646
17747
  },
17647
17748
  async handler(input) {
17648
17749
  const result = ownedSubagentTree(ctx);
@@ -17662,7 +17763,7 @@ ${card.task}${reportInstruction}`;
17662
17763
  name: "delete_subagent",
17663
17764
  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.",
17664
17765
  schema: {
17665
- topic_id: z4.string().describe("Exact subagent room id returned by list_subagents; names are not accepted.")
17766
+ topic_id: z5.string().describe("Exact subagent room id returned by list_subagents; names are not accepted.")
17666
17767
  },
17667
17768
  async handler(input) {
17668
17769
  const result = ownedSubagentTree(ctx);
@@ -17686,8 +17787,8 @@ ${card.task}${reportInstruction}`;
17686
17787
  name: "grant_subagent_tell",
17687
17788
  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.",
17688
17789
  schema: {
17689
- subagent_topic_id: z4.string().describe("Source descendant subagent topic id."),
17690
- target_topic_id: z4.string().describe("Target topic id: this manager room or another descendant in its tree.")
17790
+ subagent_topic_id: z5.string().describe("Source descendant subagent topic id."),
17791
+ target_topic_id: z5.string().describe("Target topic id: this manager room or another descendant in its tree.")
17691
17792
  },
17692
17793
  async handler(input) {
17693
17794
  const result = ownedSubagentTree(ctx);
@@ -17713,8 +17814,8 @@ ${card.task}${reportInstruction}`;
17713
17814
  name: "revoke_subagent_tell",
17714
17815
  description: "Remove an extra tell_session connection previously granted to a descendant subagent.",
17715
17816
  schema: {
17716
- subagent_topic_id: z4.string().describe("Source descendant subagent topic id."),
17717
- target_topic_id: z4.string().describe("Granted target topic id.")
17817
+ subagent_topic_id: z5.string().describe("Source descendant subagent topic id."),
17818
+ target_topic_id: z5.string().describe("Granted target topic id.")
17718
17819
  },
17719
17820
  async handler(input) {
17720
17821
  const result = ownedSubagentTree(ctx);
@@ -17740,12 +17841,12 @@ ${card.task}${reportInstruction}`;
17740
17841
  name: "spawn_subagent",
17741
17842
  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.",
17742
17843
  schema: {
17743
- task: z4.string().describe("Self-contained task brief. The worker has topic memory but no parent conversation history, so include all task-specific context."),
17744
- name: z4.string().optional().describe("Short name for the subagent room. Auto-generated when omitted."),
17745
- agent: z4.enum(["claude", "codex", "maestro"]).optional().describe("Agent backend override. Defaults to this room's agent."),
17746
- 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.`),
17747
- 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."),
17748
- 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.")
17844
+ task: z5.string().describe("Self-contained task brief. The worker has topic memory but no parent conversation history, so include all task-specific context."),
17845
+ name: z5.string().optional().describe("Short name for the subagent room. Auto-generated when omitted."),
17846
+ agent: z5.enum(["claude", "codex", "maestro"]).optional().describe("Agent backend override. Defaults to this room's agent."),
17847
+ 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.`),
17848
+ 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."),
17849
+ 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.")
17749
17850
  },
17750
17851
  async handler(input) {
17751
17852
  return createSubagent(ctx, input, true);
@@ -17757,12 +17858,12 @@ ${card.task}${reportInstruction}`;
17757
17858
  name: "create_subagent",
17758
17859
  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.",
17759
17860
  schema: {
17760
- task: z4.string().describe("Self-contained task brief for the subagent."),
17761
- name: z4.string().optional().describe("Short room name. Auto-generated when omitted."),
17762
- agent: z4.enum(["claude", "codex", "maestro"]).optional(),
17763
- model: z4.string().optional(),
17764
- 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."),
17765
- report_mode: z4.enum(["auto", "tell", "status-only"]).optional()
17861
+ task: z5.string().describe("Self-contained task brief for the subagent."),
17862
+ name: z5.string().optional().describe("Short room name. Auto-generated when omitted."),
17863
+ agent: z5.enum(["claude", "codex", "maestro"]).optional(),
17864
+ model: z5.string().optional(),
17865
+ 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."),
17866
+ report_mode: z5.enum(["auto", "tell", "status-only"]).optional()
17766
17867
  },
17767
17868
  async handler(input) {
17768
17869
  return createSubagent(ctx, input, false);
@@ -20637,9 +20738,285 @@ function createCompactionLogMcpServer(context) {
20637
20738
  });
20638
20739
  return server;
20639
20740
  }
20640
- // ../../packages/core/src/mcp/factories/session-comm.ts
20741
+ // ../../packages/core/src/mcp/factories/decision.ts
20641
20742
  import { McpServer as McpServer3 } from "@modelcontextprotocol/sdk/server/mcp.js";
20642
- import { z as z5 } from "zod";
20743
+ import { z as z3 } from "zod";
20744
+
20745
+ // ../../packages/core/src/storage/decisions.ts
20746
+ init_logger();
20747
+ await init_storage_host();
20748
+ import { existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync10, renameSync as renameSync5, writeFileSync as writeFileSync7 } from "fs";
20749
+ import { dirname as dirname8, join as join12 } from "path";
20750
+ var DECISION_STATUS_VALUES = [
20751
+ "proposed",
20752
+ "accepted",
20753
+ "executed",
20754
+ "rejected",
20755
+ "superseded"
20756
+ ];
20757
+ function safeDecisionScopeKey(scopeKey) {
20758
+ const safe = sanitizeFileName(scopeKey);
20759
+ if (!safe || safe === "." || safe === "..") {
20760
+ throw new Error(`decisions: refusing unsafe scope key: ${scopeKey}`);
20761
+ }
20762
+ return safe;
20763
+ }
20764
+ function decisionScopeKey(opts) {
20765
+ return opts.topicId?.trim() || opts.session || "default";
20766
+ }
20767
+ function getDecisionFilePath(userId, scopeKey) {
20768
+ return join12(resolveStorageDataDir(), "decisions", `${safeDecisionScopeKey(scopeKey)}.json`);
20769
+ }
20770
+ function readDecisions(userId, scopeKey) {
20771
+ const path = getDecisionFilePath(userId, scopeKey);
20772
+ if (!existsSync11(path))
20773
+ return [];
20774
+ try {
20775
+ const parsed = JSON.parse(readFileSync10(path, "utf-8"));
20776
+ return Array.isArray(parsed?.decisions) ? parsed.decisions : [];
20777
+ } catch (error) {
20778
+ logger.warn({ err: error, path }, "decisions: failed to read decision store");
20779
+ return [];
20780
+ }
20781
+ }
20782
+ function writeDecisions(userId, scopeKey, decisions) {
20783
+ const path = getDecisionFilePath(userId, scopeKey);
20784
+ mkdirSync9(dirname8(path), { recursive: true });
20785
+ const payload = { version: 1, decisions };
20786
+ const tmp = `${path}.${process.pid}.tmp`;
20787
+ writeFileSync7(tmp, JSON.stringify(payload, null, 2), "utf-8");
20788
+ renameSync5(tmp, path);
20789
+ }
20790
+ function nextDecisionId(decisions) {
20791
+ let max = 0;
20792
+ for (const decision of decisions) {
20793
+ const id = Number(decision.id);
20794
+ if (Number.isInteger(id) && id > max)
20795
+ max = id;
20796
+ }
20797
+ return max + 1;
20798
+ }
20799
+ function normalizedIds(ids) {
20800
+ if (!ids)
20801
+ return;
20802
+ const unique = [...new Set(ids.map((id) => id.trim()).filter(Boolean))];
20803
+ return unique.length > 0 ? unique : undefined;
20804
+ }
20805
+ function validateDecisionGraph(decisions) {
20806
+ const ids = new Set(decisions.map((decision) => decision.id));
20807
+ for (const decision of decisions) {
20808
+ for (const upstream of decision.causedBy ?? []) {
20809
+ if (!ids.has(upstream)) {
20810
+ throw new Error(`Decision #${decision.id} references missing decision #${upstream}.`);
20811
+ }
20812
+ if (upstream === decision.id) {
20813
+ throw new Error(`Decision #${decision.id} cannot cause itself.`);
20814
+ }
20815
+ }
20816
+ }
20817
+ const visiting = new Set;
20818
+ const visited = new Set;
20819
+ const byId = new Map(decisions.map((decision) => [decision.id, decision]));
20820
+ const visit = (id) => {
20821
+ if (visited.has(id))
20822
+ return;
20823
+ if (visiting.has(id))
20824
+ throw new Error(`Decision graph contains a cycle at #${id}.`);
20825
+ visiting.add(id);
20826
+ for (const upstream of byId.get(id)?.causedBy ?? [])
20827
+ visit(upstream);
20828
+ visiting.delete(id);
20829
+ visited.add(id);
20830
+ };
20831
+ for (const id of ids)
20832
+ visit(id);
20833
+ }
20834
+ function createDecisions(decisions, inputs) {
20835
+ const out = [...decisions];
20836
+ const created = [];
20837
+ let id = nextDecisionId(out);
20838
+ for (const input of inputs) {
20839
+ const decision = {
20840
+ id: String(id++),
20841
+ action: input.action.trim(),
20842
+ reasoning: input.reasoning.trim(),
20843
+ agent: input.agent,
20844
+ status: input.status ?? "accepted",
20845
+ timestamp: input.timestamp ?? Date.now(),
20846
+ ...input.model ? { model: input.model } : {},
20847
+ ...normalizedIds(input.causedBy) ? { causedBy: normalizedIds(input.causedBy) } : {}
20848
+ };
20849
+ out.push(decision);
20850
+ created.push(decision);
20851
+ }
20852
+ validateDecisionGraph(out);
20853
+ return { decisions: out, created };
20854
+ }
20855
+ function updateDecisions(decisions, updates) {
20856
+ const out = decisions.map((decision) => ({
20857
+ ...decision,
20858
+ ...decision.causedBy ? { causedBy: [...decision.causedBy] } : {}
20859
+ }));
20860
+ const byId = new Map(out.map((decision) => [decision.id, decision]));
20861
+ const missing = [];
20862
+ for (const update of updates) {
20863
+ const decision = byId.get(update.id);
20864
+ if (!decision) {
20865
+ missing.push(update.id);
20866
+ continue;
20867
+ }
20868
+ if (update.action !== undefined)
20869
+ decision.action = update.action.trim();
20870
+ if (update.reasoning !== undefined)
20871
+ decision.reasoning = update.reasoning.trim();
20872
+ if (update.status !== undefined)
20873
+ decision.status = update.status;
20874
+ if (update.causedBy !== undefined) {
20875
+ const ids = normalizedIds(update.causedBy);
20876
+ if (ids)
20877
+ decision.causedBy = ids;
20878
+ else
20879
+ delete decision.causedBy;
20880
+ }
20881
+ }
20882
+ validateDecisionGraph(out);
20883
+ return { decisions: out, missing };
20884
+ }
20885
+ function deleteDecisions(decisions, opts) {
20886
+ if (opts.all)
20887
+ return { decisions: [], removed: decisions.length };
20888
+ const ids = new Set(opts.ids ?? []);
20889
+ const kept = decisions.filter((decision) => !ids.has(decision.id)).map((decision) => {
20890
+ const causedBy = decision.causedBy?.filter((id) => !ids.has(id));
20891
+ const next = { ...decision };
20892
+ if (causedBy && causedBy.length > 0)
20893
+ next.causedBy = causedBy;
20894
+ else
20895
+ delete next.causedBy;
20896
+ return next;
20897
+ });
20898
+ return { decisions: kept, removed: decisions.length - kept.length };
20899
+ }
20900
+ function renderDecisionList(decisions) {
20901
+ if (decisions.length === 0)
20902
+ return "Decisions (0 recorded)";
20903
+ return [
20904
+ `Decisions (${decisions.length} recorded)`,
20905
+ ...decisions.map((decision) => {
20906
+ const causes = decision.causedBy?.length ? ` <- ${decision.causedBy.map((id) => `#${id}`).join(", ")}` : "";
20907
+ return `[${decision.status}] #${decision.id} ${decision.action}${causes}
20908
+ ${decision.reasoning}`;
20909
+ })
20910
+ ].join(`
20911
+ `);
20912
+ }
20913
+
20914
+ // ../../packages/core/src/mcp/factories/decision.ts
20915
+ init_mcp_helpers();
20916
+ var defaultDecisionMcpHost = { readDecisions, writeDecisions };
20917
+ function createDecisionMcpServer(context, host = defaultDecisionMcpHost) {
20918
+ const scopeKey = context.topic ? decisionScopeKey({ topicId: context.topicId, session: context.topic }) : "";
20919
+ const server = new McpServer3({ name: "decision", version: "1.0.0" });
20920
+ const requireContext = () => !context.userId || !scopeKey ? mcpError("Error: missing userId/topic context.") : null;
20921
+ const statusEnum = z3.enum(DECISION_STATUS_VALUES);
20922
+ server.tool("decision_create", "Record one or more durable decisions in this topic. Include the rationale and causal predecessors.", {
20923
+ decisions: z3.array(z3.object({
20924
+ action: z3.string().min(1).describe("Concise statement of what was decided"),
20925
+ reasoning: z3.string().min(1).describe("Why this choice was made"),
20926
+ status: statusEnum.optional().describe("Defaults to accepted"),
20927
+ caused_by: z3.array(z3.string()).optional().describe("Upstream decision ids")
20928
+ })).min(1)
20929
+ }, async ({ decisions: inputs }) => {
20930
+ const guard = requireContext();
20931
+ if (guard)
20932
+ return guard;
20933
+ try {
20934
+ const current = host.readDecisions(context.userId, scopeKey);
20935
+ const { decisions, created } = createDecisions(current, inputs.map((input) => ({
20936
+ action: input.action,
20937
+ reasoning: input.reasoning,
20938
+ status: input.status,
20939
+ causedBy: input.caused_by,
20940
+ agent: context.agent,
20941
+ model: context.model
20942
+ })));
20943
+ host.writeDecisions(context.userId, scopeKey, decisions);
20944
+ return mcpOk(`${created.length} decision(s) recorded (${created.map((item) => `#${item.id}`).join(", ")})
20945
+
20946
+ ${renderDecisionList(decisions)}`);
20947
+ } catch (error) {
20948
+ return mcpError(`decision_create failed: ${errMsg(error)}`);
20949
+ }
20950
+ });
20951
+ server.tool("decision_update", "Update decisions or their causal links in this topic.", {
20952
+ updates: z3.array(z3.object({
20953
+ id: z3.string(),
20954
+ action: z3.string().min(1).optional(),
20955
+ reasoning: z3.string().min(1).optional(),
20956
+ status: statusEnum.optional(),
20957
+ caused_by: z3.array(z3.string()).optional().describe("Replacement upstream decision ids")
20958
+ })).min(1)
20959
+ }, async ({ updates }) => {
20960
+ const guard = requireContext();
20961
+ if (guard)
20962
+ return guard;
20963
+ try {
20964
+ const current = host.readDecisions(context.userId, scopeKey);
20965
+ const { decisions, missing } = updateDecisions(current, updates.map((update) => ({
20966
+ ...update,
20967
+ causedBy: update.caused_by
20968
+ })));
20969
+ host.writeDecisions(context.userId, scopeKey, decisions);
20970
+ const warning = missing.length ? `
20971
+ Missing ids ignored: ${missing.join(", ")}` : "";
20972
+ return mcpOk(`${renderDecisionList(decisions)}${warning}`);
20973
+ } catch (error) {
20974
+ return mcpError(`decision_update failed: ${errMsg(error)}`);
20975
+ }
20976
+ });
20977
+ server.tool("decision_list", "Read this topic's decision graph as a concise list.", {}, async () => {
20978
+ const guard = requireContext();
20979
+ if (guard)
20980
+ return guard;
20981
+ try {
20982
+ return mcpOk(renderDecisionList(host.readDecisions(context.userId, scopeKey)));
20983
+ } catch (error) {
20984
+ return mcpError(`decision_list failed: ${errMsg(error)}`);
20985
+ }
20986
+ });
20987
+ server.tool("decision_get", "Read one topic decision as JSON.", { id: z3.string() }, async ({ id }) => {
20988
+ const guard = requireContext();
20989
+ if (guard)
20990
+ return guard;
20991
+ try {
20992
+ const decision = host.readDecisions(context.userId, scopeKey).find((item) => item.id === id);
20993
+ return decision ? mcpOk(JSON.stringify(decision, null, 2)) : mcpError(`Decision #${id} not found.`);
20994
+ } catch (error) {
20995
+ return mcpError(`decision_get failed: ${errMsg(error)}`);
20996
+ }
20997
+ });
20998
+ server.tool("decision_delete", "Delete decisions. Downstream causal references to deleted decisions are removed.", { ids: z3.array(z3.string()).optional(), all: z3.boolean().optional() }, async ({ ids, all }) => {
20999
+ const guard = requireContext();
21000
+ if (guard)
21001
+ return guard;
21002
+ if (!all && (!ids || ids.length === 0))
21003
+ return mcpError("Provide ids or all=true.");
21004
+ try {
21005
+ const current = host.readDecisions(context.userId, scopeKey);
21006
+ const { decisions, removed } = deleteDecisions(current, { ids, all });
21007
+ host.writeDecisions(context.userId, scopeKey, decisions);
21008
+ return mcpOk(`${removed} decision(s) deleted
21009
+
21010
+ ${renderDecisionList(decisions)}`);
21011
+ } catch (error) {
21012
+ return mcpError(`decision_delete failed: ${errMsg(error)}`);
21013
+ }
21014
+ });
21015
+ return server;
21016
+ }
21017
+ // ../../packages/core/src/mcp/factories/session-comm.ts
21018
+ import { McpServer as McpServer4 } from "@modelcontextprotocol/sdk/server/mcp.js";
21019
+ import { z as z6 } from "zod";
20643
21020
 
20644
21021
  // ../../packages/core/src/mcp/session-comm/tell-permissions.ts
20645
21022
  await init_spawn_subagent();
@@ -20672,34 +21049,34 @@ function canSubagentTellTargetByName(identity, target) {
20672
21049
 
20673
21050
  // ../../packages/core/src/mcp/factories/session-comm.ts
20674
21051
  function createSessionCommMcpServer(context, host, options = {}) {
20675
- const server = new McpServer3({ name: "session-comm", version: "2.0.0" });
21052
+ const server = new McpServer4({ name: "session-comm", version: "2.0.0" });
20676
21053
  const required = options.requiredMcpServers?.join(", ") || "none";
20677
21054
  const optional = options.optionalMcpServers?.join(", ") || "none";
20678
21055
  const subagentIdentity = resolveSubagentTellIdentity(context.currentTopicId, context.subagentParentTopicId);
20679
21056
  server.tool("list_sessions", "List available sessions and remote peer sessions for inter-session communication.", {}, async () => host.listSessions(context));
20680
21057
  server.tool("configure_mcp", `Configure optional MCP servers for the current topic. Required: ${required}. Optional: ${optional}.`, {
20681
- enabled: z5.array(z5.string()).nullable().optional()
21058
+ enabled: z6.array(z6.string()).nullable().optional()
20682
21059
  }, async ({ enabled }) => host.configureMcp(context, enabled));
20683
21060
  server.tool("get_mcp_config", "Get the current topic MCP configuration.", {}, async () => host.getMcpConfig(context));
20684
21061
  if (host.getBrowserProfile) {
20685
21062
  server.tool("get_browser_profile", "Get this topic's browser profile and the profiles owned by the same user.", {}, async () => host.getBrowserProfile(context));
20686
21063
  }
20687
21064
  if (host.setBrowserProfile) {
20688
- server.tool("set_browser_profile", "Assign this topic to a named shared browser profile. Takes effect next turn.", { profile: z5.string() }, async ({ profile }) => host.setBrowserProfile(context, profile));
21065
+ 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));
20689
21066
  }
20690
21067
  if (host.deleteBrowserProfile) {
20691
- 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: z5.string() }, async ({ profile }) => host.deleteBrowserProfile(context, profile));
21068
+ 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));
20692
21069
  }
20693
21070
  server.tool("peek_session", "Inspect running and idle sessions, including pending ask_session calls.", {}, async () => host.peekSession(context));
20694
- server.tool("set_description", "Set the current session description used as a routing hint.", { description: z5.string() }, async ({ description }) => host.setDescription(context, description));
21071
+ server.tool("set_description", "Set the current session description used as a routing hint.", { description: z6.string() }, async ({ description }) => host.setDescription(context, description));
20695
21072
  if (!context.replyOnly) {
20696
21073
  if (!subagentIdentity.restricted) {
20697
- server.tool("ask_session", "Ask another local or remote session a question and wait for its answer.", { to: z5.string(), message: z5.string() }, async (input) => host.askSession(context, input));
21074
+ 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));
20698
21075
  }
20699
21076
  if (!subagentIdentity.restricted) {
20700
- server.tool("abort_session", "Abort the active query in another session.", { to: z5.string() }, async ({ to }) => host.abortSession(context, to));
21077
+ server.tool("abort_session", "Abort the active query in another session.", { to: z6.string() }, async ({ to }) => host.abortSession(context, to));
20701
21078
  }
20702
- server.tool("tell_session", "Send a one-way message to another local or remote session.", { to: z5.string(), message: z5.string() }, async (input) => {
21079
+ server.tool("tell_session", "Send a one-way message to another local or remote session.", { to: z6.string(), message: z6.string() }, async (input) => {
20703
21080
  if (subagentIdentity.restricted && !canSubagentTellTargetByName(subagentIdentity, input.to)) {
20704
21081
  return {
20705
21082
  content: [
@@ -20737,7 +21114,7 @@ init_mcp_helpers();
20737
21114
  import { execFile } from "child_process";
20738
21115
  import { cpus, freemem, loadavg, totalmem } from "os";
20739
21116
  import { promisify } from "util";
20740
- import { McpServer as McpServer4 } from "@modelcontextprotocol/sdk/server/mcp.js";
21117
+ import { McpServer as McpServer5 } from "@modelcontextprotocol/sdk/server/mcp.js";
20741
21118
  var execFileAsync = promisify(execFile);
20742
21119
  function fmtBytes(bytes) {
20743
21120
  const gb = bytes / 1024 ** 3;
@@ -20830,7 +21207,7 @@ var defaultSystemHealthMcpHost = {
20830
21207
  }
20831
21208
  };
20832
21209
  function createSystemHealthMcpServer(host = defaultSystemHealthMcpHost) {
20833
- const server = new McpServer4({ name: "system-health", version: "1.0.0" });
21210
+ const server = new McpServer5({ name: "system-health", version: "1.0.0" });
20834
21211
  server.tool("get_system_health", [
20835
21212
  "\uC2DC\uC2A4\uD15C \uC0C1\uD0DC\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4.",
20836
21213
  "- CPU \uBD80\uD558 (load average 1/5/15\uBD84)",
@@ -20863,28 +21240,28 @@ function createSystemHealthMcpServer(host = defaultSystemHealthMcpHost) {
20863
21240
  // ../../packages/core/src/mcp/factories/task.ts
20864
21241
  await init_tasks();
20865
21242
  init_mcp_helpers();
20866
- import { McpServer as McpServer5 } from "@modelcontextprotocol/sdk/server/mcp.js";
20867
- import { z as z6 } from "zod";
21243
+ import { McpServer as McpServer6 } from "@modelcontextprotocol/sdk/server/mcp.js";
21244
+ import { z as z7 } from "zod";
20868
21245
  var defaultTaskMcpHost = {
20869
21246
  readTasks,
20870
21247
  writeTasks
20871
21248
  };
20872
21249
  function createTaskMcpServer(context, host = defaultTaskMcpHost) {
20873
21250
  const scopeKey = context.topic ? taskScopeKey({ topicId: context.topicId, session: context.topic }) : "";
20874
- const server = new McpServer5({ name: "task", version: "1.0.0" });
21251
+ const server = new McpServer6({ name: "task", version: "1.0.0" });
20875
21252
  const requireContext = () => {
20876
21253
  if (!context.userId || !scopeKey) {
20877
21254
  return mcpError("Error: missing userId/topic context.");
20878
21255
  }
20879
21256
  return null;
20880
21257
  };
20881
- const statusEnum = z6.enum(TASK_STATUS_VALUES);
21258
+ const statusEnum = z7.enum(TASK_STATUS_VALUES);
20882
21259
  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.", {
20883
- tasks: z6.array(z6.object({
20884
- subject: z6.string().min(1).describe("Short imperative task description"),
20885
- active_form: z6.string().optional().describe("Label to show while the task is in progress"),
20886
- blocked_by: z6.array(z6.string()).optional().describe("Task ids blocking this task"),
20887
- owner: z6.string().optional().describe("Owner/agent label for multi-agent work")
21260
+ tasks: z7.array(z7.object({
21261
+ subject: z7.string().min(1).describe("Short imperative task description"),
21262
+ active_form: z7.string().optional().describe("Label to show while the task is in progress"),
21263
+ blocked_by: z7.array(z7.string()).optional().describe("Task ids blocking this task"),
21264
+ owner: z7.string().optional().describe("Owner/agent label for multi-agent work")
20888
21265
  })).min(1)
20889
21266
  }, async ({ tasks: inputs }) => {
20890
21267
  const guard = requireContext();
@@ -20908,13 +21285,13 @@ ${renderTaskList(tasks2)}`);
20908
21285
  }
20909
21286
  });
20910
21287
  server.tool("task_update", "Update task status/content in this topic's shared Otium task list. Batch related status changes in one call.", {
20911
- updates: z6.array(z6.object({
20912
- id: z6.string().describe("Target task id"),
21288
+ updates: z7.array(z7.object({
21289
+ id: z7.string().describe("Target task id"),
20913
21290
  status: statusEnum.optional().describe("New status"),
20914
- subject: z6.string().min(1).optional().describe("Replacement task description"),
20915
- active_form: z6.string().optional().describe("Replacement in-progress label; empty string clears it"),
20916
- blocked_by: z6.array(z6.string()).optional().describe("Replacement blocker id list"),
20917
- owner: z6.string().optional().describe("Replacement owner; empty string clears it")
21291
+ subject: z7.string().min(1).optional().describe("Replacement task description"),
21292
+ active_form: z7.string().optional().describe("Replacement in-progress label; empty string clears it"),
21293
+ blocked_by: z7.array(z7.string()).optional().describe("Replacement blocker id list"),
21294
+ owner: z7.string().optional().describe("Replacement owner; empty string clears it")
20918
21295
  })).min(1)
20919
21296
  }, async ({ updates }) => {
20920
21297
  const guard = requireContext();
@@ -20948,7 +21325,7 @@ Missing ids ignored: ${missing.join(", ")}` : "";
20948
21325
  return mcpError(`task_list failed: ${errMsg(error)}`);
20949
21326
  }
20950
21327
  });
20951
- server.tool("task_get", "Read one task from this topic's shared Otium task list as JSON.", { id: z6.string().describe("Task id") }, async ({ id }) => {
21328
+ 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 }) => {
20952
21329
  const guard = requireContext();
20953
21330
  if (guard)
20954
21331
  return guard;
@@ -20962,8 +21339,8 @@ Missing ids ignored: ${missing.join(", ")}` : "";
20962
21339
  }
20963
21340
  });
20964
21341
  server.tool("task_delete", "Delete tasks from this topic's shared Otium task list. Use all=true only when starting a new plan.", {
20965
- ids: z6.array(z6.string()).optional().describe("Task ids to delete"),
20966
- all: z6.boolean().optional().describe("Delete the whole list")
21342
+ ids: z7.array(z7.string()).optional().describe("Task ids to delete"),
21343
+ all: z7.boolean().optional().describe("Delete the whole list")
20967
21344
  }, async ({ ids, all }) => {
20968
21345
  const guard = requireContext();
20969
21346
  if (guard)
@@ -20985,8 +21362,8 @@ ${renderTaskList(tasks2)}`);
20985
21362
  }
20986
21363
  // ../../packages/core/src/mcp/factories/token-stats.ts
20987
21364
  await init_token_stats();
20988
- import { McpServer as McpServer6 } from "@modelcontextprotocol/sdk/server/mcp.js";
20989
- import { z as z7 } from "zod";
21365
+ import { McpServer as McpServer7 } from "@modelcontextprotocol/sdk/server/mcp.js";
21366
+ import { z as z8 } from "zod";
20990
21367
  var defaultTokenStatsMcpHost = {
20991
21368
  getStats,
20992
21369
  calcCost
@@ -20994,7 +21371,7 @@ var defaultTokenStatsMcpHost = {
20994
21371
  function createTokenStatsMcpServer(context, host = defaultTokenStatsMcpHost) {
20995
21372
  if (!context.userId)
20996
21373
  throw new Error("token-stats MCP requires userId");
20997
- const server = new McpServer6({ name: "token-stats", version: "1.0.0" });
21374
+ const server = new McpServer7({ name: "token-stats", version: "1.0.0" });
20998
21375
  server.tool("get_usage_stats", [
20999
21376
  "Claude/Codex/Maestro \uC0AC\uC6A9\uB7C9\uACFC provider session\uC758 \uCD5C\uC2E0 context \uC810\uC720\uC728\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. \uD604\uC7AC \uC2E4\uD589 \uC911\uC778 turn\uC740 \uC644\uB8CC \uD6C4 \uC9D1\uACC4\uB429\uB2C8\uB2E4.",
21000
21377
  "",
@@ -21006,9 +21383,9 @@ function createTokenStatsMcpServer(context, host = defaultTokenStatsMcpHost) {
21006
21383
  "groupBy: 'session'\uC73C\uB85C \uC124\uC815\uD558\uBA74 \uC138\uC158\uBCC4 \uD1A0\uD070 \uC0AC\uC6A9\uB7C9 \uB7AD\uD0B9\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4."
21007
21384
  ].join(`
21008
21385
  `), {
21009
- from: z7.string().optional().describe("\uC2DC\uC791 \uC2DC\uAC01 ISO 8601. \uC608: '2026-03-28T09:00:00Z'"),
21010
- to: z7.string().optional().describe("\uC885\uB8CC \uC2DC\uAC01 ISO 8601. \uC0DD\uB7B5 \uC2DC \uD604\uC7AC"),
21011
- groupBy: z7.enum(["hour", "session"]).optional().describe("\uC9D1\uACC4 \uAE30\uC900. \uAE30\uBCF8\uAC12: hour")
21386
+ from: z8.string().optional().describe("\uC2DC\uC791 \uC2DC\uAC01 ISO 8601. \uC608: '2026-03-28T09:00:00Z'"),
21387
+ to: z8.string().optional().describe("\uC885\uB8CC \uC2DC\uAC01 ISO 8601. \uC0DD\uB7B5 \uC2DC \uD604\uC7AC"),
21388
+ groupBy: z8.enum(["hour", "session"]).optional().describe("\uC9D1\uACC4 \uAE30\uC900. \uAE30\uBCF8\uAC12: hour")
21012
21389
  }, async ({ from, to, groupBy = "hour" }) => {
21013
21390
  const snapshot = host.getStats(context.userId, from, to);
21014
21391
  const {
@@ -21082,11 +21459,11 @@ function createTokenStatsMcpServer(context, host = defaultTokenStatsMcpHost) {
21082
21459
  return server;
21083
21460
  }
21084
21461
  // ../../packages/core/src/mcp/factories/vault.ts
21085
- import { McpServer as McpServer7 } from "@modelcontextprotocol/sdk/server/mcp.js";
21086
- import { z as z8 } from "zod";
21462
+ import { McpServer as McpServer8 } from "@modelcontextprotocol/sdk/server/mcp.js";
21463
+ import { z as z9 } from "zod";
21087
21464
  init_mcp_helpers();
21088
21465
  function createVaultMcpServer(context, host, executors = {}) {
21089
- const server = new McpServer7({ name: "vault", version: "1.0.0" });
21466
+ const server = new McpServer8({ name: "vault", version: "1.0.0" });
21090
21467
  const run = executors.run ?? executeVaultRun;
21091
21468
  const http = executors.http ?? executeVaultHttpRequest;
21092
21469
  server.tool("vault_list", context.httpOnly ? "List the user's Vault keys and descriptions without exposing values. Use vault_http_request for HTTPS APIs that need a credential." : "List the user's Vault keys and descriptions without exposing values. Use vault_http_request for APIs and vault_run for shell/CLI work that needs a credential.", {}, () => {
@@ -21102,9 +21479,9 @@ ${lines.join(`
21102
21479
  });
21103
21480
  if (!context.listOnly && !context.httpOnly) {
21104
21481
  server.tool("vault_run", "Run a shell command containing {{KEY}} references inside Otium's credential broker. Expanded command input never reaches the model/provider, and stdout/stderr are redacted before return. Prefer vault_http_request for HTTP APIs.", {
21105
- command: z8.string().min(1).max(64 * 1024),
21106
- timeout_ms: z8.number().int().min(1000).max(600000).optional(),
21107
- max_output_bytes: z8.number().int().min(1024).max(2 * 1024 * 1024).optional()
21482
+ command: z9.string().min(1).max(64 * 1024),
21483
+ timeout_ms: z9.number().int().min(1000).max(600000).optional(),
21484
+ max_output_bytes: z9.number().int().min(1024).max(2 * 1024 * 1024).optional()
21108
21485
  }, async ({ command, timeout_ms, max_output_bytes }) => {
21109
21486
  if (!context.userId)
21110
21487
  return mcpError("Vault unavailable: no user context");
@@ -21119,12 +21496,12 @@ ${lines.join(`
21119
21496
  }
21120
21497
  if (!context.listOnly)
21121
21498
  server.tool("vault_http_request", "Make an HTTPS request with {{KEY}} references resolved inside Otium. Put secrets in headers or body, never in the URL. The expanded request is not returned; the response is redacted before the model sees it.", {
21122
- method: z8.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("GET"),
21123
- url: z8.string().url().describe("Absolute HTTPS URL without Vault placeholders"),
21124
- headers: z8.record(z8.string(), z8.string()).optional(),
21125
- body: z8.string().optional(),
21126
- timeout_ms: z8.number().int().min(1000).max(120000).optional(),
21127
- max_response_bytes: z8.number().int().min(1024).max(1024 * 1024).optional()
21499
+ method: z9.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("GET"),
21500
+ url: z9.string().url().describe("Absolute HTTPS URL without Vault placeholders"),
21501
+ headers: z9.record(z9.string(), z9.string()).optional(),
21502
+ body: z9.string().optional(),
21503
+ timeout_ms: z9.number().int().min(1000).max(120000).optional(),
21504
+ max_response_bytes: z9.number().int().min(1024).max(1024 * 1024).optional()
21128
21505
  }, async ({ method, url, headers, body, timeout_ms, max_response_bytes }) => {
21129
21506
  if (!context.userId)
21130
21507
  return mcpError("Vault unavailable: no user context");
@@ -21148,6 +21525,7 @@ export {
21148
21525
  defaultTokenStatsMcpHost,
21149
21526
  defaultTaskMcpHost,
21150
21527
  defaultSystemHealthMcpHost,
21528
+ defaultDecisionMcpHost,
21151
21529
  createWikiMcpServer,
21152
21530
  createVaultMcpServer,
21153
21531
  createTokenStatsMcpServer,
@@ -21155,8 +21533,9 @@ export {
21155
21533
  createSystemHealthMcpServer,
21156
21534
  createSessionTargetCatalog,
21157
21535
  createSessionCommMcpServer,
21536
+ createDecisionMcpServer,
21158
21537
  createCompactionLogMcpServer,
21159
21538
  createAgentHealthMcpServer
21160
21539
  };
21161
21540
 
21162
- //# debugId=71C4DD8A3EF6D5BE64756E2164756E21
21541
+ //# debugId=A4B482066B2CF06D64756E2164756E21