negotium 0.3.13 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +2 -6
  2. package/dist/agent-helpers.js +328 -185
  3. package/dist/agent-helpers.js.map +9 -8
  4. package/dist/{chunk-0ynjwr50.js → chunk-zq2tcq4k.js} +12 -29
  5. package/dist/{chunk-0ynjwr50.js.map → chunk-zq2tcq4k.js.map} +4 -4
  6. package/dist/hosted-agent.js +207 -64
  7. package/dist/hosted-agent.js.map +8 -7
  8. package/dist/main.js +753 -886
  9. package/dist/main.js.map +13 -13
  10. package/dist/mcp-factories.js +300 -468
  11. package/dist/mcp-factories.js.map +9 -10
  12. package/dist/registry.js +3 -3
  13. package/dist/registry.js.map +2 -2
  14. package/dist/rollout.js +1 -1
  15. package/dist/runtime/src/agents/codex-provider.ts +33 -8
  16. package/dist/runtime/src/agents/codex-vault-hook-bridge.ts +192 -0
  17. package/dist/runtime/src/agents/codex-vault-hook.mjs +48 -0
  18. package/dist/runtime/src/agents/execution-host.ts +1 -14
  19. package/dist/runtime/src/agents/public-helpers.ts +0 -9
  20. package/dist/runtime/src/agents/vault-tool-policy.ts +9 -51
  21. package/dist/runtime/src/mcp/factories/index.ts +1 -8
  22. package/dist/runtime/src/mcp/factories/vault.ts +6 -104
  23. package/dist/runtime/src/mcp/vault-server.ts +4 -20
  24. package/dist/runtime/src/prompts/sessions/_shared-tools.md +1 -1
  25. package/dist/runtime/src/version.ts +1 -1
  26. package/dist/types/packages/core/src/agents/codex-vault-hook-bridge.d.ts +27 -0
  27. package/dist/types/packages/core/src/agents/execution-host.d.ts +0 -2
  28. package/dist/types/packages/core/src/agents/public-helpers.d.ts +0 -1
  29. package/dist/types/packages/core/src/agents/vault-tool-policy.d.ts +1 -14
  30. package/dist/types/packages/core/src/mcp/factories/index.d.ts +1 -3
  31. package/dist/types/packages/core/src/mcp/factories/vault.d.ts +4 -13
  32. package/dist/types/packages/core/src/version.d.ts +1 -1
  33. package/package.json +1 -1
  34. package/dist/runtime/src/mcp/factories/vault-host.ts +0 -8
  35. package/dist/runtime/src/mcp/vault-http.ts +0 -235
  36. package/dist/runtime/src/mcp/vault-run.ts +0 -154
  37. package/dist/types/packages/core/src/mcp/factories/vault-host.d.ts +0 -7
  38. package/dist/types/packages/core/src/mcp/vault-http.d.ts +0 -23
  39. package/dist/types/packages/core/src/mcp/vault-run.d.ts +0 -19
package/dist/main.js CHANGED
@@ -1869,7 +1869,7 @@ var exports_version = {};
1869
1869
  __export(exports_version, {
1870
1870
  NEGOTIUM_VERSION: () => NEGOTIUM_VERSION
1871
1871
  });
1872
- var NEGOTIUM_VERSION = "0.3.13";
1872
+ var NEGOTIUM_VERSION = "0.4.0";
1873
1873
 
1874
1874
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
1875
1875
  import { spawn } from "child_process";
@@ -2139,30 +2139,21 @@ function shouldSubstituteVaultToolInput(toolName) {
2139
2139
  const leaf = leafToolName(toolName);
2140
2140
  return leaf.startsWith("browser_") || DIRECT_VAULT_EXECUTION_TOOLS.has(leaf);
2141
2141
  }
2142
- function createVaultToolPolicy(host) {
2143
- function isVaultBrokerTool(toolName) {
2144
- return toolName.includes("vault_run") || toolName.includes("vault_http_request");
2145
- }
2146
- function referencesRuntimeSecretStorage(value) {
2147
- if (typeof value === "string") {
2148
- const lower = value.toLowerCase();
2149
- if (SENSITIVE_RUNTIME_NAMES.some((name) => lower.includes(name)))
2150
- return true;
2151
- return value.startsWith("/") && host.isSensitivePath(value);
2152
- }
2153
- if (Array.isArray(value))
2154
- return value.some(referencesRuntimeSecretStorage);
2155
- if (value && typeof value === "object") {
2156
- return Object.values(value).some(referencesRuntimeSecretStorage);
2157
- }
2158
- return false;
2142
+ function referencesRuntimeSecretStorage(value) {
2143
+ if (typeof value === "string") {
2144
+ const lower = value.toLowerCase();
2145
+ if (SENSITIVE_RUNTIME_NAMES.some((name) => lower.includes(name)))
2146
+ return true;
2147
+ return value.startsWith("/") && isSensitivePath(value);
2159
2148
  }
2160
- function shouldRedirectVaultTool(userId, toolName, input) {
2161
- return false;
2149
+ if (Array.isArray(value))
2150
+ return value.some(referencesRuntimeSecretStorage);
2151
+ if (value && typeof value === "object") {
2152
+ return Object.values(value).some(referencesRuntimeSecretStorage);
2162
2153
  }
2163
- return { isVaultBrokerTool, referencesRuntimeSecretStorage, shouldRedirectVaultTool };
2154
+ return false;
2164
2155
  }
2165
- var SENSITIVE_RUNTIME_NAMES, DIRECT_VAULT_EXECUTION_TOOLS, defaultVaultToolPolicy, isVaultBrokerTool, referencesRuntimeSecretStorage, shouldRedirectVaultTool;
2156
+ var SENSITIVE_RUNTIME_NAMES, DIRECT_VAULT_EXECUTION_TOOLS;
2166
2157
  var init_vault_tool_policy = __esm(() => {
2167
2158
  init_sensitive_path();
2168
2159
  SENSITIVE_RUNTIME_NAMES = [
@@ -2172,13 +2163,6 @@ var init_vault_tool_policy = __esm(() => {
2172
2163
  "sessions.db"
2173
2164
  ];
2174
2165
  DIRECT_VAULT_EXECUTION_TOOLS = new Set(["Bash", "WebFetch"]);
2175
- defaultVaultToolPolicy = createVaultToolPolicy({
2176
- isSensitivePath,
2177
- valueReferencesVaultKey: () => false
2178
- });
2179
- isVaultBrokerTool = defaultVaultToolPolicy.isVaultBrokerTool;
2180
- referencesRuntimeSecretStorage = defaultVaultToolPolicy.referencesRuntimeSecretStorage;
2181
- shouldRedirectVaultTool = defaultVaultToolPolicy.shouldRedirectVaultTool;
2182
2166
  });
2183
2167
 
2184
2168
  // ../../packages/core/src/mcp/canonical-bridge-config.ts
@@ -3317,7 +3301,6 @@ var init_execution_host = __esm(async () => {
3317
3301
  redactVaultSecrets,
3318
3302
  substituteVaultSecrets: (userId, value) => vaultSubstituteDetailed(userId, value).text,
3319
3303
  referencesRuntimeSecretStorage,
3320
- shouldRedirectVaultTool,
3321
3304
  claudeCodeExecutablePath: () => CLAUDE_EXECUTABLE,
3322
3305
  codexAuthFilePath
3323
3306
  };
@@ -5256,9 +5239,163 @@ var init_claude_provider = __esm(async () => {
5256
5239
  ]);
5257
5240
  });
5258
5241
 
5242
+ // ../../packages/core/src/agents/codex-vault-hook-bridge.ts
5243
+ import { randomBytes as randomBytes5 } from "crypto";
5244
+ import { existsSync as existsSync13 } from "fs";
5245
+ import { chmod, mkdtemp, rm, writeFile } from "fs/promises";
5246
+ import { createServer } from "net";
5247
+ import { tmpdir as tmpdir2 } from "os";
5248
+ import { dirname as dirname9, join as join13, resolve as resolve7 } from "path";
5249
+ import { fileURLToPath as fileURLToPath2 } from "url";
5250
+ function evaluateCodexVaultPreToolUse(input, userId, operations) {
5251
+ if (operations.referencesSensitiveStorage(input.tool_input)) {
5252
+ return {
5253
+ hookSpecificOutput: {
5254
+ hookEventName: "PreToolUse",
5255
+ permissionDecision: "deny",
5256
+ permissionDecisionReason: SENSITIVE_STORAGE_DENIAL
5257
+ }
5258
+ };
5259
+ }
5260
+ if (!shouldSubstituteVaultToolInput(input.tool_name))
5261
+ return {};
5262
+ const updatedInput = deepMapStrings(input.tool_input, (value) => operations.substitute(userId, value));
5263
+ if (JSON.stringify(updatedInput) === JSON.stringify(input.tool_input))
5264
+ return {};
5265
+ return {
5266
+ hookSpecificOutput: {
5267
+ hookEventName: "PreToolUse",
5268
+ permissionDecision: "allow",
5269
+ updatedInput
5270
+ }
5271
+ };
5272
+ }
5273
+ function shellQuote(value) {
5274
+ return `'${value.replaceAll("'", `'"'"'`)}'`;
5275
+ }
5276
+ function hookClientPath() {
5277
+ const moduleDir = dirname9(fileURLToPath2(import.meta.url));
5278
+ const adjacent = resolve7(moduleDir, "codex-vault-hook.mjs");
5279
+ if (existsSync13(adjacent))
5280
+ return adjacent;
5281
+ const packaged = resolve7(moduleDir, "runtime/src/agents/codex-vault-hook.mjs");
5282
+ if (existsSync13(packaged))
5283
+ return packaged;
5284
+ throw new Error("Codex Vault hook client is missing from this installation");
5285
+ }
5286
+ function privateCodexWrapper(codexScript) {
5287
+ return [
5288
+ "#!/bin/sh",
5289
+ 'if [ "$1" = "exec" ]; then',
5290
+ " shift",
5291
+ ` exec ${shellQuote(process.execPath)} ${shellQuote(codexScript)} exec --dangerously-bypass-hook-trust "$@"`,
5292
+ "fi",
5293
+ `exec ${shellQuote(process.execPath)} ${shellQuote(codexScript)} "$@"`,
5294
+ ""
5295
+ ].join(`
5296
+ `);
5297
+ }
5298
+ async function createCodexVaultHookBridge(userId) {
5299
+ const root = await mkdtemp(join13(tmpdir2(), "negotium-codex-vault-"));
5300
+ await chmod(root, 448);
5301
+ const socketPath = join13(root, "hook.sock");
5302
+ const wrapperPath = join13(root, "codex-with-hooks");
5303
+ const token = randomBytes5(32).toString("hex");
5304
+ const connections = new Set;
5305
+ const server = createServer((socket) => {
5306
+ connections.add(socket);
5307
+ socket.once("close", () => connections.delete(socket));
5308
+ socket.setEncoding("utf8");
5309
+ let request = "";
5310
+ let handled = false;
5311
+ const handleRequest = () => {
5312
+ if (handled)
5313
+ return;
5314
+ handled = true;
5315
+ try {
5316
+ const parsed = JSON.parse(request.trimEnd());
5317
+ if (parsed.token !== token)
5318
+ throw new Error("invalid hook capability");
5319
+ if (!parsed.input || typeof parsed.input !== "object" || typeof parsed.input.tool_name !== "string") {
5320
+ throw new Error("invalid PreToolUse payload");
5321
+ }
5322
+ const output = evaluateCodexVaultPreToolUse(parsed.input, userId, {
5323
+ referencesSensitiveStorage: referencesHostedSecretStorage,
5324
+ substitute: substituteHostedSecrets
5325
+ });
5326
+ socket.end(JSON.stringify({ ok: true, output }));
5327
+ } catch (error) {
5328
+ const message = error instanceof Error ? error.message : String(error);
5329
+ socket.end(JSON.stringify({ ok: false, error: message }));
5330
+ }
5331
+ };
5332
+ socket.on("data", (chunk) => {
5333
+ request += chunk;
5334
+ if (Buffer.byteLength(request) > MAX_HOOK_REQUEST_BYTES)
5335
+ socket.destroy();
5336
+ else if (request.endsWith(`
5337
+ `))
5338
+ handleRequest();
5339
+ });
5340
+ socket.on("end", handleRequest);
5341
+ });
5342
+ try {
5343
+ await new Promise((resolveListen, reject) => {
5344
+ server.once("error", reject);
5345
+ server.listen(socketPath, () => {
5346
+ server.off("error", reject);
5347
+ resolveListen();
5348
+ });
5349
+ });
5350
+ await chmod(socketPath, 384);
5351
+ await writeFile(wrapperPath, privateCodexWrapper(codexCliScriptPath()), { mode: 448 });
5352
+ const command = [
5353
+ shellQuote(process.execPath),
5354
+ shellQuote(hookClientPath()),
5355
+ shellQuote(socketPath),
5356
+ shellQuote(token)
5357
+ ].join(" ");
5358
+ return {
5359
+ codexPathOverride: wrapperPath,
5360
+ hooks: {
5361
+ PreToolUse: [
5362
+ {
5363
+ matcher: "*",
5364
+ hooks: [
5365
+ {
5366
+ type: "command",
5367
+ command,
5368
+ timeout: 30,
5369
+ statusMessage: "Resolving Vault placeholders"
5370
+ }
5371
+ ]
5372
+ }
5373
+ ]
5374
+ },
5375
+ async close() {
5376
+ for (const socket of connections)
5377
+ socket.destroy();
5378
+ await new Promise((resolveClose) => server.close(() => resolveClose()));
5379
+ await rm(root, { recursive: true, force: true });
5380
+ }
5381
+ };
5382
+ } catch (error) {
5383
+ server.close();
5384
+ await rm(root, { recursive: true, force: true });
5385
+ throw error;
5386
+ }
5387
+ }
5388
+ var MAX_HOOK_REQUEST_BYTES, SENSITIVE_STORAGE_DENIAL = "Runtime secret storage access is not permitted";
5389
+ var init_codex_vault_hook_bridge = __esm(async () => {
5390
+ init_codex_native_multi_agent();
5391
+ await init_execution_host();
5392
+ init_vault_tool_policy();
5393
+ MAX_HOOK_REQUEST_BYTES = 1024 * 1024;
5394
+ });
5395
+
5259
5396
  // ../../packages/core/src/agents/tool-format.ts
5260
5397
  import { readFileSync as readFileSync10, statSync as statSync4 } from "fs";
5261
- import { isAbsolute, resolve as resolve7 } from "path";
5398
+ import { isAbsolute, resolve as resolve8 } from "path";
5262
5399
  import { diffLines as computeLineDiff } from "diff";
5263
5400
  function summarizeDisplayText(value) {
5264
5401
  const normalized = value.replace(/\s+/g, " ").trim();
@@ -5582,7 +5719,7 @@ function sourceStartLine(input, before, after, cwd) {
5582
5719
  const rawPath = input.file_path ?? input.path;
5583
5720
  if (typeof rawPath !== "string" || !rawPath.trim())
5584
5721
  return 1;
5585
- const path = isAbsolute(rawPath) ? rawPath : resolve7(cwd ?? process.cwd(), rawPath);
5722
+ const path = isAbsolute(rawPath) ? rawPath : resolve8(cwd ?? process.cwd(), rawPath);
5586
5723
  try {
5587
5724
  if (statSync4(path).size > TOOL_DIFF_SOURCE_MAX_BYTES)
5588
5725
  return 1;
@@ -5780,9 +5917,9 @@ __export(exports_codex_provider, {
5780
5917
  codexProvider: () => codexProvider
5781
5918
  });
5782
5919
  import { execFileSync as execFileSync5 } from "child_process";
5783
- import { existsSync as existsSync13, readFileSync as readFileSync11, realpathSync as realpathSync3, statSync as statSync5 } from "fs";
5920
+ import { existsSync as existsSync14, readFileSync as readFileSync11, realpathSync as realpathSync3, statSync as statSync5 } from "fs";
5784
5921
  import { homedir as homedir7 } from "os";
5785
- import { dirname as dirname9, isAbsolute as isAbsolute2, join as join13, relative, resolve as resolve8 } from "path";
5922
+ import { dirname as dirname10, isAbsolute as isAbsolute2, join as join14, relative, resolve as resolve9 } from "path";
5786
5923
  import { Codex } from "@openai/codex-sdk";
5787
5924
  function sameCodexUsage(usage, total) {
5788
5925
  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;
@@ -5810,8 +5947,8 @@ function codexMcpServerName(name) {
5810
5947
  return CODEX_MCP_SERVER_NAME_OVERRIDES[name] ?? name;
5811
5948
  }
5812
5949
  function globalCodexMcpServerNames(authFilePath) {
5813
- const configPath = join13(dirname9(authFilePath), "config.toml");
5814
- if (!existsSync13(configPath))
5950
+ const configPath = join14(dirname10(authFilePath), "config.toml");
5951
+ if (!existsSync14(configPath))
5815
5952
  return [];
5816
5953
  try {
5817
5954
  const names = new Set;
@@ -5906,13 +6043,13 @@ function summarizeMcpToolCallResult(item) {
5906
6043
  }
5907
6044
  function canonicalPath(path) {
5908
6045
  try {
5909
- return realpathSync3(resolve8(path));
6046
+ return realpathSync3(resolve9(path));
5910
6047
  } catch {
5911
- return resolve8(path);
6048
+ return resolve9(path);
5912
6049
  }
5913
6050
  }
5914
6051
  function textFile(path, maxBytes = CODEX_DIFF_FILE_LIMIT) {
5915
- if (!existsSync13(path))
6052
+ if (!existsSync14(path))
5916
6053
  return null;
5917
6054
  try {
5918
6055
  const byteLimit = Math.min(CODEX_DIFF_FILE_LIMIT, Math.max(0, maxBytes));
@@ -5952,7 +6089,7 @@ class CodexFilePreviewTracker {
5952
6089
  const code = record.slice(0, 2);
5953
6090
  const path = record.slice(3);
5954
6091
  const remainingBytes = CODEX_DIFF_BASELINE_BYTE_LIMIT - loadedBytes;
5955
- const content = loadedFiles < CODEX_DIFF_BASELINE_FILE_LIMIT && remainingBytes > 0 ? textFile(resolve8(this.#root, path), remainingBytes) : undefined;
6092
+ const content = loadedFiles < CODEX_DIFF_BASELINE_FILE_LIMIT && remainingBytes > 0 ? textFile(resolve9(this.#root, path), remainingBytes) : undefined;
5956
6093
  this.#baseline.set(path, content);
5957
6094
  if (typeof content === "string") {
5958
6095
  loadedFiles += 1;
@@ -5965,7 +6102,7 @@ class CodexFilePreviewTracker {
5965
6102
  preview(path, succeeded) {
5966
6103
  if (!this.#root || !this.#baselineAvailable || !succeeded)
5967
6104
  return {};
5968
- const absolute = canonicalPath(isAbsolute2(path) ? path : resolve8(this.#cwd, path));
6105
+ const absolute = canonicalPath(isAbsolute2(path) ? path : resolve9(this.#cwd, path));
5969
6106
  const relativePath = relative(this.#root, absolute);
5970
6107
  if (!relativePath || relativePath.startsWith("..") || isAbsolute2(relativePath))
5971
6108
  return {};
@@ -6000,7 +6137,7 @@ class CodexFilePreviewTracker {
6000
6137
  }
6001
6138
  }
6002
6139
  async function fileChangeEvents(item, previews, cwd, threadId, consumedPatchCallIds) {
6003
- const expectedPaths = item.changes.map((change) => isAbsolute2(change.path) ? change.path : resolve8(cwd, change.path));
6140
+ const expectedPaths = item.changes.map((change) => isAbsolute2(change.path) ? change.path : resolve9(cwd, change.path));
6004
6141
  let nativePreview;
6005
6142
  if (threadId && item.status === "completed") {
6006
6143
  await new Promise((resolveDelay) => setTimeout(resolveDelay, 20));
@@ -6124,7 +6261,7 @@ async function* codexProvider(opts) {
6124
6261
  const filePreviews = new CodexFilePreviewTracker(opts.cwd);
6125
6262
  const consumedPatchCallIds = new Set(opts.sessionId ? readCodexPatchCallIds(opts.sessionId) : []);
6126
6263
  const codexAuthPath = hostedCodexAuthFilePath();
6127
- if (!existsSync13(codexAuthPath)) {
6264
+ if (!existsSync14(codexAuthPath)) {
6128
6265
  yield {
6129
6266
  type: "error",
6130
6267
  content: `Codex auth file not found at ${codexAuthPath}. Run \`codex login\` to authenticate.`
@@ -6163,21 +6300,30 @@ async function* codexProvider(opts) {
6163
6300
  model: opts.model ?? "(sdk default)",
6164
6301
  effort: opts.effort ?? "(off)",
6165
6302
  cwd: opts.cwd,
6166
- cwdExists: existsSync13(opts.cwd),
6303
+ cwdExists: existsSync14(opts.cwd),
6167
6304
  resume: Boolean(opts.sessionId),
6168
6305
  mcpServerCount: Object.keys(codexMcpServers).length
6169
6306
  }, "codexProvider: starting turn");
6170
6307
  const hostedCodexHome = hostedCodexHomePath();
6171
- const inheritedCodexHome = process.env.CODEX_HOME || join13(homedir7(), ".codex");
6172
- const codexEnvironment = scopedBrowserCapability || resolve8(hostedCodexHome) !== resolve8(inheritedCodexHome) ? {
6308
+ const inheritedCodexHome = process.env.CODEX_HOME || join14(homedir7(), ".codex");
6309
+ const codexEnvironment = scopedBrowserCapability || resolve9(hostedCodexHome) !== resolve9(inheritedCodexHome) ? {
6173
6310
  ...Object.fromEntries(Object.entries(process.env).filter((entry) => typeof entry[1] === "string")),
6174
6311
  CODEX_HOME: hostedCodexHome,
6175
6312
  ...scopedBrowserCapability ? { [CODEX_BROWSER_CAPABILITY_ENV]: scopedBrowserCapability } : {}
6176
6313
  } : undefined;
6314
+ let vaultHook;
6315
+ try {
6316
+ vaultHook = await createCodexVaultHookBridge(opts.vaultUserId ?? opts.userId ?? "");
6317
+ } catch (err) {
6318
+ yield { type: "error", content: `Failed to initialize Codex Vault hooks: ${errMsg(err)}` };
6319
+ return;
6320
+ }
6177
6321
  const codex = new Codex({
6322
+ codexPathOverride: vaultHook.codexPathOverride,
6178
6323
  ...codexEnvironment ? { env: codexEnvironment } : {},
6179
6324
  config: {
6180
- features: { multi_agent: false, multi_agent_v2: false, enable_fanout: false },
6325
+ features: { hooks: true, multi_agent: false, multi_agent_v2: false, enable_fanout: false },
6326
+ hooks: vaultHook.hooks,
6181
6327
  model_catalog_json: codexModelCatalogPath,
6182
6328
  mcp_servers: codexMcpServers,
6183
6329
  ...opts.toolPolicy ? { sandbox_permissions: [] } : {}
@@ -6281,7 +6427,7 @@ async function* codexProvider(opts) {
6281
6427
  if (!item)
6282
6428
  break;
6283
6429
  if (item.type === "command_execution") {
6284
- const command = String(item.command ?? "");
6430
+ const command = redactHostedSecrets(opts.vaultUserId ?? opts.userId ?? "", String(item.command ?? ""));
6285
6431
  yield {
6286
6432
  type: "tool_use",
6287
6433
  name: "Bash",
@@ -6289,10 +6435,11 @@ async function* codexProvider(opts) {
6289
6435
  toolUseId: String(item.id ?? "")
6290
6436
  };
6291
6437
  } else if (item.type === "mcp_tool_call") {
6438
+ const input = item.arguments && typeof item.arguments === "object" ? item.arguments : {};
6292
6439
  yield {
6293
6440
  type: "tool_use",
6294
6441
  name: String(item.tool ?? "unknown"),
6295
- input: item.arguments && typeof item.arguments === "object" ? item.arguments : {},
6442
+ input: deepMapStrings(input, (value) => redactHostedSecrets(opts.vaultUserId ?? opts.userId ?? "", value)),
6296
6443
  toolUseId: String(item.id ?? "")
6297
6444
  };
6298
6445
  }
@@ -6335,7 +6482,7 @@ async function* codexProvider(opts) {
6335
6482
  yield {
6336
6483
  type: "tool_result",
6337
6484
  toolUseId: String(item.id ?? ""),
6338
- content: summarizeMcpToolCallResult(item),
6485
+ content: redactHostedSecrets(opts.vaultUserId ?? opts.userId ?? "", summarizeMcpToolCallResult(item)),
6339
6486
  ...isError ? { isError: true } : {}
6340
6487
  };
6341
6488
  } else if (item.type === "command_execution") {
@@ -6343,7 +6490,7 @@ async function* codexProvider(opts) {
6343
6490
  yield {
6344
6491
  type: "tool_result",
6345
6492
  toolUseId: String(item.id ?? ""),
6346
- content: String(item.aggregated_output ?? "").slice(0, 200),
6493
+ content: redactHostedSecrets(opts.vaultUserId ?? opts.userId ?? "", String(item.aggregated_output ?? "").slice(0, 200)),
6347
6494
  ...isError ? { isError: true } : {}
6348
6495
  };
6349
6496
  } else if (item.type === "file_change") {
@@ -6440,12 +6587,14 @@ async function* codexProvider(opts) {
6440
6587
  abortSignal?.removeEventListener("abort", onAbortKill);
6441
6588
  if (trackedPids.pids.length > 0)
6442
6589
  unregisterOwnedCodexPids(trackedPids.pids);
6590
+ await vaultHook.close();
6443
6591
  }
6444
6592
  }
6445
6593
  var CODEX_MCP_SERVER_NAME_OVERRIDES, CODEX_DIFF_FILE_LIMIT, CODEX_DIFF_BASELINE_FILE_LIMIT = 200, CODEX_DIFF_BASELINE_BYTE_LIMIT, CODEX_STARTUP_TIMEOUT_MS = 90000;
6446
6594
  var init_codex_provider = __esm(async () => {
6447
6595
  init_codex_native_multi_agent();
6448
6596
  init_codex_tree_kill();
6597
+ await init_codex_vault_hook_bridge();
6449
6598
  await init_execution_host();
6450
6599
  await init_codex();
6451
6600
  init_tool_format();
@@ -6468,7 +6617,7 @@ __export(exports_maestro_provider, {
6468
6617
  buildMaestroToolHooks: () => buildMaestroToolHooks,
6469
6618
  buildMaestroDisallowedTools: () => buildMaestroDisallowedTools
6470
6619
  });
6471
- import { resolve as resolve9 } from "path";
6620
+ import { resolve as resolve10 } from "path";
6472
6621
  import { maestroProvider as sdkMaestroProvider, setMcpResolver } from "maestro-agent-sdk";
6473
6622
  function buildMaestroDisallowedTools(callerDisallowedTools = [], toolPolicy) {
6474
6623
  return [
@@ -6602,13 +6751,13 @@ var init_maestro_provider = __esm(async () => {
6602
6751
  "ToolSearch",
6603
6752
  ...DEFAULT_MAESTRO_DISALLOWED_TOOLS
6604
6753
  ];
6605
- MAESTRO_TOOL_OUTPUT_DIR = resolve9(RUN_DIR, "maestro-tool-outputs");
6754
+ MAESTRO_TOOL_OUTPUT_DIR = resolve10(RUN_DIR, "maestro-tool-outputs");
6606
6755
  });
6607
6756
 
6608
6757
  // ../../packages/core/src/agents/index.ts
6609
- import { existsSync as existsSync14 } from "fs";
6758
+ import { existsSync as existsSync15 } from "fs";
6610
6759
  import { homedir as homedir8 } from "os";
6611
- import { join as join14 } from "path";
6760
+ import { join as join15 } from "path";
6612
6761
  async function* dispatchAgent(opts) {
6613
6762
  switch (opts.agent) {
6614
6763
  case "claude": {
@@ -6642,11 +6791,11 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
6642
6791
  switch (agent) {
6643
6792
  case "claude": {
6644
6793
  const encodedCwd = encodeClaudeCwd(cwd);
6645
- const path = join14(homedir8(), ".claude", "projects", encodedCwd, `${sessionId}.jsonl`);
6646
- return !existsSync14(path);
6794
+ const path = join15(homedir8(), ".claude", "projects", encodedCwd, `${sessionId}.jsonl`);
6795
+ return !existsSync15(path);
6647
6796
  }
6648
6797
  case "codex": {
6649
- const sessionsDir = join14(hostedCodexHomePath(), "sessions");
6798
+ const sessionsDir = join15(hostedCodexHomePath(), "sessions");
6650
6799
  const glob = new Bun.Glob(`**/rollout-*-${sessionId}.jsonl`);
6651
6800
  for await (const _rel of glob.scan({ cwd: sessionsDir, onlyFiles: true })) {
6652
6801
  return false;
@@ -6655,7 +6804,7 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
6655
6804
  }
6656
6805
  case "maestro": {
6657
6806
  const { hasActiveMaestroSession, maestroSessionPath: maestroSessionPath2 } = await import("maestro-agent-sdk");
6658
- return !existsSync14(maestroSessionPath2(sessionId)) && !hasActiveMaestroSession(sessionId);
6807
+ return !existsSync15(maestroSessionPath2(sessionId)) && !hasActiveMaestroSession(sessionId);
6659
6808
  }
6660
6809
  default: {
6661
6810
  const _exhaustive = agent;
@@ -8874,7 +9023,7 @@ async function waitForRuntimeProcessLease(role, options = {}) {
8874
9023
  const remaining = deadline - Date.now();
8875
9024
  if (remaining <= 0)
8876
9025
  return null;
8877
- await new Promise((resolve10) => setTimeout(resolve10, Math.min(interval, remaining)));
9026
+ await new Promise((resolve11) => setTimeout(resolve11, Math.min(interval, remaining)));
8878
9027
  }
8879
9028
  }
8880
9029
  async function waitForRequiredRuntimeProcessLease(role, options = {}) {
@@ -9151,8 +9300,8 @@ function createAskUserRuntime(host) {
9151
9300
  return errorResult(`Error: failed to persist ask_user_question: ${error instanceof Error ? error.message : String(error)}`);
9152
9301
  }
9153
9302
  let resolveAnswer;
9154
- const promise = new Promise((resolve10) => {
9155
- resolveAnswer = resolve10;
9303
+ const promise = new Promise((resolve11) => {
9304
+ resolveAnswer = resolve11;
9156
9305
  });
9157
9306
  pendingAsks.set(message.id, {
9158
9307
  topicId: ctx.topicId,
@@ -9808,11 +9957,11 @@ var init_manager_utils = () => {};
9808
9957
  // ../../packages/core/src/platform/playwright/browser-processes.ts
9809
9958
  import { execFileSync as execFileSync6 } from "child_process";
9810
9959
  import { readdirSync as readdirSync2, unlinkSync as unlinkSync10 } from "fs";
9811
- import { createServer } from "net";
9812
- import { resolve as resolve10, sep } from "path";
9960
+ import { createServer as createServer2 } from "net";
9961
+ import { resolve as resolve11, sep } from "path";
9813
9962
  function isPortInUse(port) {
9814
9963
  return new Promise((resolveProbe) => {
9815
- const server = createServer();
9964
+ const server = createServer2();
9816
9965
  let settled = false;
9817
9966
  const finish = (occupied) => {
9818
9967
  if (settled)
@@ -9842,8 +9991,8 @@ async function reserveAvailableLoopbackPort(minPort, maxPort, reservedPorts, pro
9842
9991
  return null;
9843
9992
  }
9844
9993
  function killBrowserProcsForUserDataDir(userDataDir) {
9845
- const target = resolve10(userDataDir);
9846
- const profileRoot = resolve10(BROWSER_PROFILES_DIR);
9994
+ const target = resolve11(userDataDir);
9995
+ const profileRoot = resolve11(BROWSER_PROFILES_DIR);
9847
9996
  if (target !== profileRoot && !target.startsWith(`${profileRoot}${sep}`))
9848
9997
  return;
9849
9998
  let pids;
@@ -9864,7 +10013,7 @@ function killBrowserProcsForUserDataDir(userDataDir) {
9864
10013
  stdio: "pipe"
9865
10014
  }).toString().trim();
9866
10015
  const argDir = extractUserDataDirArg(cmdline);
9867
- if (!argDir || resolve10(argDir) !== target)
10016
+ if (!argDir || resolve11(argDir) !== target)
9868
10017
  continue;
9869
10018
  killProcessTreeChildren(pidNum);
9870
10019
  process.kill(pidNum, "SIGKILL");
@@ -9875,13 +10024,13 @@ function killBrowserProcsForUserDataDir(userDataDir) {
9875
10024
  }
9876
10025
  }
9877
10026
  function selectOrphanBrowserPids(procs, liveUserDataDirs, profileRoot, selfPid) {
9878
- const root = resolve10(profileRoot);
9879
- const live = new Set([...liveUserDataDirs].map((d) => resolve10(d)));
10027
+ const root = resolve11(profileRoot);
10028
+ const live = new Set([...liveUserDataDirs].map((d) => resolve11(d)));
9880
10029
  const out = [];
9881
10030
  for (const { pid, userDataDir } of procs) {
9882
10031
  if (pid === selfPid || !userDataDir)
9883
10032
  continue;
9884
- const dir = resolve10(userDataDir);
10033
+ const dir = resolve11(userDataDir);
9885
10034
  if (dir !== root && !dir.startsWith(`${root}${sep}`))
9886
10035
  continue;
9887
10036
  if (live.has(dir))
@@ -9897,7 +10046,7 @@ function reapOrphanBrowsers(liveUserDataDirs) {
9897
10046
  const daemonLease = getRuntimeProcessLease("node-daemon", Date.now(), Number.POSITIVE_INFINITY);
9898
10047
  if (!isBrowserJanitorOwner(daemonLease?.pid ?? null, process.pid))
9899
10048
  return;
9900
- const profileRoot = resolve10(BROWSER_PROFILES_DIR);
10049
+ const profileRoot = resolve11(BROWSER_PROFILES_DIR);
9901
10050
  let pids;
9902
10051
  try {
9903
10052
  pids = execFileSync6("pgrep", ["-f", "--", profileRoot], { stdio: "pipe" }).toString().trim();
@@ -9936,7 +10085,7 @@ function cleanSingletonFiles(userDataDir) {
9936
10085
  for (const f of files) {
9937
10086
  if (f.startsWith("Singleton")) {
9938
10087
  try {
9939
- unlinkSync10(resolve10(userDataDir, f));
10088
+ unlinkSync10(resolve11(userDataDir, f));
9940
10089
  logger.info({ file: f, userDataDir }, "Removed stale Singleton file");
9941
10090
  } catch (e) {
9942
10091
  logger.warn({ err: e, file: f }, "Failed to remove stale Chrome Singleton file");
@@ -9976,9 +10125,9 @@ var init_browser_processes = __esm(async () => {
9976
10125
 
9977
10126
  // ../../packages/core/src/platform/playwright/headed-launch.ts
9978
10127
  import { accessSync as accessSync2, constants as constants2 } from "fs";
9979
- import { delimiter, isAbsolute as isAbsolute3, resolve as resolve11 } from "path";
10128
+ import { delimiter, isAbsolute as isAbsolute3, resolve as resolve12 } from "path";
9980
10129
  function findExecutableOnPath(command, environment = process.env) {
9981
- const candidates = isAbsolute3(command) ? [command] : (environment.PATH ?? "").split(delimiter).filter(Boolean).map((directory) => resolve11(directory, command));
10130
+ const candidates = isAbsolute3(command) ? [command] : (environment.PATH ?? "").split(delimiter).filter(Boolean).map((directory) => resolve12(directory, command));
9982
10131
  for (const candidate of candidates) {
9983
10132
  try {
9984
10133
  accessSync2(candidate, constants2.X_OK);
@@ -10099,11 +10248,11 @@ var init_vault_public = __esm(async () => {
10099
10248
  });
10100
10249
 
10101
10250
  // ../../packages/core/src/platform/playwright/vault-broker.ts
10102
- import { randomBytes as randomBytes5, timingSafeEqual as timingSafeEqual2 } from "crypto";
10251
+ import { randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual2 } from "crypto";
10103
10252
  import { chmodSync as chmodSync4 } from "fs";
10104
- import { createServer as createServer2 } from "net";
10105
- import { tmpdir as tmpdir2 } from "os";
10106
- import { join as join15 } from "path";
10253
+ import { createServer as createServer3 } from "net";
10254
+ import { tmpdir as tmpdir3 } from "os";
10255
+ import { join as join16 } from "path";
10107
10256
  function deepMapStrings2(value, transform) {
10108
10257
  if (typeof value === "string")
10109
10258
  return transform(value);
@@ -10189,8 +10338,8 @@ function authorized(actual, expected) {
10189
10338
  return left.length === right.length && timingSafeEqual2(left, right);
10190
10339
  }
10191
10340
  async function createBrowserVaultBroker(userId) {
10192
- const token = randomBytes5(32).toString("hex");
10193
- const socketPath = join15(process.platform === "win32" ? tmpdir2() : "/tmp", `negotium-browser-vault-${process.pid}-${randomBytes5(8).toString("hex")}.sock`);
10341
+ const token = randomBytes6(32).toString("hex");
10342
+ const socketPath = join16(process.platform === "win32" ? tmpdir3() : "/tmp", `negotium-browser-vault-${process.pid}-${randomBytes6(8).toString("hex")}.sock`);
10194
10343
  const retainedForms = new Map;
10195
10344
  const leases = new Map;
10196
10345
  const sockets = new Set;
@@ -10229,7 +10378,7 @@ async function createBrowserVaultBroker(userId) {
10229
10378
  return result.text;
10230
10379
  });
10231
10380
  const prepared = prepareInput(request.tool, substituted);
10232
- const lease = randomBytes5(24).toString("hex");
10381
+ const lease = randomBytes6(24).toString("hex");
10233
10382
  leases.set(lease, { boundary: prepared.boundary, createdAt: Date.now() });
10234
10383
  return {
10235
10384
  id,
@@ -10261,7 +10410,7 @@ async function createBrowserVaultBroker(userId) {
10261
10410
  }
10262
10411
  return { id, ok: false, error: "unknown operation" };
10263
10412
  };
10264
- const server = createServer2((socket) => {
10413
+ const server = createServer3((socket) => {
10265
10414
  sockets.add(socket);
10266
10415
  let pending = "";
10267
10416
  socket.setEncoding("utf8");
@@ -10446,24 +10595,24 @@ var init_browser_profiles = __esm(async () => {
10446
10595
 
10447
10596
  // ../../packages/core/src/platform/playwright/manager.ts
10448
10597
  import { execFileSync as execFileSync7, spawn as spawn5 } from "child_process";
10449
- import { createHash as createHash4, randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual3 } from "crypto";
10598
+ import { createHash as createHash4, randomBytes as randomBytes7, timingSafeEqual as timingSafeEqual3 } from "crypto";
10450
10599
  import {
10451
10600
  cpSync,
10452
- existsSync as existsSync15,
10601
+ existsSync as existsSync16,
10453
10602
  mkdirSync as mkdirSync10,
10454
10603
  readFileSync as readFileSync12,
10455
10604
  rmSync as rmSync2,
10456
10605
  unlinkSync as unlinkSync11,
10457
10606
  writeFileSync as writeFileSync8
10458
10607
  } from "fs";
10459
- import { dirname as dirname10, join as join16, resolve as resolve12 } from "path";
10608
+ import { dirname as dirname11, join as join17, resolve as resolve13 } from "path";
10460
10609
  function removeDefaultProfileDataDir(userDataDir) {
10461
- const root = resolve12(BROWSER_PROFILES_DIR);
10462
- const target = resolve12(userDataDir);
10463
- if (dirname10(target) !== root) {
10610
+ const root = resolve13(BROWSER_PROFILES_DIR);
10611
+ const target = resolve13(userDataDir);
10612
+ if (dirname11(target) !== root) {
10464
10613
  throw new Error(`Refusing to delete browser profile outside managed root: ${target}`);
10465
10614
  }
10466
- const existed = existsSync15(target);
10615
+ const existed = existsSync16(target);
10467
10616
  rmSync2(target, { recursive: true, force: true });
10468
10617
  return existed;
10469
10618
  }
@@ -10546,14 +10695,14 @@ function portFileName(instanceKey) {
10546
10695
  function writePortFile(instanceKey, port) {
10547
10696
  try {
10548
10697
  mkdirSync10(managerHost.portsDir, { recursive: true });
10549
- writeFileSync8(join16(managerHost.portsDir, portFileName(instanceKey)), String(port));
10698
+ writeFileSync8(join17(managerHost.portsDir, portFileName(instanceKey)), String(port));
10550
10699
  } catch (e) {
10551
10700
  logger.warn({ err: e, instanceKey, port }, "Failed to save playwright port file");
10552
10701
  }
10553
10702
  }
10554
10703
  function deletePortFile(instanceKey) {
10555
10704
  try {
10556
- unlinkSync11(join16(managerHost.portsDir, portFileName(instanceKey)));
10705
+ unlinkSync11(join17(managerHost.portsDir, portFileName(instanceKey)));
10557
10706
  } catch (e) {
10558
10707
  if (e.code === "ENOENT")
10559
10708
  return;
@@ -10700,7 +10849,7 @@ function releasePort(port) {
10700
10849
  usedPorts.delete(port);
10701
10850
  }
10702
10851
  function defaultProfileDir(_ownerId, profile) {
10703
- return resolve12(BROWSER_PROFILES_DIR, profile);
10852
+ return resolve13(BROWSER_PROFILES_DIR, profile);
10704
10853
  }
10705
10854
  function resolveUserDataDir(instanceKey) {
10706
10855
  return managerHost.resolveInstanceDataDir(instanceKey);
@@ -10792,8 +10941,8 @@ async function spawnPlaywright(instanceKey, ownerId, reservedPort, browserBin =
10792
10941
  releasePort(port);
10793
10942
  throw new Error("Browser.rs managed mode does not support authenticated egress proxies");
10794
10943
  }
10795
- const capability = randomBytes6(32).toString("hex");
10796
- const spawnNonce = randomBytes6(32).toString("hex");
10944
+ const capability = randomBytes7(32).toString("hex");
10945
+ const spawnNonce = randomBytes7(32).toString("hex");
10797
10946
  let vaultBroker;
10798
10947
  try {
10799
10948
  vaultBroker = await createBrowserVaultBroker(ownerId);
@@ -11061,14 +11210,14 @@ async function cloneProfileForChild(opts) {
11061
11210
  killInstance(dstKey);
11062
11211
  await delay(500);
11063
11212
  }
11064
- if (!existsSync15(srcDir)) {
11213
+ if (!existsSync16(srcDir)) {
11065
11214
  return { copied: false, srcDir, dstDir, reason: "src-missing" };
11066
11215
  }
11067
11216
  try {
11068
- if (existsSync15(dstDir)) {
11217
+ if (existsSync16(dstDir)) {
11069
11218
  rmSync2(dstDir, { recursive: true, force: true });
11070
11219
  }
11071
- mkdirSync10(dirname10(dstDir), { recursive: true });
11220
+ mkdirSync10(dirname11(dstDir), { recursive: true });
11072
11221
  if (process.platform === "darwin") {
11073
11222
  try {
11074
11223
  execFileSync7("cp", ["-cR", srcDir, dstDir], { stdio: "pipe" });
@@ -11086,7 +11235,7 @@ async function cloneProfileForChild(opts) {
11086
11235
  cleanSingletonFiles(dstDir);
11087
11236
  for (const f of ["DevToolsActivePort", "LOCK"]) {
11088
11237
  try {
11089
- unlinkSync11(resolve12(dstDir, f));
11238
+ unlinkSync11(resolve13(dstDir, f));
11090
11239
  } catch {}
11091
11240
  }
11092
11241
  logger.info({ srcKey, dstKey, srcDir, dstDir }, "Cloned Playwright profile for child topic");
@@ -11589,9 +11738,9 @@ var init_personal_general = __esm(async () => {
11589
11738
 
11590
11739
  // ../../packages/core/src/prompts/builders.ts
11591
11740
  import { readFileSync as readFileSync13 } from "fs";
11592
- import { resolve as resolve13 } from "path";
11741
+ import { resolve as resolve14 } from "path";
11593
11742
  function loadPrompt(filename, dir = SESSIONS_DIR) {
11594
- const raw = readFileSync13(resolve13(dir, filename), "utf-8");
11743
+ const raw = readFileSync13(resolve14(dir, filename), "utf-8");
11595
11744
  return raw.replace(/\{\{RESOURCES_DIR\}\}/g, RESOURCES_DIR);
11596
11745
  }
11597
11746
  function replaceVars(template, vars) {
@@ -11640,7 +11789,7 @@ function visualDesignGuide() {
11640
11789
  return _visualDesignGuide;
11641
11790
  }
11642
11791
  function loadAgentPrompt(filename) {
11643
- const raw = readFileSync13(resolve13(AGENTS_PROMPTS_DIR, filename), "utf-8");
11792
+ const raw = readFileSync13(resolve14(AGENTS_PROMPTS_DIR, filename), "utf-8");
11644
11793
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
11645
11794
  if (!match)
11646
11795
  throw new Error(`Agent prompt ${filename} is missing frontmatter`);
@@ -11955,8 +12104,8 @@ var init_builders = __esm(() => {
11955
12104
  init_model_catalog();
11956
12105
  init_config();
11957
12106
  init_logger();
11958
- PROMPTS_DIR = resolve13(PROJECT_ROOT, "src/prompts");
11959
- SESSIONS_DIR = resolve13(PROMPTS_DIR, "sessions");
12107
+ PROMPTS_DIR = resolve14(PROJECT_ROOT, "src/prompts");
12108
+ SESSIONS_DIR = resolve14(PROMPTS_DIR, "sessions");
11960
12109
  defaultPromptBuilders = createPromptBuilders();
11961
12110
  buildTopicSystemPrompt = defaultPromptBuilders.buildTopicSystemPrompt;
11962
12111
  buildChannelSystemPrompt = defaultPromptBuilders.buildChannelSystemPrompt;
@@ -12098,50 +12247,50 @@ __export(exports_wiki, {
12098
12247
  getTopicMemoryFilePaths: () => getTopicMemoryFilePaths,
12099
12248
  getSharedWikiDir: () => getSharedWikiDir
12100
12249
  });
12101
- import { existsSync as existsSync16, readdirSync as readdirSync3, statSync as statSync6 } from "fs";
12102
- import { basename as basename2, dirname as dirname11, join as join17 } from "path";
12250
+ import { existsSync as existsSync17, readdirSync as readdirSync3, statSync as statSync6 } from "fs";
12251
+ import { basename as basename2, dirname as dirname12, join as join18 } from "path";
12103
12252
  function getWikiDir(_userId, workspaceDir = resolveStorageWorkspaceDir()) {
12104
- return join17(workspaceDir, "wiki");
12253
+ return join18(workspaceDir, "wiki");
12105
12254
  }
12106
12255
  function getSharedWikiDir(workspaceDir = resolveStorageWorkspaceDir()) {
12107
- return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join17(workspaceDir, "wiki");
12256
+ return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join18(workspaceDir, "wiki");
12108
12257
  }
12109
12258
  function findLatestSummaryFile(wikiDir, safeTopic) {
12110
- const summariesDir = join17(wikiDir, "summaries");
12111
- if (!existsSync16(summariesDir))
12259
+ const summariesDir = join18(wikiDir, "summaries");
12260
+ if (!existsSync17(summariesDir))
12112
12261
  return null;
12113
12262
  const files = readdirSync3(summariesDir).filter((f) => f.endsWith(".md") && f.match(new RegExp(`^\\d{4}-\\d{2}-\\d{2}-${safeTopic}(\\.md|~\\d+\\.md)$`)) && !f.endsWith("-sent-files.md")).sort((left, right) => {
12114
- const mtimeDelta = statSync6(join17(summariesDir, right)).mtimeMs - statSync6(join17(summariesDir, left)).mtimeMs;
12263
+ const mtimeDelta = statSync6(join18(summariesDir, right)).mtimeMs - statSync6(join18(summariesDir, left)).mtimeMs;
12115
12264
  return mtimeDelta || right.localeCompare(left, undefined, { numeric: true });
12116
12265
  });
12117
- return files.length > 0 ? join17(summariesDir, files[0]) : null;
12266
+ return files.length > 0 ? join18(summariesDir, files[0]) : null;
12118
12267
  }
12119
12268
  function getTopicMemoryFilePaths(_userId, topicName, forkOrigin, workspaceDir = resolveStorageWorkspaceDir()) {
12120
12269
  const wikiDir = getSharedWikiDir(workspaceDir);
12121
12270
  const resolveBrief = (name) => {
12122
12271
  const safe = sanitizeTopicName(name, true);
12123
- const brief = join17(wikiDir, "topic", `${safe}.md`);
12272
+ const brief = join18(wikiDir, "topic", `${safe}.md`);
12124
12273
  const latestSummary = findLatestSummaryFile(wikiDir, safe);
12125
12274
  return { brief, latestSummary };
12126
12275
  };
12127
- const archiveDir = join17(wikiDir, "archive");
12276
+ const archiveDir = join18(wikiDir, "archive");
12128
12277
  const archiveExistsFor = (name) => {
12129
12278
  const safe = sanitizeTopicName(name);
12130
- return existsSync16(archiveDir) && readdirSync3(archiveDir).some((f) => f.endsWith(".jsonl") && f.startsWith(`${safe}_`));
12279
+ return existsSync17(archiveDir) && readdirSync3(archiveDir).some((f) => f.endsWith(".jsonl") && f.startsWith(`${safe}_`));
12131
12280
  };
12132
12281
  const sourceTopic = forkOrigin ?? topicName;
12133
12282
  const hasArchive = archiveExistsFor(sourceTopic) || (forkOrigin ? archiveExistsFor(topicName) : false);
12134
12283
  const target = resolveBrief(sourceTopic);
12135
- if (existsSync16(target.brief)) {
12284
+ if (existsSync17(target.brief)) {
12136
12285
  return {
12137
- memoryDir: dirname11(target.brief),
12286
+ memoryDir: dirname12(target.brief),
12138
12287
  memoryFiles: [basename2(target.brief)],
12139
12288
  ...target.latestSummary ? { latestSummaryFile: target.latestSummary } : {},
12140
12289
  ...hasArchive ? { hasArchive: true } : {}
12141
12290
  };
12142
12291
  }
12143
12292
  return {
12144
- memoryDir: join17(wikiDir, "topic"),
12293
+ memoryDir: join18(wikiDir, "topic"),
12145
12294
  memoryFiles: [],
12146
12295
  ...target.latestSummary ? { latestSummaryFile: target.latestSummary } : {},
12147
12296
  ...hasArchive ? { hasArchive: true } : {}
@@ -12153,8 +12302,8 @@ var init_wiki = __esm(async () => {
12153
12302
 
12154
12303
  // ../../packages/core/src/agents/archiver.ts
12155
12304
  import { randomUUID as randomUUID10 } from "crypto";
12156
- import { existsSync as existsSync17, readdirSync as readdirSync4, readFileSync as readFileSync14, statSync as statSync7 } from "fs";
12157
- import { join as join18 } from "path";
12305
+ import { existsSync as existsSync18, readdirSync as readdirSync4, readFileSync as readFileSync14, statSync as statSync7 } from "fs";
12306
+ import { join as join19 } from "path";
12158
12307
  function resolveMemoryLanguage() {
12159
12308
  const override = process.env.NEGOTIUM_MEMORY_LANG?.trim();
12160
12309
  return override && override.length > 0 ? override : resolveOutputLanguage();
@@ -12405,10 +12554,10 @@ function distillOneLine(summaryMd) {
12405
12554
  return "";
12406
12555
  }
12407
12556
  function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
12408
- const dir = join18(storage.getWikiDir(), "summaries");
12557
+ const dir = join19(storage.getWikiDir(), "summaries");
12409
12558
  if (!storage.fileExists(dir))
12410
12559
  return null;
12411
- const predicted = join18(dir, wikiSummaryFilename(date, topicTitle, topicId));
12560
+ const predicted = join19(dir, wikiSummaryFilename(date, topicTitle, topicId));
12412
12561
  if (storage.fileExists(predicted) && storage.fileModifiedAt(predicted) >= sinceMs)
12413
12562
  return predicted;
12414
12563
  let best = null;
@@ -12416,7 +12565,7 @@ function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
12416
12565
  if (!f.startsWith(`${date}-`) || !isTopicSummaryFile(f, topicId ?? "", topicTitle)) {
12417
12566
  continue;
12418
12567
  }
12419
- const p = join18(dir, f);
12568
+ const p = join19(dir, f);
12420
12569
  try {
12421
12570
  const m = storage.fileModifiedAt(p);
12422
12571
  if (m >= sinceMs && (!best || m > best.mtime))
@@ -12500,7 +12649,7 @@ var init_archiver = __esm(async () => {
12500
12649
  defaultArchiverRuntime = createArchiverRuntime({
12501
12650
  storage: {
12502
12651
  getWikiDir: getSharedWikiDir,
12503
- fileExists: existsSync17,
12652
+ fileExists: existsSync18,
12504
12653
  listDirectory: readdirSync4,
12505
12654
  readTextFile: (path) => readFileSync14(path, "utf-8"),
12506
12655
  fileSize: (path) => statSync7(path).size,
@@ -12632,13 +12781,13 @@ __export(exports_topic_archive, {
12632
12781
  archiveConversationEvents: () => archiveConversationEvents
12633
12782
  });
12634
12783
  import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync9 } from "fs";
12635
- import { join as join19 } from "path";
12784
+ import { join as join20 } from "path";
12636
12785
  function archiveTopicMessages(topicId, topicTitle, options = {}) {
12637
12786
  const rows = options.afterRowid !== undefined ? getMessagesForTopicAfterRowid(topicId, options.afterRowid) : getAllMessagesForTopic(topicId);
12638
12787
  if (rows.length === 0)
12639
12788
  return null;
12640
12789
  const safeTopic = sanitizeTopicName(topicTitle, true);
12641
- const archiveDir = join19(getSharedWikiDir(), "archive");
12790
+ const archiveDir = join20(getSharedWikiDir(), "archive");
12642
12791
  mkdirSync11(archiveDir, { recursive: true });
12643
12792
  const date = new Date().toISOString().slice(0, 10);
12644
12793
  const reasonSuffix = options.reason && options.reason !== "delete" ? `_${options.reason}` : "";
@@ -12651,7 +12800,7 @@ function archiveTopicMessages(topicId, topicTitle, options = {}) {
12651
12800
  let path;
12652
12801
  while (true) {
12653
12802
  filename = `${safeTopic}_${date}${reasonSuffix}${counter === 1 ? "" : `_${counter}`}.jsonl`;
12654
- path = join19(archiveDir, filename);
12803
+ path = join20(archiveDir, filename);
12655
12804
  try {
12656
12805
  writeFileSync9(path, body, { flag: "wx" });
12657
12806
  break;
@@ -12696,7 +12845,7 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
12696
12845
  const entries = readRawConversation(userId, topicTitle);
12697
12846
  if (entries.length === 0)
12698
12847
  return null;
12699
- const archiveDir = join19(getSharedWikiDir(), "archive");
12848
+ const archiveDir = join20(getSharedWikiDir(), "archive");
12700
12849
  mkdirSync11(archiveDir, { recursive: true });
12701
12850
  const safeTopic = sanitizeTopicName(topicTitle, true);
12702
12851
  const date = new Date().toISOString().slice(0, 10);
@@ -12724,7 +12873,7 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
12724
12873
  let counter = 1;
12725
12874
  while (true) {
12726
12875
  const suffix = counter === 1 ? "" : `_${counter}`;
12727
- const path = join19(archiveDir, `${safeTopic}_${date}${reasonSuffix}_events${suffix}.jsonl`);
12876
+ const path = join20(archiveDir, `${safeTopic}_${date}${reasonSuffix}_events${suffix}.jsonl`);
12728
12877
  try {
12729
12878
  writeFileSync9(path, body, { flag: "wx" });
12730
12879
  logger.info({ topicId, topicTitle, archive: path, eventCount: entries.length }, "archiveConversationEvents: archived raw conversation events");
@@ -13051,7 +13200,7 @@ var init_idle_archiver = __esm(async () => {
13051
13200
 
13052
13201
  // ../../packages/core/src/agents/topic-cleanup.ts
13053
13202
  import { mkdirSync as mkdirSync12, renameSync as renameSync6, unlinkSync as unlinkSync12, writeFileSync as writeFileSync10 } from "fs";
13054
- import { dirname as dirname12 } from "path";
13203
+ import { dirname as dirname13 } from "path";
13055
13204
  function collectSessionIdsByAgent(entries, extraSessions = []) {
13056
13205
  const out = new Map;
13057
13206
  for (const e of entries) {
@@ -13117,7 +13266,7 @@ function createTopicLogMaintenance(host) {
13117
13266
  const path = runtimeHost.activeConversationPath(opts.userId, opts.topicName);
13118
13267
  const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
13119
13268
  try {
13120
- mkdirSync12(dirname12(path), { recursive: true });
13269
+ mkdirSync12(dirname13(path), { recursive: true });
13121
13270
  writeFileSync10(tempPath, retained.length > 0 ? `${retained.map((entry) => JSON.stringify(entry)).join(`
13122
13271
  `)}
13123
13272
  ` : "", { flag: "wx" });
@@ -13734,15 +13883,15 @@ var init_runtime_turn_requests = __esm(async () => {
13734
13883
  // ../../packages/core/src/topics/session.ts
13735
13884
  import { randomUUID as randomUUID11 } from "crypto";
13736
13885
  import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "fs";
13737
- import { tmpdir as tmpdir3 } from "os";
13738
- import { join as join20 } from "path";
13886
+ import { tmpdir as tmpdir4 } from "os";
13887
+ import { join as join21 } from "path";
13739
13888
  async function waitForMemoryArchive(settled, timeoutMs) {
13740
13889
  let timer;
13741
13890
  try {
13742
13891
  return await Promise.race([
13743
13892
  settled.then(() => true),
13744
- new Promise((resolve14) => {
13745
- timer = setTimeout(() => resolve14(false), timeoutMs);
13893
+ new Promise((resolve15) => {
13894
+ timer = setTimeout(() => resolve15(false), timeoutMs);
13746
13895
  timer.unref?.();
13747
13896
  })
13748
13897
  ]);
@@ -13806,8 +13955,8 @@ async function restartTopicSession(topicId, userId, reason = "topic-session-rest
13806
13955
  };
13807
13956
  }
13808
13957
  let settleMemoryArchive;
13809
- const memoryArchiveSettled = new Promise((resolve14) => {
13810
- settleMemoryArchive = resolve14;
13958
+ const memoryArchiveSettled = new Promise((resolve15) => {
13959
+ settleMemoryArchive = resolve15;
13811
13960
  });
13812
13961
  const archiveStatus = (options.archiveMemory ?? archiveActiveTopicForMemory)(topicId, userId, {
13813
13962
  reason: "reset",
@@ -13988,7 +14137,7 @@ function formatCompactElapsed(startedAt) {
13988
14137
  async function summarizeTopicContext(request) {
13989
14138
  const startedAt = Date.now();
13990
14139
  const sessionIds = [];
13991
- const compactCwd = mkdtempSync2(join20(tmpdir3(), "negotium-compact-"));
14140
+ const compactCwd = mkdtempSync2(join21(tmpdir4(), "negotium-compact-"));
13992
14141
  const abortController = new AbortController;
13993
14142
  const relayAbort = () => abortController.abort(request.signal?.reason);
13994
14143
  if (request.signal?.aborted)
@@ -14015,7 +14164,7 @@ async function summarizeTopicContext(request) {
14015
14164
  let error = "";
14016
14165
  let toolViolation = false;
14017
14166
  let compactionLogCalls = 0;
14018
- const compactionLogPath = join20(compactCwd, "conversation.log");
14167
+ const compactionLogPath = join21(compactCwd, "conversation.log");
14019
14168
  try {
14020
14169
  const compactionMcp = useCompactionLog ? {
14021
14170
  compact_log: {
@@ -15336,14 +15485,14 @@ var init_self_config = __esm(async () => {
15336
15485
  });
15337
15486
 
15338
15487
  // ../../packages/core/src/query/session-inbox-path.ts
15339
- import { join as join21 } from "path";
15488
+ import { join as join22 } from "path";
15340
15489
  function sessionInboxPath(userId, topicId) {
15341
15490
  const key = Buffer.from(topicId, "utf8").toString("base64url");
15342
- return join21(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${JSONL_SUFFIX}`);
15491
+ return join22(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${JSONL_SUFFIX}`);
15343
15492
  }
15344
15493
  function scheduledSessionInboxPath(userId, topicId) {
15345
15494
  const key = Buffer.from(topicId, "utf8").toString("base64url");
15346
- return join21(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${SCHEDULE_SUFFIX}`);
15495
+ return join22(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${SCHEDULE_SUFFIX}`);
15347
15496
  }
15348
15497
  function decodeTopicIdFileName(fileName, suffix) {
15349
15498
  if (!fileName.startsWith(TOPIC_ID_FILE_PREFIX) || !fileName.endsWith(suffix))
@@ -15371,13 +15520,13 @@ var init_session_inbox_path = __esm(() => {
15371
15520
 
15372
15521
  // ../../packages/core/src/query/session-inbox-cleanup.ts
15373
15522
  import { unlinkSync as unlinkSync14 } from "fs";
15374
- import { basename as basename3, join as join22 } from "path";
15523
+ import { basename as basename3, join as join23 } from "path";
15375
15524
  function cleanupSessionInboxFiles(userId, topicId, legacyTopicTitle) {
15376
15525
  const live = sessionInboxPath(userId, topicId);
15377
15526
  const scheduled = scheduledSessionInboxPath(userId, topicId);
15378
15527
  const candidates = new Set([live, `${live}.processing`, scheduled, `${scheduled}.processing`]);
15379
15528
  if (legacyTopicTitle && legacyTopicTitle !== "." && legacyTopicTitle !== ".." && basename3(legacyTopicTitle) === legacyTopicTitle) {
15380
- const legacyBase = join22(SESSION_INBOX_DIR, userId, legacyTopicTitle);
15529
+ const legacyBase = join23(SESSION_INBOX_DIR, userId, legacyTopicTitle);
15381
15530
  for (const suffix of [".jsonl", ".jsonl.processing", ".schedule", ".schedule.processing"]) {
15382
15531
  candidates.add(`${legacyBase}${suffix}`);
15383
15532
  }
@@ -15403,16 +15552,16 @@ var init_session_inbox_cleanup = __esm(() => {
15403
15552
 
15404
15553
  // ../../packages/core/src/query/state.ts
15405
15554
  import { mkdirSync as mkdirSync14, renameSync as renameSync7, unlinkSync as unlinkSync15, writeFileSync as writeFileSync12 } from "fs";
15406
- import { basename as basename4, join as join23 } from "path";
15555
+ import { basename as basename4, join as join24 } from "path";
15407
15556
  function createQueryStateStore(options) {
15408
15557
  const sanitize = options.sanitizeTopicId ?? sanitizeId;
15409
- const queryStateDirPath = (userId) => join23(options.usersLogDir, String(userId), "active-queries");
15410
- const queryStateFile = (userId, topicId) => join23(queryStateDirPath(userId), `${sanitize(topicId)}.json`);
15558
+ const queryStateDirPath = (userId) => join24(options.usersLogDir, String(userId), "active-queries");
15559
+ const queryStateFile = (userId, topicId) => join24(queryStateDirPath(userId), `${sanitize(topicId)}.json`);
15411
15560
  const legacyQueryStateFile = (userId, topicName) => {
15412
15561
  if (!topicName || topicName === "." || topicName === ".." || basename4(topicName) !== topicName) {
15413
15562
  return null;
15414
15563
  }
15415
- return join23(queryStateDirPath(userId), `${topicName}.json`);
15564
+ return join24(queryStateDirPath(userId), `${topicName}.json`);
15416
15565
  };
15417
15566
  return {
15418
15567
  write(userId, topicId, topicName, task) {
@@ -15573,30 +15722,30 @@ import {
15573
15722
  unlinkSync as unlinkSync16,
15574
15723
  writeFileSync as writeFileSync13
15575
15724
  } from "fs";
15576
- import { dirname as dirname13, join as join24 } from "path";
15725
+ import { dirname as dirname14, join as join25 } from "path";
15577
15726
  function pendingAskDir(userId) {
15578
15727
  const rawUserId = String(userId);
15579
15728
  const safeUserId = /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${createHash6("sha256").update(rawUserId).digest("hex")}`;
15580
- return join24(resolveStorageSessionAsksDir(), safeUserId);
15729
+ return join25(resolveStorageSessionAsksDir(), safeUserId);
15581
15730
  }
15582
15731
  function encodeAskKey(key) {
15583
15732
  return JSON.stringify([key.from, key.to]);
15584
15733
  }
15585
15734
  function pendingAskPath(key) {
15586
15735
  const digest = createHash6("sha256").update(encodeAskKey(key)).digest("hex");
15587
- return join24(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
15736
+ return join25(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
15588
15737
  }
15589
15738
  function v2PendingAskPath(key) {
15590
15739
  const encoded = Buffer.from(encodeAskKey(key), "utf8").toString("base64url");
15591
- return join24(pendingAskDir(key.userId), `${V2_ASK_FILENAME_PREFIX}${encoded}.pending`);
15740
+ return join25(pendingAskDir(key.userId), `${V2_ASK_FILENAME_PREFIX}${encoded}.pending`);
15592
15741
  }
15593
15742
  function legacyPendingAskPath(key) {
15594
15743
  if (key.from.includes("/") || key.from.includes("\\") || key.to.includes("/") || key.to.includes("\\") || key.from.includes("\x00") || key.to.includes("\x00")) {
15595
15744
  return null;
15596
15745
  }
15597
15746
  const dir = pendingAskDir(key.userId);
15598
- const candidate = join24(dir, `${key.from}___${key.to}.pending`);
15599
- return dirname13(candidate) === dir ? candidate : null;
15747
+ const candidate = join25(dir, `${key.from}___${key.to}.pending`);
15748
+ return dirname14(candidate) === dir ? candidate : null;
15600
15749
  }
15601
15750
  function parsePendingAskFilename(fileName) {
15602
15751
  if (!fileName.endsWith(".pending"))
@@ -15836,7 +15985,7 @@ function listPendingAsksForCaller(args) {
15836
15985
  const parsed = isV3 ? { from: args.from, to: "" } : parsePendingAskFilename(fileName);
15837
15986
  if (!parsed)
15838
15987
  continue;
15839
- const path = join24(dir, fileName);
15988
+ const path = join25(dir, fileName);
15840
15989
  const record = readPendingAskFile(path, {
15841
15990
  userId: args.userId,
15842
15991
  from: parsed.from,
@@ -15878,7 +16027,7 @@ function deletePendingAsksForTopic(args) {
15878
16027
  }
15879
16028
  let deleted = 0;
15880
16029
  for (const fileName of files) {
15881
- const path = join24(dir, fileName);
16030
+ const path = join25(dir, fileName);
15882
16031
  const parsed = parsePendingAskFilename(fileName);
15883
16032
  const record = readPendingAskFile(path, {
15884
16033
  userId: args.userId,
@@ -16296,7 +16445,7 @@ __export(exports_token_stats, {
16296
16445
  });
16297
16446
  import { createHash as createHash7 } from "crypto";
16298
16447
  import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "fs";
16299
- import { join as join25 } from "path";
16448
+ import { join as join26 } from "path";
16300
16449
  function emptyBucket() {
16301
16450
  return {
16302
16451
  inputTokens: 0,
@@ -16315,7 +16464,7 @@ function queriesPath(userId) {
16315
16464
  const fileId = tokenStatsFileId(userId);
16316
16465
  const logDir = resolveStorageLogDir();
16317
16466
  mkdirSync16(logDir, { recursive: true });
16318
- return join25(logDir, `token-queries-${fileId}.jsonl`);
16467
+ return join26(logDir, `token-queries-${fileId}.jsonl`);
16319
16468
  }
16320
16469
  function loadRecords(userId) {
16321
16470
  try {
@@ -16744,7 +16893,7 @@ var init_lifecycle = __esm(async () => {
16744
16893
  // ../../packages/core/src/runtime/attachments.ts
16745
16894
  import { randomUUID as randomUUID14 } from "crypto";
16746
16895
  import { copyFileSync as copyFileSync2, mkdirSync as mkdirSync17, writeFileSync as writeFileSync15 } from "fs";
16747
- import { basename as basename5, join as join26 } from "path";
16896
+ import { basename as basename5, join as join27 } from "path";
16748
16897
  function workspaceCwdFor(topicId) {
16749
16898
  return resolveTopicWorkspaceDir(topicId);
16750
16899
  }
@@ -16757,7 +16906,7 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
16757
16906
  if (!attachmentIds?.length)
16758
16907
  return [];
16759
16908
  const out = [];
16760
- const destDir = join26(workspaceCwdFor(topicId), "attachments", queryId);
16909
+ const destDir = join27(workspaceCwdFor(topicId), "attachments", queryId);
16761
16910
  for (const rawId of attachmentIds) {
16762
16911
  if (typeof rawId !== "string")
16763
16912
  continue;
@@ -16774,7 +16923,7 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
16774
16923
  mkdirSync17(destDir, { recursive: true });
16775
16924
  const index = String(out.length + 1).padStart(2, "0");
16776
16925
  const safeName = safeAttachmentFilename(attachment.filename, fileId);
16777
- const destPath = join26(destDir, `${index}-${fileId.slice(0, 8)}-${safeName}`);
16926
+ const destPath = join27(destDir, `${index}-${fileId.slice(0, 8)}-${safeName}`);
16778
16927
  copyFileSync2(sourcePath, destPath);
16779
16928
  out.push({
16780
16929
  id: attachment.id,
@@ -16804,10 +16953,10 @@ function promptWithAttachments(prompt, attachments) {
16804
16953
  return composeAttachmentPrompt(prompt, attachments.map(({ filename, path }) => attachmentPromptLine(filename, path)));
16805
16954
  }
16806
16955
  function ingestAttachment(args) {
16807
- const destDir = join26(UPLOADS_DIR, args.topicId);
16956
+ const destDir = join27(UPLOADS_DIR, args.topicId);
16808
16957
  mkdirSync17(destDir, { recursive: true });
16809
16958
  const safeName = safeAttachmentFilename(args.filename, "upload");
16810
- const destPath = join26(destDir, `${Date.now()}-${randomUUID14().slice(0, 8)}-${safeName}`);
16959
+ const destPath = join27(destDir, `${Date.now()}-${randomUUID14().slice(0, 8)}-${safeName}`);
16811
16960
  if (args.sourcePath !== undefined) {
16812
16961
  copyFileSync2(args.sourcePath, destPath);
16813
16962
  } else if (args.bytes !== undefined) {
@@ -16982,12 +17131,12 @@ var init_errors = __esm(() => {
16982
17131
 
16983
17132
  // ../../packages/core/src/runtime/event-heartbeat.ts
16984
17133
  function nextOrHeartbeat(pending, intervalMs) {
16985
- return new Promise((resolve14, reject) => {
16986
- const timer = setTimeout(() => resolve14({ kind: "heartbeat" }), intervalMs);
17134
+ return new Promise((resolve15, reject) => {
17135
+ const timer = setTimeout(() => resolve15({ kind: "heartbeat" }), intervalMs);
16987
17136
  timer.unref?.();
16988
17137
  pending.then((result) => {
16989
17138
  clearTimeout(timer);
16990
- resolve14({ kind: "event", result });
17139
+ resolve15({ kind: "event", result });
16991
17140
  }, (error) => {
16992
17141
  clearTimeout(timer);
16993
17142
  reject(error);
@@ -17324,7 +17473,7 @@ var init_visual_html = __esm(() => {
17324
17473
 
17325
17474
  // ../../packages/core/src/runtime/visuals.ts
17326
17475
  import { realpathSync as realpathSync4 } from "fs";
17327
- import { isAbsolute as isAbsolute4, resolve as resolve14 } from "path";
17476
+ import { isAbsolute as isAbsolute4, resolve as resolve15 } from "path";
17328
17477
  function activeVisualHtmlForPrompt(html) {
17329
17478
  if (html.length <= ACTIVE_VISUAL_PROMPT_MAX_CHARS) {
17330
17479
  return { html, omittedChars: 0 };
@@ -17375,8 +17524,8 @@ function topicAllowsVisualFileId(topicId, fileId) {
17375
17524
  return topicHasAttachmentFileId(topicId, fileId) || topicHasVisualFileId(topicId, fileId);
17376
17525
  }
17377
17526
  function isPathInside(baseDir, filePath) {
17378
- const base = resolve14(baseDir);
17379
- const normalized = resolve14(filePath);
17527
+ const base = resolve15(baseDir);
17528
+ const normalized = resolve15(filePath);
17380
17529
  try {
17381
17530
  const realBase = realpathSync4(base);
17382
17531
  const real = realpathSync4(normalized);
@@ -17438,7 +17587,7 @@ function resolveVisualMediaInput(topicId, input) {
17438
17587
  }
17439
17588
  const rawPath = input.file_path.trim();
17440
17589
  const cwd = workspaceCwdFor(topicId);
17441
- const candidate = isAbsolute4(rawPath) ? rawPath : resolve14(cwd, rawPath);
17590
+ const candidate = isAbsolute4(rawPath) ? rawPath : resolve15(cwd, rawPath);
17442
17591
  if (!isPathInside(cwd, candidate)) {
17443
17592
  return { error: "file_path must be inside the topic workspace" };
17444
17593
  }
@@ -17476,7 +17625,7 @@ var init_visuals = __esm(async () => {
17476
17625
  // ../../packages/core/src/runtime/turn-event-stream.ts
17477
17626
  import { randomUUID as randomUUID15 } from "crypto";
17478
17627
  import { realpathSync as realpathSync5, statSync as statSync9 } from "fs";
17479
- import { isAbsolute as isAbsolute5, resolve as resolve15 } from "path";
17628
+ import { isAbsolute as isAbsolute5, resolve as resolve16 } from "path";
17480
17629
  function sessionEventMatchesCurrentExecution(topicId, queryId, agent, model) {
17481
17630
  if (getRoomQuery(topicId)?.queryId !== queryId)
17482
17631
  return false;
@@ -17829,7 +17978,7 @@ ${JSON.stringify(event.input ?? {})}`);
17829
17978
  case "file":
17830
17979
  if (!silent && peerBridge) {
17831
17980
  const cwd = workspaceCwdFor(topicId);
17832
- const path = isAbsolute5(event.path) ? event.path : resolve15(cwd, event.path);
17981
+ const path = isAbsolute5(event.path) ? event.path : resolve16(cwd, event.path);
17833
17982
  if (!isPathInside(cwd, path)) {
17834
17983
  logger.warn({ topicId, path }, "peer output file is outside the topic workspace");
17835
17984
  break;
@@ -18117,15 +18266,15 @@ __export(exports_app_settings, {
18117
18266
  getGlobalAiName: () => getGlobalAiName,
18118
18267
  DEFAULT_AI_NAME: () => DEFAULT_AI_NAME
18119
18268
  });
18120
- import { existsSync as existsSync18, mkdirSync as mkdirSync18, readFileSync as readFileSync16, writeFileSync as writeFileSync16 } from "fs";
18121
- import { dirname as dirname14, join as join27 } from "path";
18269
+ import { existsSync as existsSync19, mkdirSync as mkdirSync18, readFileSync as readFileSync16, writeFileSync as writeFileSync16 } from "fs";
18270
+ import { dirname as dirname15, join as join28 } from "path";
18122
18271
  function settingsFile() {
18123
- return join27(resolveStorageDataDir(), "otium-settings.json");
18272
+ return join28(resolveStorageDataDir(), "otium-settings.json");
18124
18273
  }
18125
18274
  function getGlobalAiName() {
18126
18275
  const path = settingsFile();
18127
18276
  try {
18128
- if (existsSync18(path)) {
18277
+ if (existsSync19(path)) {
18129
18278
  const data = JSON.parse(readFileSync16(path, "utf8"));
18130
18279
  if (typeof data.aiName === "string" && data.aiName.trim()) {
18131
18280
  return data.aiName.trim();
@@ -18138,7 +18287,7 @@ function setGlobalAiName(name) {
18138
18287
  const path = settingsFile();
18139
18288
  const aiName = name.trim() || DEFAULT_AI_NAME;
18140
18289
  try {
18141
- mkdirSync18(dirname14(path), { recursive: true });
18290
+ mkdirSync18(dirname15(path), { recursive: true });
18142
18291
  writeFileSync16(path, JSON.stringify({ aiName }, null, 2));
18143
18292
  } catch {}
18144
18293
  return aiName;
@@ -18223,8 +18372,8 @@ __export(exports_turn_runner, {
18223
18372
  AGENT_DISPLAY_NAME: () => AGENT_DISPLAY_NAME
18224
18373
  });
18225
18374
  import { randomUUID as randomUUID16 } from "crypto";
18226
- import { existsSync as existsSync19, mkdirSync as mkdirSync19, readdirSync as readdirSync6, statSync as statSync10 } from "fs";
18227
- import { join as join28 } from "path";
18375
+ import { existsSync as existsSync20, mkdirSync as mkdirSync19, readdirSync as readdirSync6, statSync as statSync10 } from "fs";
18376
+ import { join as join29 } from "path";
18228
18377
  function withDefaultPlaywright(configuredMcp, isManager) {
18229
18378
  if (isManager)
18230
18379
  return configuredMcp;
@@ -18279,8 +18428,8 @@ function appendAskReplyMessage(topicId, text2, agentType) {
18279
18428
  return message;
18280
18429
  }
18281
18430
  function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, matchesTitle, preferExact = true) {
18282
- const preferred = join28(directory, preferredFilename);
18283
- if (preferExact && existsSync19(preferred))
18431
+ const preferred = join29(directory, preferredFilename);
18432
+ if (preferExact && existsSync20(preferred))
18284
18433
  return preferred;
18285
18434
  let newestLegacyId = null;
18286
18435
  let newestTitle = null;
@@ -18290,7 +18439,7 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
18290
18439
  const legacyIdMatch = !titleMatch && matchesLegacyId(filename);
18291
18440
  if (!titleMatch && !legacyIdMatch)
18292
18441
  continue;
18293
- const path = join28(directory, filename);
18442
+ const path = join29(directory, filename);
18294
18443
  const mtimeMs = statSync10(path).mtimeMs;
18295
18444
  if (titleMatch) {
18296
18445
  if (!newestTitle || mtimeMs > newestTitle.mtimeMs) {
@@ -18304,10 +18453,10 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
18304
18453
  return newestTitle?.path ?? newestLegacyId?.path ?? preferred;
18305
18454
  }
18306
18455
  function resolveWikiMemoryMirror(wikiDir, topicId, topicTitle) {
18307
- const briefFile = resolveWikiMirrorPath(join28(wikiDir, "topic"), `${wikiBriefStorageKey(topicTitle, topicId)}.md`, (filename) => isTopicBriefFile(filename, topicId), (filename) => isTopicBriefFile(filename, topicId, topicTitle));
18308
- const hasBriefFile = existsSync19(briefFile) && statSync10(briefFile).isFile();
18309
- const latestSummaryCandidate = resolveWikiMirrorPath(join28(wikiDir, "summaries"), `__missing__-${wikiSummaryFilename("0000-00-00", topicTitle, topicId)}`, (filename) => isTopicSummaryFile(filename, topicId), (filename) => isTopicSummaryFile(filename, topicId, topicTitle), false);
18310
- const latestSummaryFile = existsSync19(latestSummaryCandidate) && statSync10(latestSummaryCandidate).isFile() ? latestSummaryCandidate : null;
18456
+ const briefFile = resolveWikiMirrorPath(join29(wikiDir, "topic"), `${wikiBriefStorageKey(topicTitle, topicId)}.md`, (filename) => isTopicBriefFile(filename, topicId), (filename) => isTopicBriefFile(filename, topicId, topicTitle));
18457
+ const hasBriefFile = existsSync20(briefFile) && statSync10(briefFile).isFile();
18458
+ const latestSummaryCandidate = resolveWikiMirrorPath(join29(wikiDir, "summaries"), `__missing__-${wikiSummaryFilename("0000-00-00", topicTitle, topicId)}`, (filename) => isTopicSummaryFile(filename, topicId), (filename) => isTopicSummaryFile(filename, topicId, topicTitle), false);
18459
+ const latestSummaryFile = existsSync20(latestSummaryCandidate) && statSync10(latestSummaryCandidate).isFile() ? latestSummaryCandidate : null;
18311
18460
  return { briefFile, hasBriefFile, latestSummaryFile };
18312
18461
  }
18313
18462
  async function streamAgentEvents(topicId, topicTitle, queryId, events, control, agentType, model, effort, userId, retryableSessionExpired = true, onSessionId, execution) {
@@ -21054,9 +21203,9 @@ var init_vault_command = __esm(async () => {
21054
21203
 
21055
21204
  // ../../packages/core/src/media/text-extractor.ts
21056
21205
  import { execFile } from "child_process";
21057
- import { existsSync as existsSync20, mkdirSync as mkdirSync21, readFileSync as readFileSync17, rmSync as rmSync7 } from "fs";
21206
+ import { existsSync as existsSync21, mkdirSync as mkdirSync21, readFileSync as readFileSync17, rmSync as rmSync7 } from "fs";
21058
21207
  import { readFile } from "fs/promises";
21059
- import { extname, join as join29 } from "path";
21208
+ import { extname, join as join30 } from "path";
21060
21209
  import { promisify } from "util";
21061
21210
  async function extractText(filePath) {
21062
21211
  const ext = extname(filePath).toLowerCase();
@@ -21161,7 +21310,7 @@ async function extractFromImage(filePath) {
21161
21310
  function isTranscriptionConfigured(opts = {}) {
21162
21311
  const ffmpeg = opts.ffmpegBin ?? FFMPEG_BIN;
21163
21312
  const wrapper = opts.wrapperPath ?? FASTER_WHISPER_WRAPPER;
21164
- return Boolean(ffmpeg) && existsSync20(wrapper);
21313
+ return Boolean(ffmpeg) && existsSync21(wrapper);
21165
21314
  }
21166
21315
  async function transcribeAudio(filePath, opts = {}) {
21167
21316
  if (!isTranscriptionConfigured(opts))
@@ -21173,7 +21322,7 @@ async function extractFromAudio(filePath, opts = {}) {
21173
21322
  const tmpDir = `${filePath}_whisper_tmp`;
21174
21323
  try {
21175
21324
  mkdirSync21(tmpDir, { recursive: true });
21176
- const mp3Path = join29(tmpDir, "audio.mp3");
21325
+ const mp3Path = join30(tmpDir, "audio.mp3");
21177
21326
  await execFileAsync(opts.ffmpegBin ?? FFMPEG_BIN2, [
21178
21327
  "-y",
21179
21328
  "-i",
@@ -21200,8 +21349,8 @@ async function extractFromAudio(filePath, opts = {}) {
21200
21349
  "--output-format",
21201
21350
  "txt"
21202
21351
  ], { timeout: 120000 });
21203
- const txtPath = join29(tmpDir, "audio.txt");
21204
- const text2 = existsSync20(txtPath) ? readFileSync17(txtPath, "utf-8").trim() : null;
21352
+ const txtPath = join30(tmpDir, "audio.txt");
21353
+ const text2 = existsSync21(txtPath) ? readFileSync17(txtPath, "utf-8").trim() : null;
21205
21354
  if (!text2) {
21206
21355
  return { text: null, method: "whisper", error: "Whisper produced no text" };
21207
21356
  }
@@ -21372,19 +21521,19 @@ var init_lifecycle2 = __esm(() => {
21372
21521
  });
21373
21522
 
21374
21523
  // ../../packages/core/src/platform/log-rotation.ts
21375
- import { existsSync as existsSync21, renameSync as renameSync8, statSync as statSync11 } from "fs";
21376
- import { join as join30 } from "path";
21524
+ import { existsSync as existsSync22, renameSync as renameSync8, statSync as statSync11 } from "fs";
21525
+ import { join as join31 } from "path";
21377
21526
  function rotateOversizedLog(logPath, maxBytes = DEFAULT_DAEMON_LOG_ROTATE_BYTES) {
21378
21527
  try {
21379
- if (!existsSync21(logPath))
21528
+ if (!existsSync22(logPath))
21380
21529
  return;
21381
21530
  const { size } = statSync11(logPath);
21382
21531
  if (size < maxBytes)
21383
21532
  return;
21384
21533
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
21385
- const dir = join30(logPath, "..");
21534
+ const dir = join31(logPath, "..");
21386
21535
  const base = logPath.slice(dir.length + 1);
21387
- renameSync8(logPath, join30(dir, `${base}.${stamp}`));
21536
+ renameSync8(logPath, join31(dir, `${base}.${stamp}`));
21388
21537
  } catch {}
21389
21538
  }
21390
21539
  var DEFAULT_DAEMON_LOG_ROTATE_BYTES;
@@ -21577,7 +21726,7 @@ import {
21577
21726
  rmSync as rmSync8,
21578
21727
  statSync as statSync12
21579
21728
  } from "fs";
21580
- import { join as join31 } from "path";
21729
+ import { join as join32 } from "path";
21581
21730
  function setBashrsCompletionSink(sink) {
21582
21731
  completionSink = sink ?? defaultSink;
21583
21732
  }
@@ -21604,15 +21753,15 @@ function readTail(filePath) {
21604
21753
  }
21605
21754
  }
21606
21755
  function buildMessage(dir, result) {
21607
- const stdout = readTail(join31(dir, "stdout.log"));
21608
- const stderr = readTail(join31(dir, "stderr.log"));
21756
+ const stdout = readTail(join32(dir, "stdout.log"));
21757
+ const stderr = readTail(join32(dir, "stderr.log"));
21609
21758
  const parts = [];
21610
21759
  if (stdout.text.trim() || stdout.truncated) {
21611
- parts.push(`stdout${stdout.truncated ? ` (truncated, full output: ${join31(dir, "stdout.log")})` : ""}:
21760
+ parts.push(`stdout${stdout.truncated ? ` (truncated, full output: ${join32(dir, "stdout.log")})` : ""}:
21612
21761
  ${stdout.text.trim()}`);
21613
21762
  }
21614
21763
  if (stderr.text.trim() || stderr.truncated) {
21615
- parts.push(`stderr${stderr.truncated ? ` (truncated, full output: ${join31(dir, "stderr.log")})` : ""}:
21764
+ parts.push(`stderr${stderr.truncated ? ` (truncated, full output: ${join32(dir, "stderr.log")})` : ""}:
21616
21765
  ${stderr.text.trim()}`);
21617
21766
  }
21618
21767
  const header = watchHeader(result) ?? `[background_bash ${result.bash_id} finished]`;
@@ -21646,7 +21795,7 @@ function parseOwner(owner) {
21646
21795
  return { userId, topicId };
21647
21796
  }
21648
21797
  function injectedMarker(dir) {
21649
- return join31(dir, "result.json.injected");
21798
+ return join32(dir, "result.json.injected");
21650
21799
  }
21651
21800
  async function flushBashrsCompletions() {
21652
21801
  let entries;
@@ -21657,8 +21806,8 @@ async function flushBashrsCompletions() {
21657
21806
  }
21658
21807
  const now = Date.now();
21659
21808
  for (const bashId of entries) {
21660
- const dir = join31(BASHRS_SPILL_ROOT, bashId);
21661
- const resultPath = join31(dir, "result.json");
21809
+ const dir = join32(BASHRS_SPILL_ROOT, bashId);
21810
+ const resultPath = join32(dir, "result.json");
21662
21811
  const marker = injectedMarker(dir);
21663
21812
  try {
21664
21813
  const markerStat = statSync12(marker);
@@ -21794,8 +21943,8 @@ function prepareDeliveryAck(messageId, claimTimeoutMs, deliveryTimeoutMs) {
21794
21943
  let finishPromise = () => {};
21795
21944
  let timer;
21796
21945
  let unsubscribe = () => {};
21797
- const promise = new Promise((resolve16) => {
21798
- finishPromise = resolve16;
21946
+ const promise = new Promise((resolve17) => {
21947
+ finishPromise = resolve17;
21799
21948
  });
21800
21949
  unsubscribe = runtimeBus().subscribe((event) => {
21801
21950
  if (event.type !== "ai-status")
@@ -21864,7 +22013,7 @@ function renderTurnFooter(msg) {
21864
22013
 
21865
22014
  // ../../packages/core/src/outbox/file-ops.ts
21866
22015
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
21867
- import { appendFileSync as appendFileSync2, existsSync as existsSync22, readFileSync as readFileSync19, renameSync as renameSync10, unlinkSync as unlinkSync17 } from "fs";
22016
+ import { appendFileSync as appendFileSync2, existsSync as existsSync23, readFileSync as readFileSync19, renameSync as renameSync10, unlinkSync as unlinkSync17 } from "fs";
21868
22017
  function host() {
21869
22018
  return hostContext.getStore() ?? defaultHost2;
21870
22019
  }
@@ -21881,8 +22030,8 @@ function tryUnlink(path, label, reason) {
21881
22030
  }
21882
22031
  function drainOutboxFile(filePath, label) {
21883
22032
  const processingPath = filePath + PROCESSING_SUFFIX;
21884
- const hasPending = existsSync22(filePath);
21885
- const hasLeftover = existsSync22(processingPath);
22033
+ const hasPending = existsSync23(filePath);
22034
+ const hasLeftover = existsSync23(processingPath);
21886
22035
  if (hasLeftover) {
21887
22036
  const logMsg = `${label}: merging leftover .processing before claim`;
21888
22037
  const logCtx = { filePath, processingPath, hadPending: hasPending };
@@ -21902,7 +22051,7 @@ function drainOutboxFile(filePath, label) {
21902
22051
  tryUnlink(processingPath, label, "failed to clean up leftover after merge error");
21903
22052
  }
21904
22053
  }
21905
- if (!existsSync22(filePath))
22054
+ if (!existsSync23(filePath))
21906
22055
  return null;
21907
22056
  try {
21908
22057
  renameSync10(filePath, processingPath);
@@ -21956,7 +22105,7 @@ var init_file_ops = __esm(() => {
21956
22105
  // ../../packages/core/src/runtime/inbox.ts
21957
22106
  import { createHash as createHash9, randomUUID as randomUUID20 } from "crypto";
21958
22107
  import { readdirSync as readdirSync8, statSync as statSync13, writeFileSync as writeFileSync17 } from "fs";
21959
- import { join as join32 } from "path";
22108
+ import { join as join33 } from "path";
21960
22109
  async function createAskForkPlan(options) {
21961
22110
  const snapshot = structuredClone(options.entries);
21962
22111
  const prepareSession = async () => options.synthesize(structuredClone(snapshot));
@@ -22096,7 +22245,7 @@ async function flushSessionInbox() {
22096
22245
  return;
22097
22246
  }
22098
22247
  for (const uid of userDirs) {
22099
- const userInboxDir = join32(SESSION_INBOX_DIR, uid);
22248
+ const userInboxDir = join33(SESSION_INBOX_DIR, uid);
22100
22249
  let entries;
22101
22250
  try {
22102
22251
  entries = readdirSync8(userInboxDir);
@@ -22104,7 +22253,7 @@ async function flushSessionInbox() {
22104
22253
  continue;
22105
22254
  }
22106
22255
  for (const entry of entries) {
22107
- const entryPath = join32(userInboxDir, entry);
22256
+ const entryPath = join33(userInboxDir, entry);
22108
22257
  let isDir = false;
22109
22258
  try {
22110
22259
  isDir = statSync13(entryPath).isDirectory();
@@ -22194,7 +22343,7 @@ function sweepScheduledSessionInbox(nowMs = Date.now()) {
22194
22343
  return;
22195
22344
  }
22196
22345
  for (const userId of userDirs) {
22197
- const userInboxDir = join32(SESSION_INBOX_DIR, userId);
22346
+ const userInboxDir = join33(SESSION_INBOX_DIR, userId);
22198
22347
  let files;
22199
22348
  try {
22200
22349
  files = readdirSync8(userInboxDir);
@@ -22206,7 +22355,7 @@ function sweepScheduledSessionInbox(nowMs = Date.now()) {
22206
22355
  const topicId = topicIdFromScheduledSessionInboxFileName(file);
22207
22356
  if (!topicId)
22208
22357
  continue;
22209
- const schedulePath = join32(userInboxDir, file);
22358
+ const schedulePath = join33(userInboxDir, file);
22210
22359
  const hasProcessingClaim = files.includes(`${file}.processing`);
22211
22360
  if (!hasProcessingClaim && !scheduledFileNeedsClaim(schedulePath, nowMs))
22212
22361
  continue;
@@ -23042,8 +23191,8 @@ var init_src = __esm(async () => {
23042
23191
  });
23043
23192
 
23044
23193
  // ../../packages/core/src/storage/conversation-migration.ts
23045
- import { copyFileSync as copyFileSync3, existsSync as existsSync23, mkdirSync as mkdirSync23, readFileSync as readFileSync20 } from "fs";
23046
- import { dirname as dirname15, join as join33 } from "path";
23194
+ import { copyFileSync as copyFileSync3, existsSync as existsSync24, mkdirSync as mkdirSync23, readFileSync as readFileSync20 } from "fs";
23195
+ import { dirname as dirname16, join as join34 } from "path";
23047
23196
  function readEntries(path) {
23048
23197
  const entries = [];
23049
23198
  for (const [index, line] of readFileSync20(path, "utf8").split(`
@@ -23106,20 +23255,20 @@ function migrateLegacyCompactedConversations() {
23106
23255
  const userId = participant.userId;
23107
23256
  const rawPath = getConversationPath(userId, topic.title);
23108
23257
  const activePath = getActiveConversationPath(userId, topic.title);
23109
- if (!existsSync23(rawPath) || existsSync23(activePath)) {
23258
+ if (!existsSync24(rawPath) || existsSync24(activePath)) {
23110
23259
  result.skipped++;
23111
23260
  continue;
23112
23261
  }
23113
- const backupPath = join33(resolveStorageDataDir(), "conversation-migration-backups", MIGRATION_NAME, userId, `${sanitizeTopicName(topic.title, true)}.jsonl`);
23114
- const sourcePath = existsSync23(backupPath) ? backupPath : rawPath;
23262
+ const backupPath = join34(resolveStorageDataDir(), "conversation-migration-backups", MIGRATION_NAME, userId, `${sanitizeTopicName(topic.title, true)}.jsonl`);
23263
+ const sourcePath = existsSync24(backupPath) ? backupPath : rawPath;
23115
23264
  const legacyEntries = readEntries(sourcePath);
23116
23265
  const compactIndex = compactionEntryIndex(legacyEntries);
23117
23266
  if (compactIndex < 0) {
23118
23267
  result.skipped++;
23119
23268
  continue;
23120
23269
  }
23121
- if (!existsSync23(backupPath)) {
23122
- mkdirSync23(dirname15(backupPath), { recursive: true });
23270
+ if (!existsSync24(backupPath)) {
23271
+ mkdirSync23(dirname16(backupPath), { recursive: true });
23123
23272
  copyFileSync3(rawPath, backupPath);
23124
23273
  }
23125
23274
  const compactedAt = legacyEntries[compactIndex]?.ts;
@@ -23169,8 +23318,8 @@ __export(exports_decisions, {
23169
23318
  createDecisions: () => createDecisions,
23170
23319
  DECISION_STATUS_VALUES: () => DECISION_STATUS_VALUES
23171
23320
  });
23172
- import { existsSync as existsSync24, mkdirSync as mkdirSync24, readFileSync as readFileSync21, renameSync as renameSync11, writeFileSync as writeFileSync18 } from "fs";
23173
- import { dirname as dirname16, join as join34 } from "path";
23321
+ import { existsSync as existsSync25, mkdirSync as mkdirSync24, readFileSync as readFileSync21, renameSync as renameSync11, writeFileSync as writeFileSync18 } from "fs";
23322
+ import { dirname as dirname17, join as join35 } from "path";
23174
23323
  function safeDecisionScopeKey(scopeKey) {
23175
23324
  const safe = sanitizeFileName(scopeKey);
23176
23325
  if (!safe || safe === "." || safe === "..") {
@@ -23182,14 +23331,14 @@ function decisionScopeKey(opts) {
23182
23331
  return opts.topicId?.trim() || opts.session || "default";
23183
23332
  }
23184
23333
  function getDecisionFilePath(userId, scopeKey) {
23185
- return join34(resolveStorageDataDir(), "decisions", `${safeDecisionScopeKey(scopeKey)}.json`);
23334
+ return join35(resolveStorageDataDir(), "decisions", `${safeDecisionScopeKey(scopeKey)}.json`);
23186
23335
  }
23187
23336
  function getDecisionGraphSvgPath(userId, scopeKey) {
23188
- return join34(resolveStorageDataDir(), "decision-renders", safeDecisionScopeKey(scopeKey), "latest.svg");
23337
+ return join35(resolveStorageDataDir(), "decision-renders", safeDecisionScopeKey(scopeKey), "latest.svg");
23189
23338
  }
23190
23339
  function writeDecisionGraphSvg(userId, scopeKey, svg) {
23191
23340
  const path = getDecisionGraphSvgPath(userId, scopeKey);
23192
- mkdirSync24(dirname16(path), { recursive: true });
23341
+ mkdirSync24(dirname17(path), { recursive: true });
23193
23342
  const tmp = `${path}.${process.pid}.tmp`;
23194
23343
  writeFileSync18(tmp, svg, "utf-8");
23195
23344
  renameSync11(tmp, path);
@@ -23197,7 +23346,7 @@ function writeDecisionGraphSvg(userId, scopeKey, svg) {
23197
23346
  }
23198
23347
  function readDecisions(userId, scopeKey) {
23199
23348
  const path = getDecisionFilePath(userId, scopeKey);
23200
- if (!existsSync24(path))
23349
+ if (!existsSync25(path))
23201
23350
  return [];
23202
23351
  try {
23203
23352
  const parsed = JSON.parse(readFileSync21(path, "utf-8"));
@@ -23209,7 +23358,7 @@ function readDecisions(userId, scopeKey) {
23209
23358
  }
23210
23359
  function writeDecisions(userId, scopeKey, decisions) {
23211
23360
  const path = getDecisionFilePath(userId, scopeKey);
23212
- mkdirSync24(dirname16(path), { recursive: true });
23361
+ mkdirSync24(dirname17(path), { recursive: true });
23213
23362
  const payload = { version: 1, decisions };
23214
23363
  const tmp = `${path}.${process.pid}.tmp`;
23215
23364
  writeFileSync18(tmp, JSON.stringify(payload, null, 2), "utf-8");
@@ -23468,8 +23617,8 @@ var init_node_host = __esm(async () => {
23468
23617
  });
23469
23618
 
23470
23619
  // ../../packages/core/src/agents/mcp-tools/inline-assets.ts
23471
- import { existsSync as existsSync25, readFileSync as readFileSync22, statSync as statSync14 } from "fs";
23472
- import { extname as extname2, isAbsolute as isAbsolute6, resolve as resolve16 } from "path";
23620
+ import { existsSync as existsSync26, readFileSync as readFileSync22, statSync as statSync14 } from "fs";
23621
+ import { extname as extname2, isAbsolute as isAbsolute6, resolve as resolve17 } from "path";
23473
23622
  function isNonLocal(ref) {
23474
23623
  return /^(?:[a-z][a-z0-9+.-]*:|\/\/|#)/i.test(ref.trim());
23475
23624
  }
@@ -23493,10 +23642,10 @@ function inlineLocalAssets(html, options) {
23493
23642
  const relPath = stripSuffix(ref);
23494
23643
  if (!relPath)
23495
23644
  return null;
23496
- const abs = isAbsolute6(relPath) ? relPath : resolve16(baseDir, relPath);
23645
+ const abs = isAbsolute6(relPath) ? relPath : resolve17(baseDir, relPath);
23497
23646
  let bytes;
23498
23647
  try {
23499
- if (!existsSync25(abs) || !statSync14(abs).isFile()) {
23648
+ if (!existsSync26(abs) || !statSync14(abs).isFile()) {
23500
23649
  unresolved.push(ref);
23501
23650
  cache.set(ref, null);
23502
23651
  return null;
@@ -24444,8 +24593,8 @@ __export(exports_agent_health, {
24444
24593
  createAgentHealthMcpServer: () => createAgentHealthMcpServer
24445
24594
  });
24446
24595
  import { spawn as spawn6 } from "child_process";
24447
- import { existsSync as existsSync26, readdirSync as readdirSync9 } from "fs";
24448
- import { join as join35 } from "path";
24596
+ import { existsSync as existsSync27, readdirSync as readdirSync9 } from "fs";
24597
+ import { join as join36 } from "path";
24449
24598
  import { McpServer as McpServer5 } from "@modelcontextprotocol/sdk/server/mcp.js";
24450
24599
  import { z as z10 } from "zod";
24451
24600
  function signalProcessTree2(child, signal) {
@@ -24461,7 +24610,7 @@ function signalProcessTree2(child, signal) {
24461
24610
  }
24462
24611
  }
24463
24612
  function spawnCapture(cmd, args, timeoutMs, signal, env) {
24464
- return new Promise((resolve17, reject) => {
24613
+ return new Promise((resolve18, reject) => {
24465
24614
  const child = spawn6(cmd, args, {
24466
24615
  env: env ?? process.env,
24467
24616
  stdio: ["ignore", "pipe", "pipe"],
@@ -24519,7 +24668,7 @@ function spawnCapture(cmd, args, timeoutMs, signal, env) {
24519
24668
  if (killTimer)
24520
24669
  clearTimeout(killTimer);
24521
24670
  if (code === 0)
24522
- resolve17({ stdout, stderr });
24671
+ resolve18({ stdout, stderr });
24523
24672
  else
24524
24673
  reject(new Error(stderr.trim() || `${cmd} exited with ${code ?? exitSignal}`));
24525
24674
  });
@@ -24597,11 +24746,11 @@ async function checkMaestro(model, timeoutMs, signal) {
24597
24746
  const remaining = deadline - Date.now();
24598
24747
  if (remaining <= 0)
24599
24748
  throw new Error("timeout");
24600
- const eventResult = await new Promise((resolve17, reject) => {
24749
+ const eventResult = await new Promise((resolve18, reject) => {
24601
24750
  const timeout = setTimeout(() => reject(new Error("timeout")), remaining);
24602
24751
  const onAbort = () => reject(new Error("aborted"));
24603
24752
  signal.addEventListener("abort", onAbort, { once: true });
24604
- activeIterator.next().then(resolve17, reject).finally(() => {
24753
+ activeIterator.next().then(resolve18, reject).finally(() => {
24605
24754
  clearTimeout(timeout);
24606
24755
  signal.removeEventListener("abort", onAbort);
24607
24756
  });
@@ -24631,7 +24780,7 @@ function getDefaultModel(registry) {
24631
24780
  }
24632
24781
  async function withCheckSlot(operation) {
24633
24782
  if (activeChecks >= MAX_CONCURRENT_CHECKS) {
24634
- await new Promise((resolve17) => checkWaiters.push(resolve17));
24783
+ await new Promise((resolve18) => checkWaiters.push(resolve18));
24635
24784
  }
24636
24785
  activeChecks += 1;
24637
24786
  try {
@@ -24689,8 +24838,8 @@ ${results.every((result) => result.ok) ? "All agents healthy" : "Some agents fai
24689
24838
  server.tool("list_active_queries", "\uD604\uC7AC \uC0AC\uC6A9\uC790 \uBC94\uC704\uC5D0\uC11C \uC2E4\uD589 \uC911\uC778 \uD1A0\uD53D \uCFFC\uB9AC \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4.", {}, async () => {
24690
24839
  if (!context.userId)
24691
24840
  return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC870\uD68C \uBD88\uAC00 (user context \uC5C6\uC74C)");
24692
- const stateDir = join35(USERS_LOG_DIR, context.userId, "active-queries");
24693
- if (!existsSync26(stateDir))
24841
+ const stateDir = join36(USERS_LOG_DIR, context.userId, "active-queries");
24842
+ if (!existsSync27(stateDir))
24694
24843
  return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC5C6\uC74C");
24695
24844
  let files;
24696
24845
  try {
@@ -24702,7 +24851,7 @@ ${results.every((result) => result.ok) ? "All agents healthy" : "Some agents fai
24702
24851
  const entries = files.flatMap((file) => {
24703
24852
  if (!file.endsWith(".json"))
24704
24853
  return [];
24705
- const state = readJsonFile(join35(stateDir, file));
24854
+ const state = readJsonFile(join36(stateDir, file));
24706
24855
  if (!state)
24707
24856
  return [];
24708
24857
  const sinceMs = new Date(state.since).getTime();
@@ -24935,7 +25084,7 @@ var exports_default_host = {};
24935
25084
  __export(exports_default_host, {
24936
25085
  createDefaultSessionCommMcpHost: () => createDefaultSessionCommMcpHost
24937
25086
  });
24938
- import { basename as basename6, join as join36 } from "path";
25087
+ import { basename as basename6, join as join37 } from "path";
24939
25088
  function ok2(text2) {
24940
25089
  return { content: [{ type: "text", text: text2 }] };
24941
25090
  }
@@ -24982,10 +25131,10 @@ function remoteTarget(context, to) {
24982
25131
  return { node: to.slice(0, slash), topic: to.slice(slash + 1) };
24983
25132
  }
24984
25133
  function activeQuery(context, topicId, title) {
24985
- const dir = join36(USERS_LOG_DIR, context.userId, "active-queries");
24986
- const candidates = [join36(dir, `${sanitizeId(topicId)}.json`)];
25134
+ const dir = join37(USERS_LOG_DIR, context.userId, "active-queries");
25135
+ const candidates = [join37(dir, `${sanitizeId(topicId)}.json`)];
24987
25136
  if (title && basename6(title) === title && title !== "." && title !== "..") {
24988
- candidates.push(join36(dir, `${title}.json`));
25137
+ candidates.push(join37(dir, `${title}.json`));
24989
25138
  }
24990
25139
  for (const path of candidates) {
24991
25140
  const state = readJsonFile(path);
@@ -25276,298 +25425,15 @@ var init_default_host = __esm(async () => {
25276
25425
  init_types();
25277
25426
  });
25278
25427
 
25279
- // ../../packages/core/src/mcp/vault-http.ts
25280
- function substituteObject(userId, values, host2) {
25281
- const used = new Set;
25282
- const substituted = {};
25283
- for (const [key, value] of Object.entries(values)) {
25284
- if (FORBIDDEN_REQUEST_HEADERS.has(key.toLowerCase())) {
25285
- throw new Error(`Header "${key}" is not allowed`);
25286
- }
25287
- const result = host2.substitute(userId, value);
25288
- for (const usedKey of result.usedKeys)
25289
- used.add(usedKey);
25290
- substituted[key] = result.text;
25291
- }
25292
- return { values: substituted, usedKeys: [...used] };
25293
- }
25294
- function safeResponseHeaders(userId, headers, host2) {
25295
- const output = {};
25296
- for (const [key, value] of headers.entries()) {
25297
- if (!SAFE_RESPONSE_HEADERS.has(key.toLowerCase()))
25298
- continue;
25299
- output[key] = host2.redact(userId, value);
25300
- }
25301
- return output;
25302
- }
25303
- async function readBoundedBody(response, maxBytes) {
25304
- if (!response.body)
25305
- return { bytes: new Uint8Array, truncated: false };
25306
- const reader = response.body.getReader();
25307
- const chunks = [];
25308
- let keptBytes = 0;
25309
- let truncated = false;
25310
- try {
25311
- while (true) {
25312
- const next = await reader.read();
25313
- if (next.done)
25314
- break;
25315
- const remaining = maxBytes - keptBytes;
25316
- if (remaining <= 0) {
25317
- truncated = true;
25318
- await reader.cancel();
25319
- break;
25320
- }
25321
- const visible = next.value.subarray(0, remaining);
25322
- chunks.push(visible);
25323
- keptBytes += visible.byteLength;
25324
- if (visible.byteLength < next.value.byteLength) {
25325
- truncated = true;
25326
- await reader.cancel();
25327
- break;
25328
- }
25329
- }
25330
- } finally {
25331
- reader.releaseLock();
25332
- }
25333
- const bytes = new Uint8Array(keptBytes);
25334
- let offset = 0;
25335
- for (const chunk of chunks) {
25336
- bytes.set(chunk, offset);
25337
- offset += chunk.byteLength;
25338
- }
25339
- return { bytes, truncated };
25340
- }
25341
- async function executeVaultHttpRequest(userId, request, host2, fetchImpl = fetch) {
25342
- let parsedUrl;
25343
- try {
25344
- parsedUrl = new URL(request.url);
25345
- } catch {
25346
- return { ok: false, error: "url must be an absolute HTTPS URL" };
25347
- }
25348
- if (parsedUrl.protocol !== "https:") {
25349
- return {
25350
- ok: false,
25351
- error: "Vault credentials may only be sent over HTTPS"
25352
- };
25353
- }
25354
- if (parsedUrl.username || parsedUrl.password || /\{\{[^}]+\}\}/.test(request.url)) {
25355
- return {
25356
- ok: false,
25357
- error: "Keep Vault placeholders out of URLs; put credentials in headers or body"
25358
- };
25359
- }
25360
- let headers;
25361
- let headerKeys;
25362
- try {
25363
- const substituted = substituteObject(userId, request.headers ?? {}, host2);
25364
- headers = substituted.values;
25365
- headerKeys = substituted.usedKeys;
25366
- } catch (error2) {
25367
- return {
25368
- ok: false,
25369
- error: error2 instanceof Error ? error2.message : String(error2)
25370
- };
25371
- }
25372
- const bodyResult = request.body === undefined ? { text: undefined, usedKeys: [] } : host2.substitute(userId, request.body);
25373
- const usedKeys = [...new Set([...headerKeys, ...bodyResult.usedKeys])].sort();
25374
- if (usedKeys.length === 0) {
25375
- return {
25376
- ok: false,
25377
- error: "No valid Vault placeholder was found in headers or body"
25378
- };
25379
- }
25380
- if (request.method === "GET" && request.body !== undefined) {
25381
- return {
25382
- ok: false,
25383
- error: "GET requests cannot include a credential-bearing body"
25384
- };
25385
- }
25386
- const controller = new AbortController;
25387
- const timeoutMs = Math.min(Math.max(request.timeoutMs ?? 30000, 1000), 120000);
25388
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
25389
- const startedAt = Date.now();
25390
- try {
25391
- const response = await fetchImpl(parsedUrl, {
25392
- method: request.method,
25393
- headers,
25394
- body: request.method === "GET" ? undefined : bodyResult.text,
25395
- redirect: "manual",
25396
- signal: controller.signal
25397
- });
25398
- const maxBytes = Math.min(Math.max(request.maxResponseBytes ?? 256 * 1024, 1024), 1024 * 1024);
25399
- const { bytes, truncated } = await readBoundedBody(response, maxBytes);
25400
- const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
25401
- const textual = contentType.startsWith("text/") || contentType.includes("json") || contentType.includes("xml") || contentType.includes("javascript") || contentType === "";
25402
- const body = textual ? host2.redact(userId, new TextDecoder().decode(bytes)) : `[binary response omitted: ${bytes.byteLength} bytes, ${contentType || "unknown content type"}]`;
25403
- host2.log?.("info", {
25404
- userId,
25405
- vaultKeys: usedKeys,
25406
- host: parsedUrl.hostname,
25407
- method: request.method,
25408
- status: response.status,
25409
- durationMs: Date.now() - startedAt
25410
- }, "vault credential used");
25411
- return {
25412
- ok: response.ok,
25413
- status: response.status,
25414
- statusText: response.statusText,
25415
- headers: safeResponseHeaders(userId, response.headers, host2),
25416
- body,
25417
- truncated
25418
- };
25419
- } catch (error2) {
25420
- const raw = error2 instanceof Error ? error2.message : String(error2);
25421
- host2.log?.("warn", {
25422
- userId,
25423
- vaultKeys: usedKeys,
25424
- host: parsedUrl.hostname,
25425
- method: request.method,
25426
- durationMs: Date.now() - startedAt
25427
- }, "vault credential request failed");
25428
- return { ok: false, error: host2.redact(userId, raw) };
25429
- } finally {
25430
- clearTimeout(timeout);
25431
- }
25432
- }
25433
- var SAFE_RESPONSE_HEADERS, FORBIDDEN_REQUEST_HEADERS;
25434
- var init_vault_http = __esm(() => {
25435
- SAFE_RESPONSE_HEADERS = new Set([
25436
- "content-type",
25437
- "content-length",
25438
- "location",
25439
- "retry-after",
25440
- "x-ratelimit-limit",
25441
- "x-ratelimit-remaining",
25442
- "x-ratelimit-reset"
25443
- ]);
25444
- FORBIDDEN_REQUEST_HEADERS = new Set([
25445
- "connection",
25446
- "content-length",
25447
- "host",
25448
- "proxy-authorization",
25449
- "transfer-encoding"
25450
- ]);
25451
- });
25452
-
25453
- // ../../packages/core/src/mcp/vault-run.ts
25454
- import { spawn as spawn7 } from "child_process";
25455
- function appendBounded(chunks, chunk, state, maxBytes) {
25456
- if (state.bytes >= maxBytes) {
25457
- state.truncated = true;
25458
- return;
25459
- }
25460
- const remaining = maxBytes - state.bytes;
25461
- const visible = chunk.subarray(0, remaining);
25462
- chunks.push(visible);
25463
- state.bytes += visible.byteLength;
25464
- if (visible.byteLength < chunk.byteLength)
25465
- state.truncated = true;
25466
- }
25467
- async function executeVaultRun(userId, request, host2) {
25468
- const substitution = host2.substitute(userId, request.command);
25469
- if (substitution.usedKeys.length === 0) {
25470
- return {
25471
- ok: false,
25472
- exitCode: null,
25473
- signal: null,
25474
- stdout: "",
25475
- stderr: "",
25476
- truncated: false,
25477
- usedKeys: [],
25478
- error: "No valid Vault placeholder was found in command"
25479
- };
25480
- }
25481
- const timeoutMs = Math.min(Math.max(request.timeoutMs ?? 120000, 1000), 600000);
25482
- const maxOutputBytes = Math.min(Math.max(request.maxOutputBytes ?? 512 * 1024, 1024), 2 * 1024 * 1024);
25483
- const stdoutChunks = [];
25484
- const stderrChunks = [];
25485
- const stdoutState = { bytes: 0, truncated: false };
25486
- const stderrState = { bytes: 0, truncated: false };
25487
- const startedAt = Date.now();
25488
- return await new Promise((resolve17) => {
25489
- const child = spawn7(process.env.SHELL || "/bin/sh", ["-s"], {
25490
- cwd: request.cwd,
25491
- detached: true,
25492
- env: process.env,
25493
- stdio: ["pipe", "pipe", "pipe"]
25494
- });
25495
- let settled = false;
25496
- let timedOut = false;
25497
- const signalTree = (signal) => {
25498
- if (!child.pid)
25499
- return;
25500
- try {
25501
- process.kill(-child.pid, signal);
25502
- } catch {
25503
- try {
25504
- child.kill(signal);
25505
- } catch {}
25506
- }
25507
- };
25508
- const finish = (result) => {
25509
- if (settled)
25510
- return;
25511
- settled = true;
25512
- clearTimeout(timeout);
25513
- host2.log?.("info", {
25514
- userId,
25515
- vaultKeys: substitution.usedKeys,
25516
- exitCode: result.exitCode,
25517
- signal: result.signal,
25518
- durationMs: Date.now() - startedAt,
25519
- timedOut
25520
- }, "vault credential command used");
25521
- resolve17({ ...result, usedKeys: substitution.usedKeys });
25522
- };
25523
- child.stdout.on("data", (chunk) => appendBounded(stdoutChunks, chunk, stdoutState, maxOutputBytes));
25524
- child.stderr.on("data", (chunk) => appendBounded(stderrChunks, chunk, stderrState, maxOutputBytes));
25525
- child.once("error", (error2) => {
25526
- finish({
25527
- ok: false,
25528
- exitCode: null,
25529
- signal: null,
25530
- stdout: "",
25531
- stderr: "",
25532
- truncated: false,
25533
- error: host2.redact(userId, error2.message)
25534
- });
25535
- });
25536
- child.once("close", (exitCode, signal) => {
25537
- signalTree("SIGTERM");
25538
- const stdout = host2.redact(userId, Buffer.concat(stdoutChunks).toString("utf8"));
25539
- const stderr = host2.redact(userId, Buffer.concat(stderrChunks).toString("utf8"));
25540
- finish({
25541
- ok: !timedOut && exitCode === 0,
25542
- exitCode,
25543
- signal,
25544
- stdout,
25545
- stderr,
25546
- truncated: stdoutState.truncated || stderrState.truncated,
25547
- ...timedOut ? { error: `Command timed out after ${timeoutMs}ms` } : {}
25548
- });
25549
- });
25550
- const timeout = setTimeout(() => {
25551
- timedOut = true;
25552
- signalTree("SIGKILL");
25553
- }, timeoutMs);
25554
- child.stdin.end(substitution.text);
25555
- });
25556
- }
25557
- var init_vault_run = () => {};
25558
-
25559
25428
  // ../../packages/core/src/mcp/factories/vault.ts
25560
25429
  var exports_vault = {};
25561
25430
  __export(exports_vault, {
25562
25431
  createVaultMcpServer: () => createVaultMcpServer
25563
25432
  });
25564
25433
  import { McpServer as McpServer7 } from "@modelcontextprotocol/sdk/server/mcp.js";
25565
- import { z as z12 } from "zod";
25566
- function createVaultMcpServer(context, host2, executors = {}) {
25434
+ function createVaultMcpServer(context, host2) {
25567
25435
  const server = new McpServer7({ name: "vault", version: "1.0.0" });
25568
- const run = executors.run ?? executeVaultRun;
25569
- const http = executors.http ?? executeVaultHttpRequest;
25570
- 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.", {}, () => {
25436
+ server.tool("vault_list", "List the user's Vault keys and descriptions without exposing values. Use {{KEY}} placeholders directly in supported transient tool inputs.", {}, () => {
25571
25437
  if (!context.userId)
25572
25438
  return mcpOk("(vault unavailable: no user context)");
25573
25439
  const entries = host2.list(context.userId);
@@ -25578,54 +25444,14 @@ function createVaultMcpServer(context, host2, executors = {}) {
25578
25444
  ${lines.join(`
25579
25445
  `)}`);
25580
25446
  });
25581
- if (!context.listOnly && !context.httpOnly) {
25582
- 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.", {
25583
- command: z12.string().min(1).max(64 * 1024),
25584
- timeout_ms: z12.number().int().min(1000).max(600000).optional(),
25585
- max_output_bytes: z12.number().int().min(1024).max(2 * 1024 * 1024).optional()
25586
- }, async ({ command, timeout_ms, max_output_bytes }) => {
25587
- if (!context.userId)
25588
- return mcpError("Vault unavailable: no user context");
25589
- const result = await run(context.userId, {
25590
- command,
25591
- timeoutMs: timeout_ms,
25592
- maxOutputBytes: max_output_bytes,
25593
- cwd: context.cwd
25594
- }, host2);
25595
- return result.error && result.exitCode === null ? mcpError(result.error) : mcpOk(JSON.stringify(result, null, 2));
25596
- });
25597
- }
25598
- if (!context.listOnly)
25599
- 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.", {
25600
- method: z12.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("GET"),
25601
- url: z12.string().url().describe("Absolute HTTPS URL without Vault placeholders"),
25602
- headers: z12.record(z12.string(), z12.string()).optional(),
25603
- body: z12.string().optional(),
25604
- timeout_ms: z12.number().int().min(1000).max(120000).optional(),
25605
- max_response_bytes: z12.number().int().min(1024).max(1024 * 1024).optional()
25606
- }, async ({ method, url, headers, body, timeout_ms, max_response_bytes }) => {
25607
- if (!context.userId)
25608
- return mcpError("Vault unavailable: no user context");
25609
- const result = await http(context.userId, {
25610
- method,
25611
- url,
25612
- headers,
25613
- body,
25614
- timeoutMs: timeout_ms,
25615
- maxResponseBytes: max_response_bytes
25616
- }, host2);
25617
- return result.error ? mcpError(result.error) : mcpOk(JSON.stringify(result, null, 2));
25618
- });
25619
25447
  return server;
25620
25448
  }
25621
25449
  var init_vault2 = __esm(() => {
25622
- init_vault_http();
25623
- init_vault_run();
25624
25450
  init_mcp_helpers();
25625
25451
  });
25626
25452
 
25627
25453
  // ../../packages/core/src/mcp/wiki-search-index.ts
25628
- import { chmodSync as chmodSync5, existsSync as existsSync27, readFileSync as readFileSync23, unlinkSync as unlinkSync18 } from "fs";
25454
+ import { chmodSync as chmodSync5, existsSync as existsSync28, readFileSync as readFileSync23, unlinkSync as unlinkSync18 } from "fs";
25629
25455
  function documentId(source) {
25630
25456
  return `${source.kind}:${source.key}`;
25631
25457
  }
@@ -25827,7 +25653,7 @@ class WikiSearchIndex {
25827
25653
  }
25828
25654
  function resetCorruptWikiSearchIndex(path) {
25829
25655
  for (const candidate of [path, `${path}-journal`, `${path}-wal`, `${path}-shm`]) {
25830
- if (!existsSync27(candidate))
25656
+ if (!existsSync28(candidate))
25831
25657
  continue;
25832
25658
  unlinkSync18(candidate);
25833
25659
  }
@@ -25847,7 +25673,7 @@ import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
25847
25673
  import { randomUUID as randomUUID21 } from "crypto";
25848
25674
  import {
25849
25675
  closeSync as closeSync4,
25850
- existsSync as existsSync28,
25676
+ existsSync as existsSync29,
25851
25677
  mkdirSync as mkdirSync25,
25852
25678
  openSync as openSync4,
25853
25679
  readdirSync as readdirSync10,
@@ -25857,7 +25683,7 @@ import {
25857
25683
  unlinkSync as unlinkSync19,
25858
25684
  writeFileSync as writeFileSync19
25859
25685
  } from "fs";
25860
- import { basename as basename7, dirname as dirname17, join as join37, relative as relative2, resolve as resolve17 } from "path";
25686
+ import { basename as basename7, dirname as dirname18, join as join38, relative as relative2, resolve as resolve18 } from "path";
25861
25687
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
25862
25688
  import { StdioServerTransport as StdioServerTransport2 } from "@modelcontextprotocol/sdk/server/stdio.js";
25863
25689
  import {
@@ -26092,7 +25918,7 @@ function trigramSimilarity(left, right) {
26092
25918
  return 2 * overlap / (a.size + b.size);
26093
25919
  }
26094
25920
  function openWikiSearchIndex() {
26095
- const path = resolve17(runtime().wikiDir, WIKI_SEARCH_INDEX_FILENAME);
25921
+ const path = resolve18(runtime().wikiDir, WIKI_SEARCH_INDEX_FILENAME);
26096
25922
  try {
26097
25923
  return new WikiSearchIndex(path);
26098
25924
  } catch (error2) {
@@ -26119,7 +25945,7 @@ function prepareWikiSearchDocument(kind, key, text2) {
26119
25945
  }
26120
25946
  function indexWikiSearchDocument(kind, key, text2) {
26121
25947
  const root = kind === "article" ? runtime().articlesDir : runtime().summariesDir;
26122
- const path = resolve17(root, `${key}.md`);
25948
+ const path = resolve18(root, `${key}.md`);
26123
25949
  const index = openWikiSearchIndex();
26124
25950
  if (!index)
26125
25951
  return false;
@@ -26134,7 +25960,7 @@ function indexWikiSearchDocument(kind, key, text2) {
26134
25960
  }
26135
25961
  }
26136
25962
  function wikiIndexPath(kind) {
26137
- return resolve17(runtime().wikiDir, WIKI_INDEX_FILENAMES[kind]);
25963
+ return resolve18(runtime().wikiDir, WIKI_INDEX_FILENAMES[kind]);
26138
25964
  }
26139
25965
  function normalizeIndexDescription(raw) {
26140
25966
  const description = raw.replaceAll(/\s+/g, " ").trim();
@@ -26190,7 +26016,7 @@ function indexCandidateExists(candidate) {
26190
26016
  if (candidate.kind === "topic")
26191
26017
  return true;
26192
26018
  const root = candidate.kind === "article" ? runtime().articlesDir : runtime().summariesDir;
26193
- const filePath = resolve17(root, `${candidate.key}.md`);
26019
+ const filePath = resolve18(root, `${candidate.key}.md`);
26194
26020
  const relativePath = relative2(root, filePath);
26195
26021
  if (relativePath.startsWith("..") || relativePath.startsWith("/"))
26196
26022
  return false;
@@ -26272,8 +26098,8 @@ function wikiQuery(args) {
26272
26098
  const canReadTopicMemory = runtime().host.canReadTopicMemory;
26273
26099
  const temporalDirection = /\b(?:latest|newest|recent)\b|\uCD5C\uADFC|\uCD5C\uC2E0/.test(normalizedQuery) ? "newest" : /\b(?:earliest|oldest)\b|\uAC00\uC7A5 \uC624\uB798\uB41C|\uCD5C\uCD08/.test(normalizedQuery) ? "oldest" : undefined;
26274
26100
  const visibleIndexCandidates = [
26275
- ...parseWikiIndex(resolve17(runtime().wikiDir, "topic-index.md")),
26276
- ...parseWikiIndex(resolve17(runtime().wikiDir, "article-index.md"))
26101
+ ...parseWikiIndex(resolve18(runtime().wikiDir, "topic-index.md")),
26102
+ ...parseWikiIndex(resolve18(runtime().wikiDir, "article-index.md"))
26277
26103
  ].filter((candidate) => kind === "all" || candidate.kind === kind).filter((candidate) => candidate.kind !== "topic" || !canReadTopicMemory || canReadTopicMemory(candidate.key, runtime().userId));
26278
26104
  const hasStaleExactIndexMatch = visibleIndexCandidates.some((candidate) => !indexCandidateExists(candidate) && scoreIndexKey(question, candidate.key) >= 100);
26279
26105
  const indexCandidates = visibleIndexCandidates.filter(indexCandidateExists).map((candidate) => ({
@@ -26307,13 +26133,13 @@ function wikiQuery(args) {
26307
26133
  try {
26308
26134
  for (const entry of readdirSync10(dir, { withFileTypes: true })) {
26309
26135
  if (entry.isDirectory()) {
26310
- const sub = join37(dir, entry.name);
26136
+ const sub = join38(dir, entry.name);
26311
26137
  if (label === "articles" || label === "skills") {
26312
26138
  try {
26313
26139
  for (const f of readdirSync10(sub, { withFileTypes: true })) {
26314
26140
  if (!f.isFile() || !f.name.endsWith(".md"))
26315
26141
  continue;
26316
- const fp = join37(sub, f.name);
26142
+ const fp = join38(sub, f.name);
26317
26143
  try {
26318
26144
  const text2 = readFileSync24(fp, "utf-8");
26319
26145
  const key = `${entry.name}/${f.name.replace(/\.md$/i, "")}`;
@@ -26334,7 +26160,7 @@ function wikiQuery(args) {
26334
26160
  if (label === "topic" && canReadTopicMemory && !canReadTopicMemory(entry.name.replace(/\.md$/i, ""), runtime().userId)) {
26335
26161
  continue;
26336
26162
  }
26337
- const fp = join37(dir, entry.name);
26163
+ const fp = join38(dir, entry.name);
26338
26164
  try {
26339
26165
  const text2 = readFileSync24(fp, "utf-8");
26340
26166
  const key = entry.name.replace(/\.md$/i, "");
@@ -26618,7 +26444,7 @@ function wikiTopicBrief(args) {
26618
26444
  };
26619
26445
  }
26620
26446
  const topicKey = wikiSummarySlug(rawTopic.replace(/^topic\//, "").replace(/\.md$/i, ""));
26621
- const filePath = resolve17(runtime().topicsDir, `${topicKey}.md`);
26447
+ const filePath = resolve18(runtime().topicsDir, `${topicKey}.md`);
26622
26448
  try {
26623
26449
  const content = readFileSync24(filePath, "utf-8");
26624
26450
  return {
@@ -26666,7 +26492,7 @@ Adopted topic memory: ${memoryKey}` }]
26666
26492
  }
26667
26493
  const root = kind === "summary" ? runtime().summariesDir : runtime().articlesDir;
26668
26494
  const key = rawKey.replace(new RegExp(`^(?:${kind === "summary" ? "summaries" : "articles"})/`), "").replace(/\.md$/i, "");
26669
- const filePath = resolve17(root, `${key}.md`);
26495
+ const filePath = resolve18(root, `${key}.md`);
26670
26496
  const relativePath = relative2(root, filePath);
26671
26497
  if (relativePath.startsWith("..") || relativePath.startsWith("/")) {
26672
26498
  return { content: [{ type: "text", text: "Invalid wiki document key." }], isError: true };
@@ -26690,18 +26516,18 @@ function wikiLastConversation(args) {
26690
26516
  return { content: [{ type: "text", text: "No topic specified." }] };
26691
26517
  }
26692
26518
  const name = slugify(topicNameFrom(rawTopic));
26693
- const archiveDir = resolve17(runtime().archiveDir, name);
26519
+ const archiveDir = resolve18(runtime().archiveDir, name);
26694
26520
  try {
26695
- let files = existsSync28(archiveDir) ? readdirSync10(archiveDir).filter((f) => f.endsWith(".jsonl")).sort().reverse() : [];
26521
+ let files = existsSync29(archiveDir) ? readdirSync10(archiveDir).filter((f) => f.endsWith(".jsonl")).sort().reverse() : [];
26696
26522
  if (files.length === 0) {
26697
- files = existsSync28(runtime().archiveDir) ? readdirSync10(runtime().archiveDir).filter((f) => f.startsWith(name) && f.endsWith(".jsonl")).sort().reverse() : [];
26523
+ files = existsSync29(runtime().archiveDir) ? readdirSync10(runtime().archiveDir).filter((f) => f.startsWith(name) && f.endsWith(".jsonl")).sort().reverse() : [];
26698
26524
  }
26699
26525
  if (files.length === 0) {
26700
26526
  return {
26701
26527
  content: [{ type: "text", text: `No archive found for topic: ${rawTopic}` }]
26702
26528
  };
26703
26529
  }
26704
- const actualPath = files[0].includes("/") ? files[0] : resolve17(existsSync28(archiveDir) && readdirSync10(archiveDir).includes(files[0]) ? archiveDir : runtime().archiveDir, files[0]);
26530
+ const actualPath = files[0].includes("/") ? files[0] : resolve18(existsSync29(archiveDir) && readdirSync10(archiveDir).includes(files[0]) ? archiveDir : runtime().archiveDir, files[0]);
26705
26531
  try {
26706
26532
  const raw = readFileSync24(actualPath, "utf-8");
26707
26533
  const lines = raw.trim().split(`
@@ -26774,7 +26600,7 @@ function skillQuery(args) {
26774
26600
  })) {
26775
26601
  if (!entry.isDirectory())
26776
26602
  continue;
26777
- const skillFile = resolve17(runtime().skillsDir, entry.name, "skill.md");
26603
+ const skillFile = resolve18(runtime().skillsDir, entry.name, "skill.md");
26778
26604
  try {
26779
26605
  const text2 = readFileSync24(skillFile, "utf-8");
26780
26606
  const lower = text2.toLowerCase();
@@ -26842,9 +26668,9 @@ ${fresh.join(`
26842
26668
  `)}
26843
26669
  `;
26844
26670
  }
26845
- const skillDir = resolve17(runtime().skillsDir, name);
26671
+ const skillDir = resolve18(runtime().skillsDir, name);
26846
26672
  ensureDir(skillDir);
26847
- const skillPath = resolve17(skillDir, "skill.md");
26673
+ const skillPath = resolve18(skillDir, "skill.md");
26848
26674
  let finalContent = content;
26849
26675
  try {
26850
26676
  const existing = readFileSync24(skillPath, "utf-8");
@@ -26869,7 +26695,7 @@ function writeSummaryDocument(rawTopic, content, dateStr) {
26869
26695
  let counter = 1;
26870
26696
  while (true) {
26871
26697
  try {
26872
- writeFileSync19(resolve17(runtime().summariesDir, summaryName), content, {
26698
+ writeFileSync19(resolve18(runtime().summariesDir, summaryName), content, {
26873
26699
  encoding: "utf-8",
26874
26700
  flag: "wx"
26875
26701
  });
@@ -26886,8 +26712,8 @@ function writeSummaryDocument(rawTopic, content, dateStr) {
26886
26712
  let sqliteUpdated = false;
26887
26713
  if (topicId && setTopicBrief2) {
26888
26714
  try {
26889
- const briefPath = resolve17(runtime().topicsDir, `${fileSlug}.md`);
26890
- let briefMd = existsSync28(briefPath) ? readFileSync24(briefPath, "utf-8") : undefined;
26715
+ const briefPath = resolve18(runtime().topicsDir, `${fileSlug}.md`);
26716
+ let briefMd = existsSync29(briefPath) ? readFileSync24(briefPath, "utf-8") : undefined;
26891
26717
  if (briefMd === undefined && getTopicBrief2 && !getTopicBrief2(fileSlug)?.briefMd) {
26892
26718
  const legacy = getTopicBrief2(topicId)?.briefMd;
26893
26719
  if (legacy)
@@ -26906,9 +26732,9 @@ function writeSummaryDocument(rawTopic, content, dateStr) {
26906
26732
  function writeTopicDocument(rawTopic, content) {
26907
26733
  const topicId = runtime().topicId;
26908
26734
  const fileSlug = wikiBriefStorageKey(rawTopic, topicId);
26909
- const briefPath = resolve17(runtime().topicsDir, `${fileSlug}.md`);
26910
- ensureDir(dirname17(briefPath));
26911
- const existed = existsSync28(briefPath);
26735
+ const briefPath = resolve18(runtime().topicsDir, `${fileSlug}.md`);
26736
+ ensureDir(dirname18(briefPath));
26737
+ const existed = existsSync29(briefPath);
26912
26738
  writeFileSync19(briefPath, content, "utf-8");
26913
26739
  const setTopicBrief2 = runtime().host.setTopicBrief;
26914
26740
  let sqliteUpdated = false;
@@ -26921,9 +26747,9 @@ function writeTopicDocument(rawTopic, content) {
26921
26747
  return { slug: fileSlug, existed, sqliteUpdated };
26922
26748
  }
26923
26749
  function writeArticleDocument(slug, content) {
26924
- const articlePath = resolve17(runtime().articlesDir, `${slug}.md`);
26925
- ensureDir(dirname17(articlePath));
26926
- const existed = existsSync28(articlePath);
26750
+ const articlePath = resolve18(runtime().articlesDir, `${slug}.md`);
26751
+ ensureDir(dirname18(articlePath));
26752
+ const existed = existsSync29(articlePath);
26927
26753
  atomicWriteFile(articlePath, content);
26928
26754
  return { existed };
26929
26755
  }
@@ -27055,21 +26881,21 @@ function collectIndexableDocuments() {
27055
26881
  }
27056
26882
  for (const entry of entries) {
27057
26883
  if (entry.isFile() && entry.name.endsWith(".md")) {
27058
- add(kind, entry.name.replace(/\.md$/i, ""), join37(root, entry.name));
26884
+ add(kind, entry.name.replace(/\.md$/i, ""), join38(root, entry.name));
27059
26885
  continue;
27060
26886
  }
27061
26887
  if (!entry.isDirectory() || !allowSubdirectories)
27062
26888
  continue;
27063
26889
  let nested;
27064
26890
  try {
27065
- nested = readdirSync10(join37(root, entry.name), { withFileTypes: true });
26891
+ nested = readdirSync10(join38(root, entry.name), { withFileTypes: true });
27066
26892
  } catch {
27067
26893
  continue;
27068
26894
  }
27069
26895
  for (const file of nested) {
27070
26896
  if (!file.isFile() || !file.name.endsWith(".md"))
27071
26897
  continue;
27072
- add(kind, `${entry.name}/${file.name.replace(/\.md$/i, "")}`, join37(root, entry.name, file.name));
26898
+ add(kind, `${entry.name}/${file.name.replace(/\.md$/i, "")}`, join38(root, entry.name, file.name));
27073
26899
  }
27074
26900
  }
27075
26901
  }
@@ -27126,7 +26952,7 @@ function indexRowOrPartialWrite(label, options) {
27126
26952
  function upsertIndexRow(options) {
27127
26953
  const { kind, slug, description, date, section } = options;
27128
26954
  const indexPath = wikiIndexPath(kind);
27129
- ensureDir(dirname17(indexPath));
26955
+ ensureDir(dirname18(indexPath));
27130
26956
  const releaseLock = acquireFileLock(indexPath);
27131
26957
  try {
27132
26958
  let index;
@@ -27183,7 +27009,7 @@ function upsertIndexRow(options) {
27183
27009
  }
27184
27010
  function wikiDocumentExists(kind, slug) {
27185
27011
  const root = kind === "article" ? runtime().articlesDir : kind === "summary" ? runtime().summariesDir : kind === "topic" ? runtime().topicsDir : runtime().skillsDir;
27186
- const filePath = resolve17(root, `${slug}.md`);
27012
+ const filePath = resolve18(root, `${slug}.md`);
27187
27013
  if (relative2(root, filePath).startsWith(".."))
27188
27014
  return false;
27189
27015
  try {
@@ -27235,7 +27061,7 @@ function indexUpsert(args) {
27235
27061
  }
27236
27062
  function createWikiMcpServer(context, host2) {
27237
27063
  const surface = context.surface ?? "all";
27238
- const wikiDir = resolve17(host2.wikiRoot);
27064
+ const wikiDir = resolve18(host2.wikiRoot);
27239
27065
  const current3 = {
27240
27066
  userId: context.userId,
27241
27067
  currentTopicId: context.currentTopicId ?? context.topicId,
@@ -27243,11 +27069,11 @@ function createWikiMcpServer(context, host2) {
27243
27069
  surface,
27244
27070
  host: host2,
27245
27071
  wikiDir,
27246
- skillsDir: resolve17(wikiDir, "skills"),
27247
- topicsDir: resolve17(wikiDir, "topic"),
27248
- summariesDir: resolve17(wikiDir, "summaries"),
27249
- articlesDir: resolve17(wikiDir, "articles"),
27250
- archiveDir: resolve17(wikiDir, "archive")
27072
+ skillsDir: resolve18(wikiDir, "skills"),
27073
+ topicsDir: resolve18(wikiDir, "topic"),
27074
+ summariesDir: resolve18(wikiDir, "summaries"),
27075
+ articlesDir: resolve18(wikiDir, "articles"),
27076
+ archiveDir: resolve18(wikiDir, "archive")
27251
27077
  };
27252
27078
  const tools = surface === "wiki" ? WIKI_TOOLS : surface === "skills" ? SKILL_TOOLS : [...WIKI_TOOLS, ...SKILL_TOOLS];
27253
27079
  for (const directory of [
@@ -27570,7 +27396,7 @@ __export(exports_activity_log, {
27570
27396
  readSentFilesForTopic: () => readSentFilesForTopic
27571
27397
  });
27572
27398
  import {
27573
- existsSync as existsSync29,
27399
+ existsSync as existsSync30,
27574
27400
  mkdirSync as mkdirSync26,
27575
27401
  readdirSync as readdirSync11,
27576
27402
  renameSync as renameSync13,
@@ -27578,7 +27404,7 @@ import {
27578
27404
  unlinkSync as unlinkSync20,
27579
27405
  writeFileSync as writeFileSync20
27580
27406
  } from "fs";
27581
- import { basename as basename8, join as join38 } from "path";
27407
+ import { basename as basename8, join as join39 } from "path";
27582
27408
  function startLogRotation() {
27583
27409
  if (rotationTimer)
27584
27410
  return;
@@ -27592,7 +27418,7 @@ function writeLog(entry) {
27592
27418
  mkdirSync26(logDir, { recursive: true });
27593
27419
  const safeSession = sanitizeTopicName(entry.session);
27594
27420
  const sidShort = entry.sessionId ? entry.sessionId.slice(0, 8) : "new";
27595
- const file = join38(logDir, `${entry.userId}_${safeSession}_${sidShort}.jsonl`);
27421
+ const file = join39(logDir, `${entry.userId}_${safeSession}_${sidShort}.jsonl`);
27596
27422
  try {
27597
27423
  const stat = statSync16(file);
27598
27424
  if (stat.size >= MAX_FILE_SIZE) {
@@ -27612,7 +27438,7 @@ function rotateOldLogs() {
27612
27438
  try {
27613
27439
  const files = readdirSync11(logDir).filter((f) => f.endsWith(".jsonl")).map((f) => {
27614
27440
  try {
27615
- const stat = statSync16(join38(logDir, f));
27441
+ const stat = statSync16(join39(logDir, f));
27616
27442
  return { name: f, size: stat.size, mtimeMs: stat.mtimeMs };
27617
27443
  } catch {
27618
27444
  return null;
@@ -27628,7 +27454,7 @@ function rotateOldLogs() {
27628
27454
  if (freed >= toFree)
27629
27455
  break;
27630
27456
  try {
27631
- unlinkSync20(join38(logDir, file.name));
27457
+ unlinkSync20(join39(logDir, file.name));
27632
27458
  freed += file.size;
27633
27459
  logger.info({ file: file.name, sizeKB: (file.size / 1024).toFixed(0) }, "Rotated out old log");
27634
27460
  } catch (e) {
@@ -27640,9 +27466,9 @@ function rotateOldLogs() {
27640
27466
  }
27641
27467
  }
27642
27468
  function writeSentFileLog(entry) {
27643
- const dir = join38(resolveStorageUsersLogDir(), String(entry.userId));
27469
+ const dir = join39(resolveStorageUsersLogDir(), String(entry.userId));
27644
27470
  mkdirSync26(dir, { recursive: true });
27645
- const file = join38(dir, "sent-files.jsonl");
27471
+ const file = join39(dir, "sent-files.jsonl");
27646
27472
  try {
27647
27473
  appendJsonlEntry(file, { ...entry, fileName: basename8(entry.filePath) });
27648
27474
  } catch (e) {
@@ -27653,8 +27479,8 @@ function entryMatchesTopic(e, topicName) {
27653
27479
  return e.topicName === topicName;
27654
27480
  }
27655
27481
  function readSentFilesForTopic(userId, topicName) {
27656
- const file = join38(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
27657
- if (!existsSync29(file))
27482
+ const file = join39(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
27483
+ if (!existsSync30(file))
27658
27484
  return [];
27659
27485
  try {
27660
27486
  return readJsonlLines(file).map((line) => JSON.parse(line)).filter((e) => entryMatchesTopic(e, topicName));
@@ -27664,8 +27490,8 @@ function readSentFilesForTopic(userId, topicName) {
27664
27490
  }
27665
27491
  }
27666
27492
  function removeSentFilesForTopic(userId, topicName) {
27667
- const file = join38(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
27668
- if (!existsSync29(file))
27493
+ const file = join39(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
27494
+ if (!existsSync30(file))
27669
27495
  return;
27670
27496
  try {
27671
27497
  const remaining = readJsonlLines(file).filter((line) => {
@@ -28429,7 +28255,7 @@ var init_storage_public = __esm(async () => {
28429
28255
  });
28430
28256
 
28431
28257
  // ../../packages/mcp/src/hosted-surfaces.ts
28432
- import { resolve as resolve18 } from "path";
28258
+ import { resolve as resolve19 } from "path";
28433
28259
  function isActiveHostedMcpSurface(surface) {
28434
28260
  return ACTIVE_HOSTED_MCP_SURFACES.includes(surface);
28435
28261
  }
@@ -28486,23 +28312,11 @@ async function buildHostedSurfaceServer(surface, context) {
28486
28312
  });
28487
28313
  }
28488
28314
  case "vault": {
28489
- const [{ createVaultMcpServer: createVaultMcpServer2 }, vault, { logger: logger2 }] = await Promise.all([
28315
+ const [{ createVaultMcpServer: createVaultMcpServer2 }, vault] = await Promise.all([
28490
28316
  Promise.resolve().then(() => (init_vault2(), exports_vault)),
28491
- init_vault_public().then(() => exports_vault_public),
28492
- init_mcp_runtime_host().then(() => exports_mcp_runtime_host)
28317
+ init_vault_public().then(() => exports_vault_public)
28493
28318
  ]);
28494
- return createVaultMcpServer2({
28495
- userId: context.userId,
28496
- listOnly: context.agent !== "codex",
28497
- cwd: context.cwd
28498
- }, {
28499
- list: vault.vaultList,
28500
- substitute: vault.vaultSubstituteDetailed,
28501
- redact: vault.redactVaultSecrets,
28502
- log(level, details, message) {
28503
- logger2[level](details, message);
28504
- }
28505
- });
28319
+ return createVaultMcpServer2({ userId: context.userId }, { list: vault.vaultList });
28506
28320
  }
28507
28321
  case "wiki":
28508
28322
  case "skills": {
@@ -28525,7 +28339,7 @@ async function buildHostedSurfaceServer(surface, context) {
28525
28339
  ...topicId ? { topicId } : {},
28526
28340
  surface
28527
28341
  }, {
28528
- wikiRoot: resolve18(WORKSPACE_DIR2, "wiki"),
28342
+ wikiRoot: resolve19(WORKSPACE_DIR2, "wiki"),
28529
28343
  getTopicBrief: storage.getTopicBrief,
28530
28344
  resolveTopicBrief: accessibleTopicBrief,
28531
28345
  setTopicBrief: storage.setTopicBrief
@@ -28656,10 +28470,10 @@ var init_sse_transport = () => {};
28656
28470
  // ../../packages/mcp/src/server.ts
28657
28471
  import { createHash as createHash10, randomUUID as randomUUID23 } from "crypto";
28658
28472
  import { realpathSync as realpathSync6, statSync as statSync17 } from "fs";
28659
- import { basename as basename9, extname as extname3, resolve as resolve19 } from "path";
28473
+ import { basename as basename9, extname as extname3, resolve as resolve20 } from "path";
28660
28474
  import { McpServer as McpServer8 } from "@modelcontextprotocol/sdk/server/mcp.js";
28661
28475
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
28662
- import { z as z13 } from "zod";
28476
+ import { z as z12 } from "zod";
28663
28477
  function requireTopicAccess(ctx) {
28664
28478
  const topic = getTopic(ctx.topicId);
28665
28479
  if (!topic)
@@ -28670,8 +28484,8 @@ function requireTopicAccess(ctx) {
28670
28484
  return { topic };
28671
28485
  }
28672
28486
  function isPathInside2(baseDir, filePath) {
28673
- const cwd = resolve19(baseDir);
28674
- const normalized = resolve19(filePath);
28487
+ const cwd = resolve20(baseDir);
28488
+ const normalized = resolve20(filePath);
28675
28489
  try {
28676
28490
  const realCwd = realpathSync6(cwd);
28677
28491
  const real = realpathSync6(normalized);
@@ -28687,7 +28501,7 @@ function localFileInfo(ctx, filePath) {
28687
28501
  if (!isPathInside2(ctx.cwd, filePath)) {
28688
28502
  return { error: "Access denied. File must be within the topic workspace." };
28689
28503
  }
28690
- const normalizedPath = resolve19(filePath);
28504
+ const normalizedPath = resolve20(filePath);
28691
28505
  if (isSensitivePath(normalizedPath)) {
28692
28506
  return { error: "Access denied. Path matches the sensitive-file blacklist." };
28693
28507
  }
@@ -28879,8 +28693,8 @@ function buildNegotiumMcpServer(ctx) {
28879
28693
  }
28880
28694
  }
28881
28695
  if (ctx.fileDeliveryTools === true) {
28882
- server.tool("send_file", "Send a local file to the user in the chat. Use this when you want to share a file (image, document, PDF, code, etc.) with the user. The file will appear as a downloadable item in the chat.", { file_path: z13.string().describe("Absolute path to the file to send") }, async ({ file_path }) => deliverFile(ctx, file_path));
28883
- server.tool("send_files", "Send multiple local files to the user in the chat at once.", { file_paths: z13.array(z13.string()).describe("Array of absolute file paths to send") }, async ({ file_paths }) => {
28696
+ server.tool("send_file", "Send a local file to the user in the chat. Use this when you want to share a file (image, document, PDF, code, etc.) with the user. The file will appear as a downloadable item in the chat.", { file_path: z12.string().describe("Absolute path to the file to send") }, async ({ file_path }) => deliverFile(ctx, file_path));
28697
+ server.tool("send_files", "Send multiple local files to the user in the chat at once.", { file_paths: z12.array(z12.string()).describe("Array of absolute file paths to send") }, async ({ file_paths }) => {
28884
28698
  const results = await Promise.all(file_paths.map((filePath) => deliverFile(ctx, filePath)));
28885
28699
  const hasError = results.some((result) => ("isError" in result) && result.isError);
28886
28700
  return {
@@ -29164,37 +28978,37 @@ var init_src2 = __esm(async () => {
29164
28978
 
29165
28979
  // ../../packages/mcp-host/src/paths.ts
29166
28980
  import { homedir as homedir9 } from "os";
29167
- import { resolve as resolve20 } from "path";
28981
+ import { resolve as resolve21 } from "path";
29168
28982
  function stateDir() {
29169
28983
  const env = process.env.NEGOTIUM_STATE_DIR?.trim();
29170
- return env ? resolve20(env) : resolve20(homedir9(), ".negotium");
28984
+ return env ? resolve21(env) : resolve21(homedir9(), ".negotium");
29171
28985
  }
29172
28986
  function defaultPortsDir() {
29173
- return resolve20(stateDir(), "run", "mcp-ports");
28987
+ return resolve21(stateDir(), "run", "mcp-ports");
29174
28988
  }
29175
28989
  function defaultManifestFile() {
29176
- return resolve20(stateDir(), "data", "mcp-manifest.json");
28990
+ return resolve21(stateDir(), "data", "mcp-manifest.json");
29177
28991
  }
29178
28992
  var init_paths = () => {};
29179
28993
 
29180
28994
  // ../../packages/mcp-host/src/spec.ts
29181
- import { z as z14 } from "zod";
28995
+ import { z as z13 } from "zod";
29182
28996
  var KEY_PATTERN, portSchema, mcpServerSpecSchema;
29183
28997
  var init_spec = __esm(() => {
29184
28998
  KEY_PATTERN = /^[a-zA-Z0-9._-]+$/;
29185
- portSchema = z14.number().int().min(1).max(65535);
29186
- mcpServerSpecSchema = z14.object({
29187
- key: z14.string().min(1).regex(KEY_PATTERN, "key may only contain [a-zA-Z0-9._-]").refine((k) => k !== "." && k !== "..", "key may not be a dot path"),
29188
- transport: z14.enum(["stdio", "http"]),
29189
- command: z14.string().min(1),
29190
- args: z14.array(z14.string()).optional(),
29191
- env: z14.record(z14.string(), z14.string()).optional(),
29192
- cwd: z14.string().optional(),
29193
- portRange: z14.object({ base: portSchema, max: portSchema }).optional(),
29194
- scope: z14.enum(["node", "instance"]),
29195
- readyTimeoutMs: z14.number().int().positive().optional(),
29196
- idleEvictMs: z14.number().int().positive().optional(),
29197
- healthIntervalMs: z14.number().int().positive().optional()
28999
+ portSchema = z13.number().int().min(1).max(65535);
29000
+ mcpServerSpecSchema = z13.object({
29001
+ key: z13.string().min(1).regex(KEY_PATTERN, "key may only contain [a-zA-Z0-9._-]").refine((k) => k !== "." && k !== "..", "key may not be a dot path"),
29002
+ transport: z13.enum(["stdio", "http"]),
29003
+ command: z13.string().min(1),
29004
+ args: z13.array(z13.string()).optional(),
29005
+ env: z13.record(z13.string(), z13.string()).optional(),
29006
+ cwd: z13.string().optional(),
29007
+ portRange: z13.object({ base: portSchema, max: portSchema }).optional(),
29008
+ scope: z13.enum(["node", "instance"]),
29009
+ readyTimeoutMs: z13.number().int().positive().optional(),
29010
+ idleEvictMs: z13.number().int().positive().optional(),
29011
+ healthIntervalMs: z13.number().int().positive().optional()
29198
29012
  }).superRefine((spec, ctx) => {
29199
29013
  if (spec.transport === "http") {
29200
29014
  if (!spec.portRange) {
@@ -29215,9 +29029,9 @@ var init_spec = __esm(() => {
29215
29029
  });
29216
29030
 
29217
29031
  // ../../packages/mcp-host/src/manifest.ts
29218
- import { existsSync as existsSync30, mkdirSync as mkdirSync27, readFileSync as readFileSync25, renameSync as renameSync14, writeFileSync as writeFileSync21 } from "fs";
29219
- import { dirname as dirname18 } from "path";
29220
- import { z as z15 } from "zod";
29032
+ import { existsSync as existsSync31, mkdirSync as mkdirSync27, readFileSync as readFileSync25, renameSync as renameSync14, writeFileSync as writeFileSync21 } from "fs";
29033
+ import { dirname as dirname19 } from "path";
29034
+ import { z as z14 } from "zod";
29221
29035
 
29222
29036
  class McpManifest {
29223
29037
  file;
@@ -29260,7 +29074,7 @@ class McpManifest {
29260
29074
  return this.entries.get(key)?.enabled ?? false;
29261
29075
  }
29262
29076
  load() {
29263
- if (!existsSync30(this.file))
29077
+ if (!existsSync31(this.file))
29264
29078
  return;
29265
29079
  let parsed;
29266
29080
  try {
@@ -29276,7 +29090,7 @@ class McpManifest {
29276
29090
  }
29277
29091
  }
29278
29092
  save() {
29279
- mkdirSync27(dirname18(this.file), { recursive: true });
29093
+ mkdirSync27(dirname19(this.file), { recursive: true });
29280
29094
  const payload = {
29281
29095
  version: 1,
29282
29096
  servers: [...this.entries.values()]
@@ -29291,13 +29105,13 @@ var manifestEntrySchema, manifestFileSchema;
29291
29105
  var init_manifest = __esm(() => {
29292
29106
  init_paths();
29293
29107
  init_spec();
29294
- manifestEntrySchema = z15.object({
29108
+ manifestEntrySchema = z14.object({
29295
29109
  spec: mcpServerSpecSchema,
29296
- enabled: z15.boolean()
29110
+ enabled: z14.boolean()
29297
29111
  });
29298
- manifestFileSchema = z15.object({
29299
- version: z15.literal(1),
29300
- servers: z15.array(manifestEntrySchema)
29112
+ manifestFileSchema = z14.object({
29113
+ version: z14.literal(1),
29114
+ servers: z14.array(manifestEntrySchema)
29301
29115
  });
29302
29116
  });
29303
29117
 
@@ -29310,7 +29124,7 @@ function sanitizePathComponent(name) {
29310
29124
  }
29311
29125
 
29312
29126
  // ../../packages/mcp-host/src/manager.ts
29313
- import { execFileSync as execFileSync8, spawn as spawn8 } from "child_process";
29127
+ import { execFileSync as execFileSync8, spawn as spawn7 } from "child_process";
29314
29128
  import {
29315
29129
  mkdirSync as mkdirSync28,
29316
29130
  readdirSync as readdirSync12,
@@ -29319,8 +29133,8 @@ import {
29319
29133
  unlinkSync as unlinkSync21,
29320
29134
  writeFileSync as writeFileSync22
29321
29135
  } from "fs";
29322
- import { connect, createServer as createServer3 } from "net";
29323
- import { join as join39 } from "path";
29136
+ import { connect, createServer as createServer4 } from "net";
29137
+ import { join as join40 } from "path";
29324
29138
  function regKey(key, instanceKey) {
29325
29139
  return `${key}\x00${instanceKey}`;
29326
29140
  }
@@ -29336,16 +29150,16 @@ function resolveInstanceKey(spec, instanceKey) {
29336
29150
  return instanceKey;
29337
29151
  }
29338
29152
  function canBind(port) {
29339
- return new Promise((resolve21) => {
29340
- const srv = createServer3();
29341
- srv.once("error", () => resolve21(false));
29153
+ return new Promise((resolve22) => {
29154
+ const srv = createServer4();
29155
+ srv.once("error", () => resolve22(false));
29342
29156
  srv.listen({ port, host: "127.0.0.1", exclusive: true }, () => {
29343
- srv.close(() => resolve21(true));
29157
+ srv.close(() => resolve22(true));
29344
29158
  });
29345
29159
  });
29346
29160
  }
29347
29161
  function tcpProbe(port, timeoutMs = PROBE_TIMEOUT_MS2) {
29348
- return new Promise((resolve21) => {
29162
+ return new Promise((resolve22) => {
29349
29163
  const sock = connect({ port, host: "127.0.0.1" });
29350
29164
  let settled = false;
29351
29165
  const done = (ok3) => {
@@ -29353,7 +29167,7 @@ function tcpProbe(port, timeoutMs = PROBE_TIMEOUT_MS2) {
29353
29167
  return;
29354
29168
  settled = true;
29355
29169
  sock.destroy();
29356
- resolve21(ok3);
29170
+ resolve22(ok3);
29357
29171
  };
29358
29172
  sock.once("connect", () => done(true));
29359
29173
  sock.once("error", () => done(false));
@@ -29381,12 +29195,12 @@ function killProcessTree(pid) {
29381
29195
  function waitExit(proc, timeoutMs) {
29382
29196
  if (proc.exitCode !== null || proc.signalCode !== null)
29383
29197
  return Promise.resolve(true);
29384
- return new Promise((resolve21) => {
29385
- const timer = setTimeout(() => resolve21(false), timeoutMs);
29198
+ return new Promise((resolve22) => {
29199
+ const timer = setTimeout(() => resolve22(false), timeoutMs);
29386
29200
  timer.unref?.();
29387
29201
  const settle = () => {
29388
29202
  clearTimeout(timer);
29389
- resolve21(true);
29203
+ resolve22(true);
29390
29204
  };
29391
29205
  proc.once("exit", settle);
29392
29206
  proc.once("error", settle);
@@ -29520,7 +29334,7 @@ class McpHost {
29520
29334
  let spawnFailed = false;
29521
29335
  let proc;
29522
29336
  try {
29523
- proc = spawn8(spec.command, args, {
29337
+ proc = spawn7(spec.command, args, {
29524
29338
  stdio: "ignore",
29525
29339
  detached: false,
29526
29340
  cwd: spec.cwd,
@@ -29622,7 +29436,7 @@ class McpHost {
29622
29436
  if (file.includes(".tmp-"))
29623
29437
  continue;
29624
29438
  try {
29625
- const port = Number.parseInt(readFileSync26(join39(this.portsDir, file), "utf8").trim(), 10);
29439
+ const port = Number.parseInt(readFileSync26(join40(this.portsDir, file), "utf8").trim(), 10);
29626
29440
  if (!Number.isNaN(port))
29627
29441
  claims.set(file, port);
29628
29442
  } catch {}
@@ -29632,7 +29446,7 @@ class McpHost {
29632
29446
  writePortFile(fileName, port) {
29633
29447
  try {
29634
29448
  mkdirSync28(this.portsDir, { recursive: true });
29635
- const file = join39(this.portsDir, fileName);
29449
+ const file = join40(this.portsDir, fileName);
29636
29450
  const tmp = `${file}.tmp-${process.pid}`;
29637
29451
  writeFileSync22(tmp, String(port));
29638
29452
  renameSync15(tmp, file);
@@ -29642,7 +29456,7 @@ class McpHost {
29642
29456
  }
29643
29457
  deletePortFile(fileName) {
29644
29458
  try {
29645
- unlinkSync21(join39(this.portsDir, fileName));
29459
+ unlinkSync21(join40(this.portsDir, fileName));
29646
29460
  } catch {}
29647
29461
  }
29648
29462
  cleanupRunning(rkey, inst) {
@@ -29729,7 +29543,7 @@ var DEFAULT_READY_TIMEOUT_MS = 20000, READY_POLL_MS = 150, KILL_GRACE_MS = 5000,
29729
29543
  fn(`[mcp-host] ${msg}`, ctx);
29730
29544
  else
29731
29545
  fn(`[mcp-host] ${msg}`);
29732
- }, delay2 = (ms) => new Promise((resolve21) => setTimeout(resolve21, ms));
29546
+ }, delay2 = (ms) => new Promise((resolve22) => setTimeout(resolve22, ms));
29733
29547
  var init_manager3 = __esm(() => {
29734
29548
  init_manifest();
29735
29549
  init_paths();
@@ -29747,14 +29561,14 @@ var init_src3 = __esm(() => {
29747
29561
  import { randomUUID as randomUUID24 } from "crypto";
29748
29562
  import {
29749
29563
  copyFileSync as copyFileSync4,
29750
- existsSync as existsSync31,
29564
+ existsSync as existsSync32,
29751
29565
  mkdirSync as mkdirSync29,
29752
29566
  readFileSync as readFileSync27,
29753
29567
  rmSync as rmSync9,
29754
29568
  statSync as statSync18,
29755
29569
  writeFileSync as writeFileSync23
29756
29570
  } from "fs";
29757
- import { basename as basename10, extname as extname4, join as join40 } from "path";
29571
+ import { basename as basename10, extname as extname4, join as join41 } from "path";
29758
29572
  function safeExtension(filename) {
29759
29573
  const extension = extname4(basename10(filename));
29760
29574
  return /^\.[A-Za-z0-9]{1,16}$/.test(extension) ? extension.toLowerCase() : "";
@@ -29772,14 +29586,14 @@ function contentDisposition(filename) {
29772
29586
 
29773
29587
  class NodeFileStore {
29774
29588
  uploadDir;
29775
- constructor(uploadDir = join40(DATA_DIR, "uploads")) {
29589
+ constructor(uploadDir = join41(DATA_DIR, "uploads")) {
29776
29590
  this.uploadDir = uploadDir;
29777
29591
  }
29778
29592
  #ensureDir() {
29779
29593
  mkdirSync29(this.uploadDir, { recursive: true });
29780
29594
  }
29781
29595
  #metadataPath(fileId) {
29782
- return join40(this.uploadDir, `${fileId}.meta.json`);
29596
+ return join41(this.uploadDir, `${fileId}.meta.json`);
29783
29597
  }
29784
29598
  #metadata(fileId) {
29785
29599
  if (!FILE_ID_RE.test(fileId))
@@ -29808,7 +29622,7 @@ class NodeFileStore {
29808
29622
  hooks = {
29809
29623
  resolveAttachmentByFileId: (fileId) => {
29810
29624
  const metadata = this.#metadata(fileId);
29811
- if (!metadata || !existsSync31(join40(this.uploadDir, metadata.savedName)))
29625
+ if (!metadata || !existsSync32(join41(this.uploadDir, metadata.savedName)))
29812
29626
  return null;
29813
29627
  return this.#attachment(fileId, metadata);
29814
29628
  },
@@ -29816,8 +29630,8 @@ class NodeFileStore {
29816
29630
  const metadata = this.#metadata(fileId);
29817
29631
  if (!metadata)
29818
29632
  return null;
29819
- const path = join40(this.uploadDir, metadata.savedName);
29820
- return existsSync31(path) ? path : null;
29633
+ const path = join41(this.uploadDir, metadata.savedName);
29634
+ return existsSync32(path) ? path : null;
29821
29635
  },
29822
29636
  storeLocalFileAsUpload: (absPath, access = {}) => this.store(absPath, access),
29823
29637
  deleteFilesForTopic: (topicId) => this.deleteForTopic(topicId)
@@ -29831,11 +29645,11 @@ class NodeFileStore {
29831
29645
  const mimeType = file.type || MIME_BY_EXT2[extension] || "application/octet-stream";
29832
29646
  const existing = this.#metadata(fileId);
29833
29647
  if (existing) {
29834
- const matches = existing.filename === filename && existing.mimeType === mimeType && existing.sizeBytes === file.size && existing.ownerUserId === access.ownerUserId && existing.topicId === access.topicId && existsSync31(join40(this.uploadDir, existing.savedName));
29648
+ const matches = existing.filename === filename && existing.mimeType === mimeType && existing.sizeBytes === file.size && existing.ownerUserId === access.ownerUserId && existing.topicId === access.topicId && existsSync32(join41(this.uploadDir, existing.savedName));
29835
29649
  return matches ? this.#attachment(fileId, existing) : null;
29836
29650
  }
29837
29651
  const savedName = `${fileId}${extension}`;
29838
- const savedPath = join40(this.uploadDir, savedName);
29652
+ const savedPath = join41(this.uploadDir, savedName);
29839
29653
  try {
29840
29654
  const sizeBytes = await Bun.write(savedPath, file);
29841
29655
  if (sizeBytes !== file.size)
@@ -29860,14 +29674,14 @@ class NodeFileStore {
29860
29674
  }
29861
29675
  allows(fileId, access) {
29862
29676
  const metadata = this.#metadata(fileId);
29863
- return Boolean(metadata && metadata.ownerUserId === access.ownerUserId && metadata.topicId === access.topicId && existsSync31(join40(this.uploadDir, metadata.savedName)));
29677
+ return Boolean(metadata && metadata.ownerUserId === access.ownerUserId && metadata.topicId === access.topicId && existsSync32(join41(this.uploadDir, metadata.savedName)));
29864
29678
  }
29865
29679
  store(absPath, access = {}) {
29866
29680
  this.#ensureDir();
29867
29681
  const fileId = randomUUID24();
29868
29682
  const extension = safeExtension(absPath);
29869
29683
  const savedName = `${fileId}${extension}`;
29870
- const savedPath = join40(this.uploadDir, savedName);
29684
+ const savedPath = join41(this.uploadDir, savedName);
29871
29685
  try {
29872
29686
  const stats = statSync18(absPath);
29873
29687
  if (!stats.isFile() || stats.size > MAX_NODE_UPLOAD_BYTES)
@@ -29899,8 +29713,8 @@ class NodeFileStore {
29899
29713
  const allowed = metadata.visibility === "workspace" || metadata.ownerUserId === userId || Boolean(topic && isParticipant(topic, userId));
29900
29714
  if (!allowed)
29901
29715
  return null;
29902
- const path = join40(this.uploadDir, metadata.savedName);
29903
- if (!existsSync31(path))
29716
+ const path = join41(this.uploadDir, metadata.savedName);
29717
+ if (!existsSync32(path))
29904
29718
  return null;
29905
29719
  return new Response(Bun.file(path), {
29906
29720
  headers: {
@@ -29918,7 +29732,7 @@ class NodeFileStore {
29918
29732
  const metadata = this.#metadata(fileId);
29919
29733
  if (metadata?.topicId !== topicId)
29920
29734
  continue;
29921
- rmSync9(join40(this.uploadDir, metadata.savedName), { force: true });
29735
+ rmSync9(join41(this.uploadDir, metadata.savedName), { force: true });
29922
29736
  rmSync9(this.#metadataPath(fileId), { force: true });
29923
29737
  }
29924
29738
  }
@@ -30070,14 +29884,14 @@ function createPollingSseStream(req, options) {
30070
29884
  import { randomUUID as randomUUID25, timingSafeEqual as timingSafeEqual4 } from "crypto";
30071
29885
  import {
30072
29886
  chmodSync as chmodSync6,
30073
- existsSync as existsSync32,
29887
+ existsSync as existsSync33,
30074
29888
  mkdirSync as mkdirSync30,
30075
29889
  readFileSync as readFileSync28,
30076
29890
  renameSync as renameSync16,
30077
29891
  unlinkSync as unlinkSync22,
30078
29892
  writeFileSync as writeFileSync24
30079
29893
  } from "fs";
30080
- import { dirname as dirname19, resolve as resolve21 } from "path";
29894
+ import { dirname as dirname20, resolve as resolve22 } from "path";
30081
29895
  function jsonError(status, error2) {
30082
29896
  return Response.json({ ok: false, error: error2 }, { status });
30083
29897
  }
@@ -30906,7 +30720,7 @@ function writeNodeDaemonInfo(port, startedAt) {
30906
30720
  stateDir: STATE_DIR,
30907
30721
  startedAt
30908
30722
  };
30909
- mkdirSync30(dirname19(NODE_DAEMON_INFO_PATH), { recursive: true });
30723
+ mkdirSync30(dirname20(NODE_DAEMON_INFO_PATH), { recursive: true });
30910
30724
  const temporary = `${NODE_DAEMON_INFO_PATH}.${process.pid}.${randomUUID25()}.tmp`;
30911
30725
  writeFileSync24(temporary, `${JSON.stringify(info, null, 2)}
30912
30726
  `, { mode: 384 });
@@ -30916,7 +30730,7 @@ function writeNodeDaemonInfo(port, startedAt) {
30916
30730
  return info;
30917
30731
  }
30918
30732
  function readNodeDaemonInfo() {
30919
- if (!existsSync32(NODE_DAEMON_INFO_PATH))
30733
+ if (!existsSync33(NODE_DAEMON_INFO_PATH))
30920
30734
  return null;
30921
30735
  try {
30922
30736
  const value = JSON.parse(readFileSync28(NODE_DAEMON_INFO_PATH, "utf8"));
@@ -31001,7 +30815,7 @@ var init_control = __esm(async () => {
31001
30815
  await init_node_host();
31002
30816
  await init_files();
31003
30817
  NODE_RUNTIME_CONTRACT_BASE_PATH = `${NODE_CONTROL_BASE_PATH}/runtime/v1`;
31004
- NODE_DAEMON_INFO_PATH = resolve21(RUN_DIR, "node-daemon.json");
30818
+ NODE_DAEMON_INFO_PATH = resolve22(RUN_DIR, "node-daemon.json");
31005
30819
  NODE_VERSION = NEGOTIUM_VERSION;
31006
30820
  ControlRequestError = class ControlRequestError extends Error {
31007
30821
  constructor(message) {
@@ -31202,9 +31016,9 @@ var init_schedule = __esm(() => {
31202
31016
  });
31203
31017
 
31204
31018
  // ../../packages/module-cron/src/scripts.ts
31205
- import { spawn as spawn9 } from "child_process";
31206
- import { existsSync as existsSync33, mkdirSync as mkdirSync31, readdirSync as readdirSync13 } from "fs";
31207
- import { resolve as resolve22, sep as sep2 } from "path";
31019
+ import { spawn as spawn8 } from "child_process";
31020
+ import { existsSync as existsSync34, mkdirSync as mkdirSync31, readdirSync as readdirSync13 } from "fs";
31021
+ import { resolve as resolve23, sep as sep2 } from "path";
31208
31022
  function validateCronScriptName(script) {
31209
31023
  if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]*\.py$/.test(script) || script.includes("..") || script.includes("/") || script.includes("\\")) {
31210
31024
  return { ok: false, error: `script must be a plain .py filename under ${CRON_JOBS_DIR}` };
@@ -31215,7 +31029,7 @@ function resolveCronScriptPath(script) {
31215
31029
  const valid = validateCronScriptName(script);
31216
31030
  if (!valid.ok)
31217
31031
  throw new Error(valid.error);
31218
- const path = resolve22(CRON_JOBS_DIR, script);
31032
+ const path = resolve23(CRON_JOBS_DIR, script);
31219
31033
  if (!path.startsWith(`${CRON_JOBS_DIR}${sep2}`))
31220
31034
  throw new Error("script path escaped jobs directory");
31221
31035
  return path;
@@ -31226,7 +31040,7 @@ function listCronScripts() {
31226
31040
  }
31227
31041
  function cronScriptExists(script) {
31228
31042
  try {
31229
- return existsSync33(resolveCronScriptPath(script));
31043
+ return existsSync34(resolveCronScriptPath(script));
31230
31044
  } catch {
31231
31045
  return false;
31232
31046
  }
@@ -31258,14 +31072,14 @@ async function runCronPromptScript(options) {
31258
31072
  if (options.signal?.aborted)
31259
31073
  throw new CronScriptError("script run aborted");
31260
31074
  const scriptPath = resolveCronScriptPath(options.script);
31261
- if (!existsSync33(scriptPath))
31075
+ if (!existsSync34(scriptPath))
31262
31076
  throw new CronScriptError(`cron script not found: ${options.script}`);
31263
31077
  const timeoutMs = Math.max(5000, options.timeoutMs ?? DEFAULT_SCRIPT_TIMEOUT_MS);
31264
31078
  const outputLimitBytes = Math.max(1024, options.outputLimitBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES);
31265
31079
  const command = commandForScript(scriptPath);
31266
31080
  let child;
31267
31081
  try {
31268
- child = spawn9(command.executable, command.args, {
31082
+ child = spawn8(command.executable, command.args, {
31269
31083
  cwd: options.cwd,
31270
31084
  detached: true,
31271
31085
  env: {
@@ -31331,7 +31145,7 @@ async function runCronPromptScript(options) {
31331
31145
  var CRON_JOBS_DIR, DEFAULT_SCRIPT_TIMEOUT_MS, DEFAULT_OUTPUT_LIMIT_BYTES, KILL_GRACE_MS2 = 2500, CronScriptError;
31332
31146
  var init_scripts = __esm(async () => {
31333
31147
  await init_cron_host();
31334
- CRON_JOBS_DIR = resolve22(process.env.NEGOTIUM_CRON_JOBS_DIR?.trim() || resolve22(WORKSPACE_DIR, "cron", "jobs"));
31148
+ CRON_JOBS_DIR = resolve23(process.env.NEGOTIUM_CRON_JOBS_DIR?.trim() || resolve23(WORKSPACE_DIR, "cron", "jobs"));
31335
31149
  DEFAULT_SCRIPT_TIMEOUT_MS = 10 * 60000;
31336
31150
  DEFAULT_OUTPUT_LIMIT_BYTES = 1024 * 1024;
31337
31151
  CronScriptError = class CronScriptError extends Error {
@@ -32451,8 +32265,8 @@ var init_scheduler = __esm(async () => {
32451
32265
  });
32452
32266
 
32453
32267
  // ../../packages/module-cron/src/module.ts
32454
- import { existsSync as existsSync34 } from "fs";
32455
- import { fileURLToPath as fileURLToPath2 } from "url";
32268
+ import { existsSync as existsSync35 } from "fs";
32269
+ import { fileURLToPath as fileURLToPath3 } from "url";
32456
32270
  function envMs(name, fallback) {
32457
32271
  const parsed = Number.parseInt(process.env[name] ?? "", 10);
32458
32272
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
@@ -32482,7 +32296,7 @@ function createCronModule(options = {}) {
32482
32296
  if (event.type === "topic-deleted")
32483
32297
  trackCleanup(resetCronTopicContext(event.topicId));
32484
32298
  });
32485
- const mcpServerFile = options.mcpServerFile ?? (existsSync34(SOURCE_MCP_SERVER_FILE) ? SOURCE_MCP_SERVER_FILE : PACKAGED_MCP_SERVER_FILE);
32299
+ const mcpServerFile = options.mcpServerFile ?? (existsSync35(SOURCE_MCP_SERVER_FILE) ? SOURCE_MCP_SERVER_FILE : PACKAGED_MCP_SERVER_FILE);
32486
32300
  const unregisterMcp = host2.registerRuntimeMcpServer("cron-manager", {
32487
32301
  scopes: ["forum", "manager"],
32488
32302
  forumRequired: true,
@@ -32575,8 +32389,8 @@ var init_module = __esm(async () => {
32575
32389
  await init_scheduler();
32576
32390
  await init_scripts();
32577
32391
  await init_store();
32578
- SOURCE_MCP_SERVER_FILE = fileURLToPath2(new URL("./mcp-server.ts", import.meta.url));
32579
- PACKAGED_MCP_SERVER_FILE = fileURLToPath2(new URL("./runtime/cron/mcp-server.ts", import.meta.url));
32392
+ SOURCE_MCP_SERVER_FILE = fileURLToPath3(new URL("./mcp-server.ts", import.meta.url));
32393
+ PACKAGED_MCP_SERVER_FILE = fileURLToPath3(new URL("./runtime/cron/mcp-server.ts", import.meta.url));
32580
32394
  });
32581
32395
 
32582
32396
  // ../../packages/module-cron/src/summarize.ts
@@ -32762,10 +32576,10 @@ __export(exports_src3, {
32762
32576
  NODE_CONTROL_PROTOCOL_VERSION: () => NODE_CONTROL_PROTOCOL_VERSION,
32763
32577
  NODE_CONTROL_BASE_PATH: () => NODE_CONTROL_BASE_PATH
32764
32578
  });
32765
- import { createServer as createServer4 } from "net";
32579
+ import { createServer as createServer5 } from "net";
32766
32580
  async function availableLoopbackPort() {
32767
- return new Promise((resolve23, reject) => {
32768
- const probe = createServer4();
32581
+ return new Promise((resolve24, reject) => {
32582
+ const probe = createServer5();
32769
32583
  probe.unref();
32770
32584
  probe.once("error", reject);
32771
32585
  probe.listen(0, "127.0.0.1", () => {
@@ -32775,7 +32589,7 @@ async function availableLoopbackPort() {
32775
32589
  if (error2)
32776
32590
  reject(error2);
32777
32591
  else if (port > 0)
32778
- resolve23(port);
32592
+ resolve24(port);
32779
32593
  else
32780
32594
  reject(new Error("failed to allocate a loopback port"));
32781
32595
  });
@@ -32914,8 +32728,8 @@ function startNode(opts = {}) {
32914
32728
  const manifest = new McpManifest;
32915
32729
  const stopSweeper = mcpHost.startSweeper();
32916
32730
  let resolveCompleted;
32917
- const completed = new Promise((resolve23) => {
32918
- resolveCompleted = resolve23;
32731
+ const completed = new Promise((resolve24) => {
32732
+ resolveCompleted = resolve24;
32919
32733
  });
32920
32734
  let advertised = null;
32921
32735
  try {
@@ -32994,7 +32808,7 @@ async function runNodeDaemon(opts = {}) {
32994
32808
  singleton: true
32995
32809
  });
32996
32810
  await node.completed;
32997
- await new Promise((resolve23) => setImmediate(resolve23));
32811
+ await new Promise((resolve24) => setImmediate(resolve24));
32998
32812
  process.exit(0);
32999
32813
  }
33000
32814
  var init_src5 = __esm(async () => {
@@ -34040,9 +33854,9 @@ var init_context_usage = __esm(async () => {
34040
33854
 
34041
33855
  // ../../adapters/terminal/src/path-suggest.ts
34042
33856
  import { execFile as execFile3 } from "child_process";
34043
- import { existsSync as existsSync35, readdirSync as readdirSync14, statSync as statSync19 } from "fs";
33857
+ import { existsSync as existsSync36, readdirSync as readdirSync14, statSync as statSync19 } from "fs";
34044
33858
  import { homedir as homedir10 } from "os";
34045
- import { basename as basename11, dirname as dirname20, join as join41, sep as sep3 } from "path";
33859
+ import { basename as basename11, dirname as dirname21, join as join42, sep as sep3 } from "path";
34046
33860
  import { promisify as promisify3 } from "util";
34047
33861
  function indexRecursivePaths(files) {
34048
33862
  const paths = [];
@@ -34143,15 +33957,15 @@ function resolveFragment(frag) {
34143
33957
  else if (frag === "~/")
34144
33958
  path = home;
34145
33959
  else if (frag.startsWith("~/"))
34146
- path = join41(home, frag.slice(2));
33960
+ path = join42(home, frag.slice(2));
34147
33961
  else if (frag.startsWith("/"))
34148
33962
  path = frag;
34149
33963
  else
34150
- path = join41(home, frag);
33964
+ path = join42(home, frag);
34151
33965
  if (frag.endsWith("/") || frag === "" || frag === "~") {
34152
33966
  return { dir: path, prefix: "" };
34153
33967
  }
34154
- return { dir: dirname20(path), prefix: basename11(path) };
33968
+ return { dir: dirname21(path), prefix: basename11(path) };
34155
33969
  }
34156
33970
  function toToken(fullPath, isDir) {
34157
33971
  const home = homedir10();
@@ -34176,7 +33990,7 @@ function rankAndSlice(dir, candidates) {
34176
33990
  });
34177
33991
  return candidates.slice(0, MAX_SUGGESTIONS).map(({ relPath, isDir }) => ({
34178
33992
  label: `${relPath}${isDir ? "/" : ""}`,
34179
- value: toToken(join41(dir, relPath), isDir),
33993
+ value: toToken(join42(dir, relPath), isDir),
34180
33994
  isDir
34181
33995
  }));
34182
33996
  }
@@ -34255,7 +34069,7 @@ function pathSuggestions(lineText, col) {
34255
34069
  let isDir = entry.isDirectory();
34256
34070
  if (!isDir && entry.isSymbolicLink()) {
34257
34071
  try {
34258
- isDir = statSync19(join41(dir, entry.name)).isDirectory();
34072
+ isDir = statSync19(join42(dir, entry.name)).isDirectory();
34259
34073
  } catch {}
34260
34074
  }
34261
34075
  return {
@@ -34282,14 +34096,14 @@ function fragmentToAbsolutePath(frag) {
34282
34096
  if (frag === "~" || frag === "~/")
34283
34097
  return home;
34284
34098
  if (frag.startsWith("~/"))
34285
- return join41(home, frag.slice(2));
34099
+ return join42(home, frag.slice(2));
34286
34100
  if (frag.startsWith("/"))
34287
34101
  return frag;
34288
- return join41(home, frag);
34102
+ return join42(home, frag);
34289
34103
  }
34290
34104
  function fragmentResolves(frag) {
34291
34105
  try {
34292
- return existsSync35(fragmentToAbsolutePath(frag));
34106
+ return existsSync36(fragmentToAbsolutePath(frag));
34293
34107
  } catch {
34294
34108
  return false;
34295
34109
  }
@@ -38193,8 +38007,8 @@ class TerminalApp {
38193
38007
  this.#refreshBackgroundSessions();
38194
38008
  }, 1000);
38195
38009
  this.#backgroundRefreshTimer.unref?.();
38196
- await new Promise((resolve23) => {
38197
- this.#finishRun = resolve23;
38010
+ await new Promise((resolve24) => {
38011
+ this.#finishRun = resolve24;
38198
38012
  });
38199
38013
  } finally {
38200
38014
  this.#running = false;
@@ -40791,7 +40605,7 @@ var init_commands2 = __esm(async () => {
40791
40605
  // ../../adapters/telegram/src/mapping-store.ts
40792
40606
  import { Database as Database2 } from "bun:sqlite";
40793
40607
  import { mkdirSync as mkdirSync32 } from "fs";
40794
- import { dirname as dirname21, join as join42 } from "path";
40608
+ import { dirname as dirname22, join as join43 } from "path";
40795
40609
  function outboxRowToEntry(row) {
40796
40610
  return {
40797
40611
  id: row.id,
@@ -40857,9 +40671,9 @@ function migrateOutboxSchema(db4) {
40857
40671
  db4.run("CREATE INDEX IF NOT EXISTS idx_telegram_outbox_runtime_message ON outbox(runtime_message_id)");
40858
40672
  }
40859
40673
  function openMappingStore(path) {
40860
- const dbPath = path ?? join42(DATA_DIR, "adapter-telegram.db");
40674
+ const dbPath = path ?? join43(DATA_DIR, "adapter-telegram.db");
40861
40675
  if (dbPath !== ":memory:")
40862
- mkdirSync32(dirname21(dbPath), { recursive: true });
40676
+ mkdirSync32(dirname22(dbPath), { recursive: true });
40863
40677
  const db4 = new Database2(dbPath);
40864
40678
  migrateLegacySchema(db4);
40865
40679
  createTables(db4);
@@ -41094,8 +40908,8 @@ ${caption}` : voiceText : caption;
41094
40908
  return;
41095
40909
  }
41096
40910
  let release;
41097
- const ready = new Promise((resolve23) => {
41098
- release = resolve23;
40911
+ const ready = new Promise((resolve24) => {
40912
+ release = resolve24;
41099
40913
  });
41100
40914
  const entry = {
41101
40915
  messages: [msg],
@@ -41217,7 +41031,7 @@ function renderOutbound(text2, maxLen = 4096) {
41217
41031
  }
41218
41032
 
41219
41033
  // ../../adapters/telegram/src/adapter.ts
41220
- import { existsSync as existsSync36 } from "fs";
41034
+ import { existsSync as existsSync37 } from "fs";
41221
41035
  import { basename as basename12, extname as extname5 } from "path";
41222
41036
  function startTelegramAdapter(opts) {
41223
41037
  const { client, forumChatId } = opts;
@@ -41328,7 +41142,7 @@ function startTelegramAdapter(opts) {
41328
41142
  attempt += 1;
41329
41143
  const waitMs = Math.min(5000, 250 * 2 ** Math.min(attempt - 1, 5));
41330
41144
  logger.warn({ err: err2, attempt, waitMs }, "telegram adapter: getMe failed while resolving onboarding identity; retrying");
41331
- await new Promise((resolve23) => setTimeout(resolve23, waitMs));
41145
+ await new Promise((resolve24) => setTimeout(resolve24, waitMs));
41332
41146
  }
41333
41147
  }
41334
41148
  return;
@@ -41715,7 +41529,7 @@ function startTelegramAdapter(opts) {
41715
41529
  logger.warn({ path, chatId }, "telegram adapter: blocked sensitive file path");
41716
41530
  return { ref: null, delivered: false, error: "path matches the sensitive-file blacklist" };
41717
41531
  }
41718
- if (!existsSync36(path)) {
41532
+ if (!existsSync37(path)) {
41719
41533
  const sent = await client.sendMessage(chatId, `File: ${path}`, base).catch(() => null);
41720
41534
  const messageId = sentMessageId(sent);
41721
41535
  return {
@@ -41815,14 +41629,14 @@ function startTelegramAdapter(opts) {
41815
41629
  const sendQueues = new Map;
41816
41630
  function enqueueSend(topicId, task) {
41817
41631
  const prev = sendQueues.get(topicId) ?? Promise.resolve();
41818
- const next = prev.then(() => new Promise((resolve23) => {
41632
+ const next = prev.then(() => new Promise((resolve24) => {
41819
41633
  const timer = setTimeout(() => {
41820
41634
  logger.warn({ topicId, sendTimeoutMs }, "telegram adapter: send timed out \u2014 abandoning it and continuing the queue");
41821
- resolve23();
41635
+ resolve24();
41822
41636
  }, sendTimeoutMs);
41823
41637
  task().catch((err2) => logger.warn({ err: err2, topicId }, "telegram adapter: send task failed")).finally(() => {
41824
41638
  clearTimeout(timer);
41825
- resolve23();
41639
+ resolve24();
41826
41640
  });
41827
41641
  }));
41828
41642
  sendQueues.set(topicId, next);
@@ -42068,7 +41882,7 @@ ${suffix}`;
42068
41882
  continue;
42069
41883
  }
42070
41884
  if (retryDelayMs > 0) {
42071
- await new Promise((resolve23) => setTimeout(resolve23, retryDelayMs));
41885
+ await new Promise((resolve24) => setTimeout(resolve24, retryDelayMs));
42072
41886
  }
42073
41887
  try {
42074
41888
  await client.editMessageText(text2, editOptions);
@@ -42709,8 +42523,8 @@ async function runTelegramCli(args = process.argv.slice(2)) {
42709
42523
  onShutdown("telegram-channel", 100, () => channel.stop());
42710
42524
  onShutdown("telegram-singleton", 90, () => singleton.stop());
42711
42525
  let resolveCompleted;
42712
- const completed = new Promise((resolve23) => {
42713
- resolveCompleted = resolve23;
42526
+ const completed = new Promise((resolve24) => {
42527
+ resolveCompleted = resolve24;
42714
42528
  });
42715
42529
  onShutdown("telegram-completed", -100, resolveCompleted);
42716
42530
  process.stdout.write(`negotium Telegram adapter connected to canonical node pid ${initialNode.info?.pid ?? "unknown"}
@@ -42735,11 +42549,11 @@ var exports_join_status = {};
42735
42549
  __export(exports_join_status, {
42736
42550
  hasConfiguredOtiumJoin: () => hasConfiguredOtiumJoin
42737
42551
  });
42738
- import { existsSync as existsSync37 } from "fs";
42739
- import { resolve as resolve23 } from "path";
42552
+ import { existsSync as existsSync38 } from "fs";
42553
+ import { resolve as resolve24 } from "path";
42740
42554
  function hasConfiguredOtiumJoin() {
42741
42555
  const envJoin = Boolean(process.env.OTIUM_CENTRAL_URL?.trim() && process.env.OTIUM_CELL_ID?.trim() && process.env.OTIUM_CELL_SECRET?.trim());
42742
- return envJoin || existsSync37(resolve23(DATA_DIR, "otium-join.json"));
42556
+ return envJoin || existsSync38(resolve24(DATA_DIR, "otium-join.json"));
42743
42557
  }
42744
42558
  var init_join_status = __esm(() => {
42745
42559
  init_config_public();
@@ -42757,9 +42571,9 @@ function firstCell() {
42757
42571
  return cell;
42758
42572
  return null;
42759
42573
  }
42760
- function attachOtiumCentralCell(join43) {
42761
- cells.set(join43.cellId, {
42762
- join: join43,
42574
+ function attachOtiumCentralCell(join44) {
42575
+ cells.set(join44.cellId, {
42576
+ join: join44,
42763
42577
  nodesCache: null,
42764
42578
  verifyCache: new Map,
42765
42579
  tokenCache: new Map
@@ -42771,10 +42585,10 @@ function detachOtiumCentralCell(cellId) {
42771
42585
  function attachedOtiumCells() {
42772
42586
  return [...cells.values()].map((cell) => cell.join);
42773
42587
  }
42774
- function configureOtiumCentral(join43) {
42588
+ function configureOtiumCentral(join44) {
42775
42589
  cells.clear();
42776
- if (join43)
42777
- attachOtiumCentralCell(join43);
42590
+ if (join44)
42591
+ attachOtiumCentralCell(join44);
42778
42592
  }
42779
42593
  function isOtiumCentralConfigured() {
42780
42594
  return cells.size > 0;
@@ -43188,7 +43002,7 @@ import { createHash as createHash11, randomUUID as randomUUID28 } from "crypto";
43188
43002
  import {
43189
43003
  chmodSync as chmodSync7,
43190
43004
  closeSync as closeSync5,
43191
- existsSync as existsSync38,
43005
+ existsSync as existsSync39,
43192
43006
  fsyncSync as fsyncSync2,
43193
43007
  linkSync,
43194
43008
  lstatSync,
@@ -43201,9 +43015,9 @@ import {
43201
43015
  unlinkSync as unlinkSync23,
43202
43016
  writeFileSync as writeFileSync25
43203
43017
  } from "fs";
43204
- import { dirname as dirname22, resolve as resolve24 } from "path";
43018
+ import { dirname as dirname23, resolve as resolve25 } from "path";
43205
43019
  function joinFilePath() {
43206
- return resolve24(DATA_DIR, "otium-join.json");
43020
+ return resolve25(DATA_DIR, "otium-join.json");
43207
43021
  }
43208
43022
  function isHttpUrl(value) {
43209
43023
  return /^https?:\/\//.test(value);
@@ -43258,7 +43072,7 @@ function parseInviteCode(code) {
43258
43072
  return normalizeJoin(parsed);
43259
43073
  }
43260
43074
  function joinLockPath() {
43261
- return resolve24(DATA_DIR, ".otium-join.lock");
43075
+ return resolve25(DATA_DIR, ".otium-join.lock");
43262
43076
  }
43263
43077
  function processIsAlive(pid) {
43264
43078
  if (!Number.isSafeInteger(pid) || pid <= 0)
@@ -43272,9 +43086,9 @@ function processIsAlive(pid) {
43272
43086
  }
43273
43087
  function withJoinCredentialLock(operation) {
43274
43088
  const lockPath = joinLockPath();
43275
- const ownerPath = resolve24(lockPath, "owner.json");
43089
+ const ownerPath = resolve25(lockPath, "owner.json");
43276
43090
  const owner = { pid: process.pid, token: randomUUID28() };
43277
- mkdirSync33(dirname22(lockPath), { recursive: true });
43091
+ mkdirSync33(dirname23(lockPath), { recursive: true });
43278
43092
  for (let attempt = 0;; attempt += 1) {
43279
43093
  let created = false;
43280
43094
  try {
@@ -43338,20 +43152,20 @@ function withJoinCredentialLock(operation) {
43338
43152
  function joinsEqual(left, right) {
43339
43153
  return left.central === right.central && left.relay === right.relay && left.cellId === right.cellId && left.secret === right.secret;
43340
43154
  }
43341
- function normalizedJoin(join43) {
43155
+ function normalizedJoin(join44) {
43342
43156
  return normalizeJoin({
43343
- v: join43.v,
43344
- central: join43.central,
43345
- relay: join43.relay,
43346
- cellId: join43.cellId,
43347
- secret: join43.secret
43157
+ v: join44.v,
43158
+ central: join44.central,
43159
+ relay: join44.relay,
43160
+ cellId: join44.cellId,
43161
+ secret: join44.secret
43348
43162
  });
43349
43163
  }
43350
- function joinCredentialDigest(join43) {
43351
- return createHash11("sha256").update(JSON.stringify(normalizedJoin(join43))).digest("base64url");
43164
+ function joinCredentialDigest(join44) {
43165
+ return createHash11("sha256").update(JSON.stringify(normalizedJoin(join44))).digest("base64url");
43352
43166
  }
43353
43167
  function readPersistedJoins(path = joinFilePath()) {
43354
- if (!existsSync38(path))
43168
+ if (!existsSync39(path))
43355
43169
  return [];
43356
43170
  const parsed = JSON.parse(readFileSync29(path, "utf-8"));
43357
43171
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -43367,9 +43181,9 @@ function readPersistedJoins(path = joinFilePath()) {
43367
43181
  return normalizeJoin(entry);
43368
43182
  });
43369
43183
  }
43370
- function isJoinPersisted(join43) {
43184
+ function isJoinPersisted(join44) {
43371
43185
  try {
43372
- const normalized = normalizedJoin(join43);
43186
+ const normalized = normalizedJoin(join44);
43373
43187
  return readPersistedJoins().some((persisted) => joinsEqual(persisted, normalized));
43374
43188
  } catch {
43375
43189
  return false;
@@ -43385,8 +43199,8 @@ function fsyncPath(target) {
43385
43199
  }
43386
43200
  function writeJoins(joins, allowOverwrite) {
43387
43201
  const path = joinFilePath();
43388
- const directory = dirname22(path);
43389
- const temporaryPath = resolve24(directory, `.otium-join.json.${process.pid}.${randomUUID28()}.tmp`);
43202
+ const directory = dirname23(path);
43203
+ const temporaryPath = resolve25(directory, `.otium-join.json.${process.pid}.${randomUUID28()}.tmp`);
43390
43204
  const payload = { v: JOIN_FILE_VERSION, joins };
43391
43205
  let fd;
43392
43206
  try {
@@ -43407,18 +43221,18 @@ function writeJoins(joins, allowOverwrite) {
43407
43221
  } catch (error2) {
43408
43222
  if (fd !== undefined)
43409
43223
  closeSync5(fd);
43410
- if (existsSync38(temporaryPath))
43224
+ if (existsSync39(temporaryPath))
43411
43225
  unlinkSync23(temporaryPath);
43412
43226
  throw error2;
43413
43227
  }
43414
43228
  return path;
43415
43229
  }
43416
- function saveJoinWhileLocked(join43, options = {}) {
43230
+ function saveJoinWhileLocked(join44, options = {}) {
43417
43231
  const path = joinFilePath();
43418
- const directory = dirname22(path);
43419
- const normalized = normalizedJoin(join43);
43232
+ const directory = dirname23(path);
43233
+ const normalized = normalizedJoin(join44);
43420
43234
  mkdirSync33(directory, { recursive: true });
43421
- if (!existsSync38(path))
43235
+ if (!existsSync39(path))
43422
43236
  return writeJoins([normalized], false);
43423
43237
  if (lstatSync(path).isSymbolicLink()) {
43424
43238
  throw new Error(`refusing to replace symlinked Otium join file at ${path}`);
@@ -43446,13 +43260,13 @@ function saveJoinWhileLocked(join43, options = {}) {
43446
43260
  const next = conflict ? existing.map((persisted) => persisted.cellId === normalized.cellId ? normalized : persisted) : [...existing, normalized];
43447
43261
  return writeJoins(next, true);
43448
43262
  }
43449
- function saveJoin(join43, options = {}) {
43450
- return withJoinCredentialLock(() => saveJoinWhileLocked(join43, options));
43263
+ function saveJoin(join44, options = {}) {
43264
+ return withJoinCredentialLock(() => saveJoinWhileLocked(join44, options));
43451
43265
  }
43452
43266
  function removeJoin(cellId) {
43453
43267
  return withJoinCredentialLock(() => {
43454
43268
  const path = joinFilePath();
43455
- if (!existsSync38(path))
43269
+ if (!existsSync39(path))
43456
43270
  return false;
43457
43271
  if (lstatSync(path).isSymbolicLink()) {
43458
43272
  throw new Error(`refusing to remove symlinked Otium join file at ${path}`);
@@ -43460,7 +43274,7 @@ function removeJoin(cellId) {
43460
43274
  if (cellId) {
43461
43275
  let remaining;
43462
43276
  try {
43463
- remaining = readPersistedJoins(path).filter((join43) => join43.cellId !== cellId);
43277
+ remaining = readPersistedJoins(path).filter((join44) => join44.cellId !== cellId);
43464
43278
  } catch {
43465
43279
  return false;
43466
43280
  }
@@ -43472,7 +43286,7 @@ function removeJoin(cellId) {
43472
43286
  }
43473
43287
  }
43474
43288
  unlinkSync23(path);
43475
- fsyncPath(dirname22(path));
43289
+ fsyncPath(dirname23(path));
43476
43290
  return true;
43477
43291
  });
43478
43292
  }
@@ -43493,7 +43307,7 @@ function loadJoins() {
43493
43307
  logger.warn("otium: OTIUM_CENTRAL_URL, OTIUM_CELL_ID, OTIUM_CELL_SECRET must be set together \u2014 ignoring partial env");
43494
43308
  }
43495
43309
  const path = joinFilePath();
43496
- if (!existsSync38(path))
43310
+ if (!existsSync39(path))
43497
43311
  return [];
43498
43312
  try {
43499
43313
  return readPersistedJoins(path);
@@ -43513,7 +43327,7 @@ var init_join = __esm(async () => {
43513
43327
  // ../../adapters/otium/src/peer-files.ts
43514
43328
  import { randomUUID as randomUUID29 } from "crypto";
43515
43329
  import { copyFileSync as copyFileSync5, mkdirSync as mkdirSync34, rmSync as rmSync11, statSync as statSync21 } from "fs";
43516
- import { basename as basename13, extname as extname6, join as join43 } from "path";
43330
+ import { basename as basename13, extname as extname6, join as join44 } from "path";
43517
43331
  function fileType(mimeType) {
43518
43332
  if (mimeType.startsWith("image/"))
43519
43333
  return "image";
@@ -43579,7 +43393,7 @@ function recordFile(args) {
43579
43393
  function insertLocalFile(args) {
43580
43394
  const id = randomUUID29();
43581
43395
  mkdirSync34(PEER_FILES_DIR, { recursive: true });
43582
- const path = join43(PEER_FILES_DIR, `${id}-${safeFilename(args.filename)}`);
43396
+ const path = join44(PEER_FILES_DIR, `${id}-${safeFilename(args.filename)}`);
43583
43397
  copyFileSync5(args.sourcePath, path);
43584
43398
  return recordFile({ id, path, sizeBytes: statSync21(path).size, ...args });
43585
43399
  }
@@ -43625,7 +43439,7 @@ function installPeerFileHooks() {
43625
43439
  var PEER_FILES_DIR;
43626
43440
  var init_peer_files = __esm(async () => {
43627
43441
  await init_src();
43628
- PEER_FILES_DIR = join43(DATA_DIR, "otium-peer-files");
43442
+ PEER_FILES_DIR = join44(DATA_DIR, "otium-peer-files");
43629
43443
  db.exec(`
43630
43444
  CREATE TABLE IF NOT EXISTS otium_peer_files (
43631
43445
  id TEXT PRIMARY KEY,
@@ -44018,14 +43832,14 @@ var init_store2 = __esm(async () => {
44018
43832
  // ../../adapters/otium/src/workspace-scope.ts
44019
43833
  import { createHash as createHash13 } from "crypto";
44020
43834
  import { mkdirSync as mkdirSync35, readFileSync as readFileSync30, writeFileSync as writeFileSync26 } from "fs";
44021
- import { dirname as dirname23, resolve as resolve25 } from "path";
43835
+ import { dirname as dirname24, resolve as resolve26 } from "path";
44022
43836
  function surfaceScopeFor(central, workspaceId) {
44023
43837
  const digest = createHash13("sha256").update(`${central.trim().replace(/\/+$/, "")}
44024
43838
  ${workspaceId.trim()}`).digest("hex");
44025
43839
  return `ws_${digest.slice(0, 24)}`;
44026
43840
  }
44027
43841
  function scopeCachePath() {
44028
- return resolve25(DATA_DIR, "otium-workspace.json");
43842
+ return resolve26(DATA_DIR, "otium-workspace.json");
44029
43843
  }
44030
43844
  function readCache() {
44031
43845
  try {
@@ -44037,19 +43851,19 @@ function readCache() {
44037
43851
  return {};
44038
43852
  }
44039
43853
  }
44040
- function cachedSurfaceScope(join44) {
44041
- const record = readCache()[join44.cellId];
44042
- if (!record || record.central !== join44.central || !record.workspaceId)
43854
+ function cachedSurfaceScope(join45) {
43855
+ const record = readCache()[join45.cellId];
43856
+ if (!record || record.central !== join45.central || !record.workspaceId)
44043
43857
  return null;
44044
43858
  return surfaceScopeFor(record.central, record.workspaceId);
44045
43859
  }
44046
- function cacheSurfaceScope(join44, workspaceId) {
44047
- const scope = surfaceScopeFor(join44.central, workspaceId);
43860
+ function cacheSurfaceScope(join45, workspaceId) {
43861
+ const scope = surfaceScopeFor(join45.central, workspaceId);
44048
43862
  const path = scopeCachePath();
44049
43863
  try {
44050
- mkdirSync35(dirname23(path), { recursive: true });
43864
+ mkdirSync35(dirname24(path), { recursive: true });
44051
43865
  const cache = readCache();
44052
- cache[join44.cellId] = { central: join44.central, workspaceId, scope };
43866
+ cache[join45.cellId] = { central: join45.central, workspaceId, scope };
44053
43867
  writeFileSync26(path, `${JSON.stringify(cache, null, 2)}
44054
43868
  `, { mode: 384 });
44055
43869
  } catch (err2) {
@@ -44057,23 +43871,23 @@ function cacheSurfaceScope(join44, workspaceId) {
44057
43871
  }
44058
43872
  return scope;
44059
43873
  }
44060
- async function resolveSurfaceScope(join44) {
44061
- const cached = cachedSurfaceScope(join44);
43874
+ async function resolveSurfaceScope(join45) {
43875
+ const cached = cachedSurfaceScope(join45);
44062
43876
  if (cached)
44063
43877
  return cached;
44064
43878
  try {
44065
- const workspaceId = await peerWorkspaceIdForCell(join44.cellId);
43879
+ const workspaceId = await peerWorkspaceIdForCell(join45.cellId);
44066
43880
  if (!workspaceId)
44067
43881
  return null;
44068
- return cacheSurfaceScope(join44, workspaceId);
43882
+ return cacheSurfaceScope(join45, workspaceId);
44069
43883
  } catch (err2) {
44070
43884
  logger.warn({ err: err2 }, "otium: workspace scope unresolved (will retry on the next contact)");
44071
43885
  return null;
44072
43886
  }
44073
43887
  }
44074
43888
  function surfaceScopeForCell(cellId) {
44075
- const join44 = attachedOtiumCells().find((candidate) => candidate.cellId === cellId);
44076
- return join44 ? cachedSurfaceScope(join44) : null;
43889
+ const join45 = attachedOtiumCells().find((candidate) => candidate.cellId === cellId);
43890
+ return join45 ? cachedSurfaceScope(join45) : null;
44077
43891
  }
44078
43892
  function unscopedRoomsAddressable() {
44079
43893
  return attachedOtiumCells().length < 2;
@@ -45086,7 +44900,7 @@ import {
45086
44900
  import {
45087
44901
  chmodSync as chmodSync8,
45088
44902
  closeSync as closeSync6,
45089
- existsSync as existsSync39,
44903
+ existsSync as existsSync40,
45090
44904
  fsyncSync as fsyncSync3,
45091
44905
  linkSync as linkSync2,
45092
44906
  mkdirSync as mkdirSync36,
@@ -45096,7 +44910,7 @@ import {
45096
44910
  unlinkSync as unlinkSync24,
45097
44911
  writeFileSync as writeFileSync27
45098
44912
  } from "fs";
45099
- import { dirname as dirname24, resolve as resolve26 } from "path";
44913
+ import { dirname as dirname25, resolve as resolve27 } from "path";
45100
44914
  function parseEnrollmentInvite(code) {
45101
44915
  let parsed;
45102
44916
  try {
@@ -45117,11 +44931,11 @@ function parseEnrollmentInvite(code) {
45117
44931
  return { v: 2, central, token };
45118
44932
  }
45119
44933
  function pendingEnrollmentPath() {
45120
- return resolve26(DATA_DIR, "otium-enrollment-pending.json");
44934
+ return resolve27(DATA_DIR, "otium-enrollment-pending.json");
45121
44935
  }
45122
44936
  function isEnrollmentPending(invite) {
45123
44937
  const path = pendingEnrollmentPath();
45124
- if (!existsSync39(path))
44938
+ if (!existsSync40(path))
45125
44939
  return false;
45126
44940
  try {
45127
44941
  const saved = JSON.parse(readFileSync31(path, "utf8"));
@@ -45132,7 +44946,7 @@ function isEnrollmentPending(invite) {
45132
44946
  }
45133
44947
  function loadOrCreatePending(invite, nodeName) {
45134
44948
  const path = pendingEnrollmentPath();
45135
- if (existsSync39(path)) {
44949
+ if (existsSync40(path)) {
45136
44950
  const saved = JSON.parse(readFileSync31(path, "utf8"));
45137
44951
  if (saved.central === invite.central && saved.token === invite.token)
45138
44952
  return saved;
@@ -45146,8 +44960,8 @@ function loadOrCreatePending(invite, nodeName) {
45146
44960
  publicKey: pair.publicKey.export({ format: "der", type: "spki" }).toString("base64url"),
45147
44961
  ...nodeName ? { nodeName } : {}
45148
44962
  };
45149
- mkdirSync36(dirname24(path), { recursive: true });
45150
- const temporaryPath = resolve26(dirname24(path), `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
44963
+ mkdirSync36(dirname25(path), { recursive: true });
44964
+ const temporaryPath = resolve27(dirname25(path), `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
45151
44965
  let fd;
45152
44966
  try {
45153
44967
  fd = openSync6(temporaryPath, "wx", 384);
@@ -45159,7 +44973,7 @@ function loadOrCreatePending(invite, nodeName) {
45159
44973
  linkSync2(temporaryPath, path);
45160
44974
  unlinkSync24(temporaryPath);
45161
44975
  chmodSync8(path, 384);
45162
- const directoryFd = openSync6(dirname24(path), "r");
44976
+ const directoryFd = openSync6(dirname25(path), "r");
45163
44977
  try {
45164
44978
  fsyncSync3(directoryFd);
45165
44979
  } finally {
@@ -45168,9 +44982,9 @@ function loadOrCreatePending(invite, nodeName) {
45168
44982
  } catch (error2) {
45169
44983
  if (fd !== undefined)
45170
44984
  closeSync6(fd);
45171
- if (existsSync39(temporaryPath))
44985
+ if (existsSync40(temporaryPath))
45172
44986
  unlinkSync24(temporaryPath);
45173
- if (error2.code === "EEXIST" && existsSync39(path)) {
44987
+ if (error2.code === "EEXIST" && existsSync40(path)) {
45174
44988
  const saved = JSON.parse(readFileSync31(path, "utf8"));
45175
44989
  if (saved.central === invite.central && saved.token === invite.token)
45176
44990
  return saved;
@@ -45181,8 +44995,8 @@ function loadOrCreatePending(invite, nodeName) {
45181
44995
  }
45182
44996
  function replacePendingEnrollment(pending2) {
45183
44997
  const path = pendingEnrollmentPath();
45184
- const directory = dirname24(path);
45185
- const temporaryPath = resolve26(directory, `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
44998
+ const directory = dirname25(path);
44999
+ const temporaryPath = resolve27(directory, `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
45186
45000
  let fd;
45187
45001
  try {
45188
45002
  fd = openSync6(temporaryPath, "wx", 384);
@@ -45201,18 +45015,18 @@ function replacePendingEnrollment(pending2) {
45201
45015
  } catch (error2) {
45202
45016
  if (fd !== undefined)
45203
45017
  closeSync6(fd);
45204
- if (existsSync39(temporaryPath))
45018
+ if (existsSync40(temporaryPath))
45205
45019
  unlinkSync24(temporaryPath);
45206
45020
  throw error2;
45207
45021
  }
45208
45022
  }
45209
- function recordClaimedCredential(pending2, join44) {
45023
+ function recordClaimedCredential(pending2, join45) {
45210
45024
  withJoinCredentialLock(() => {
45211
45025
  const current3 = JSON.parse(readFileSync31(pendingEnrollmentPath(), "utf8"));
45212
45026
  if (current3.central !== pending2.central || current3.token !== pending2.token || current3.idempotencyKey !== pending2.idempotencyKey || current3.publicKey !== pending2.publicKey) {
45213
45027
  throw new Error("pending Otium enrollment changed while its claim was in flight");
45214
45028
  }
45215
- const claimed = { digest: joinCredentialDigest(join44), cellId: join44.cellId };
45029
+ const claimed = { digest: joinCredentialDigest(join45), cellId: join45.cellId };
45216
45030
  if (current3.claimed && (current3.claimed.digest !== claimed.digest || current3.claimed.cellId !== claimed.cellId)) {
45217
45031
  throw new Error("central returned different credentials for an idempotent enrollment claim");
45218
45032
  }
@@ -45270,36 +45084,36 @@ async function claimEnrollment(invite, nodeName) {
45270
45084
  const secret = openCredential(credential, pending2.privateKey);
45271
45085
  if (!secret.startsWith("rcs_"))
45272
45086
  throw new Error("central returned an invalid runtime credential");
45273
- const join44 = {
45087
+ const join45 = {
45274
45088
  v: 2,
45275
45089
  central: invite.central,
45276
45090
  relay: String(response.relayUrl),
45277
45091
  cellId: String(response.cell?.id),
45278
45092
  secret
45279
45093
  };
45280
- if (!join44.relay || !join44.cellId || join44.cellId === "undefined") {
45094
+ if (!join45.relay || !join45.cellId || join45.cellId === "undefined") {
45281
45095
  throw new Error("central returned an incomplete enrollment response");
45282
45096
  }
45283
- assertSecureRelayUrl(join44.relay);
45284
- recordClaimedCredential(pending2, join44);
45285
- return join44;
45097
+ assertSecureRelayUrl(join45.relay);
45098
+ recordClaimedCredential(pending2, join45);
45099
+ return join45;
45286
45100
  }
45287
- function commitEnrollment(join44, options = {}) {
45101
+ function commitEnrollment(join45, options = {}) {
45288
45102
  return withJoinCredentialLock(() => {
45289
45103
  const pendingPath = pendingEnrollmentPath();
45290
- const pending2 = existsSync39(pendingPath) ? JSON.parse(readFileSync31(pendingPath, "utf8")) : null;
45291
- const digest = joinCredentialDigest(join44);
45292
- if (pending2 && (!pending2.claimed || pending2.claimed.digest !== digest || pending2.claimed.cellId !== join44.cellId)) {
45104
+ const pending2 = existsSync40(pendingPath) ? JSON.parse(readFileSync31(pendingPath, "utf8")) : null;
45105
+ const digest = joinCredentialDigest(join45);
45106
+ if (pending2 && (!pending2.claimed || pending2.claimed.digest !== digest || pending2.claimed.cellId !== join45.cellId)) {
45293
45107
  throw new Error(`pending Otium enrollment at ${pendingPath} does not match these credentials`);
45294
45108
  }
45295
- const path = saveJoinWhileLocked(join44, options);
45296
- if (!isJoinPersisted(join44)) {
45109
+ const path = saveJoinWhileLocked(join45, options);
45110
+ if (!isJoinPersisted(join45)) {
45297
45111
  throw new Error("Otium join credentials were not durably persisted");
45298
45112
  }
45299
45113
  if (!pending2)
45300
45114
  return path;
45301
45115
  unlinkSync24(pendingPath);
45302
- const directoryFd = openSync6(dirname24(pendingPath), "r");
45116
+ const directoryFd = openSync6(dirname25(pendingPath), "r");
45303
45117
  try {
45304
45118
  fsyncSync3(directoryFd);
45305
45119
  } finally {
@@ -45887,24 +45701,24 @@ function refreshDefaultSurfaceScope() {
45887
45701
  setSurfaceScopeRequired(scopes.length > 1);
45888
45702
  }
45889
45703
  function startOtiumNodeRuntime(options) {
45890
- const { join: join44 } = options;
45891
- attachOtiumCentralCell(join44);
45704
+ const { join: join45 } = options;
45705
+ attachOtiumCentralCell(join45);
45892
45706
  const releaseGlobals = acquireGlobalOtiumServices();
45893
45707
  let stopped = false;
45894
- logger.info({ central: join44.central, cellId: join44.cellId }, "otium: worker mode enabled");
45895
- mountedScopes.set(join44.cellId, cachedSurfaceScope(join44));
45708
+ logger.info({ central: join45.central, cellId: join45.cellId }, "otium: worker mode enabled");
45709
+ mountedScopes.set(join45.cellId, cachedSurfaceScope(join45));
45896
45710
  refreshDefaultSurfaceScope();
45897
- selfPeerNodeForCell(join44.cellId).then((self) => {
45711
+ selfPeerNodeForCell(join45.cellId).then((self) => {
45898
45712
  if (self) {
45899
45713
  logger.info({ nodeName: self.nodeName, baseUrl: self.baseUrl }, "otium: attached to workspace");
45900
45714
  }
45901
45715
  }).catch((err2) => {
45902
45716
  logger.warn({ err: err2 }, "otium: self check against central failed (will retry per request)");
45903
45717
  });
45904
- resolveSurfaceScope(join44).then((scope) => {
45718
+ resolveSurfaceScope(join45).then((scope) => {
45905
45719
  if (!scope || stopped)
45906
45720
  return;
45907
- mountedScopes.set(join44.cellId, scope);
45721
+ mountedScopes.set(join45.cellId, scope);
45908
45722
  refreshDefaultSurfaceScope();
45909
45723
  if (mountedScopes.size === 1)
45910
45724
  stampUnscopedOtiumTopics(scope);
@@ -45913,14 +45727,14 @@ function startOtiumNodeRuntime(options) {
45913
45727
  });
45914
45728
  return {
45915
45729
  name: "otium",
45916
- join: join44,
45730
+ join: join45,
45917
45731
  stop: () => {
45918
45732
  if (stopped)
45919
45733
  return;
45920
45734
  stopped = true;
45921
- mountedScopes.delete(join44.cellId);
45735
+ mountedScopes.delete(join45.cellId);
45922
45736
  refreshDefaultSurfaceScope();
45923
- detachOtiumCentralCell(join44.cellId);
45737
+ detachOtiumCentralCell(join45.cellId);
45924
45738
  releaseGlobals();
45925
45739
  }
45926
45740
  };
@@ -46015,14 +45829,14 @@ __export(exports_node_runtime, {
46015
45829
  function sameCredentials(left, right) {
46016
45830
  return left.central === right.central && left.relay === right.relay && left.secret === right.secret;
46017
45831
  }
46018
- function attachOtiumWorkspace(join44) {
46019
- const current3 = mounted.get(join44.cellId);
45832
+ function attachOtiumWorkspace(join45) {
45833
+ const current3 = mounted.get(join45.cellId);
46020
45834
  if (current3) {
46021
- if (sameCredentials(current3.join, join44))
45835
+ if (sameCredentials(current3.join, join45))
46022
45836
  return false;
46023
- detachOtiumWorkspace(join44.cellId);
45837
+ detachOtiumWorkspace(join45.cellId);
46024
45838
  }
46025
- mounted.set(join44.cellId, startOtiumNodeRuntime({ join: join44 }));
45839
+ mounted.set(join45.cellId, startOtiumNodeRuntime({ join: join45 }));
46026
45840
  return true;
46027
45841
  }
46028
45842
  function detachOtiumWorkspace(cellId) {
@@ -46037,7 +45851,7 @@ function mountedOtiumWorkspaces() {
46037
45851
  return [...mounted.values()].map((runtime2) => runtime2.join);
46038
45852
  }
46039
45853
  function reconcileOtiumWorkspaces(joins = loadJoins()) {
46040
- const wanted = new Map(joins.map((join44) => [join44.cellId, join44]));
45854
+ const wanted = new Map(joins.map((join45) => [join45.cellId, join45]));
46041
45855
  const detached = [];
46042
45856
  for (const cellId of [...mounted.keys()]) {
46043
45857
  if (wanted.has(cellId))
@@ -46046,8 +45860,8 @@ function reconcileOtiumWorkspaces(joins = loadJoins()) {
46046
45860
  detached.push(cellId);
46047
45861
  }
46048
45862
  const attached = [];
46049
- for (const [cellId, join44] of wanted) {
46050
- if (attachOtiumWorkspace(join44))
45863
+ for (const [cellId, join45] of wanted) {
45864
+ if (attachOtiumWorkspace(join45))
46051
45865
  attached.push(cellId);
46052
45866
  }
46053
45867
  if (attached.length > 0 || detached.length > 0) {
@@ -46070,9 +45884,9 @@ async function handleOtiumAdapterControlRequest(req) {
46070
45884
  if (req.method === "GET") {
46071
45885
  return Response.json({
46072
45886
  ok: true,
46073
- workspaces: mountedOtiumWorkspaces().map((join44) => ({
46074
- cellId: join44.cellId,
46075
- central: join44.central
45887
+ workspaces: mountedOtiumWorkspaces().map((join45) => ({
45888
+ cellId: join45.cellId,
45889
+ central: join45.central
46076
45890
  }))
46077
45891
  });
46078
45892
  }
@@ -46093,8 +45907,8 @@ function mountConfiguredOtiumNodeRuntime() {
46093
45907
  const joins = loadJoins();
46094
45908
  if (joins.length === 0)
46095
45909
  return null;
46096
- for (const join44 of joins)
46097
- attachOtiumWorkspace(join44);
45910
+ for (const join45 of joins)
45911
+ attachOtiumWorkspace(join45);
46098
45912
  registerNodeRequestHandler("otium-adapter-control", handleOtiumAdapterControlRequest);
46099
45913
  let stopped = false;
46100
45914
  return {
@@ -46206,11 +46020,11 @@ async function joinCommand(args) {
46206
46020
  process.exitCode = 1;
46207
46021
  return;
46208
46022
  }
46209
- let join44;
46023
+ let join45;
46210
46024
  let productionEnrollment = false;
46211
46025
  if (args.includes("--legacy")) {
46212
46026
  try {
46213
- join44 = parseInviteCode(code);
46027
+ join45 = parseInviteCode(code);
46214
46028
  } catch (err2) {
46215
46029
  console.error(`invalid legacy invite code: ${err2 instanceof Error ? err2.message : err2}`);
46216
46030
  process.exitCode = 1;
@@ -46236,7 +46050,7 @@ async function joinCommand(args) {
46236
46050
  return;
46237
46051
  }
46238
46052
  }
46239
- join44 = await claimEnrollment(invite, nodeName);
46053
+ join45 = await claimEnrollment(invite, nodeName);
46240
46054
  productionEnrollment = true;
46241
46055
  } catch (err2) {
46242
46056
  console.error(`enrollment failed: ${err2 instanceof Error ? err2.message : err2}`);
@@ -46247,16 +46061,16 @@ async function joinCommand(args) {
46247
46061
  let path;
46248
46062
  try {
46249
46063
  const saveOptions = { replaceExisting: args.includes("--replace") };
46250
- path = productionEnrollment ? commitEnrollment(join44, saveOptions) : saveJoin(join44, saveOptions);
46064
+ path = productionEnrollment ? commitEnrollment(join45, saveOptions) : saveJoin(join45, saveOptions);
46251
46065
  } catch (err2) {
46252
46066
  console.error(`could not save join credentials: ${err2 instanceof Error ? err2.message : err2}`);
46253
46067
  process.exitCode = 1;
46254
46068
  return;
46255
46069
  }
46256
46070
  console.log(`otium join credentials saved to ${path}`);
46257
- console.log(` central: ${join44.central}`);
46258
- console.log(` cellId: ${join44.cellId}`);
46259
- configureOtiumCentral(join44);
46071
+ console.log(` central: ${join45.central}`);
46072
+ console.log(` cellId: ${join45.cellId}`);
46073
+ configureOtiumCentral(join45);
46260
46074
  try {
46261
46075
  const self = await selfPeerNode();
46262
46076
  if (self) {
@@ -46284,6 +46098,50 @@ var init_join_cli = __esm(async () => {
46284
46098
  await init_join();
46285
46099
  });
46286
46100
 
46101
+ // ../../adapters/otium/src/status-cli.ts
46102
+ var exports_status_cli = {};
46103
+ __export(exports_status_cli, {
46104
+ statusCommand: () => statusCommand
46105
+ });
46106
+ async function statusCommand() {
46107
+ try {
46108
+ const joins = loadJoins();
46109
+ if (joins.length === 0) {
46110
+ console.log("not joined to any Otium workspace");
46111
+ process.exitCode = 1;
46112
+ return;
46113
+ }
46114
+ configureOtiumCentral(joins[0] ?? null);
46115
+ for (const join45 of joins.slice(1))
46116
+ attachOtiumCentralCell(join45);
46117
+ for (const [index, join45] of joins.entries()) {
46118
+ if (index > 0)
46119
+ console.log("");
46120
+ console.log(`cellId: ${join45.cellId}`);
46121
+ console.log(`central: ${join45.central}`);
46122
+ if (join45.relay)
46123
+ console.log(`relay: ${join45.relay}`);
46124
+ try {
46125
+ const self = await selfPeerNodeForCell(join45.cellId);
46126
+ if (self) {
46127
+ console.log(`node: ${self.nodeName ?? join45.cellId}${self.isPrimary ? " (primary)" : ""}`);
46128
+ console.log(`baseUrl: ${self.baseUrl}`);
46129
+ } else {
46130
+ console.warn(" warning: central answered but this cell has no visible assignment yet \u2014 check the workspace assignment");
46131
+ }
46132
+ } catch (err2) {
46133
+ console.warn(` warning: could not verify against central (${err2 instanceof Error ? err2.message : err2}) \u2014 showing locally known info only`);
46134
+ }
46135
+ }
46136
+ } finally {
46137
+ configureOtiumCentral(null);
46138
+ }
46139
+ }
46140
+ var init_status_cli = __esm(async () => {
46141
+ await init_central();
46142
+ await init_join();
46143
+ });
46144
+
46287
46145
  // ../../adapters/otium/src/sidecar.ts
46288
46146
  var exports_sidecar = {};
46289
46147
  __export(exports_sidecar, {
@@ -46302,13 +46160,13 @@ function resolveTunnelTargets(joins, relayOverride) {
46302
46160
  const targets = [];
46303
46161
  const skippedNoRelay = [];
46304
46162
  const envRelay = process.env.OTIUM_RELAY_URL?.trim();
46305
- for (const join44 of joins) {
46306
- const relayUrl = relayOverride?.trim() || join44.relay || envRelay;
46163
+ for (const join45 of joins) {
46164
+ const relayUrl = relayOverride?.trim() || join45.relay || envRelay;
46307
46165
  if (!relayUrl) {
46308
- skippedNoRelay.push(join44.cellId);
46166
+ skippedNoRelay.push(join45.cellId);
46309
46167
  continue;
46310
46168
  }
46311
- targets.push({ cellId: join44.cellId, relayUrl, secret: join44.secret });
46169
+ targets.push({ cellId: join45.cellId, relayUrl, secret: join45.secret });
46312
46170
  }
46313
46171
  return { targets, skippedNoRelay };
46314
46172
  }
@@ -46395,8 +46253,8 @@ async function runOtiumSidecar(options) {
46395
46253
  for (const tunnel of tunnels.values())
46396
46254
  tunnel.start();
46397
46255
  let resolveCompleted;
46398
- const completed = new Promise((resolve27) => {
46399
- resolveCompleted = resolve27;
46256
+ const completed = new Promise((resolve28) => {
46257
+ resolveCompleted = resolve28;
46400
46258
  });
46401
46259
  onShutdown("otium-sidecar-server", 130, () => server?.stop(true));
46402
46260
  onShutdown("otium-sidecar-tunnel", 120, () => {
@@ -46524,6 +46382,14 @@ async function runOtiumCli(args = process.argv.slice(2)) {
46524
46382
  await joinCommand2(commandArgs);
46525
46383
  break;
46526
46384
  }
46385
+ case "status": {
46386
+ if (commandArgs.length > 0) {
46387
+ throw new Error("usage: negotium otium status");
46388
+ }
46389
+ const { statusCommand: statusCommand2 } = await init_status_cli().then(() => exports_status_cli);
46390
+ await statusCommand2();
46391
+ break;
46392
+ }
46527
46393
  case "leave": {
46528
46394
  const targetCellId = commandArgs[0]?.trim();
46529
46395
  if (commandArgs.length > 1 || targetCellId?.startsWith("-")) {
@@ -46537,9 +46403,9 @@ async function runOtiumCli(args = process.argv.slice(2)) {
46537
46403
  if (joins.length === 0)
46538
46404
  throw new Error("not joined to an Otium workspace");
46539
46405
  if (!targetCellId && joins.length > 1) {
46540
- throw new Error(`this node is joined to ${joins.length} workspaces; name one to leave: ${joins.map((join44) => join44.cellId).join(", ")}`);
46406
+ throw new Error(`this node is joined to ${joins.length} workspaces; name one to leave: ${joins.map((join45) => join45.cellId).join(", ")}`);
46541
46407
  }
46542
- if (targetCellId && !joins.some((join44) => join44.cellId === targetCellId)) {
46408
+ if (targetCellId && !joins.some((join45) => join45.cellId === targetCellId)) {
46543
46409
  throw new Error(`not joined as ${targetCellId}`);
46544
46410
  }
46545
46411
  removeJoin2(targetCellId);
@@ -46562,10 +46428,11 @@ async function runOtiumCli(args = process.argv.slice(2)) {
46562
46428
  console.log([
46563
46429
  "negotium otium \u2014 attach a Negotium node to an Otium workspace",
46564
46430
  "",
46565
- "usage: negotium otium <join|leave|serve> [args]",
46431
+ "usage: negotium otium <join|leave|status|serve> [args]",
46566
46432
  "",
46567
46433
  " join <code> store credentials from an Otium invite code",
46568
46434
  " leave remove the stored workspace credentials",
46435
+ " status show which workspace(s)/central this node is joined to",
46569
46436
  " serve [--port <port>] [--relay <url>]",
46570
46437
  " run peer routes and an outbound relay tunnel",
46571
46438
  "",
@@ -47126,7 +46993,7 @@ var CLI_COMMANDS = [
47126
46993
  },
47127
46994
  {
47128
46995
  name: "otium",
47129
- usage: "otium join|leave|serve",
46996
+ usage: "otium join|leave|status|serve",
47130
46997
  description: "manage the Otium workspace connection",
47131
46998
  group: "Channels"
47132
46999
  }
@@ -47191,7 +47058,7 @@ async function runCanonicalNode(port) {
47191
47058
  });
47192
47059
  console.log(`negotium node listening on 127.0.0.1:${node.port} (ctrl-c to stop)`);
47193
47060
  await node.completed;
47194
- await new Promise((resolve27) => setImmediate(resolve27));
47061
+ await new Promise((resolve28) => setImmediate(resolve28));
47195
47062
  process.exit(0);
47196
47063
  }
47197
47064
  async function stopAdapter(name) {
@@ -47325,4 +47192,4 @@ switch (command) {
47325
47192
  }
47326
47193
  }
47327
47194
 
47328
- //# debugId=28B32B58D5A6FD9664756E2164756E21
47195
+ //# debugId=8E2AD65C6830FCEF64756E2164756E21