negotium 0.3.14 → 0.4.1

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 +333 -187
  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 +214 -65
  7. package/dist/hosted-agent.js.map +8 -7
  8. package/dist/main.js +712 -895
  9. package/dist/main.js.map +10 -11
  10. package/dist/mcp-factories.js +305 -470
  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 +40 -9
  16. package/dist/runtime/src/agents/codex-vault-hook-bridge.ts +198 -0
  17. package/dist/runtime/src/agents/codex-vault-hook.mjs +50 -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 +28 -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 +2 -2
  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.14";
1872
+ var NEGOTIUM_VERSION = "0.4.1";
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, socketPath, token) {
5287
+ return [
5288
+ "#!/bin/sh",
5289
+ `export ${HOOK_SOCKET_ENV}=${shellQuote(socketPath)}`,
5290
+ `export ${HOOK_TOKEN_ENV}=${shellQuote(token)}`,
5291
+ 'if [ "$1" = "exec" ]; then',
5292
+ " shift",
5293
+ ` exec ${shellQuote(process.execPath)} ${shellQuote(codexScript)} exec --dangerously-bypass-hook-trust "$@"`,
5294
+ "fi",
5295
+ `exec ${shellQuote(process.execPath)} ${shellQuote(codexScript)} "$@"`,
5296
+ ""
5297
+ ].join(`
5298
+ `);
5299
+ }
5300
+ async function createCodexVaultHookBridge(userId) {
5301
+ const root = await mkdtemp(join13(tmpdir2(), "negotium-codex-vault-"));
5302
+ await chmod(root, 448);
5303
+ const socketPath = join13(root, "hook.sock");
5304
+ const wrapperPath = join13(root, "codex-with-hooks");
5305
+ const token = randomBytes5(32).toString("hex");
5306
+ const connections = new Set;
5307
+ const server = createServer((socket) => {
5308
+ connections.add(socket);
5309
+ socket.once("close", () => connections.delete(socket));
5310
+ socket.setEncoding("utf8");
5311
+ let request = "";
5312
+ let handled = false;
5313
+ const handleRequest = () => {
5314
+ if (handled)
5315
+ return;
5316
+ handled = true;
5317
+ try {
5318
+ const parsed = JSON.parse(request.trimEnd());
5319
+ if (parsed.token !== token)
5320
+ throw new Error("invalid hook capability");
5321
+ if (!parsed.input || typeof parsed.input !== "object" || typeof parsed.input.tool_name !== "string") {
5322
+ throw new Error("invalid PreToolUse payload");
5323
+ }
5324
+ const output = evaluateCodexVaultPreToolUse(parsed.input, userId, {
5325
+ referencesSensitiveStorage: referencesHostedSecretStorage,
5326
+ substitute: substituteHostedSecrets
5327
+ });
5328
+ socket.end(JSON.stringify({ ok: true, output }));
5329
+ } catch (error) {
5330
+ const message = error instanceof Error ? error.message : String(error);
5331
+ socket.end(JSON.stringify({ ok: false, error: message }));
5332
+ }
5333
+ };
5334
+ socket.on("data", (chunk) => {
5335
+ request += chunk;
5336
+ if (Buffer.byteLength(request) > MAX_HOOK_REQUEST_BYTES)
5337
+ socket.destroy();
5338
+ else if (request.endsWith(`
5339
+ `))
5340
+ handleRequest();
5341
+ });
5342
+ socket.on("end", handleRequest);
5343
+ });
5344
+ try {
5345
+ await new Promise((resolveListen, reject) => {
5346
+ server.once("error", reject);
5347
+ server.listen(socketPath, () => {
5348
+ server.off("error", reject);
5349
+ resolveListen();
5350
+ });
5351
+ });
5352
+ await chmod(socketPath, 384);
5353
+ await writeFile(wrapperPath, privateCodexWrapper(codexCliScriptPath(), socketPath, token), {
5354
+ mode: 448
5355
+ });
5356
+ const command = [shellQuote(process.execPath), shellQuote(hookClientPath())].join(" ");
5357
+ return {
5358
+ codexPathOverride: wrapperPath,
5359
+ environment: { [HOOK_SOCKET_ENV]: socketPath, [HOOK_TOKEN_ENV]: token },
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", HOOK_SOCKET_ENV = "NEGOTIUM_CODEX_VAULT_HOOK_SOCKET", HOOK_TOKEN_ENV = "NEGOTIUM_CODEX_VAULT_HOOK_TOKEN";
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") {
@@ -6352,7 +6499,10 @@ async function* codexProvider(opts) {
6352
6499
  yield fileEvent;
6353
6500
  }
6354
6501
  } else if (item.type === "error") {
6355
- yield { type: "error", content: String(item.message ?? "") };
6502
+ const message = String(item.message ?? "");
6503
+ if (message !== CODEX_HOOK_TRUST_BYPASS_NOTICE) {
6504
+ yield { type: "error", content: message };
6505
+ }
6356
6506
  }
6357
6507
  break;
6358
6508
  }
@@ -6440,12 +6590,14 @@ async function* codexProvider(opts) {
6440
6590
  abortSignal?.removeEventListener("abort", onAbortKill);
6441
6591
  if (trackedPids.pids.length > 0)
6442
6592
  unregisterOwnedCodexPids(trackedPids.pids);
6593
+ await vaultHook.close();
6443
6594
  }
6444
6595
  }
6445
- 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;
6596
+ var CODEX_HOOK_TRUST_BYPASS_NOTICE = "`--dangerously-bypass-hook-trust` is enabled. Enabled hooks may run without review for this invocation.", 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
6597
  var init_codex_provider = __esm(async () => {
6447
6598
  init_codex_native_multi_agent();
6448
6599
  init_codex_tree_kill();
6600
+ await init_codex_vault_hook_bridge();
6449
6601
  await init_execution_host();
6450
6602
  await init_codex();
6451
6603
  init_tool_format();
@@ -6468,7 +6620,7 @@ __export(exports_maestro_provider, {
6468
6620
  buildMaestroToolHooks: () => buildMaestroToolHooks,
6469
6621
  buildMaestroDisallowedTools: () => buildMaestroDisallowedTools
6470
6622
  });
6471
- import { resolve as resolve9 } from "path";
6623
+ import { resolve as resolve10 } from "path";
6472
6624
  import { maestroProvider as sdkMaestroProvider, setMcpResolver } from "maestro-agent-sdk";
6473
6625
  function buildMaestroDisallowedTools(callerDisallowedTools = [], toolPolicy) {
6474
6626
  return [
@@ -6602,13 +6754,13 @@ var init_maestro_provider = __esm(async () => {
6602
6754
  "ToolSearch",
6603
6755
  ...DEFAULT_MAESTRO_DISALLOWED_TOOLS
6604
6756
  ];
6605
- MAESTRO_TOOL_OUTPUT_DIR = resolve9(RUN_DIR, "maestro-tool-outputs");
6757
+ MAESTRO_TOOL_OUTPUT_DIR = resolve10(RUN_DIR, "maestro-tool-outputs");
6606
6758
  });
6607
6759
 
6608
6760
  // ../../packages/core/src/agents/index.ts
6609
- import { existsSync as existsSync14 } from "fs";
6761
+ import { existsSync as existsSync15 } from "fs";
6610
6762
  import { homedir as homedir8 } from "os";
6611
- import { join as join14 } from "path";
6763
+ import { join as join15 } from "path";
6612
6764
  async function* dispatchAgent(opts) {
6613
6765
  switch (opts.agent) {
6614
6766
  case "claude": {
@@ -6642,11 +6794,11 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
6642
6794
  switch (agent) {
6643
6795
  case "claude": {
6644
6796
  const encodedCwd = encodeClaudeCwd(cwd);
6645
- const path = join14(homedir8(), ".claude", "projects", encodedCwd, `${sessionId}.jsonl`);
6646
- return !existsSync14(path);
6797
+ const path = join15(homedir8(), ".claude", "projects", encodedCwd, `${sessionId}.jsonl`);
6798
+ return !existsSync15(path);
6647
6799
  }
6648
6800
  case "codex": {
6649
- const sessionsDir = join14(hostedCodexHomePath(), "sessions");
6801
+ const sessionsDir = join15(hostedCodexHomePath(), "sessions");
6650
6802
  const glob = new Bun.Glob(`**/rollout-*-${sessionId}.jsonl`);
6651
6803
  for await (const _rel of glob.scan({ cwd: sessionsDir, onlyFiles: true })) {
6652
6804
  return false;
@@ -6655,7 +6807,7 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
6655
6807
  }
6656
6808
  case "maestro": {
6657
6809
  const { hasActiveMaestroSession, maestroSessionPath: maestroSessionPath2 } = await import("maestro-agent-sdk");
6658
- return !existsSync14(maestroSessionPath2(sessionId)) && !hasActiveMaestroSession(sessionId);
6810
+ return !existsSync15(maestroSessionPath2(sessionId)) && !hasActiveMaestroSession(sessionId);
6659
6811
  }
6660
6812
  default: {
6661
6813
  const _exhaustive = agent;
@@ -8874,7 +9026,7 @@ async function waitForRuntimeProcessLease(role, options = {}) {
8874
9026
  const remaining = deadline - Date.now();
8875
9027
  if (remaining <= 0)
8876
9028
  return null;
8877
- await new Promise((resolve10) => setTimeout(resolve10, Math.min(interval, remaining)));
9029
+ await new Promise((resolve11) => setTimeout(resolve11, Math.min(interval, remaining)));
8878
9030
  }
8879
9031
  }
8880
9032
  async function waitForRequiredRuntimeProcessLease(role, options = {}) {
@@ -9151,8 +9303,8 @@ function createAskUserRuntime(host) {
9151
9303
  return errorResult(`Error: failed to persist ask_user_question: ${error instanceof Error ? error.message : String(error)}`);
9152
9304
  }
9153
9305
  let resolveAnswer;
9154
- const promise = new Promise((resolve10) => {
9155
- resolveAnswer = resolve10;
9306
+ const promise = new Promise((resolve11) => {
9307
+ resolveAnswer = resolve11;
9156
9308
  });
9157
9309
  pendingAsks.set(message.id, {
9158
9310
  topicId: ctx.topicId,
@@ -9808,11 +9960,11 @@ var init_manager_utils = () => {};
9808
9960
  // ../../packages/core/src/platform/playwright/browser-processes.ts
9809
9961
  import { execFileSync as execFileSync6 } from "child_process";
9810
9962
  import { readdirSync as readdirSync2, unlinkSync as unlinkSync10 } from "fs";
9811
- import { createServer } from "net";
9812
- import { resolve as resolve10, sep } from "path";
9963
+ import { createServer as createServer2 } from "net";
9964
+ import { resolve as resolve11, sep } from "path";
9813
9965
  function isPortInUse(port) {
9814
9966
  return new Promise((resolveProbe) => {
9815
- const server = createServer();
9967
+ const server = createServer2();
9816
9968
  let settled = false;
9817
9969
  const finish = (occupied) => {
9818
9970
  if (settled)
@@ -9842,8 +9994,8 @@ async function reserveAvailableLoopbackPort(minPort, maxPort, reservedPorts, pro
9842
9994
  return null;
9843
9995
  }
9844
9996
  function killBrowserProcsForUserDataDir(userDataDir) {
9845
- const target = resolve10(userDataDir);
9846
- const profileRoot = resolve10(BROWSER_PROFILES_DIR);
9997
+ const target = resolve11(userDataDir);
9998
+ const profileRoot = resolve11(BROWSER_PROFILES_DIR);
9847
9999
  if (target !== profileRoot && !target.startsWith(`${profileRoot}${sep}`))
9848
10000
  return;
9849
10001
  let pids;
@@ -9864,7 +10016,7 @@ function killBrowserProcsForUserDataDir(userDataDir) {
9864
10016
  stdio: "pipe"
9865
10017
  }).toString().trim();
9866
10018
  const argDir = extractUserDataDirArg(cmdline);
9867
- if (!argDir || resolve10(argDir) !== target)
10019
+ if (!argDir || resolve11(argDir) !== target)
9868
10020
  continue;
9869
10021
  killProcessTreeChildren(pidNum);
9870
10022
  process.kill(pidNum, "SIGKILL");
@@ -9875,13 +10027,13 @@ function killBrowserProcsForUserDataDir(userDataDir) {
9875
10027
  }
9876
10028
  }
9877
10029
  function selectOrphanBrowserPids(procs, liveUserDataDirs, profileRoot, selfPid) {
9878
- const root = resolve10(profileRoot);
9879
- const live = new Set([...liveUserDataDirs].map((d) => resolve10(d)));
10030
+ const root = resolve11(profileRoot);
10031
+ const live = new Set([...liveUserDataDirs].map((d) => resolve11(d)));
9880
10032
  const out = [];
9881
10033
  for (const { pid, userDataDir } of procs) {
9882
10034
  if (pid === selfPid || !userDataDir)
9883
10035
  continue;
9884
- const dir = resolve10(userDataDir);
10036
+ const dir = resolve11(userDataDir);
9885
10037
  if (dir !== root && !dir.startsWith(`${root}${sep}`))
9886
10038
  continue;
9887
10039
  if (live.has(dir))
@@ -9897,7 +10049,7 @@ function reapOrphanBrowsers(liveUserDataDirs) {
9897
10049
  const daemonLease = getRuntimeProcessLease("node-daemon", Date.now(), Number.POSITIVE_INFINITY);
9898
10050
  if (!isBrowserJanitorOwner(daemonLease?.pid ?? null, process.pid))
9899
10051
  return;
9900
- const profileRoot = resolve10(BROWSER_PROFILES_DIR);
10052
+ const profileRoot = resolve11(BROWSER_PROFILES_DIR);
9901
10053
  let pids;
9902
10054
  try {
9903
10055
  pids = execFileSync6("pgrep", ["-f", "--", profileRoot], { stdio: "pipe" }).toString().trim();
@@ -9936,7 +10088,7 @@ function cleanSingletonFiles(userDataDir) {
9936
10088
  for (const f of files) {
9937
10089
  if (f.startsWith("Singleton")) {
9938
10090
  try {
9939
- unlinkSync10(resolve10(userDataDir, f));
10091
+ unlinkSync10(resolve11(userDataDir, f));
9940
10092
  logger.info({ file: f, userDataDir }, "Removed stale Singleton file");
9941
10093
  } catch (e) {
9942
10094
  logger.warn({ err: e, file: f }, "Failed to remove stale Chrome Singleton file");
@@ -9976,9 +10128,9 @@ var init_browser_processes = __esm(async () => {
9976
10128
 
9977
10129
  // ../../packages/core/src/platform/playwright/headed-launch.ts
9978
10130
  import { accessSync as accessSync2, constants as constants2 } from "fs";
9979
- import { delimiter, isAbsolute as isAbsolute3, resolve as resolve11 } from "path";
10131
+ import { delimiter, isAbsolute as isAbsolute3, resolve as resolve12 } from "path";
9980
10132
  function findExecutableOnPath(command, environment = process.env) {
9981
- const candidates = isAbsolute3(command) ? [command] : (environment.PATH ?? "").split(delimiter).filter(Boolean).map((directory) => resolve11(directory, command));
10133
+ const candidates = isAbsolute3(command) ? [command] : (environment.PATH ?? "").split(delimiter).filter(Boolean).map((directory) => resolve12(directory, command));
9982
10134
  for (const candidate of candidates) {
9983
10135
  try {
9984
10136
  accessSync2(candidate, constants2.X_OK);
@@ -10099,11 +10251,11 @@ var init_vault_public = __esm(async () => {
10099
10251
  });
10100
10252
 
10101
10253
  // ../../packages/core/src/platform/playwright/vault-broker.ts
10102
- import { randomBytes as randomBytes5, timingSafeEqual as timingSafeEqual2 } from "crypto";
10254
+ import { randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual2 } from "crypto";
10103
10255
  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";
10256
+ import { createServer as createServer3 } from "net";
10257
+ import { tmpdir as tmpdir3 } from "os";
10258
+ import { join as join16 } from "path";
10107
10259
  function deepMapStrings2(value, transform) {
10108
10260
  if (typeof value === "string")
10109
10261
  return transform(value);
@@ -10189,8 +10341,8 @@ function authorized(actual, expected) {
10189
10341
  return left.length === right.length && timingSafeEqual2(left, right);
10190
10342
  }
10191
10343
  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`);
10344
+ const token = randomBytes6(32).toString("hex");
10345
+ const socketPath = join16(process.platform === "win32" ? tmpdir3() : "/tmp", `negotium-browser-vault-${process.pid}-${randomBytes6(8).toString("hex")}.sock`);
10194
10346
  const retainedForms = new Map;
10195
10347
  const leases = new Map;
10196
10348
  const sockets = new Set;
@@ -10229,7 +10381,7 @@ async function createBrowserVaultBroker(userId) {
10229
10381
  return result.text;
10230
10382
  });
10231
10383
  const prepared = prepareInput(request.tool, substituted);
10232
- const lease = randomBytes5(24).toString("hex");
10384
+ const lease = randomBytes6(24).toString("hex");
10233
10385
  leases.set(lease, { boundary: prepared.boundary, createdAt: Date.now() });
10234
10386
  return {
10235
10387
  id,
@@ -10261,7 +10413,7 @@ async function createBrowserVaultBroker(userId) {
10261
10413
  }
10262
10414
  return { id, ok: false, error: "unknown operation" };
10263
10415
  };
10264
- const server = createServer2((socket) => {
10416
+ const server = createServer3((socket) => {
10265
10417
  sockets.add(socket);
10266
10418
  let pending = "";
10267
10419
  socket.setEncoding("utf8");
@@ -10446,24 +10598,24 @@ var init_browser_profiles = __esm(async () => {
10446
10598
 
10447
10599
  // ../../packages/core/src/platform/playwright/manager.ts
10448
10600
  import { execFileSync as execFileSync7, spawn as spawn5 } from "child_process";
10449
- import { createHash as createHash4, randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual3 } from "crypto";
10601
+ import { createHash as createHash4, randomBytes as randomBytes7, timingSafeEqual as timingSafeEqual3 } from "crypto";
10450
10602
  import {
10451
10603
  cpSync,
10452
- existsSync as existsSync15,
10604
+ existsSync as existsSync16,
10453
10605
  mkdirSync as mkdirSync10,
10454
10606
  readFileSync as readFileSync12,
10455
10607
  rmSync as rmSync2,
10456
10608
  unlinkSync as unlinkSync11,
10457
10609
  writeFileSync as writeFileSync8
10458
10610
  } from "fs";
10459
- import { dirname as dirname10, join as join16, resolve as resolve12 } from "path";
10611
+ import { dirname as dirname11, join as join17, resolve as resolve13 } from "path";
10460
10612
  function removeDefaultProfileDataDir(userDataDir) {
10461
- const root = resolve12(BROWSER_PROFILES_DIR);
10462
- const target = resolve12(userDataDir);
10463
- if (dirname10(target) !== root) {
10613
+ const root = resolve13(BROWSER_PROFILES_DIR);
10614
+ const target = resolve13(userDataDir);
10615
+ if (dirname11(target) !== root) {
10464
10616
  throw new Error(`Refusing to delete browser profile outside managed root: ${target}`);
10465
10617
  }
10466
- const existed = existsSync15(target);
10618
+ const existed = existsSync16(target);
10467
10619
  rmSync2(target, { recursive: true, force: true });
10468
10620
  return existed;
10469
10621
  }
@@ -10546,14 +10698,14 @@ function portFileName(instanceKey) {
10546
10698
  function writePortFile(instanceKey, port) {
10547
10699
  try {
10548
10700
  mkdirSync10(managerHost.portsDir, { recursive: true });
10549
- writeFileSync8(join16(managerHost.portsDir, portFileName(instanceKey)), String(port));
10701
+ writeFileSync8(join17(managerHost.portsDir, portFileName(instanceKey)), String(port));
10550
10702
  } catch (e) {
10551
10703
  logger.warn({ err: e, instanceKey, port }, "Failed to save playwright port file");
10552
10704
  }
10553
10705
  }
10554
10706
  function deletePortFile(instanceKey) {
10555
10707
  try {
10556
- unlinkSync11(join16(managerHost.portsDir, portFileName(instanceKey)));
10708
+ unlinkSync11(join17(managerHost.portsDir, portFileName(instanceKey)));
10557
10709
  } catch (e) {
10558
10710
  if (e.code === "ENOENT")
10559
10711
  return;
@@ -10700,7 +10852,7 @@ function releasePort(port) {
10700
10852
  usedPorts.delete(port);
10701
10853
  }
10702
10854
  function defaultProfileDir(_ownerId, profile) {
10703
- return resolve12(BROWSER_PROFILES_DIR, profile);
10855
+ return resolve13(BROWSER_PROFILES_DIR, profile);
10704
10856
  }
10705
10857
  function resolveUserDataDir(instanceKey) {
10706
10858
  return managerHost.resolveInstanceDataDir(instanceKey);
@@ -10792,8 +10944,8 @@ async function spawnPlaywright(instanceKey, ownerId, reservedPort, browserBin =
10792
10944
  releasePort(port);
10793
10945
  throw new Error("Browser.rs managed mode does not support authenticated egress proxies");
10794
10946
  }
10795
- const capability = randomBytes6(32).toString("hex");
10796
- const spawnNonce = randomBytes6(32).toString("hex");
10947
+ const capability = randomBytes7(32).toString("hex");
10948
+ const spawnNonce = randomBytes7(32).toString("hex");
10797
10949
  let vaultBroker;
10798
10950
  try {
10799
10951
  vaultBroker = await createBrowserVaultBroker(ownerId);
@@ -11061,14 +11213,14 @@ async function cloneProfileForChild(opts) {
11061
11213
  killInstance(dstKey);
11062
11214
  await delay(500);
11063
11215
  }
11064
- if (!existsSync15(srcDir)) {
11216
+ if (!existsSync16(srcDir)) {
11065
11217
  return { copied: false, srcDir, dstDir, reason: "src-missing" };
11066
11218
  }
11067
11219
  try {
11068
- if (existsSync15(dstDir)) {
11220
+ if (existsSync16(dstDir)) {
11069
11221
  rmSync2(dstDir, { recursive: true, force: true });
11070
11222
  }
11071
- mkdirSync10(dirname10(dstDir), { recursive: true });
11223
+ mkdirSync10(dirname11(dstDir), { recursive: true });
11072
11224
  if (process.platform === "darwin") {
11073
11225
  try {
11074
11226
  execFileSync7("cp", ["-cR", srcDir, dstDir], { stdio: "pipe" });
@@ -11086,7 +11238,7 @@ async function cloneProfileForChild(opts) {
11086
11238
  cleanSingletonFiles(dstDir);
11087
11239
  for (const f of ["DevToolsActivePort", "LOCK"]) {
11088
11240
  try {
11089
- unlinkSync11(resolve12(dstDir, f));
11241
+ unlinkSync11(resolve13(dstDir, f));
11090
11242
  } catch {}
11091
11243
  }
11092
11244
  logger.info({ srcKey, dstKey, srcDir, dstDir }, "Cloned Playwright profile for child topic");
@@ -11589,9 +11741,9 @@ var init_personal_general = __esm(async () => {
11589
11741
 
11590
11742
  // ../../packages/core/src/prompts/builders.ts
11591
11743
  import { readFileSync as readFileSync13 } from "fs";
11592
- import { resolve as resolve13 } from "path";
11744
+ import { resolve as resolve14 } from "path";
11593
11745
  function loadPrompt(filename, dir = SESSIONS_DIR) {
11594
- const raw = readFileSync13(resolve13(dir, filename), "utf-8");
11746
+ const raw = readFileSync13(resolve14(dir, filename), "utf-8");
11595
11747
  return raw.replace(/\{\{RESOURCES_DIR\}\}/g, RESOURCES_DIR);
11596
11748
  }
11597
11749
  function replaceVars(template, vars) {
@@ -11640,7 +11792,7 @@ function visualDesignGuide() {
11640
11792
  return _visualDesignGuide;
11641
11793
  }
11642
11794
  function loadAgentPrompt(filename) {
11643
- const raw = readFileSync13(resolve13(AGENTS_PROMPTS_DIR, filename), "utf-8");
11795
+ const raw = readFileSync13(resolve14(AGENTS_PROMPTS_DIR, filename), "utf-8");
11644
11796
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
11645
11797
  if (!match)
11646
11798
  throw new Error(`Agent prompt ${filename} is missing frontmatter`);
@@ -11955,8 +12107,8 @@ var init_builders = __esm(() => {
11955
12107
  init_model_catalog();
11956
12108
  init_config();
11957
12109
  init_logger();
11958
- PROMPTS_DIR = resolve13(PROJECT_ROOT, "src/prompts");
11959
- SESSIONS_DIR = resolve13(PROMPTS_DIR, "sessions");
12110
+ PROMPTS_DIR = resolve14(PROJECT_ROOT, "src/prompts");
12111
+ SESSIONS_DIR = resolve14(PROMPTS_DIR, "sessions");
11960
12112
  defaultPromptBuilders = createPromptBuilders();
11961
12113
  buildTopicSystemPrompt = defaultPromptBuilders.buildTopicSystemPrompt;
11962
12114
  buildChannelSystemPrompt = defaultPromptBuilders.buildChannelSystemPrompt;
@@ -12098,50 +12250,50 @@ __export(exports_wiki, {
12098
12250
  getTopicMemoryFilePaths: () => getTopicMemoryFilePaths,
12099
12251
  getSharedWikiDir: () => getSharedWikiDir
12100
12252
  });
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";
12253
+ import { existsSync as existsSync17, readdirSync as readdirSync3, statSync as statSync6 } from "fs";
12254
+ import { basename as basename2, dirname as dirname12, join as join18 } from "path";
12103
12255
  function getWikiDir(_userId, workspaceDir = resolveStorageWorkspaceDir()) {
12104
- return join17(workspaceDir, "wiki");
12256
+ return join18(workspaceDir, "wiki");
12105
12257
  }
12106
12258
  function getSharedWikiDir(workspaceDir = resolveStorageWorkspaceDir()) {
12107
- return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join17(workspaceDir, "wiki");
12259
+ return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join18(workspaceDir, "wiki");
12108
12260
  }
12109
12261
  function findLatestSummaryFile(wikiDir, safeTopic) {
12110
- const summariesDir = join17(wikiDir, "summaries");
12111
- if (!existsSync16(summariesDir))
12262
+ const summariesDir = join18(wikiDir, "summaries");
12263
+ if (!existsSync17(summariesDir))
12112
12264
  return null;
12113
12265
  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;
12266
+ const mtimeDelta = statSync6(join18(summariesDir, right)).mtimeMs - statSync6(join18(summariesDir, left)).mtimeMs;
12115
12267
  return mtimeDelta || right.localeCompare(left, undefined, { numeric: true });
12116
12268
  });
12117
- return files.length > 0 ? join17(summariesDir, files[0]) : null;
12269
+ return files.length > 0 ? join18(summariesDir, files[0]) : null;
12118
12270
  }
12119
12271
  function getTopicMemoryFilePaths(_userId, topicName, forkOrigin, workspaceDir = resolveStorageWorkspaceDir()) {
12120
12272
  const wikiDir = getSharedWikiDir(workspaceDir);
12121
12273
  const resolveBrief = (name) => {
12122
12274
  const safe = sanitizeTopicName(name, true);
12123
- const brief = join17(wikiDir, "topic", `${safe}.md`);
12275
+ const brief = join18(wikiDir, "topic", `${safe}.md`);
12124
12276
  const latestSummary = findLatestSummaryFile(wikiDir, safe);
12125
12277
  return { brief, latestSummary };
12126
12278
  };
12127
- const archiveDir = join17(wikiDir, "archive");
12279
+ const archiveDir = join18(wikiDir, "archive");
12128
12280
  const archiveExistsFor = (name) => {
12129
12281
  const safe = sanitizeTopicName(name);
12130
- return existsSync16(archiveDir) && readdirSync3(archiveDir).some((f) => f.endsWith(".jsonl") && f.startsWith(`${safe}_`));
12282
+ return existsSync17(archiveDir) && readdirSync3(archiveDir).some((f) => f.endsWith(".jsonl") && f.startsWith(`${safe}_`));
12131
12283
  };
12132
12284
  const sourceTopic = forkOrigin ?? topicName;
12133
12285
  const hasArchive = archiveExistsFor(sourceTopic) || (forkOrigin ? archiveExistsFor(topicName) : false);
12134
12286
  const target = resolveBrief(sourceTopic);
12135
- if (existsSync16(target.brief)) {
12287
+ if (existsSync17(target.brief)) {
12136
12288
  return {
12137
- memoryDir: dirname11(target.brief),
12289
+ memoryDir: dirname12(target.brief),
12138
12290
  memoryFiles: [basename2(target.brief)],
12139
12291
  ...target.latestSummary ? { latestSummaryFile: target.latestSummary } : {},
12140
12292
  ...hasArchive ? { hasArchive: true } : {}
12141
12293
  };
12142
12294
  }
12143
12295
  return {
12144
- memoryDir: join17(wikiDir, "topic"),
12296
+ memoryDir: join18(wikiDir, "topic"),
12145
12297
  memoryFiles: [],
12146
12298
  ...target.latestSummary ? { latestSummaryFile: target.latestSummary } : {},
12147
12299
  ...hasArchive ? { hasArchive: true } : {}
@@ -12153,8 +12305,8 @@ var init_wiki = __esm(async () => {
12153
12305
 
12154
12306
  // ../../packages/core/src/agents/archiver.ts
12155
12307
  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";
12308
+ import { existsSync as existsSync18, readdirSync as readdirSync4, readFileSync as readFileSync14, statSync as statSync7 } from "fs";
12309
+ import { join as join19 } from "path";
12158
12310
  function resolveMemoryLanguage() {
12159
12311
  const override = process.env.NEGOTIUM_MEMORY_LANG?.trim();
12160
12312
  return override && override.length > 0 ? override : resolveOutputLanguage();
@@ -12405,10 +12557,10 @@ function distillOneLine(summaryMd) {
12405
12557
  return "";
12406
12558
  }
12407
12559
  function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
12408
- const dir = join18(storage.getWikiDir(), "summaries");
12560
+ const dir = join19(storage.getWikiDir(), "summaries");
12409
12561
  if (!storage.fileExists(dir))
12410
12562
  return null;
12411
- const predicted = join18(dir, wikiSummaryFilename(date, topicTitle, topicId));
12563
+ const predicted = join19(dir, wikiSummaryFilename(date, topicTitle, topicId));
12412
12564
  if (storage.fileExists(predicted) && storage.fileModifiedAt(predicted) >= sinceMs)
12413
12565
  return predicted;
12414
12566
  let best = null;
@@ -12416,7 +12568,7 @@ function findSummaryFile(storage, topicTitle, date, sinceMs, topicId) {
12416
12568
  if (!f.startsWith(`${date}-`) || !isTopicSummaryFile(f, topicId ?? "", topicTitle)) {
12417
12569
  continue;
12418
12570
  }
12419
- const p = join18(dir, f);
12571
+ const p = join19(dir, f);
12420
12572
  try {
12421
12573
  const m = storage.fileModifiedAt(p);
12422
12574
  if (m >= sinceMs && (!best || m > best.mtime))
@@ -12500,7 +12652,7 @@ var init_archiver = __esm(async () => {
12500
12652
  defaultArchiverRuntime = createArchiverRuntime({
12501
12653
  storage: {
12502
12654
  getWikiDir: getSharedWikiDir,
12503
- fileExists: existsSync17,
12655
+ fileExists: existsSync18,
12504
12656
  listDirectory: readdirSync4,
12505
12657
  readTextFile: (path) => readFileSync14(path, "utf-8"),
12506
12658
  fileSize: (path) => statSync7(path).size,
@@ -12632,13 +12784,13 @@ __export(exports_topic_archive, {
12632
12784
  archiveConversationEvents: () => archiveConversationEvents
12633
12785
  });
12634
12786
  import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync9 } from "fs";
12635
- import { join as join19 } from "path";
12787
+ import { join as join20 } from "path";
12636
12788
  function archiveTopicMessages(topicId, topicTitle, options = {}) {
12637
12789
  const rows = options.afterRowid !== undefined ? getMessagesForTopicAfterRowid(topicId, options.afterRowid) : getAllMessagesForTopic(topicId);
12638
12790
  if (rows.length === 0)
12639
12791
  return null;
12640
12792
  const safeTopic = sanitizeTopicName(topicTitle, true);
12641
- const archiveDir = join19(getSharedWikiDir(), "archive");
12793
+ const archiveDir = join20(getSharedWikiDir(), "archive");
12642
12794
  mkdirSync11(archiveDir, { recursive: true });
12643
12795
  const date = new Date().toISOString().slice(0, 10);
12644
12796
  const reasonSuffix = options.reason && options.reason !== "delete" ? `_${options.reason}` : "";
@@ -12651,7 +12803,7 @@ function archiveTopicMessages(topicId, topicTitle, options = {}) {
12651
12803
  let path;
12652
12804
  while (true) {
12653
12805
  filename = `${safeTopic}_${date}${reasonSuffix}${counter === 1 ? "" : `_${counter}`}.jsonl`;
12654
- path = join19(archiveDir, filename);
12806
+ path = join20(archiveDir, filename);
12655
12807
  try {
12656
12808
  writeFileSync9(path, body, { flag: "wx" });
12657
12809
  break;
@@ -12696,7 +12848,7 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
12696
12848
  const entries = readRawConversation(userId, topicTitle);
12697
12849
  if (entries.length === 0)
12698
12850
  return null;
12699
- const archiveDir = join19(getSharedWikiDir(), "archive");
12851
+ const archiveDir = join20(getSharedWikiDir(), "archive");
12700
12852
  mkdirSync11(archiveDir, { recursive: true });
12701
12853
  const safeTopic = sanitizeTopicName(topicTitle, true);
12702
12854
  const date = new Date().toISOString().slice(0, 10);
@@ -12724,7 +12876,7 @@ function archiveConversationEvents(topicId, topicTitle, userId, options = {}) {
12724
12876
  let counter = 1;
12725
12877
  while (true) {
12726
12878
  const suffix = counter === 1 ? "" : `_${counter}`;
12727
- const path = join19(archiveDir, `${safeTopic}_${date}${reasonSuffix}_events${suffix}.jsonl`);
12879
+ const path = join20(archiveDir, `${safeTopic}_${date}${reasonSuffix}_events${suffix}.jsonl`);
12728
12880
  try {
12729
12881
  writeFileSync9(path, body, { flag: "wx" });
12730
12882
  logger.info({ topicId, topicTitle, archive: path, eventCount: entries.length }, "archiveConversationEvents: archived raw conversation events");
@@ -13051,7 +13203,7 @@ var init_idle_archiver = __esm(async () => {
13051
13203
 
13052
13204
  // ../../packages/core/src/agents/topic-cleanup.ts
13053
13205
  import { mkdirSync as mkdirSync12, renameSync as renameSync6, unlinkSync as unlinkSync12, writeFileSync as writeFileSync10 } from "fs";
13054
- import { dirname as dirname12 } from "path";
13206
+ import { dirname as dirname13 } from "path";
13055
13207
  function collectSessionIdsByAgent(entries, extraSessions = []) {
13056
13208
  const out = new Map;
13057
13209
  for (const e of entries) {
@@ -13117,7 +13269,7 @@ function createTopicLogMaintenance(host) {
13117
13269
  const path = runtimeHost.activeConversationPath(opts.userId, opts.topicName);
13118
13270
  const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
13119
13271
  try {
13120
- mkdirSync12(dirname12(path), { recursive: true });
13272
+ mkdirSync12(dirname13(path), { recursive: true });
13121
13273
  writeFileSync10(tempPath, retained.length > 0 ? `${retained.map((entry) => JSON.stringify(entry)).join(`
13122
13274
  `)}
13123
13275
  ` : "", { flag: "wx" });
@@ -13734,15 +13886,15 @@ var init_runtime_turn_requests = __esm(async () => {
13734
13886
  // ../../packages/core/src/topics/session.ts
13735
13887
  import { randomUUID as randomUUID11 } from "crypto";
13736
13888
  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";
13889
+ import { tmpdir as tmpdir4 } from "os";
13890
+ import { join as join21 } from "path";
13739
13891
  async function waitForMemoryArchive(settled, timeoutMs) {
13740
13892
  let timer;
13741
13893
  try {
13742
13894
  return await Promise.race([
13743
13895
  settled.then(() => true),
13744
- new Promise((resolve14) => {
13745
- timer = setTimeout(() => resolve14(false), timeoutMs);
13896
+ new Promise((resolve15) => {
13897
+ timer = setTimeout(() => resolve15(false), timeoutMs);
13746
13898
  timer.unref?.();
13747
13899
  })
13748
13900
  ]);
@@ -13806,8 +13958,8 @@ async function restartTopicSession(topicId, userId, reason = "topic-session-rest
13806
13958
  };
13807
13959
  }
13808
13960
  let settleMemoryArchive;
13809
- const memoryArchiveSettled = new Promise((resolve14) => {
13810
- settleMemoryArchive = resolve14;
13961
+ const memoryArchiveSettled = new Promise((resolve15) => {
13962
+ settleMemoryArchive = resolve15;
13811
13963
  });
13812
13964
  const archiveStatus = (options.archiveMemory ?? archiveActiveTopicForMemory)(topicId, userId, {
13813
13965
  reason: "reset",
@@ -13988,7 +14140,7 @@ function formatCompactElapsed(startedAt) {
13988
14140
  async function summarizeTopicContext(request) {
13989
14141
  const startedAt = Date.now();
13990
14142
  const sessionIds = [];
13991
- const compactCwd = mkdtempSync2(join20(tmpdir3(), "negotium-compact-"));
14143
+ const compactCwd = mkdtempSync2(join21(tmpdir4(), "negotium-compact-"));
13992
14144
  const abortController = new AbortController;
13993
14145
  const relayAbort = () => abortController.abort(request.signal?.reason);
13994
14146
  if (request.signal?.aborted)
@@ -14015,7 +14167,7 @@ async function summarizeTopicContext(request) {
14015
14167
  let error = "";
14016
14168
  let toolViolation = false;
14017
14169
  let compactionLogCalls = 0;
14018
- const compactionLogPath = join20(compactCwd, "conversation.log");
14170
+ const compactionLogPath = join21(compactCwd, "conversation.log");
14019
14171
  try {
14020
14172
  const compactionMcp = useCompactionLog ? {
14021
14173
  compact_log: {
@@ -15336,14 +15488,14 @@ var init_self_config = __esm(async () => {
15336
15488
  });
15337
15489
 
15338
15490
  // ../../packages/core/src/query/session-inbox-path.ts
15339
- import { join as join21 } from "path";
15491
+ import { join as join22 } from "path";
15340
15492
  function sessionInboxPath(userId, topicId) {
15341
15493
  const key = Buffer.from(topicId, "utf8").toString("base64url");
15342
- return join21(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${JSONL_SUFFIX}`);
15494
+ return join22(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${JSONL_SUFFIX}`);
15343
15495
  }
15344
15496
  function scheduledSessionInboxPath(userId, topicId) {
15345
15497
  const key = Buffer.from(topicId, "utf8").toString("base64url");
15346
- return join21(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${SCHEDULE_SUFFIX}`);
15498
+ return join22(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${SCHEDULE_SUFFIX}`);
15347
15499
  }
15348
15500
  function decodeTopicIdFileName(fileName, suffix) {
15349
15501
  if (!fileName.startsWith(TOPIC_ID_FILE_PREFIX) || !fileName.endsWith(suffix))
@@ -15371,13 +15523,13 @@ var init_session_inbox_path = __esm(() => {
15371
15523
 
15372
15524
  // ../../packages/core/src/query/session-inbox-cleanup.ts
15373
15525
  import { unlinkSync as unlinkSync14 } from "fs";
15374
- import { basename as basename3, join as join22 } from "path";
15526
+ import { basename as basename3, join as join23 } from "path";
15375
15527
  function cleanupSessionInboxFiles(userId, topicId, legacyTopicTitle) {
15376
15528
  const live = sessionInboxPath(userId, topicId);
15377
15529
  const scheduled = scheduledSessionInboxPath(userId, topicId);
15378
15530
  const candidates = new Set([live, `${live}.processing`, scheduled, `${scheduled}.processing`]);
15379
15531
  if (legacyTopicTitle && legacyTopicTitle !== "." && legacyTopicTitle !== ".." && basename3(legacyTopicTitle) === legacyTopicTitle) {
15380
- const legacyBase = join22(SESSION_INBOX_DIR, userId, legacyTopicTitle);
15532
+ const legacyBase = join23(SESSION_INBOX_DIR, userId, legacyTopicTitle);
15381
15533
  for (const suffix of [".jsonl", ".jsonl.processing", ".schedule", ".schedule.processing"]) {
15382
15534
  candidates.add(`${legacyBase}${suffix}`);
15383
15535
  }
@@ -15403,16 +15555,16 @@ var init_session_inbox_cleanup = __esm(() => {
15403
15555
 
15404
15556
  // ../../packages/core/src/query/state.ts
15405
15557
  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";
15558
+ import { basename as basename4, join as join24 } from "path";
15407
15559
  function createQueryStateStore(options) {
15408
15560
  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`);
15561
+ const queryStateDirPath = (userId) => join24(options.usersLogDir, String(userId), "active-queries");
15562
+ const queryStateFile = (userId, topicId) => join24(queryStateDirPath(userId), `${sanitize(topicId)}.json`);
15411
15563
  const legacyQueryStateFile = (userId, topicName) => {
15412
15564
  if (!topicName || topicName === "." || topicName === ".." || basename4(topicName) !== topicName) {
15413
15565
  return null;
15414
15566
  }
15415
- return join23(queryStateDirPath(userId), `${topicName}.json`);
15567
+ return join24(queryStateDirPath(userId), `${topicName}.json`);
15416
15568
  };
15417
15569
  return {
15418
15570
  write(userId, topicId, topicName, task) {
@@ -15573,30 +15725,30 @@ import {
15573
15725
  unlinkSync as unlinkSync16,
15574
15726
  writeFileSync as writeFileSync13
15575
15727
  } from "fs";
15576
- import { dirname as dirname13, join as join24 } from "path";
15728
+ import { dirname as dirname14, join as join25 } from "path";
15577
15729
  function pendingAskDir(userId) {
15578
15730
  const rawUserId = String(userId);
15579
15731
  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);
15732
+ return join25(resolveStorageSessionAsksDir(), safeUserId);
15581
15733
  }
15582
15734
  function encodeAskKey(key) {
15583
15735
  return JSON.stringify([key.from, key.to]);
15584
15736
  }
15585
15737
  function pendingAskPath(key) {
15586
15738
  const digest = createHash6("sha256").update(encodeAskKey(key)).digest("hex");
15587
- return join24(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
15739
+ return join25(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${digest}.pending`);
15588
15740
  }
15589
15741
  function v2PendingAskPath(key) {
15590
15742
  const encoded = Buffer.from(encodeAskKey(key), "utf8").toString("base64url");
15591
- return join24(pendingAskDir(key.userId), `${V2_ASK_FILENAME_PREFIX}${encoded}.pending`);
15743
+ return join25(pendingAskDir(key.userId), `${V2_ASK_FILENAME_PREFIX}${encoded}.pending`);
15592
15744
  }
15593
15745
  function legacyPendingAskPath(key) {
15594
15746
  if (key.from.includes("/") || key.from.includes("\\") || key.to.includes("/") || key.to.includes("\\") || key.from.includes("\x00") || key.to.includes("\x00")) {
15595
15747
  return null;
15596
15748
  }
15597
15749
  const dir = pendingAskDir(key.userId);
15598
- const candidate = join24(dir, `${key.from}___${key.to}.pending`);
15599
- return dirname13(candidate) === dir ? candidate : null;
15750
+ const candidate = join25(dir, `${key.from}___${key.to}.pending`);
15751
+ return dirname14(candidate) === dir ? candidate : null;
15600
15752
  }
15601
15753
  function parsePendingAskFilename(fileName) {
15602
15754
  if (!fileName.endsWith(".pending"))
@@ -15836,7 +15988,7 @@ function listPendingAsksForCaller(args) {
15836
15988
  const parsed = isV3 ? { from: args.from, to: "" } : parsePendingAskFilename(fileName);
15837
15989
  if (!parsed)
15838
15990
  continue;
15839
- const path = join24(dir, fileName);
15991
+ const path = join25(dir, fileName);
15840
15992
  const record = readPendingAskFile(path, {
15841
15993
  userId: args.userId,
15842
15994
  from: parsed.from,
@@ -15878,7 +16030,7 @@ function deletePendingAsksForTopic(args) {
15878
16030
  }
15879
16031
  let deleted = 0;
15880
16032
  for (const fileName of files) {
15881
- const path = join24(dir, fileName);
16033
+ const path = join25(dir, fileName);
15882
16034
  const parsed = parsePendingAskFilename(fileName);
15883
16035
  const record = readPendingAskFile(path, {
15884
16036
  userId: args.userId,
@@ -16296,7 +16448,7 @@ __export(exports_token_stats, {
16296
16448
  });
16297
16449
  import { createHash as createHash7 } from "crypto";
16298
16450
  import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "fs";
16299
- import { join as join25 } from "path";
16451
+ import { join as join26 } from "path";
16300
16452
  function emptyBucket() {
16301
16453
  return {
16302
16454
  inputTokens: 0,
@@ -16315,7 +16467,7 @@ function queriesPath(userId) {
16315
16467
  const fileId = tokenStatsFileId(userId);
16316
16468
  const logDir = resolveStorageLogDir();
16317
16469
  mkdirSync16(logDir, { recursive: true });
16318
- return join25(logDir, `token-queries-${fileId}.jsonl`);
16470
+ return join26(logDir, `token-queries-${fileId}.jsonl`);
16319
16471
  }
16320
16472
  function loadRecords(userId) {
16321
16473
  try {
@@ -16744,7 +16896,7 @@ var init_lifecycle = __esm(async () => {
16744
16896
  // ../../packages/core/src/runtime/attachments.ts
16745
16897
  import { randomUUID as randomUUID14 } from "crypto";
16746
16898
  import { copyFileSync as copyFileSync2, mkdirSync as mkdirSync17, writeFileSync as writeFileSync15 } from "fs";
16747
- import { basename as basename5, join as join26 } from "path";
16899
+ import { basename as basename5, join as join27 } from "path";
16748
16900
  function workspaceCwdFor(topicId) {
16749
16901
  return resolveTopicWorkspaceDir(topicId);
16750
16902
  }
@@ -16757,7 +16909,7 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
16757
16909
  if (!attachmentIds?.length)
16758
16910
  return [];
16759
16911
  const out = [];
16760
- const destDir = join26(workspaceCwdFor(topicId), "attachments", queryId);
16912
+ const destDir = join27(workspaceCwdFor(topicId), "attachments", queryId);
16761
16913
  for (const rawId of attachmentIds) {
16762
16914
  if (typeof rawId !== "string")
16763
16915
  continue;
@@ -16774,7 +16926,7 @@ function materializePromptAttachments(topicId, queryId, attachmentIds) {
16774
16926
  mkdirSync17(destDir, { recursive: true });
16775
16927
  const index = String(out.length + 1).padStart(2, "0");
16776
16928
  const safeName = safeAttachmentFilename(attachment.filename, fileId);
16777
- const destPath = join26(destDir, `${index}-${fileId.slice(0, 8)}-${safeName}`);
16929
+ const destPath = join27(destDir, `${index}-${fileId.slice(0, 8)}-${safeName}`);
16778
16930
  copyFileSync2(sourcePath, destPath);
16779
16931
  out.push({
16780
16932
  id: attachment.id,
@@ -16804,10 +16956,10 @@ function promptWithAttachments(prompt, attachments) {
16804
16956
  return composeAttachmentPrompt(prompt, attachments.map(({ filename, path }) => attachmentPromptLine(filename, path)));
16805
16957
  }
16806
16958
  function ingestAttachment(args) {
16807
- const destDir = join26(UPLOADS_DIR, args.topicId);
16959
+ const destDir = join27(UPLOADS_DIR, args.topicId);
16808
16960
  mkdirSync17(destDir, { recursive: true });
16809
16961
  const safeName = safeAttachmentFilename(args.filename, "upload");
16810
- const destPath = join26(destDir, `${Date.now()}-${randomUUID14().slice(0, 8)}-${safeName}`);
16962
+ const destPath = join27(destDir, `${Date.now()}-${randomUUID14().slice(0, 8)}-${safeName}`);
16811
16963
  if (args.sourcePath !== undefined) {
16812
16964
  copyFileSync2(args.sourcePath, destPath);
16813
16965
  } else if (args.bytes !== undefined) {
@@ -16982,12 +17134,12 @@ var init_errors = __esm(() => {
16982
17134
 
16983
17135
  // ../../packages/core/src/runtime/event-heartbeat.ts
16984
17136
  function nextOrHeartbeat(pending, intervalMs) {
16985
- return new Promise((resolve14, reject) => {
16986
- const timer = setTimeout(() => resolve14({ kind: "heartbeat" }), intervalMs);
17137
+ return new Promise((resolve15, reject) => {
17138
+ const timer = setTimeout(() => resolve15({ kind: "heartbeat" }), intervalMs);
16987
17139
  timer.unref?.();
16988
17140
  pending.then((result) => {
16989
17141
  clearTimeout(timer);
16990
- resolve14({ kind: "event", result });
17142
+ resolve15({ kind: "event", result });
16991
17143
  }, (error) => {
16992
17144
  clearTimeout(timer);
16993
17145
  reject(error);
@@ -17324,7 +17476,7 @@ var init_visual_html = __esm(() => {
17324
17476
 
17325
17477
  // ../../packages/core/src/runtime/visuals.ts
17326
17478
  import { realpathSync as realpathSync4 } from "fs";
17327
- import { isAbsolute as isAbsolute4, resolve as resolve14 } from "path";
17479
+ import { isAbsolute as isAbsolute4, resolve as resolve15 } from "path";
17328
17480
  function activeVisualHtmlForPrompt(html) {
17329
17481
  if (html.length <= ACTIVE_VISUAL_PROMPT_MAX_CHARS) {
17330
17482
  return { html, omittedChars: 0 };
@@ -17375,8 +17527,8 @@ function topicAllowsVisualFileId(topicId, fileId) {
17375
17527
  return topicHasAttachmentFileId(topicId, fileId) || topicHasVisualFileId(topicId, fileId);
17376
17528
  }
17377
17529
  function isPathInside(baseDir, filePath) {
17378
- const base = resolve14(baseDir);
17379
- const normalized = resolve14(filePath);
17530
+ const base = resolve15(baseDir);
17531
+ const normalized = resolve15(filePath);
17380
17532
  try {
17381
17533
  const realBase = realpathSync4(base);
17382
17534
  const real = realpathSync4(normalized);
@@ -17438,7 +17590,7 @@ function resolveVisualMediaInput(topicId, input) {
17438
17590
  }
17439
17591
  const rawPath = input.file_path.trim();
17440
17592
  const cwd = workspaceCwdFor(topicId);
17441
- const candidate = isAbsolute4(rawPath) ? rawPath : resolve14(cwd, rawPath);
17593
+ const candidate = isAbsolute4(rawPath) ? rawPath : resolve15(cwd, rawPath);
17442
17594
  if (!isPathInside(cwd, candidate)) {
17443
17595
  return { error: "file_path must be inside the topic workspace" };
17444
17596
  }
@@ -17476,7 +17628,7 @@ var init_visuals = __esm(async () => {
17476
17628
  // ../../packages/core/src/runtime/turn-event-stream.ts
17477
17629
  import { randomUUID as randomUUID15 } from "crypto";
17478
17630
  import { realpathSync as realpathSync5, statSync as statSync9 } from "fs";
17479
- import { isAbsolute as isAbsolute5, resolve as resolve15 } from "path";
17631
+ import { isAbsolute as isAbsolute5, resolve as resolve16 } from "path";
17480
17632
  function sessionEventMatchesCurrentExecution(topicId, queryId, agent, model) {
17481
17633
  if (getRoomQuery(topicId)?.queryId !== queryId)
17482
17634
  return false;
@@ -17829,7 +17981,7 @@ ${JSON.stringify(event.input ?? {})}`);
17829
17981
  case "file":
17830
17982
  if (!silent && peerBridge) {
17831
17983
  const cwd = workspaceCwdFor(topicId);
17832
- const path = isAbsolute5(event.path) ? event.path : resolve15(cwd, event.path);
17984
+ const path = isAbsolute5(event.path) ? event.path : resolve16(cwd, event.path);
17833
17985
  if (!isPathInside(cwd, path)) {
17834
17986
  logger.warn({ topicId, path }, "peer output file is outside the topic workspace");
17835
17987
  break;
@@ -18117,15 +18269,15 @@ __export(exports_app_settings, {
18117
18269
  getGlobalAiName: () => getGlobalAiName,
18118
18270
  DEFAULT_AI_NAME: () => DEFAULT_AI_NAME
18119
18271
  });
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";
18272
+ import { existsSync as existsSync19, mkdirSync as mkdirSync18, readFileSync as readFileSync16, writeFileSync as writeFileSync16 } from "fs";
18273
+ import { dirname as dirname15, join as join28 } from "path";
18122
18274
  function settingsFile() {
18123
- return join27(resolveStorageDataDir(), "otium-settings.json");
18275
+ return join28(resolveStorageDataDir(), "otium-settings.json");
18124
18276
  }
18125
18277
  function getGlobalAiName() {
18126
18278
  const path = settingsFile();
18127
18279
  try {
18128
- if (existsSync18(path)) {
18280
+ if (existsSync19(path)) {
18129
18281
  const data = JSON.parse(readFileSync16(path, "utf8"));
18130
18282
  if (typeof data.aiName === "string" && data.aiName.trim()) {
18131
18283
  return data.aiName.trim();
@@ -18138,7 +18290,7 @@ function setGlobalAiName(name) {
18138
18290
  const path = settingsFile();
18139
18291
  const aiName = name.trim() || DEFAULT_AI_NAME;
18140
18292
  try {
18141
- mkdirSync18(dirname14(path), { recursive: true });
18293
+ mkdirSync18(dirname15(path), { recursive: true });
18142
18294
  writeFileSync16(path, JSON.stringify({ aiName }, null, 2));
18143
18295
  } catch {}
18144
18296
  return aiName;
@@ -18223,8 +18375,8 @@ __export(exports_turn_runner, {
18223
18375
  AGENT_DISPLAY_NAME: () => AGENT_DISPLAY_NAME
18224
18376
  });
18225
18377
  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";
18378
+ import { existsSync as existsSync20, mkdirSync as mkdirSync19, readdirSync as readdirSync6, statSync as statSync10 } from "fs";
18379
+ import { join as join29 } from "path";
18228
18380
  function withDefaultPlaywright(configuredMcp, isManager) {
18229
18381
  if (isManager)
18230
18382
  return configuredMcp;
@@ -18279,8 +18431,8 @@ function appendAskReplyMessage(topicId, text2, agentType) {
18279
18431
  return message;
18280
18432
  }
18281
18433
  function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, matchesTitle, preferExact = true) {
18282
- const preferred = join28(directory, preferredFilename);
18283
- if (preferExact && existsSync19(preferred))
18434
+ const preferred = join29(directory, preferredFilename);
18435
+ if (preferExact && existsSync20(preferred))
18284
18436
  return preferred;
18285
18437
  let newestLegacyId = null;
18286
18438
  let newestTitle = null;
@@ -18290,7 +18442,7 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
18290
18442
  const legacyIdMatch = !titleMatch && matchesLegacyId(filename);
18291
18443
  if (!titleMatch && !legacyIdMatch)
18292
18444
  continue;
18293
- const path = join28(directory, filename);
18445
+ const path = join29(directory, filename);
18294
18446
  const mtimeMs = statSync10(path).mtimeMs;
18295
18447
  if (titleMatch) {
18296
18448
  if (!newestTitle || mtimeMs > newestTitle.mtimeMs) {
@@ -18304,10 +18456,10 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
18304
18456
  return newestTitle?.path ?? newestLegacyId?.path ?? preferred;
18305
18457
  }
18306
18458
  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;
18459
+ const briefFile = resolveWikiMirrorPath(join29(wikiDir, "topic"), `${wikiBriefStorageKey(topicTitle, topicId)}.md`, (filename) => isTopicBriefFile(filename, topicId), (filename) => isTopicBriefFile(filename, topicId, topicTitle));
18460
+ const hasBriefFile = existsSync20(briefFile) && statSync10(briefFile).isFile();
18461
+ const latestSummaryCandidate = resolveWikiMirrorPath(join29(wikiDir, "summaries"), `__missing__-${wikiSummaryFilename("0000-00-00", topicTitle, topicId)}`, (filename) => isTopicSummaryFile(filename, topicId), (filename) => isTopicSummaryFile(filename, topicId, topicTitle), false);
18462
+ const latestSummaryFile = existsSync20(latestSummaryCandidate) && statSync10(latestSummaryCandidate).isFile() ? latestSummaryCandidate : null;
18311
18463
  return { briefFile, hasBriefFile, latestSummaryFile };
18312
18464
  }
18313
18465
  async function streamAgentEvents(topicId, topicTitle, queryId, events, control, agentType, model, effort, userId, retryableSessionExpired = true, onSessionId, execution) {
@@ -21054,9 +21206,9 @@ var init_vault_command = __esm(async () => {
21054
21206
 
21055
21207
  // ../../packages/core/src/media/text-extractor.ts
21056
21208
  import { execFile } from "child_process";
21057
- import { existsSync as existsSync20, mkdirSync as mkdirSync21, readFileSync as readFileSync17, rmSync as rmSync7 } from "fs";
21209
+ import { existsSync as existsSync21, mkdirSync as mkdirSync21, readFileSync as readFileSync17, rmSync as rmSync7 } from "fs";
21058
21210
  import { readFile } from "fs/promises";
21059
- import { extname, join as join29 } from "path";
21211
+ import { extname, join as join30 } from "path";
21060
21212
  import { promisify } from "util";
21061
21213
  async function extractText(filePath) {
21062
21214
  const ext = extname(filePath).toLowerCase();
@@ -21161,7 +21313,7 @@ async function extractFromImage(filePath) {
21161
21313
  function isTranscriptionConfigured(opts = {}) {
21162
21314
  const ffmpeg = opts.ffmpegBin ?? FFMPEG_BIN;
21163
21315
  const wrapper = opts.wrapperPath ?? FASTER_WHISPER_WRAPPER;
21164
- return Boolean(ffmpeg) && existsSync20(wrapper);
21316
+ return Boolean(ffmpeg) && existsSync21(wrapper);
21165
21317
  }
21166
21318
  async function transcribeAudio(filePath, opts = {}) {
21167
21319
  if (!isTranscriptionConfigured(opts))
@@ -21173,7 +21325,7 @@ async function extractFromAudio(filePath, opts = {}) {
21173
21325
  const tmpDir = `${filePath}_whisper_tmp`;
21174
21326
  try {
21175
21327
  mkdirSync21(tmpDir, { recursive: true });
21176
- const mp3Path = join29(tmpDir, "audio.mp3");
21328
+ const mp3Path = join30(tmpDir, "audio.mp3");
21177
21329
  await execFileAsync(opts.ffmpegBin ?? FFMPEG_BIN2, [
21178
21330
  "-y",
21179
21331
  "-i",
@@ -21200,8 +21352,8 @@ async function extractFromAudio(filePath, opts = {}) {
21200
21352
  "--output-format",
21201
21353
  "txt"
21202
21354
  ], { timeout: 120000 });
21203
- const txtPath = join29(tmpDir, "audio.txt");
21204
- const text2 = existsSync20(txtPath) ? readFileSync17(txtPath, "utf-8").trim() : null;
21355
+ const txtPath = join30(tmpDir, "audio.txt");
21356
+ const text2 = existsSync21(txtPath) ? readFileSync17(txtPath, "utf-8").trim() : null;
21205
21357
  if (!text2) {
21206
21358
  return { text: null, method: "whisper", error: "Whisper produced no text" };
21207
21359
  }
@@ -21372,19 +21524,19 @@ var init_lifecycle2 = __esm(() => {
21372
21524
  });
21373
21525
 
21374
21526
  // ../../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";
21527
+ import { existsSync as existsSync22, renameSync as renameSync8, statSync as statSync11 } from "fs";
21528
+ import { join as join31 } from "path";
21377
21529
  function rotateOversizedLog(logPath, maxBytes = DEFAULT_DAEMON_LOG_ROTATE_BYTES) {
21378
21530
  try {
21379
- if (!existsSync21(logPath))
21531
+ if (!existsSync22(logPath))
21380
21532
  return;
21381
21533
  const { size } = statSync11(logPath);
21382
21534
  if (size < maxBytes)
21383
21535
  return;
21384
21536
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
21385
- const dir = join30(logPath, "..");
21537
+ const dir = join31(logPath, "..");
21386
21538
  const base = logPath.slice(dir.length + 1);
21387
- renameSync8(logPath, join30(dir, `${base}.${stamp}`));
21539
+ renameSync8(logPath, join31(dir, `${base}.${stamp}`));
21388
21540
  } catch {}
21389
21541
  }
21390
21542
  var DEFAULT_DAEMON_LOG_ROTATE_BYTES;
@@ -21577,7 +21729,7 @@ import {
21577
21729
  rmSync as rmSync8,
21578
21730
  statSync as statSync12
21579
21731
  } from "fs";
21580
- import { join as join31 } from "path";
21732
+ import { join as join32 } from "path";
21581
21733
  function setBashrsCompletionSink(sink) {
21582
21734
  completionSink = sink ?? defaultSink;
21583
21735
  }
@@ -21604,15 +21756,15 @@ function readTail(filePath) {
21604
21756
  }
21605
21757
  }
21606
21758
  function buildMessage(dir, result) {
21607
- const stdout = readTail(join31(dir, "stdout.log"));
21608
- const stderr = readTail(join31(dir, "stderr.log"));
21759
+ const stdout = readTail(join32(dir, "stdout.log"));
21760
+ const stderr = readTail(join32(dir, "stderr.log"));
21609
21761
  const parts = [];
21610
21762
  if (stdout.text.trim() || stdout.truncated) {
21611
- parts.push(`stdout${stdout.truncated ? ` (truncated, full output: ${join31(dir, "stdout.log")})` : ""}:
21763
+ parts.push(`stdout${stdout.truncated ? ` (truncated, full output: ${join32(dir, "stdout.log")})` : ""}:
21612
21764
  ${stdout.text.trim()}`);
21613
21765
  }
21614
21766
  if (stderr.text.trim() || stderr.truncated) {
21615
- parts.push(`stderr${stderr.truncated ? ` (truncated, full output: ${join31(dir, "stderr.log")})` : ""}:
21767
+ parts.push(`stderr${stderr.truncated ? ` (truncated, full output: ${join32(dir, "stderr.log")})` : ""}:
21616
21768
  ${stderr.text.trim()}`);
21617
21769
  }
21618
21770
  const header = watchHeader(result) ?? `[background_bash ${result.bash_id} finished]`;
@@ -21646,7 +21798,7 @@ function parseOwner(owner) {
21646
21798
  return { userId, topicId };
21647
21799
  }
21648
21800
  function injectedMarker(dir) {
21649
- return join31(dir, "result.json.injected");
21801
+ return join32(dir, "result.json.injected");
21650
21802
  }
21651
21803
  async function flushBashrsCompletions() {
21652
21804
  let entries;
@@ -21657,8 +21809,8 @@ async function flushBashrsCompletions() {
21657
21809
  }
21658
21810
  const now = Date.now();
21659
21811
  for (const bashId of entries) {
21660
- const dir = join31(BASHRS_SPILL_ROOT, bashId);
21661
- const resultPath = join31(dir, "result.json");
21812
+ const dir = join32(BASHRS_SPILL_ROOT, bashId);
21813
+ const resultPath = join32(dir, "result.json");
21662
21814
  const marker = injectedMarker(dir);
21663
21815
  try {
21664
21816
  const markerStat = statSync12(marker);
@@ -21794,8 +21946,8 @@ function prepareDeliveryAck(messageId, claimTimeoutMs, deliveryTimeoutMs) {
21794
21946
  let finishPromise = () => {};
21795
21947
  let timer;
21796
21948
  let unsubscribe = () => {};
21797
- const promise = new Promise((resolve16) => {
21798
- finishPromise = resolve16;
21949
+ const promise = new Promise((resolve17) => {
21950
+ finishPromise = resolve17;
21799
21951
  });
21800
21952
  unsubscribe = runtimeBus().subscribe((event) => {
21801
21953
  if (event.type !== "ai-status")
@@ -21864,7 +22016,7 @@ function renderTurnFooter(msg) {
21864
22016
 
21865
22017
  // ../../packages/core/src/outbox/file-ops.ts
21866
22018
  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";
22019
+ import { appendFileSync as appendFileSync2, existsSync as existsSync23, readFileSync as readFileSync19, renameSync as renameSync10, unlinkSync as unlinkSync17 } from "fs";
21868
22020
  function host() {
21869
22021
  return hostContext.getStore() ?? defaultHost2;
21870
22022
  }
@@ -21881,8 +22033,8 @@ function tryUnlink(path, label, reason) {
21881
22033
  }
21882
22034
  function drainOutboxFile(filePath, label) {
21883
22035
  const processingPath = filePath + PROCESSING_SUFFIX;
21884
- const hasPending = existsSync22(filePath);
21885
- const hasLeftover = existsSync22(processingPath);
22036
+ const hasPending = existsSync23(filePath);
22037
+ const hasLeftover = existsSync23(processingPath);
21886
22038
  if (hasLeftover) {
21887
22039
  const logMsg = `${label}: merging leftover .processing before claim`;
21888
22040
  const logCtx = { filePath, processingPath, hadPending: hasPending };
@@ -21902,7 +22054,7 @@ function drainOutboxFile(filePath, label) {
21902
22054
  tryUnlink(processingPath, label, "failed to clean up leftover after merge error");
21903
22055
  }
21904
22056
  }
21905
- if (!existsSync22(filePath))
22057
+ if (!existsSync23(filePath))
21906
22058
  return null;
21907
22059
  try {
21908
22060
  renameSync10(filePath, processingPath);
@@ -21956,7 +22108,7 @@ var init_file_ops = __esm(() => {
21956
22108
  // ../../packages/core/src/runtime/inbox.ts
21957
22109
  import { createHash as createHash9, randomUUID as randomUUID20 } from "crypto";
21958
22110
  import { readdirSync as readdirSync8, statSync as statSync13, writeFileSync as writeFileSync17 } from "fs";
21959
- import { join as join32 } from "path";
22111
+ import { join as join33 } from "path";
21960
22112
  async function createAskForkPlan(options) {
21961
22113
  const snapshot = structuredClone(options.entries);
21962
22114
  const prepareSession = async () => options.synthesize(structuredClone(snapshot));
@@ -22096,7 +22248,7 @@ async function flushSessionInbox() {
22096
22248
  return;
22097
22249
  }
22098
22250
  for (const uid of userDirs) {
22099
- const userInboxDir = join32(SESSION_INBOX_DIR, uid);
22251
+ const userInboxDir = join33(SESSION_INBOX_DIR, uid);
22100
22252
  let entries;
22101
22253
  try {
22102
22254
  entries = readdirSync8(userInboxDir);
@@ -22104,7 +22256,7 @@ async function flushSessionInbox() {
22104
22256
  continue;
22105
22257
  }
22106
22258
  for (const entry of entries) {
22107
- const entryPath = join32(userInboxDir, entry);
22259
+ const entryPath = join33(userInboxDir, entry);
22108
22260
  let isDir = false;
22109
22261
  try {
22110
22262
  isDir = statSync13(entryPath).isDirectory();
@@ -22194,7 +22346,7 @@ function sweepScheduledSessionInbox(nowMs = Date.now()) {
22194
22346
  return;
22195
22347
  }
22196
22348
  for (const userId of userDirs) {
22197
- const userInboxDir = join32(SESSION_INBOX_DIR, userId);
22349
+ const userInboxDir = join33(SESSION_INBOX_DIR, userId);
22198
22350
  let files;
22199
22351
  try {
22200
22352
  files = readdirSync8(userInboxDir);
@@ -22206,7 +22358,7 @@ function sweepScheduledSessionInbox(nowMs = Date.now()) {
22206
22358
  const topicId = topicIdFromScheduledSessionInboxFileName(file);
22207
22359
  if (!topicId)
22208
22360
  continue;
22209
- const schedulePath = join32(userInboxDir, file);
22361
+ const schedulePath = join33(userInboxDir, file);
22210
22362
  const hasProcessingClaim = files.includes(`${file}.processing`);
22211
22363
  if (!hasProcessingClaim && !scheduledFileNeedsClaim(schedulePath, nowMs))
22212
22364
  continue;
@@ -23042,8 +23194,8 @@ var init_src = __esm(async () => {
23042
23194
  });
23043
23195
 
23044
23196
  // ../../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";
23197
+ import { copyFileSync as copyFileSync3, existsSync as existsSync24, mkdirSync as mkdirSync23, readFileSync as readFileSync20 } from "fs";
23198
+ import { dirname as dirname16, join as join34 } from "path";
23047
23199
  function readEntries(path) {
23048
23200
  const entries = [];
23049
23201
  for (const [index, line] of readFileSync20(path, "utf8").split(`
@@ -23106,20 +23258,20 @@ function migrateLegacyCompactedConversations() {
23106
23258
  const userId = participant.userId;
23107
23259
  const rawPath = getConversationPath(userId, topic.title);
23108
23260
  const activePath = getActiveConversationPath(userId, topic.title);
23109
- if (!existsSync23(rawPath) || existsSync23(activePath)) {
23261
+ if (!existsSync24(rawPath) || existsSync24(activePath)) {
23110
23262
  result.skipped++;
23111
23263
  continue;
23112
23264
  }
23113
- const backupPath = join33(resolveStorageDataDir(), "conversation-migration-backups", MIGRATION_NAME, userId, `${sanitizeTopicName(topic.title, true)}.jsonl`);
23114
- const sourcePath = existsSync23(backupPath) ? backupPath : rawPath;
23265
+ const backupPath = join34(resolveStorageDataDir(), "conversation-migration-backups", MIGRATION_NAME, userId, `${sanitizeTopicName(topic.title, true)}.jsonl`);
23266
+ const sourcePath = existsSync24(backupPath) ? backupPath : rawPath;
23115
23267
  const legacyEntries = readEntries(sourcePath);
23116
23268
  const compactIndex = compactionEntryIndex(legacyEntries);
23117
23269
  if (compactIndex < 0) {
23118
23270
  result.skipped++;
23119
23271
  continue;
23120
23272
  }
23121
- if (!existsSync23(backupPath)) {
23122
- mkdirSync23(dirname15(backupPath), { recursive: true });
23273
+ if (!existsSync24(backupPath)) {
23274
+ mkdirSync23(dirname16(backupPath), { recursive: true });
23123
23275
  copyFileSync3(rawPath, backupPath);
23124
23276
  }
23125
23277
  const compactedAt = legacyEntries[compactIndex]?.ts;
@@ -23169,8 +23321,8 @@ __export(exports_decisions, {
23169
23321
  createDecisions: () => createDecisions,
23170
23322
  DECISION_STATUS_VALUES: () => DECISION_STATUS_VALUES
23171
23323
  });
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";
23324
+ import { existsSync as existsSync25, mkdirSync as mkdirSync24, readFileSync as readFileSync21, renameSync as renameSync11, writeFileSync as writeFileSync18 } from "fs";
23325
+ import { dirname as dirname17, join as join35 } from "path";
23174
23326
  function safeDecisionScopeKey(scopeKey) {
23175
23327
  const safe = sanitizeFileName(scopeKey);
23176
23328
  if (!safe || safe === "." || safe === "..") {
@@ -23182,14 +23334,14 @@ function decisionScopeKey(opts) {
23182
23334
  return opts.topicId?.trim() || opts.session || "default";
23183
23335
  }
23184
23336
  function getDecisionFilePath(userId, scopeKey) {
23185
- return join34(resolveStorageDataDir(), "decisions", `${safeDecisionScopeKey(scopeKey)}.json`);
23337
+ return join35(resolveStorageDataDir(), "decisions", `${safeDecisionScopeKey(scopeKey)}.json`);
23186
23338
  }
23187
23339
  function getDecisionGraphSvgPath(userId, scopeKey) {
23188
- return join34(resolveStorageDataDir(), "decision-renders", safeDecisionScopeKey(scopeKey), "latest.svg");
23340
+ return join35(resolveStorageDataDir(), "decision-renders", safeDecisionScopeKey(scopeKey), "latest.svg");
23189
23341
  }
23190
23342
  function writeDecisionGraphSvg(userId, scopeKey, svg) {
23191
23343
  const path = getDecisionGraphSvgPath(userId, scopeKey);
23192
- mkdirSync24(dirname16(path), { recursive: true });
23344
+ mkdirSync24(dirname17(path), { recursive: true });
23193
23345
  const tmp = `${path}.${process.pid}.tmp`;
23194
23346
  writeFileSync18(tmp, svg, "utf-8");
23195
23347
  renameSync11(tmp, path);
@@ -23197,7 +23349,7 @@ function writeDecisionGraphSvg(userId, scopeKey, svg) {
23197
23349
  }
23198
23350
  function readDecisions(userId, scopeKey) {
23199
23351
  const path = getDecisionFilePath(userId, scopeKey);
23200
- if (!existsSync24(path))
23352
+ if (!existsSync25(path))
23201
23353
  return [];
23202
23354
  try {
23203
23355
  const parsed = JSON.parse(readFileSync21(path, "utf-8"));
@@ -23209,7 +23361,7 @@ function readDecisions(userId, scopeKey) {
23209
23361
  }
23210
23362
  function writeDecisions(userId, scopeKey, decisions) {
23211
23363
  const path = getDecisionFilePath(userId, scopeKey);
23212
- mkdirSync24(dirname16(path), { recursive: true });
23364
+ mkdirSync24(dirname17(path), { recursive: true });
23213
23365
  const payload = { version: 1, decisions };
23214
23366
  const tmp = `${path}.${process.pid}.tmp`;
23215
23367
  writeFileSync18(tmp, JSON.stringify(payload, null, 2), "utf-8");
@@ -23468,8 +23620,8 @@ var init_node_host = __esm(async () => {
23468
23620
  });
23469
23621
 
23470
23622
  // ../../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";
23623
+ import { existsSync as existsSync26, readFileSync as readFileSync22, statSync as statSync14 } from "fs";
23624
+ import { extname as extname2, isAbsolute as isAbsolute6, resolve as resolve17 } from "path";
23473
23625
  function isNonLocal(ref) {
23474
23626
  return /^(?:[a-z][a-z0-9+.-]*:|\/\/|#)/i.test(ref.trim());
23475
23627
  }
@@ -23493,10 +23645,10 @@ function inlineLocalAssets(html, options) {
23493
23645
  const relPath = stripSuffix(ref);
23494
23646
  if (!relPath)
23495
23647
  return null;
23496
- const abs = isAbsolute6(relPath) ? relPath : resolve16(baseDir, relPath);
23648
+ const abs = isAbsolute6(relPath) ? relPath : resolve17(baseDir, relPath);
23497
23649
  let bytes;
23498
23650
  try {
23499
- if (!existsSync25(abs) || !statSync14(abs).isFile()) {
23651
+ if (!existsSync26(abs) || !statSync14(abs).isFile()) {
23500
23652
  unresolved.push(ref);
23501
23653
  cache.set(ref, null);
23502
23654
  return null;
@@ -24444,8 +24596,8 @@ __export(exports_agent_health, {
24444
24596
  createAgentHealthMcpServer: () => createAgentHealthMcpServer
24445
24597
  });
24446
24598
  import { spawn as spawn6 } from "child_process";
24447
- import { existsSync as existsSync26, readdirSync as readdirSync9 } from "fs";
24448
- import { join as join35 } from "path";
24599
+ import { existsSync as existsSync27, readdirSync as readdirSync9 } from "fs";
24600
+ import { join as join36 } from "path";
24449
24601
  import { McpServer as McpServer5 } from "@modelcontextprotocol/sdk/server/mcp.js";
24450
24602
  import { z as z10 } from "zod";
24451
24603
  function signalProcessTree2(child, signal) {
@@ -24461,7 +24613,7 @@ function signalProcessTree2(child, signal) {
24461
24613
  }
24462
24614
  }
24463
24615
  function spawnCapture(cmd, args, timeoutMs, signal, env) {
24464
- return new Promise((resolve17, reject) => {
24616
+ return new Promise((resolve18, reject) => {
24465
24617
  const child = spawn6(cmd, args, {
24466
24618
  env: env ?? process.env,
24467
24619
  stdio: ["ignore", "pipe", "pipe"],
@@ -24519,7 +24671,7 @@ function spawnCapture(cmd, args, timeoutMs, signal, env) {
24519
24671
  if (killTimer)
24520
24672
  clearTimeout(killTimer);
24521
24673
  if (code === 0)
24522
- resolve17({ stdout, stderr });
24674
+ resolve18({ stdout, stderr });
24523
24675
  else
24524
24676
  reject(new Error(stderr.trim() || `${cmd} exited with ${code ?? exitSignal}`));
24525
24677
  });
@@ -24597,11 +24749,11 @@ async function checkMaestro(model, timeoutMs, signal) {
24597
24749
  const remaining = deadline - Date.now();
24598
24750
  if (remaining <= 0)
24599
24751
  throw new Error("timeout");
24600
- const eventResult = await new Promise((resolve17, reject) => {
24752
+ const eventResult = await new Promise((resolve18, reject) => {
24601
24753
  const timeout = setTimeout(() => reject(new Error("timeout")), remaining);
24602
24754
  const onAbort = () => reject(new Error("aborted"));
24603
24755
  signal.addEventListener("abort", onAbort, { once: true });
24604
- activeIterator.next().then(resolve17, reject).finally(() => {
24756
+ activeIterator.next().then(resolve18, reject).finally(() => {
24605
24757
  clearTimeout(timeout);
24606
24758
  signal.removeEventListener("abort", onAbort);
24607
24759
  });
@@ -24631,7 +24783,7 @@ function getDefaultModel(registry) {
24631
24783
  }
24632
24784
  async function withCheckSlot(operation) {
24633
24785
  if (activeChecks >= MAX_CONCURRENT_CHECKS) {
24634
- await new Promise((resolve17) => checkWaiters.push(resolve17));
24786
+ await new Promise((resolve18) => checkWaiters.push(resolve18));
24635
24787
  }
24636
24788
  activeChecks += 1;
24637
24789
  try {
@@ -24689,8 +24841,8 @@ ${results.every((result) => result.ok) ? "All agents healthy" : "Some agents fai
24689
24841
  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
24842
  if (!context.userId)
24691
24843
  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))
24844
+ const stateDir = join36(USERS_LOG_DIR, context.userId, "active-queries");
24845
+ if (!existsSync27(stateDir))
24694
24846
  return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC5C6\uC74C");
24695
24847
  let files;
24696
24848
  try {
@@ -24702,7 +24854,7 @@ ${results.every((result) => result.ok) ? "All agents healthy" : "Some agents fai
24702
24854
  const entries = files.flatMap((file) => {
24703
24855
  if (!file.endsWith(".json"))
24704
24856
  return [];
24705
- const state = readJsonFile(join35(stateDir, file));
24857
+ const state = readJsonFile(join36(stateDir, file));
24706
24858
  if (!state)
24707
24859
  return [];
24708
24860
  const sinceMs = new Date(state.since).getTime();
@@ -24935,7 +25087,7 @@ var exports_default_host = {};
24935
25087
  __export(exports_default_host, {
24936
25088
  createDefaultSessionCommMcpHost: () => createDefaultSessionCommMcpHost
24937
25089
  });
24938
- import { basename as basename6, join as join36 } from "path";
25090
+ import { basename as basename6, join as join37 } from "path";
24939
25091
  function ok2(text2) {
24940
25092
  return { content: [{ type: "text", text: text2 }] };
24941
25093
  }
@@ -24982,10 +25134,10 @@ function remoteTarget(context, to) {
24982
25134
  return { node: to.slice(0, slash), topic: to.slice(slash + 1) };
24983
25135
  }
24984
25136
  function activeQuery(context, topicId, title) {
24985
- const dir = join36(USERS_LOG_DIR, context.userId, "active-queries");
24986
- const candidates = [join36(dir, `${sanitizeId(topicId)}.json`)];
25137
+ const dir = join37(USERS_LOG_DIR, context.userId, "active-queries");
25138
+ const candidates = [join37(dir, `${sanitizeId(topicId)}.json`)];
24987
25139
  if (title && basename6(title) === title && title !== "." && title !== "..") {
24988
- candidates.push(join36(dir, `${title}.json`));
25140
+ candidates.push(join37(dir, `${title}.json`));
24989
25141
  }
24990
25142
  for (const path of candidates) {
24991
25143
  const state = readJsonFile(path);
@@ -25276,298 +25428,15 @@ var init_default_host = __esm(async () => {
25276
25428
  init_types();
25277
25429
  });
25278
25430
 
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
25431
  // ../../packages/core/src/mcp/factories/vault.ts
25560
25432
  var exports_vault = {};
25561
25433
  __export(exports_vault, {
25562
25434
  createVaultMcpServer: () => createVaultMcpServer
25563
25435
  });
25564
25436
  import { McpServer as McpServer7 } from "@modelcontextprotocol/sdk/server/mcp.js";
25565
- import { z as z12 } from "zod";
25566
- function createVaultMcpServer(context, host2, executors = {}) {
25437
+ function createVaultMcpServer(context, host2) {
25567
25438
  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.", {}, () => {
25439
+ 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
25440
  if (!context.userId)
25572
25441
  return mcpOk("(vault unavailable: no user context)");
25573
25442
  const entries = host2.list(context.userId);
@@ -25578,54 +25447,14 @@ function createVaultMcpServer(context, host2, executors = {}) {
25578
25447
  ${lines.join(`
25579
25448
  `)}`);
25580
25449
  });
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
25450
  return server;
25620
25451
  }
25621
25452
  var init_vault2 = __esm(() => {
25622
- init_vault_http();
25623
- init_vault_run();
25624
25453
  init_mcp_helpers();
25625
25454
  });
25626
25455
 
25627
25456
  // ../../packages/core/src/mcp/wiki-search-index.ts
25628
- import { chmodSync as chmodSync5, existsSync as existsSync27, readFileSync as readFileSync23, unlinkSync as unlinkSync18 } from "fs";
25457
+ import { chmodSync as chmodSync5, existsSync as existsSync28, readFileSync as readFileSync23, unlinkSync as unlinkSync18 } from "fs";
25629
25458
  function documentId(source) {
25630
25459
  return `${source.kind}:${source.key}`;
25631
25460
  }
@@ -25827,7 +25656,7 @@ class WikiSearchIndex {
25827
25656
  }
25828
25657
  function resetCorruptWikiSearchIndex(path) {
25829
25658
  for (const candidate of [path, `${path}-journal`, `${path}-wal`, `${path}-shm`]) {
25830
- if (!existsSync27(candidate))
25659
+ if (!existsSync28(candidate))
25831
25660
  continue;
25832
25661
  unlinkSync18(candidate);
25833
25662
  }
@@ -25847,7 +25676,7 @@ import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
25847
25676
  import { randomUUID as randomUUID21 } from "crypto";
25848
25677
  import {
25849
25678
  closeSync as closeSync4,
25850
- existsSync as existsSync28,
25679
+ existsSync as existsSync29,
25851
25680
  mkdirSync as mkdirSync25,
25852
25681
  openSync as openSync4,
25853
25682
  readdirSync as readdirSync10,
@@ -25857,7 +25686,7 @@ import {
25857
25686
  unlinkSync as unlinkSync19,
25858
25687
  writeFileSync as writeFileSync19
25859
25688
  } from "fs";
25860
- import { basename as basename7, dirname as dirname17, join as join37, relative as relative2, resolve as resolve17 } from "path";
25689
+ import { basename as basename7, dirname as dirname18, join as join38, relative as relative2, resolve as resolve18 } from "path";
25861
25690
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
25862
25691
  import { StdioServerTransport as StdioServerTransport2 } from "@modelcontextprotocol/sdk/server/stdio.js";
25863
25692
  import {
@@ -26092,7 +25921,7 @@ function trigramSimilarity(left, right) {
26092
25921
  return 2 * overlap / (a.size + b.size);
26093
25922
  }
26094
25923
  function openWikiSearchIndex() {
26095
- const path = resolve17(runtime().wikiDir, WIKI_SEARCH_INDEX_FILENAME);
25924
+ const path = resolve18(runtime().wikiDir, WIKI_SEARCH_INDEX_FILENAME);
26096
25925
  try {
26097
25926
  return new WikiSearchIndex(path);
26098
25927
  } catch (error2) {
@@ -26119,7 +25948,7 @@ function prepareWikiSearchDocument(kind, key, text2) {
26119
25948
  }
26120
25949
  function indexWikiSearchDocument(kind, key, text2) {
26121
25950
  const root = kind === "article" ? runtime().articlesDir : runtime().summariesDir;
26122
- const path = resolve17(root, `${key}.md`);
25951
+ const path = resolve18(root, `${key}.md`);
26123
25952
  const index = openWikiSearchIndex();
26124
25953
  if (!index)
26125
25954
  return false;
@@ -26134,7 +25963,7 @@ function indexWikiSearchDocument(kind, key, text2) {
26134
25963
  }
26135
25964
  }
26136
25965
  function wikiIndexPath(kind) {
26137
- return resolve17(runtime().wikiDir, WIKI_INDEX_FILENAMES[kind]);
25966
+ return resolve18(runtime().wikiDir, WIKI_INDEX_FILENAMES[kind]);
26138
25967
  }
26139
25968
  function normalizeIndexDescription(raw) {
26140
25969
  const description = raw.replaceAll(/\s+/g, " ").trim();
@@ -26190,7 +26019,7 @@ function indexCandidateExists(candidate) {
26190
26019
  if (candidate.kind === "topic")
26191
26020
  return true;
26192
26021
  const root = candidate.kind === "article" ? runtime().articlesDir : runtime().summariesDir;
26193
- const filePath = resolve17(root, `${candidate.key}.md`);
26022
+ const filePath = resolve18(root, `${candidate.key}.md`);
26194
26023
  const relativePath = relative2(root, filePath);
26195
26024
  if (relativePath.startsWith("..") || relativePath.startsWith("/"))
26196
26025
  return false;
@@ -26272,8 +26101,8 @@ function wikiQuery(args) {
26272
26101
  const canReadTopicMemory = runtime().host.canReadTopicMemory;
26273
26102
  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
26103
  const visibleIndexCandidates = [
26275
- ...parseWikiIndex(resolve17(runtime().wikiDir, "topic-index.md")),
26276
- ...parseWikiIndex(resolve17(runtime().wikiDir, "article-index.md"))
26104
+ ...parseWikiIndex(resolve18(runtime().wikiDir, "topic-index.md")),
26105
+ ...parseWikiIndex(resolve18(runtime().wikiDir, "article-index.md"))
26277
26106
  ].filter((candidate) => kind === "all" || candidate.kind === kind).filter((candidate) => candidate.kind !== "topic" || !canReadTopicMemory || canReadTopicMemory(candidate.key, runtime().userId));
26278
26107
  const hasStaleExactIndexMatch = visibleIndexCandidates.some((candidate) => !indexCandidateExists(candidate) && scoreIndexKey(question, candidate.key) >= 100);
26279
26108
  const indexCandidates = visibleIndexCandidates.filter(indexCandidateExists).map((candidate) => ({
@@ -26307,13 +26136,13 @@ function wikiQuery(args) {
26307
26136
  try {
26308
26137
  for (const entry of readdirSync10(dir, { withFileTypes: true })) {
26309
26138
  if (entry.isDirectory()) {
26310
- const sub = join37(dir, entry.name);
26139
+ const sub = join38(dir, entry.name);
26311
26140
  if (label === "articles" || label === "skills") {
26312
26141
  try {
26313
26142
  for (const f of readdirSync10(sub, { withFileTypes: true })) {
26314
26143
  if (!f.isFile() || !f.name.endsWith(".md"))
26315
26144
  continue;
26316
- const fp = join37(sub, f.name);
26145
+ const fp = join38(sub, f.name);
26317
26146
  try {
26318
26147
  const text2 = readFileSync24(fp, "utf-8");
26319
26148
  const key = `${entry.name}/${f.name.replace(/\.md$/i, "")}`;
@@ -26334,7 +26163,7 @@ function wikiQuery(args) {
26334
26163
  if (label === "topic" && canReadTopicMemory && !canReadTopicMemory(entry.name.replace(/\.md$/i, ""), runtime().userId)) {
26335
26164
  continue;
26336
26165
  }
26337
- const fp = join37(dir, entry.name);
26166
+ const fp = join38(dir, entry.name);
26338
26167
  try {
26339
26168
  const text2 = readFileSync24(fp, "utf-8");
26340
26169
  const key = entry.name.replace(/\.md$/i, "");
@@ -26618,7 +26447,7 @@ function wikiTopicBrief(args) {
26618
26447
  };
26619
26448
  }
26620
26449
  const topicKey = wikiSummarySlug(rawTopic.replace(/^topic\//, "").replace(/\.md$/i, ""));
26621
- const filePath = resolve17(runtime().topicsDir, `${topicKey}.md`);
26450
+ const filePath = resolve18(runtime().topicsDir, `${topicKey}.md`);
26622
26451
  try {
26623
26452
  const content = readFileSync24(filePath, "utf-8");
26624
26453
  return {
@@ -26666,7 +26495,7 @@ Adopted topic memory: ${memoryKey}` }]
26666
26495
  }
26667
26496
  const root = kind === "summary" ? runtime().summariesDir : runtime().articlesDir;
26668
26497
  const key = rawKey.replace(new RegExp(`^(?:${kind === "summary" ? "summaries" : "articles"})/`), "").replace(/\.md$/i, "");
26669
- const filePath = resolve17(root, `${key}.md`);
26498
+ const filePath = resolve18(root, `${key}.md`);
26670
26499
  const relativePath = relative2(root, filePath);
26671
26500
  if (relativePath.startsWith("..") || relativePath.startsWith("/")) {
26672
26501
  return { content: [{ type: "text", text: "Invalid wiki document key." }], isError: true };
@@ -26690,18 +26519,18 @@ function wikiLastConversation(args) {
26690
26519
  return { content: [{ type: "text", text: "No topic specified." }] };
26691
26520
  }
26692
26521
  const name = slugify(topicNameFrom(rawTopic));
26693
- const archiveDir = resolve17(runtime().archiveDir, name);
26522
+ const archiveDir = resolve18(runtime().archiveDir, name);
26694
26523
  try {
26695
- let files = existsSync28(archiveDir) ? readdirSync10(archiveDir).filter((f) => f.endsWith(".jsonl")).sort().reverse() : [];
26524
+ let files = existsSync29(archiveDir) ? readdirSync10(archiveDir).filter((f) => f.endsWith(".jsonl")).sort().reverse() : [];
26696
26525
  if (files.length === 0) {
26697
- files = existsSync28(runtime().archiveDir) ? readdirSync10(runtime().archiveDir).filter((f) => f.startsWith(name) && f.endsWith(".jsonl")).sort().reverse() : [];
26526
+ files = existsSync29(runtime().archiveDir) ? readdirSync10(runtime().archiveDir).filter((f) => f.startsWith(name) && f.endsWith(".jsonl")).sort().reverse() : [];
26698
26527
  }
26699
26528
  if (files.length === 0) {
26700
26529
  return {
26701
26530
  content: [{ type: "text", text: `No archive found for topic: ${rawTopic}` }]
26702
26531
  };
26703
26532
  }
26704
- const actualPath = files[0].includes("/") ? files[0] : resolve17(existsSync28(archiveDir) && readdirSync10(archiveDir).includes(files[0]) ? archiveDir : runtime().archiveDir, files[0]);
26533
+ const actualPath = files[0].includes("/") ? files[0] : resolve18(existsSync29(archiveDir) && readdirSync10(archiveDir).includes(files[0]) ? archiveDir : runtime().archiveDir, files[0]);
26705
26534
  try {
26706
26535
  const raw = readFileSync24(actualPath, "utf-8");
26707
26536
  const lines = raw.trim().split(`
@@ -26774,7 +26603,7 @@ function skillQuery(args) {
26774
26603
  })) {
26775
26604
  if (!entry.isDirectory())
26776
26605
  continue;
26777
- const skillFile = resolve17(runtime().skillsDir, entry.name, "skill.md");
26606
+ const skillFile = resolve18(runtime().skillsDir, entry.name, "skill.md");
26778
26607
  try {
26779
26608
  const text2 = readFileSync24(skillFile, "utf-8");
26780
26609
  const lower = text2.toLowerCase();
@@ -26842,9 +26671,9 @@ ${fresh.join(`
26842
26671
  `)}
26843
26672
  `;
26844
26673
  }
26845
- const skillDir = resolve17(runtime().skillsDir, name);
26674
+ const skillDir = resolve18(runtime().skillsDir, name);
26846
26675
  ensureDir(skillDir);
26847
- const skillPath = resolve17(skillDir, "skill.md");
26676
+ const skillPath = resolve18(skillDir, "skill.md");
26848
26677
  let finalContent = content;
26849
26678
  try {
26850
26679
  const existing = readFileSync24(skillPath, "utf-8");
@@ -26869,7 +26698,7 @@ function writeSummaryDocument(rawTopic, content, dateStr) {
26869
26698
  let counter = 1;
26870
26699
  while (true) {
26871
26700
  try {
26872
- writeFileSync19(resolve17(runtime().summariesDir, summaryName), content, {
26701
+ writeFileSync19(resolve18(runtime().summariesDir, summaryName), content, {
26873
26702
  encoding: "utf-8",
26874
26703
  flag: "wx"
26875
26704
  });
@@ -26886,8 +26715,8 @@ function writeSummaryDocument(rawTopic, content, dateStr) {
26886
26715
  let sqliteUpdated = false;
26887
26716
  if (topicId && setTopicBrief2) {
26888
26717
  try {
26889
- const briefPath = resolve17(runtime().topicsDir, `${fileSlug}.md`);
26890
- let briefMd = existsSync28(briefPath) ? readFileSync24(briefPath, "utf-8") : undefined;
26718
+ const briefPath = resolve18(runtime().topicsDir, `${fileSlug}.md`);
26719
+ let briefMd = existsSync29(briefPath) ? readFileSync24(briefPath, "utf-8") : undefined;
26891
26720
  if (briefMd === undefined && getTopicBrief2 && !getTopicBrief2(fileSlug)?.briefMd) {
26892
26721
  const legacy = getTopicBrief2(topicId)?.briefMd;
26893
26722
  if (legacy)
@@ -26906,9 +26735,9 @@ function writeSummaryDocument(rawTopic, content, dateStr) {
26906
26735
  function writeTopicDocument(rawTopic, content) {
26907
26736
  const topicId = runtime().topicId;
26908
26737
  const fileSlug = wikiBriefStorageKey(rawTopic, topicId);
26909
- const briefPath = resolve17(runtime().topicsDir, `${fileSlug}.md`);
26910
- ensureDir(dirname17(briefPath));
26911
- const existed = existsSync28(briefPath);
26738
+ const briefPath = resolve18(runtime().topicsDir, `${fileSlug}.md`);
26739
+ ensureDir(dirname18(briefPath));
26740
+ const existed = existsSync29(briefPath);
26912
26741
  writeFileSync19(briefPath, content, "utf-8");
26913
26742
  const setTopicBrief2 = runtime().host.setTopicBrief;
26914
26743
  let sqliteUpdated = false;
@@ -26921,9 +26750,9 @@ function writeTopicDocument(rawTopic, content) {
26921
26750
  return { slug: fileSlug, existed, sqliteUpdated };
26922
26751
  }
26923
26752
  function writeArticleDocument(slug, content) {
26924
- const articlePath = resolve17(runtime().articlesDir, `${slug}.md`);
26925
- ensureDir(dirname17(articlePath));
26926
- const existed = existsSync28(articlePath);
26753
+ const articlePath = resolve18(runtime().articlesDir, `${slug}.md`);
26754
+ ensureDir(dirname18(articlePath));
26755
+ const existed = existsSync29(articlePath);
26927
26756
  atomicWriteFile(articlePath, content);
26928
26757
  return { existed };
26929
26758
  }
@@ -27055,21 +26884,21 @@ function collectIndexableDocuments() {
27055
26884
  }
27056
26885
  for (const entry of entries) {
27057
26886
  if (entry.isFile() && entry.name.endsWith(".md")) {
27058
- add(kind, entry.name.replace(/\.md$/i, ""), join37(root, entry.name));
26887
+ add(kind, entry.name.replace(/\.md$/i, ""), join38(root, entry.name));
27059
26888
  continue;
27060
26889
  }
27061
26890
  if (!entry.isDirectory() || !allowSubdirectories)
27062
26891
  continue;
27063
26892
  let nested;
27064
26893
  try {
27065
- nested = readdirSync10(join37(root, entry.name), { withFileTypes: true });
26894
+ nested = readdirSync10(join38(root, entry.name), { withFileTypes: true });
27066
26895
  } catch {
27067
26896
  continue;
27068
26897
  }
27069
26898
  for (const file of nested) {
27070
26899
  if (!file.isFile() || !file.name.endsWith(".md"))
27071
26900
  continue;
27072
- add(kind, `${entry.name}/${file.name.replace(/\.md$/i, "")}`, join37(root, entry.name, file.name));
26901
+ add(kind, `${entry.name}/${file.name.replace(/\.md$/i, "")}`, join38(root, entry.name, file.name));
27073
26902
  }
27074
26903
  }
27075
26904
  }
@@ -27126,7 +26955,7 @@ function indexRowOrPartialWrite(label, options) {
27126
26955
  function upsertIndexRow(options) {
27127
26956
  const { kind, slug, description, date, section } = options;
27128
26957
  const indexPath = wikiIndexPath(kind);
27129
- ensureDir(dirname17(indexPath));
26958
+ ensureDir(dirname18(indexPath));
27130
26959
  const releaseLock = acquireFileLock(indexPath);
27131
26960
  try {
27132
26961
  let index;
@@ -27183,7 +27012,7 @@ function upsertIndexRow(options) {
27183
27012
  }
27184
27013
  function wikiDocumentExists(kind, slug) {
27185
27014
  const root = kind === "article" ? runtime().articlesDir : kind === "summary" ? runtime().summariesDir : kind === "topic" ? runtime().topicsDir : runtime().skillsDir;
27186
- const filePath = resolve17(root, `${slug}.md`);
27015
+ const filePath = resolve18(root, `${slug}.md`);
27187
27016
  if (relative2(root, filePath).startsWith(".."))
27188
27017
  return false;
27189
27018
  try {
@@ -27235,7 +27064,7 @@ function indexUpsert(args) {
27235
27064
  }
27236
27065
  function createWikiMcpServer(context, host2) {
27237
27066
  const surface = context.surface ?? "all";
27238
- const wikiDir = resolve17(host2.wikiRoot);
27067
+ const wikiDir = resolve18(host2.wikiRoot);
27239
27068
  const current3 = {
27240
27069
  userId: context.userId,
27241
27070
  currentTopicId: context.currentTopicId ?? context.topicId,
@@ -27243,11 +27072,11 @@ function createWikiMcpServer(context, host2) {
27243
27072
  surface,
27244
27073
  host: host2,
27245
27074
  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")
27075
+ skillsDir: resolve18(wikiDir, "skills"),
27076
+ topicsDir: resolve18(wikiDir, "topic"),
27077
+ summariesDir: resolve18(wikiDir, "summaries"),
27078
+ articlesDir: resolve18(wikiDir, "articles"),
27079
+ archiveDir: resolve18(wikiDir, "archive")
27251
27080
  };
27252
27081
  const tools = surface === "wiki" ? WIKI_TOOLS : surface === "skills" ? SKILL_TOOLS : [...WIKI_TOOLS, ...SKILL_TOOLS];
27253
27082
  for (const directory of [
@@ -27570,7 +27399,7 @@ __export(exports_activity_log, {
27570
27399
  readSentFilesForTopic: () => readSentFilesForTopic
27571
27400
  });
27572
27401
  import {
27573
- existsSync as existsSync29,
27402
+ existsSync as existsSync30,
27574
27403
  mkdirSync as mkdirSync26,
27575
27404
  readdirSync as readdirSync11,
27576
27405
  renameSync as renameSync13,
@@ -27578,7 +27407,7 @@ import {
27578
27407
  unlinkSync as unlinkSync20,
27579
27408
  writeFileSync as writeFileSync20
27580
27409
  } from "fs";
27581
- import { basename as basename8, join as join38 } from "path";
27410
+ import { basename as basename8, join as join39 } from "path";
27582
27411
  function startLogRotation() {
27583
27412
  if (rotationTimer)
27584
27413
  return;
@@ -27592,7 +27421,7 @@ function writeLog(entry) {
27592
27421
  mkdirSync26(logDir, { recursive: true });
27593
27422
  const safeSession = sanitizeTopicName(entry.session);
27594
27423
  const sidShort = entry.sessionId ? entry.sessionId.slice(0, 8) : "new";
27595
- const file = join38(logDir, `${entry.userId}_${safeSession}_${sidShort}.jsonl`);
27424
+ const file = join39(logDir, `${entry.userId}_${safeSession}_${sidShort}.jsonl`);
27596
27425
  try {
27597
27426
  const stat = statSync16(file);
27598
27427
  if (stat.size >= MAX_FILE_SIZE) {
@@ -27612,7 +27441,7 @@ function rotateOldLogs() {
27612
27441
  try {
27613
27442
  const files = readdirSync11(logDir).filter((f) => f.endsWith(".jsonl")).map((f) => {
27614
27443
  try {
27615
- const stat = statSync16(join38(logDir, f));
27444
+ const stat = statSync16(join39(logDir, f));
27616
27445
  return { name: f, size: stat.size, mtimeMs: stat.mtimeMs };
27617
27446
  } catch {
27618
27447
  return null;
@@ -27628,7 +27457,7 @@ function rotateOldLogs() {
27628
27457
  if (freed >= toFree)
27629
27458
  break;
27630
27459
  try {
27631
- unlinkSync20(join38(logDir, file.name));
27460
+ unlinkSync20(join39(logDir, file.name));
27632
27461
  freed += file.size;
27633
27462
  logger.info({ file: file.name, sizeKB: (file.size / 1024).toFixed(0) }, "Rotated out old log");
27634
27463
  } catch (e) {
@@ -27640,9 +27469,9 @@ function rotateOldLogs() {
27640
27469
  }
27641
27470
  }
27642
27471
  function writeSentFileLog(entry) {
27643
- const dir = join38(resolveStorageUsersLogDir(), String(entry.userId));
27472
+ const dir = join39(resolveStorageUsersLogDir(), String(entry.userId));
27644
27473
  mkdirSync26(dir, { recursive: true });
27645
- const file = join38(dir, "sent-files.jsonl");
27474
+ const file = join39(dir, "sent-files.jsonl");
27646
27475
  try {
27647
27476
  appendJsonlEntry(file, { ...entry, fileName: basename8(entry.filePath) });
27648
27477
  } catch (e) {
@@ -27653,8 +27482,8 @@ function entryMatchesTopic(e, topicName) {
27653
27482
  return e.topicName === topicName;
27654
27483
  }
27655
27484
  function readSentFilesForTopic(userId, topicName) {
27656
- const file = join38(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
27657
- if (!existsSync29(file))
27485
+ const file = join39(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
27486
+ if (!existsSync30(file))
27658
27487
  return [];
27659
27488
  try {
27660
27489
  return readJsonlLines(file).map((line) => JSON.parse(line)).filter((e) => entryMatchesTopic(e, topicName));
@@ -27664,8 +27493,8 @@ function readSentFilesForTopic(userId, topicName) {
27664
27493
  }
27665
27494
  }
27666
27495
  function removeSentFilesForTopic(userId, topicName) {
27667
- const file = join38(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
27668
- if (!existsSync29(file))
27496
+ const file = join39(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
27497
+ if (!existsSync30(file))
27669
27498
  return;
27670
27499
  try {
27671
27500
  const remaining = readJsonlLines(file).filter((line) => {
@@ -28429,7 +28258,7 @@ var init_storage_public = __esm(async () => {
28429
28258
  });
28430
28259
 
28431
28260
  // ../../packages/mcp/src/hosted-surfaces.ts
28432
- import { resolve as resolve18 } from "path";
28261
+ import { resolve as resolve19 } from "path";
28433
28262
  function isActiveHostedMcpSurface(surface) {
28434
28263
  return ACTIVE_HOSTED_MCP_SURFACES.includes(surface);
28435
28264
  }
@@ -28486,23 +28315,11 @@ async function buildHostedSurfaceServer(surface, context) {
28486
28315
  });
28487
28316
  }
28488
28317
  case "vault": {
28489
- const [{ createVaultMcpServer: createVaultMcpServer2 }, vault, { logger: logger2 }] = await Promise.all([
28318
+ const [{ createVaultMcpServer: createVaultMcpServer2 }, vault] = await Promise.all([
28490
28319
  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)
28320
+ init_vault_public().then(() => exports_vault_public)
28493
28321
  ]);
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
- });
28322
+ return createVaultMcpServer2({ userId: context.userId }, { list: vault.vaultList });
28506
28323
  }
28507
28324
  case "wiki":
28508
28325
  case "skills": {
@@ -28525,7 +28342,7 @@ async function buildHostedSurfaceServer(surface, context) {
28525
28342
  ...topicId ? { topicId } : {},
28526
28343
  surface
28527
28344
  }, {
28528
- wikiRoot: resolve18(WORKSPACE_DIR2, "wiki"),
28345
+ wikiRoot: resolve19(WORKSPACE_DIR2, "wiki"),
28529
28346
  getTopicBrief: storage.getTopicBrief,
28530
28347
  resolveTopicBrief: accessibleTopicBrief,
28531
28348
  setTopicBrief: storage.setTopicBrief
@@ -28656,10 +28473,10 @@ var init_sse_transport = () => {};
28656
28473
  // ../../packages/mcp/src/server.ts
28657
28474
  import { createHash as createHash10, randomUUID as randomUUID23 } from "crypto";
28658
28475
  import { realpathSync as realpathSync6, statSync as statSync17 } from "fs";
28659
- import { basename as basename9, extname as extname3, resolve as resolve19 } from "path";
28476
+ import { basename as basename9, extname as extname3, resolve as resolve20 } from "path";
28660
28477
  import { McpServer as McpServer8 } from "@modelcontextprotocol/sdk/server/mcp.js";
28661
28478
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
28662
- import { z as z13 } from "zod";
28479
+ import { z as z12 } from "zod";
28663
28480
  function requireTopicAccess(ctx) {
28664
28481
  const topic = getTopic(ctx.topicId);
28665
28482
  if (!topic)
@@ -28670,8 +28487,8 @@ function requireTopicAccess(ctx) {
28670
28487
  return { topic };
28671
28488
  }
28672
28489
  function isPathInside2(baseDir, filePath) {
28673
- const cwd = resolve19(baseDir);
28674
- const normalized = resolve19(filePath);
28490
+ const cwd = resolve20(baseDir);
28491
+ const normalized = resolve20(filePath);
28675
28492
  try {
28676
28493
  const realCwd = realpathSync6(cwd);
28677
28494
  const real = realpathSync6(normalized);
@@ -28687,7 +28504,7 @@ function localFileInfo(ctx, filePath) {
28687
28504
  if (!isPathInside2(ctx.cwd, filePath)) {
28688
28505
  return { error: "Access denied. File must be within the topic workspace." };
28689
28506
  }
28690
- const normalizedPath = resolve19(filePath);
28507
+ const normalizedPath = resolve20(filePath);
28691
28508
  if (isSensitivePath(normalizedPath)) {
28692
28509
  return { error: "Access denied. Path matches the sensitive-file blacklist." };
28693
28510
  }
@@ -28879,8 +28696,8 @@ function buildNegotiumMcpServer(ctx) {
28879
28696
  }
28880
28697
  }
28881
28698
  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 }) => {
28699
+ 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));
28700
+ 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
28701
  const results = await Promise.all(file_paths.map((filePath) => deliverFile(ctx, filePath)));
28885
28702
  const hasError = results.some((result) => ("isError" in result) && result.isError);
28886
28703
  return {
@@ -29164,37 +28981,37 @@ var init_src2 = __esm(async () => {
29164
28981
 
29165
28982
  // ../../packages/mcp-host/src/paths.ts
29166
28983
  import { homedir as homedir9 } from "os";
29167
- import { resolve as resolve20 } from "path";
28984
+ import { resolve as resolve21 } from "path";
29168
28985
  function stateDir() {
29169
28986
  const env = process.env.NEGOTIUM_STATE_DIR?.trim();
29170
- return env ? resolve20(env) : resolve20(homedir9(), ".negotium");
28987
+ return env ? resolve21(env) : resolve21(homedir9(), ".negotium");
29171
28988
  }
29172
28989
  function defaultPortsDir() {
29173
- return resolve20(stateDir(), "run", "mcp-ports");
28990
+ return resolve21(stateDir(), "run", "mcp-ports");
29174
28991
  }
29175
28992
  function defaultManifestFile() {
29176
- return resolve20(stateDir(), "data", "mcp-manifest.json");
28993
+ return resolve21(stateDir(), "data", "mcp-manifest.json");
29177
28994
  }
29178
28995
  var init_paths = () => {};
29179
28996
 
29180
28997
  // ../../packages/mcp-host/src/spec.ts
29181
- import { z as z14 } from "zod";
28998
+ import { z as z13 } from "zod";
29182
28999
  var KEY_PATTERN, portSchema, mcpServerSpecSchema;
29183
29000
  var init_spec = __esm(() => {
29184
29001
  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()
29002
+ portSchema = z13.number().int().min(1).max(65535);
29003
+ mcpServerSpecSchema = z13.object({
29004
+ 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"),
29005
+ transport: z13.enum(["stdio", "http"]),
29006
+ command: z13.string().min(1),
29007
+ args: z13.array(z13.string()).optional(),
29008
+ env: z13.record(z13.string(), z13.string()).optional(),
29009
+ cwd: z13.string().optional(),
29010
+ portRange: z13.object({ base: portSchema, max: portSchema }).optional(),
29011
+ scope: z13.enum(["node", "instance"]),
29012
+ readyTimeoutMs: z13.number().int().positive().optional(),
29013
+ idleEvictMs: z13.number().int().positive().optional(),
29014
+ healthIntervalMs: z13.number().int().positive().optional()
29198
29015
  }).superRefine((spec, ctx) => {
29199
29016
  if (spec.transport === "http") {
29200
29017
  if (!spec.portRange) {
@@ -29215,9 +29032,9 @@ var init_spec = __esm(() => {
29215
29032
  });
29216
29033
 
29217
29034
  // ../../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";
29035
+ import { existsSync as existsSync31, mkdirSync as mkdirSync27, readFileSync as readFileSync25, renameSync as renameSync14, writeFileSync as writeFileSync21 } from "fs";
29036
+ import { dirname as dirname19 } from "path";
29037
+ import { z as z14 } from "zod";
29221
29038
 
29222
29039
  class McpManifest {
29223
29040
  file;
@@ -29260,7 +29077,7 @@ class McpManifest {
29260
29077
  return this.entries.get(key)?.enabled ?? false;
29261
29078
  }
29262
29079
  load() {
29263
- if (!existsSync30(this.file))
29080
+ if (!existsSync31(this.file))
29264
29081
  return;
29265
29082
  let parsed;
29266
29083
  try {
@@ -29276,7 +29093,7 @@ class McpManifest {
29276
29093
  }
29277
29094
  }
29278
29095
  save() {
29279
- mkdirSync27(dirname18(this.file), { recursive: true });
29096
+ mkdirSync27(dirname19(this.file), { recursive: true });
29280
29097
  const payload = {
29281
29098
  version: 1,
29282
29099
  servers: [...this.entries.values()]
@@ -29291,13 +29108,13 @@ var manifestEntrySchema, manifestFileSchema;
29291
29108
  var init_manifest = __esm(() => {
29292
29109
  init_paths();
29293
29110
  init_spec();
29294
- manifestEntrySchema = z15.object({
29111
+ manifestEntrySchema = z14.object({
29295
29112
  spec: mcpServerSpecSchema,
29296
- enabled: z15.boolean()
29113
+ enabled: z14.boolean()
29297
29114
  });
29298
- manifestFileSchema = z15.object({
29299
- version: z15.literal(1),
29300
- servers: z15.array(manifestEntrySchema)
29115
+ manifestFileSchema = z14.object({
29116
+ version: z14.literal(1),
29117
+ servers: z14.array(manifestEntrySchema)
29301
29118
  });
29302
29119
  });
29303
29120
 
@@ -29310,7 +29127,7 @@ function sanitizePathComponent(name) {
29310
29127
  }
29311
29128
 
29312
29129
  // ../../packages/mcp-host/src/manager.ts
29313
- import { execFileSync as execFileSync8, spawn as spawn8 } from "child_process";
29130
+ import { execFileSync as execFileSync8, spawn as spawn7 } from "child_process";
29314
29131
  import {
29315
29132
  mkdirSync as mkdirSync28,
29316
29133
  readdirSync as readdirSync12,
@@ -29319,8 +29136,8 @@ import {
29319
29136
  unlinkSync as unlinkSync21,
29320
29137
  writeFileSync as writeFileSync22
29321
29138
  } from "fs";
29322
- import { connect, createServer as createServer3 } from "net";
29323
- import { join as join39 } from "path";
29139
+ import { connect, createServer as createServer4 } from "net";
29140
+ import { join as join40 } from "path";
29324
29141
  function regKey(key, instanceKey) {
29325
29142
  return `${key}\x00${instanceKey}`;
29326
29143
  }
@@ -29336,16 +29153,16 @@ function resolveInstanceKey(spec, instanceKey) {
29336
29153
  return instanceKey;
29337
29154
  }
29338
29155
  function canBind(port) {
29339
- return new Promise((resolve21) => {
29340
- const srv = createServer3();
29341
- srv.once("error", () => resolve21(false));
29156
+ return new Promise((resolve22) => {
29157
+ const srv = createServer4();
29158
+ srv.once("error", () => resolve22(false));
29342
29159
  srv.listen({ port, host: "127.0.0.1", exclusive: true }, () => {
29343
- srv.close(() => resolve21(true));
29160
+ srv.close(() => resolve22(true));
29344
29161
  });
29345
29162
  });
29346
29163
  }
29347
29164
  function tcpProbe(port, timeoutMs = PROBE_TIMEOUT_MS2) {
29348
- return new Promise((resolve21) => {
29165
+ return new Promise((resolve22) => {
29349
29166
  const sock = connect({ port, host: "127.0.0.1" });
29350
29167
  let settled = false;
29351
29168
  const done = (ok3) => {
@@ -29353,7 +29170,7 @@ function tcpProbe(port, timeoutMs = PROBE_TIMEOUT_MS2) {
29353
29170
  return;
29354
29171
  settled = true;
29355
29172
  sock.destroy();
29356
- resolve21(ok3);
29173
+ resolve22(ok3);
29357
29174
  };
29358
29175
  sock.once("connect", () => done(true));
29359
29176
  sock.once("error", () => done(false));
@@ -29381,12 +29198,12 @@ function killProcessTree(pid) {
29381
29198
  function waitExit(proc, timeoutMs) {
29382
29199
  if (proc.exitCode !== null || proc.signalCode !== null)
29383
29200
  return Promise.resolve(true);
29384
- return new Promise((resolve21) => {
29385
- const timer = setTimeout(() => resolve21(false), timeoutMs);
29201
+ return new Promise((resolve22) => {
29202
+ const timer = setTimeout(() => resolve22(false), timeoutMs);
29386
29203
  timer.unref?.();
29387
29204
  const settle = () => {
29388
29205
  clearTimeout(timer);
29389
- resolve21(true);
29206
+ resolve22(true);
29390
29207
  };
29391
29208
  proc.once("exit", settle);
29392
29209
  proc.once("error", settle);
@@ -29520,7 +29337,7 @@ class McpHost {
29520
29337
  let spawnFailed = false;
29521
29338
  let proc;
29522
29339
  try {
29523
- proc = spawn8(spec.command, args, {
29340
+ proc = spawn7(spec.command, args, {
29524
29341
  stdio: "ignore",
29525
29342
  detached: false,
29526
29343
  cwd: spec.cwd,
@@ -29622,7 +29439,7 @@ class McpHost {
29622
29439
  if (file.includes(".tmp-"))
29623
29440
  continue;
29624
29441
  try {
29625
- const port = Number.parseInt(readFileSync26(join39(this.portsDir, file), "utf8").trim(), 10);
29442
+ const port = Number.parseInt(readFileSync26(join40(this.portsDir, file), "utf8").trim(), 10);
29626
29443
  if (!Number.isNaN(port))
29627
29444
  claims.set(file, port);
29628
29445
  } catch {}
@@ -29632,7 +29449,7 @@ class McpHost {
29632
29449
  writePortFile(fileName, port) {
29633
29450
  try {
29634
29451
  mkdirSync28(this.portsDir, { recursive: true });
29635
- const file = join39(this.portsDir, fileName);
29452
+ const file = join40(this.portsDir, fileName);
29636
29453
  const tmp = `${file}.tmp-${process.pid}`;
29637
29454
  writeFileSync22(tmp, String(port));
29638
29455
  renameSync15(tmp, file);
@@ -29642,7 +29459,7 @@ class McpHost {
29642
29459
  }
29643
29460
  deletePortFile(fileName) {
29644
29461
  try {
29645
- unlinkSync21(join39(this.portsDir, fileName));
29462
+ unlinkSync21(join40(this.portsDir, fileName));
29646
29463
  } catch {}
29647
29464
  }
29648
29465
  cleanupRunning(rkey, inst) {
@@ -29729,7 +29546,7 @@ var DEFAULT_READY_TIMEOUT_MS = 20000, READY_POLL_MS = 150, KILL_GRACE_MS = 5000,
29729
29546
  fn(`[mcp-host] ${msg}`, ctx);
29730
29547
  else
29731
29548
  fn(`[mcp-host] ${msg}`);
29732
- }, delay2 = (ms) => new Promise((resolve21) => setTimeout(resolve21, ms));
29549
+ }, delay2 = (ms) => new Promise((resolve22) => setTimeout(resolve22, ms));
29733
29550
  var init_manager3 = __esm(() => {
29734
29551
  init_manifest();
29735
29552
  init_paths();
@@ -29747,14 +29564,14 @@ var init_src3 = __esm(() => {
29747
29564
  import { randomUUID as randomUUID24 } from "crypto";
29748
29565
  import {
29749
29566
  copyFileSync as copyFileSync4,
29750
- existsSync as existsSync31,
29567
+ existsSync as existsSync32,
29751
29568
  mkdirSync as mkdirSync29,
29752
29569
  readFileSync as readFileSync27,
29753
29570
  rmSync as rmSync9,
29754
29571
  statSync as statSync18,
29755
29572
  writeFileSync as writeFileSync23
29756
29573
  } from "fs";
29757
- import { basename as basename10, extname as extname4, join as join40 } from "path";
29574
+ import { basename as basename10, extname as extname4, join as join41 } from "path";
29758
29575
  function safeExtension(filename) {
29759
29576
  const extension = extname4(basename10(filename));
29760
29577
  return /^\.[A-Za-z0-9]{1,16}$/.test(extension) ? extension.toLowerCase() : "";
@@ -29772,14 +29589,14 @@ function contentDisposition(filename) {
29772
29589
 
29773
29590
  class NodeFileStore {
29774
29591
  uploadDir;
29775
- constructor(uploadDir = join40(DATA_DIR, "uploads")) {
29592
+ constructor(uploadDir = join41(DATA_DIR, "uploads")) {
29776
29593
  this.uploadDir = uploadDir;
29777
29594
  }
29778
29595
  #ensureDir() {
29779
29596
  mkdirSync29(this.uploadDir, { recursive: true });
29780
29597
  }
29781
29598
  #metadataPath(fileId) {
29782
- return join40(this.uploadDir, `${fileId}.meta.json`);
29599
+ return join41(this.uploadDir, `${fileId}.meta.json`);
29783
29600
  }
29784
29601
  #metadata(fileId) {
29785
29602
  if (!FILE_ID_RE.test(fileId))
@@ -29808,7 +29625,7 @@ class NodeFileStore {
29808
29625
  hooks = {
29809
29626
  resolveAttachmentByFileId: (fileId) => {
29810
29627
  const metadata = this.#metadata(fileId);
29811
- if (!metadata || !existsSync31(join40(this.uploadDir, metadata.savedName)))
29628
+ if (!metadata || !existsSync32(join41(this.uploadDir, metadata.savedName)))
29812
29629
  return null;
29813
29630
  return this.#attachment(fileId, metadata);
29814
29631
  },
@@ -29816,8 +29633,8 @@ class NodeFileStore {
29816
29633
  const metadata = this.#metadata(fileId);
29817
29634
  if (!metadata)
29818
29635
  return null;
29819
- const path = join40(this.uploadDir, metadata.savedName);
29820
- return existsSync31(path) ? path : null;
29636
+ const path = join41(this.uploadDir, metadata.savedName);
29637
+ return existsSync32(path) ? path : null;
29821
29638
  },
29822
29639
  storeLocalFileAsUpload: (absPath, access = {}) => this.store(absPath, access),
29823
29640
  deleteFilesForTopic: (topicId) => this.deleteForTopic(topicId)
@@ -29831,11 +29648,11 @@ class NodeFileStore {
29831
29648
  const mimeType = file.type || MIME_BY_EXT2[extension] || "application/octet-stream";
29832
29649
  const existing = this.#metadata(fileId);
29833
29650
  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));
29651
+ 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
29652
  return matches ? this.#attachment(fileId, existing) : null;
29836
29653
  }
29837
29654
  const savedName = `${fileId}${extension}`;
29838
- const savedPath = join40(this.uploadDir, savedName);
29655
+ const savedPath = join41(this.uploadDir, savedName);
29839
29656
  try {
29840
29657
  const sizeBytes = await Bun.write(savedPath, file);
29841
29658
  if (sizeBytes !== file.size)
@@ -29860,14 +29677,14 @@ class NodeFileStore {
29860
29677
  }
29861
29678
  allows(fileId, access) {
29862
29679
  const metadata = this.#metadata(fileId);
29863
- return Boolean(metadata && metadata.ownerUserId === access.ownerUserId && metadata.topicId === access.topicId && existsSync31(join40(this.uploadDir, metadata.savedName)));
29680
+ return Boolean(metadata && metadata.ownerUserId === access.ownerUserId && metadata.topicId === access.topicId && existsSync32(join41(this.uploadDir, metadata.savedName)));
29864
29681
  }
29865
29682
  store(absPath, access = {}) {
29866
29683
  this.#ensureDir();
29867
29684
  const fileId = randomUUID24();
29868
29685
  const extension = safeExtension(absPath);
29869
29686
  const savedName = `${fileId}${extension}`;
29870
- const savedPath = join40(this.uploadDir, savedName);
29687
+ const savedPath = join41(this.uploadDir, savedName);
29871
29688
  try {
29872
29689
  const stats = statSync18(absPath);
29873
29690
  if (!stats.isFile() || stats.size > MAX_NODE_UPLOAD_BYTES)
@@ -29899,8 +29716,8 @@ class NodeFileStore {
29899
29716
  const allowed = metadata.visibility === "workspace" || metadata.ownerUserId === userId || Boolean(topic && isParticipant(topic, userId));
29900
29717
  if (!allowed)
29901
29718
  return null;
29902
- const path = join40(this.uploadDir, metadata.savedName);
29903
- if (!existsSync31(path))
29719
+ const path = join41(this.uploadDir, metadata.savedName);
29720
+ if (!existsSync32(path))
29904
29721
  return null;
29905
29722
  return new Response(Bun.file(path), {
29906
29723
  headers: {
@@ -29918,7 +29735,7 @@ class NodeFileStore {
29918
29735
  const metadata = this.#metadata(fileId);
29919
29736
  if (metadata?.topicId !== topicId)
29920
29737
  continue;
29921
- rmSync9(join40(this.uploadDir, metadata.savedName), { force: true });
29738
+ rmSync9(join41(this.uploadDir, metadata.savedName), { force: true });
29922
29739
  rmSync9(this.#metadataPath(fileId), { force: true });
29923
29740
  }
29924
29741
  }
@@ -30070,14 +29887,14 @@ function createPollingSseStream(req, options) {
30070
29887
  import { randomUUID as randomUUID25, timingSafeEqual as timingSafeEqual4 } from "crypto";
30071
29888
  import {
30072
29889
  chmodSync as chmodSync6,
30073
- existsSync as existsSync32,
29890
+ existsSync as existsSync33,
30074
29891
  mkdirSync as mkdirSync30,
30075
29892
  readFileSync as readFileSync28,
30076
29893
  renameSync as renameSync16,
30077
29894
  unlinkSync as unlinkSync22,
30078
29895
  writeFileSync as writeFileSync24
30079
29896
  } from "fs";
30080
- import { dirname as dirname19, resolve as resolve21 } from "path";
29897
+ import { dirname as dirname20, resolve as resolve22 } from "path";
30081
29898
  function jsonError(status, error2) {
30082
29899
  return Response.json({ ok: false, error: error2 }, { status });
30083
29900
  }
@@ -30906,7 +30723,7 @@ function writeNodeDaemonInfo(port, startedAt) {
30906
30723
  stateDir: STATE_DIR,
30907
30724
  startedAt
30908
30725
  };
30909
- mkdirSync30(dirname19(NODE_DAEMON_INFO_PATH), { recursive: true });
30726
+ mkdirSync30(dirname20(NODE_DAEMON_INFO_PATH), { recursive: true });
30910
30727
  const temporary = `${NODE_DAEMON_INFO_PATH}.${process.pid}.${randomUUID25()}.tmp`;
30911
30728
  writeFileSync24(temporary, `${JSON.stringify(info, null, 2)}
30912
30729
  `, { mode: 384 });
@@ -30916,7 +30733,7 @@ function writeNodeDaemonInfo(port, startedAt) {
30916
30733
  return info;
30917
30734
  }
30918
30735
  function readNodeDaemonInfo() {
30919
- if (!existsSync32(NODE_DAEMON_INFO_PATH))
30736
+ if (!existsSync33(NODE_DAEMON_INFO_PATH))
30920
30737
  return null;
30921
30738
  try {
30922
30739
  const value = JSON.parse(readFileSync28(NODE_DAEMON_INFO_PATH, "utf8"));
@@ -31001,7 +30818,7 @@ var init_control = __esm(async () => {
31001
30818
  await init_node_host();
31002
30819
  await init_files();
31003
30820
  NODE_RUNTIME_CONTRACT_BASE_PATH = `${NODE_CONTROL_BASE_PATH}/runtime/v1`;
31004
- NODE_DAEMON_INFO_PATH = resolve21(RUN_DIR, "node-daemon.json");
30821
+ NODE_DAEMON_INFO_PATH = resolve22(RUN_DIR, "node-daemon.json");
31005
30822
  NODE_VERSION = NEGOTIUM_VERSION;
31006
30823
  ControlRequestError = class ControlRequestError extends Error {
31007
30824
  constructor(message) {
@@ -31202,9 +31019,9 @@ var init_schedule = __esm(() => {
31202
31019
  });
31203
31020
 
31204
31021
  // ../../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";
31022
+ import { spawn as spawn8 } from "child_process";
31023
+ import { existsSync as existsSync34, mkdirSync as mkdirSync31, readdirSync as readdirSync13 } from "fs";
31024
+ import { resolve as resolve23, sep as sep2 } from "path";
31208
31025
  function validateCronScriptName(script) {
31209
31026
  if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]*\.py$/.test(script) || script.includes("..") || script.includes("/") || script.includes("\\")) {
31210
31027
  return { ok: false, error: `script must be a plain .py filename under ${CRON_JOBS_DIR}` };
@@ -31215,7 +31032,7 @@ function resolveCronScriptPath(script) {
31215
31032
  const valid = validateCronScriptName(script);
31216
31033
  if (!valid.ok)
31217
31034
  throw new Error(valid.error);
31218
- const path = resolve22(CRON_JOBS_DIR, script);
31035
+ const path = resolve23(CRON_JOBS_DIR, script);
31219
31036
  if (!path.startsWith(`${CRON_JOBS_DIR}${sep2}`))
31220
31037
  throw new Error("script path escaped jobs directory");
31221
31038
  return path;
@@ -31226,7 +31043,7 @@ function listCronScripts() {
31226
31043
  }
31227
31044
  function cronScriptExists(script) {
31228
31045
  try {
31229
- return existsSync33(resolveCronScriptPath(script));
31046
+ return existsSync34(resolveCronScriptPath(script));
31230
31047
  } catch {
31231
31048
  return false;
31232
31049
  }
@@ -31258,14 +31075,14 @@ async function runCronPromptScript(options) {
31258
31075
  if (options.signal?.aborted)
31259
31076
  throw new CronScriptError("script run aborted");
31260
31077
  const scriptPath = resolveCronScriptPath(options.script);
31261
- if (!existsSync33(scriptPath))
31078
+ if (!existsSync34(scriptPath))
31262
31079
  throw new CronScriptError(`cron script not found: ${options.script}`);
31263
31080
  const timeoutMs = Math.max(5000, options.timeoutMs ?? DEFAULT_SCRIPT_TIMEOUT_MS);
31264
31081
  const outputLimitBytes = Math.max(1024, options.outputLimitBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES);
31265
31082
  const command = commandForScript(scriptPath);
31266
31083
  let child;
31267
31084
  try {
31268
- child = spawn9(command.executable, command.args, {
31085
+ child = spawn8(command.executable, command.args, {
31269
31086
  cwd: options.cwd,
31270
31087
  detached: true,
31271
31088
  env: {
@@ -31331,7 +31148,7 @@ async function runCronPromptScript(options) {
31331
31148
  var CRON_JOBS_DIR, DEFAULT_SCRIPT_TIMEOUT_MS, DEFAULT_OUTPUT_LIMIT_BYTES, KILL_GRACE_MS2 = 2500, CronScriptError;
31332
31149
  var init_scripts = __esm(async () => {
31333
31150
  await init_cron_host();
31334
- CRON_JOBS_DIR = resolve22(process.env.NEGOTIUM_CRON_JOBS_DIR?.trim() || resolve22(WORKSPACE_DIR, "cron", "jobs"));
31151
+ CRON_JOBS_DIR = resolve23(process.env.NEGOTIUM_CRON_JOBS_DIR?.trim() || resolve23(WORKSPACE_DIR, "cron", "jobs"));
31335
31152
  DEFAULT_SCRIPT_TIMEOUT_MS = 10 * 60000;
31336
31153
  DEFAULT_OUTPUT_LIMIT_BYTES = 1024 * 1024;
31337
31154
  CronScriptError = class CronScriptError extends Error {
@@ -32451,8 +32268,8 @@ var init_scheduler = __esm(async () => {
32451
32268
  });
32452
32269
 
32453
32270
  // ../../packages/module-cron/src/module.ts
32454
- import { existsSync as existsSync34 } from "fs";
32455
- import { fileURLToPath as fileURLToPath2 } from "url";
32271
+ import { existsSync as existsSync35 } from "fs";
32272
+ import { fileURLToPath as fileURLToPath3 } from "url";
32456
32273
  function envMs(name, fallback) {
32457
32274
  const parsed = Number.parseInt(process.env[name] ?? "", 10);
32458
32275
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
@@ -32482,7 +32299,7 @@ function createCronModule(options = {}) {
32482
32299
  if (event.type === "topic-deleted")
32483
32300
  trackCleanup(resetCronTopicContext(event.topicId));
32484
32301
  });
32485
- const mcpServerFile = options.mcpServerFile ?? (existsSync34(SOURCE_MCP_SERVER_FILE) ? SOURCE_MCP_SERVER_FILE : PACKAGED_MCP_SERVER_FILE);
32302
+ const mcpServerFile = options.mcpServerFile ?? (existsSync35(SOURCE_MCP_SERVER_FILE) ? SOURCE_MCP_SERVER_FILE : PACKAGED_MCP_SERVER_FILE);
32486
32303
  const unregisterMcp = host2.registerRuntimeMcpServer("cron-manager", {
32487
32304
  scopes: ["forum", "manager"],
32488
32305
  forumRequired: true,
@@ -32575,8 +32392,8 @@ var init_module = __esm(async () => {
32575
32392
  await init_scheduler();
32576
32393
  await init_scripts();
32577
32394
  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));
32395
+ SOURCE_MCP_SERVER_FILE = fileURLToPath3(new URL("./mcp-server.ts", import.meta.url));
32396
+ PACKAGED_MCP_SERVER_FILE = fileURLToPath3(new URL("./runtime/cron/mcp-server.ts", import.meta.url));
32580
32397
  });
32581
32398
 
32582
32399
  // ../../packages/module-cron/src/summarize.ts
@@ -32762,10 +32579,10 @@ __export(exports_src3, {
32762
32579
  NODE_CONTROL_PROTOCOL_VERSION: () => NODE_CONTROL_PROTOCOL_VERSION,
32763
32580
  NODE_CONTROL_BASE_PATH: () => NODE_CONTROL_BASE_PATH
32764
32581
  });
32765
- import { createServer as createServer4 } from "net";
32582
+ import { createServer as createServer5 } from "net";
32766
32583
  async function availableLoopbackPort() {
32767
- return new Promise((resolve23, reject) => {
32768
- const probe = createServer4();
32584
+ return new Promise((resolve24, reject) => {
32585
+ const probe = createServer5();
32769
32586
  probe.unref();
32770
32587
  probe.once("error", reject);
32771
32588
  probe.listen(0, "127.0.0.1", () => {
@@ -32775,7 +32592,7 @@ async function availableLoopbackPort() {
32775
32592
  if (error2)
32776
32593
  reject(error2);
32777
32594
  else if (port > 0)
32778
- resolve23(port);
32595
+ resolve24(port);
32779
32596
  else
32780
32597
  reject(new Error("failed to allocate a loopback port"));
32781
32598
  });
@@ -32914,8 +32731,8 @@ function startNode(opts = {}) {
32914
32731
  const manifest = new McpManifest;
32915
32732
  const stopSweeper = mcpHost.startSweeper();
32916
32733
  let resolveCompleted;
32917
- const completed = new Promise((resolve23) => {
32918
- resolveCompleted = resolve23;
32734
+ const completed = new Promise((resolve24) => {
32735
+ resolveCompleted = resolve24;
32919
32736
  });
32920
32737
  let advertised = null;
32921
32738
  try {
@@ -32994,7 +32811,7 @@ async function runNodeDaemon(opts = {}) {
32994
32811
  singleton: true
32995
32812
  });
32996
32813
  await node.completed;
32997
- await new Promise((resolve23) => setImmediate(resolve23));
32814
+ await new Promise((resolve24) => setImmediate(resolve24));
32998
32815
  process.exit(0);
32999
32816
  }
33000
32817
  var init_src5 = __esm(async () => {
@@ -34040,9 +33857,9 @@ var init_context_usage = __esm(async () => {
34040
33857
 
34041
33858
  // ../../adapters/terminal/src/path-suggest.ts
34042
33859
  import { execFile as execFile3 } from "child_process";
34043
- import { existsSync as existsSync35, readdirSync as readdirSync14, statSync as statSync19 } from "fs";
33860
+ import { existsSync as existsSync36, readdirSync as readdirSync14, statSync as statSync19 } from "fs";
34044
33861
  import { homedir as homedir10 } from "os";
34045
- import { basename as basename11, dirname as dirname20, join as join41, sep as sep3 } from "path";
33862
+ import { basename as basename11, dirname as dirname21, join as join42, sep as sep3 } from "path";
34046
33863
  import { promisify as promisify3 } from "util";
34047
33864
  function indexRecursivePaths(files) {
34048
33865
  const paths = [];
@@ -34143,15 +33960,15 @@ function resolveFragment(frag) {
34143
33960
  else if (frag === "~/")
34144
33961
  path = home;
34145
33962
  else if (frag.startsWith("~/"))
34146
- path = join41(home, frag.slice(2));
33963
+ path = join42(home, frag.slice(2));
34147
33964
  else if (frag.startsWith("/"))
34148
33965
  path = frag;
34149
33966
  else
34150
- path = join41(home, frag);
33967
+ path = join42(home, frag);
34151
33968
  if (frag.endsWith("/") || frag === "" || frag === "~") {
34152
33969
  return { dir: path, prefix: "" };
34153
33970
  }
34154
- return { dir: dirname20(path), prefix: basename11(path) };
33971
+ return { dir: dirname21(path), prefix: basename11(path) };
34155
33972
  }
34156
33973
  function toToken(fullPath, isDir) {
34157
33974
  const home = homedir10();
@@ -34176,7 +33993,7 @@ function rankAndSlice(dir, candidates) {
34176
33993
  });
34177
33994
  return candidates.slice(0, MAX_SUGGESTIONS).map(({ relPath, isDir }) => ({
34178
33995
  label: `${relPath}${isDir ? "/" : ""}`,
34179
- value: toToken(join41(dir, relPath), isDir),
33996
+ value: toToken(join42(dir, relPath), isDir),
34180
33997
  isDir
34181
33998
  }));
34182
33999
  }
@@ -34255,7 +34072,7 @@ function pathSuggestions(lineText, col) {
34255
34072
  let isDir = entry.isDirectory();
34256
34073
  if (!isDir && entry.isSymbolicLink()) {
34257
34074
  try {
34258
- isDir = statSync19(join41(dir, entry.name)).isDirectory();
34075
+ isDir = statSync19(join42(dir, entry.name)).isDirectory();
34259
34076
  } catch {}
34260
34077
  }
34261
34078
  return {
@@ -34282,14 +34099,14 @@ function fragmentToAbsolutePath(frag) {
34282
34099
  if (frag === "~" || frag === "~/")
34283
34100
  return home;
34284
34101
  if (frag.startsWith("~/"))
34285
- return join41(home, frag.slice(2));
34102
+ return join42(home, frag.slice(2));
34286
34103
  if (frag.startsWith("/"))
34287
34104
  return frag;
34288
- return join41(home, frag);
34105
+ return join42(home, frag);
34289
34106
  }
34290
34107
  function fragmentResolves(frag) {
34291
34108
  try {
34292
- return existsSync35(fragmentToAbsolutePath(frag));
34109
+ return existsSync36(fragmentToAbsolutePath(frag));
34293
34110
  } catch {
34294
34111
  return false;
34295
34112
  }
@@ -38193,8 +38010,8 @@ class TerminalApp {
38193
38010
  this.#refreshBackgroundSessions();
38194
38011
  }, 1000);
38195
38012
  this.#backgroundRefreshTimer.unref?.();
38196
- await new Promise((resolve23) => {
38197
- this.#finishRun = resolve23;
38013
+ await new Promise((resolve24) => {
38014
+ this.#finishRun = resolve24;
38198
38015
  });
38199
38016
  } finally {
38200
38017
  this.#running = false;
@@ -40791,7 +40608,7 @@ var init_commands2 = __esm(async () => {
40791
40608
  // ../../adapters/telegram/src/mapping-store.ts
40792
40609
  import { Database as Database2 } from "bun:sqlite";
40793
40610
  import { mkdirSync as mkdirSync32 } from "fs";
40794
- import { dirname as dirname21, join as join42 } from "path";
40611
+ import { dirname as dirname22, join as join43 } from "path";
40795
40612
  function outboxRowToEntry(row) {
40796
40613
  return {
40797
40614
  id: row.id,
@@ -40857,9 +40674,9 @@ function migrateOutboxSchema(db4) {
40857
40674
  db4.run("CREATE INDEX IF NOT EXISTS idx_telegram_outbox_runtime_message ON outbox(runtime_message_id)");
40858
40675
  }
40859
40676
  function openMappingStore(path) {
40860
- const dbPath = path ?? join42(DATA_DIR, "adapter-telegram.db");
40677
+ const dbPath = path ?? join43(DATA_DIR, "adapter-telegram.db");
40861
40678
  if (dbPath !== ":memory:")
40862
- mkdirSync32(dirname21(dbPath), { recursive: true });
40679
+ mkdirSync32(dirname22(dbPath), { recursive: true });
40863
40680
  const db4 = new Database2(dbPath);
40864
40681
  migrateLegacySchema(db4);
40865
40682
  createTables(db4);
@@ -41094,8 +40911,8 @@ ${caption}` : voiceText : caption;
41094
40911
  return;
41095
40912
  }
41096
40913
  let release;
41097
- const ready = new Promise((resolve23) => {
41098
- release = resolve23;
40914
+ const ready = new Promise((resolve24) => {
40915
+ release = resolve24;
41099
40916
  });
41100
40917
  const entry = {
41101
40918
  messages: [msg],
@@ -41217,7 +41034,7 @@ function renderOutbound(text2, maxLen = 4096) {
41217
41034
  }
41218
41035
 
41219
41036
  // ../../adapters/telegram/src/adapter.ts
41220
- import { existsSync as existsSync36 } from "fs";
41037
+ import { existsSync as existsSync37 } from "fs";
41221
41038
  import { basename as basename12, extname as extname5 } from "path";
41222
41039
  function startTelegramAdapter(opts) {
41223
41040
  const { client, forumChatId } = opts;
@@ -41328,7 +41145,7 @@ function startTelegramAdapter(opts) {
41328
41145
  attempt += 1;
41329
41146
  const waitMs = Math.min(5000, 250 * 2 ** Math.min(attempt - 1, 5));
41330
41147
  logger.warn({ err: err2, attempt, waitMs }, "telegram adapter: getMe failed while resolving onboarding identity; retrying");
41331
- await new Promise((resolve23) => setTimeout(resolve23, waitMs));
41148
+ await new Promise((resolve24) => setTimeout(resolve24, waitMs));
41332
41149
  }
41333
41150
  }
41334
41151
  return;
@@ -41715,7 +41532,7 @@ function startTelegramAdapter(opts) {
41715
41532
  logger.warn({ path, chatId }, "telegram adapter: blocked sensitive file path");
41716
41533
  return { ref: null, delivered: false, error: "path matches the sensitive-file blacklist" };
41717
41534
  }
41718
- if (!existsSync36(path)) {
41535
+ if (!existsSync37(path)) {
41719
41536
  const sent = await client.sendMessage(chatId, `File: ${path}`, base).catch(() => null);
41720
41537
  const messageId = sentMessageId(sent);
41721
41538
  return {
@@ -41815,14 +41632,14 @@ function startTelegramAdapter(opts) {
41815
41632
  const sendQueues = new Map;
41816
41633
  function enqueueSend(topicId, task) {
41817
41634
  const prev = sendQueues.get(topicId) ?? Promise.resolve();
41818
- const next = prev.then(() => new Promise((resolve23) => {
41635
+ const next = prev.then(() => new Promise((resolve24) => {
41819
41636
  const timer = setTimeout(() => {
41820
41637
  logger.warn({ topicId, sendTimeoutMs }, "telegram adapter: send timed out \u2014 abandoning it and continuing the queue");
41821
- resolve23();
41638
+ resolve24();
41822
41639
  }, sendTimeoutMs);
41823
41640
  task().catch((err2) => logger.warn({ err: err2, topicId }, "telegram adapter: send task failed")).finally(() => {
41824
41641
  clearTimeout(timer);
41825
- resolve23();
41642
+ resolve24();
41826
41643
  });
41827
41644
  }));
41828
41645
  sendQueues.set(topicId, next);
@@ -42068,7 +41885,7 @@ ${suffix}`;
42068
41885
  continue;
42069
41886
  }
42070
41887
  if (retryDelayMs > 0) {
42071
- await new Promise((resolve23) => setTimeout(resolve23, retryDelayMs));
41888
+ await new Promise((resolve24) => setTimeout(resolve24, retryDelayMs));
42072
41889
  }
42073
41890
  try {
42074
41891
  await client.editMessageText(text2, editOptions);
@@ -42709,8 +42526,8 @@ async function runTelegramCli(args = process.argv.slice(2)) {
42709
42526
  onShutdown("telegram-channel", 100, () => channel.stop());
42710
42527
  onShutdown("telegram-singleton", 90, () => singleton.stop());
42711
42528
  let resolveCompleted;
42712
- const completed = new Promise((resolve23) => {
42713
- resolveCompleted = resolve23;
42529
+ const completed = new Promise((resolve24) => {
42530
+ resolveCompleted = resolve24;
42714
42531
  });
42715
42532
  onShutdown("telegram-completed", -100, resolveCompleted);
42716
42533
  process.stdout.write(`negotium Telegram adapter connected to canonical node pid ${initialNode.info?.pid ?? "unknown"}
@@ -42735,11 +42552,11 @@ var exports_join_status = {};
42735
42552
  __export(exports_join_status, {
42736
42553
  hasConfiguredOtiumJoin: () => hasConfiguredOtiumJoin
42737
42554
  });
42738
- import { existsSync as existsSync37 } from "fs";
42739
- import { resolve as resolve23 } from "path";
42555
+ import { existsSync as existsSync38 } from "fs";
42556
+ import { resolve as resolve24 } from "path";
42740
42557
  function hasConfiguredOtiumJoin() {
42741
42558
  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"));
42559
+ return envJoin || existsSync38(resolve24(DATA_DIR, "otium-join.json"));
42743
42560
  }
42744
42561
  var init_join_status = __esm(() => {
42745
42562
  init_config_public();
@@ -42757,9 +42574,9 @@ function firstCell() {
42757
42574
  return cell;
42758
42575
  return null;
42759
42576
  }
42760
- function attachOtiumCentralCell(join43) {
42761
- cells.set(join43.cellId, {
42762
- join: join43,
42577
+ function attachOtiumCentralCell(join44) {
42578
+ cells.set(join44.cellId, {
42579
+ join: join44,
42763
42580
  nodesCache: null,
42764
42581
  verifyCache: new Map,
42765
42582
  tokenCache: new Map
@@ -42771,10 +42588,10 @@ function detachOtiumCentralCell(cellId) {
42771
42588
  function attachedOtiumCells() {
42772
42589
  return [...cells.values()].map((cell) => cell.join);
42773
42590
  }
42774
- function configureOtiumCentral(join43) {
42591
+ function configureOtiumCentral(join44) {
42775
42592
  cells.clear();
42776
- if (join43)
42777
- attachOtiumCentralCell(join43);
42593
+ if (join44)
42594
+ attachOtiumCentralCell(join44);
42778
42595
  }
42779
42596
  function isOtiumCentralConfigured() {
42780
42597
  return cells.size > 0;
@@ -43188,7 +43005,7 @@ import { createHash as createHash11, randomUUID as randomUUID28 } from "crypto";
43188
43005
  import {
43189
43006
  chmodSync as chmodSync7,
43190
43007
  closeSync as closeSync5,
43191
- existsSync as existsSync38,
43008
+ existsSync as existsSync39,
43192
43009
  fsyncSync as fsyncSync2,
43193
43010
  linkSync,
43194
43011
  lstatSync,
@@ -43201,9 +43018,9 @@ import {
43201
43018
  unlinkSync as unlinkSync23,
43202
43019
  writeFileSync as writeFileSync25
43203
43020
  } from "fs";
43204
- import { dirname as dirname22, resolve as resolve24 } from "path";
43021
+ import { dirname as dirname23, resolve as resolve25 } from "path";
43205
43022
  function joinFilePath() {
43206
- return resolve24(DATA_DIR, "otium-join.json");
43023
+ return resolve25(DATA_DIR, "otium-join.json");
43207
43024
  }
43208
43025
  function isHttpUrl(value) {
43209
43026
  return /^https?:\/\//.test(value);
@@ -43258,7 +43075,7 @@ function parseInviteCode(code) {
43258
43075
  return normalizeJoin(parsed);
43259
43076
  }
43260
43077
  function joinLockPath() {
43261
- return resolve24(DATA_DIR, ".otium-join.lock");
43078
+ return resolve25(DATA_DIR, ".otium-join.lock");
43262
43079
  }
43263
43080
  function processIsAlive(pid) {
43264
43081
  if (!Number.isSafeInteger(pid) || pid <= 0)
@@ -43272,9 +43089,9 @@ function processIsAlive(pid) {
43272
43089
  }
43273
43090
  function withJoinCredentialLock(operation) {
43274
43091
  const lockPath = joinLockPath();
43275
- const ownerPath = resolve24(lockPath, "owner.json");
43092
+ const ownerPath = resolve25(lockPath, "owner.json");
43276
43093
  const owner = { pid: process.pid, token: randomUUID28() };
43277
- mkdirSync33(dirname22(lockPath), { recursive: true });
43094
+ mkdirSync33(dirname23(lockPath), { recursive: true });
43278
43095
  for (let attempt = 0;; attempt += 1) {
43279
43096
  let created = false;
43280
43097
  try {
@@ -43338,20 +43155,20 @@ function withJoinCredentialLock(operation) {
43338
43155
  function joinsEqual(left, right) {
43339
43156
  return left.central === right.central && left.relay === right.relay && left.cellId === right.cellId && left.secret === right.secret;
43340
43157
  }
43341
- function normalizedJoin(join43) {
43158
+ function normalizedJoin(join44) {
43342
43159
  return normalizeJoin({
43343
- v: join43.v,
43344
- central: join43.central,
43345
- relay: join43.relay,
43346
- cellId: join43.cellId,
43347
- secret: join43.secret
43160
+ v: join44.v,
43161
+ central: join44.central,
43162
+ relay: join44.relay,
43163
+ cellId: join44.cellId,
43164
+ secret: join44.secret
43348
43165
  });
43349
43166
  }
43350
- function joinCredentialDigest(join43) {
43351
- return createHash11("sha256").update(JSON.stringify(normalizedJoin(join43))).digest("base64url");
43167
+ function joinCredentialDigest(join44) {
43168
+ return createHash11("sha256").update(JSON.stringify(normalizedJoin(join44))).digest("base64url");
43352
43169
  }
43353
43170
  function readPersistedJoins(path = joinFilePath()) {
43354
- if (!existsSync38(path))
43171
+ if (!existsSync39(path))
43355
43172
  return [];
43356
43173
  const parsed = JSON.parse(readFileSync29(path, "utf-8"));
43357
43174
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -43367,9 +43184,9 @@ function readPersistedJoins(path = joinFilePath()) {
43367
43184
  return normalizeJoin(entry);
43368
43185
  });
43369
43186
  }
43370
- function isJoinPersisted(join43) {
43187
+ function isJoinPersisted(join44) {
43371
43188
  try {
43372
- const normalized = normalizedJoin(join43);
43189
+ const normalized = normalizedJoin(join44);
43373
43190
  return readPersistedJoins().some((persisted) => joinsEqual(persisted, normalized));
43374
43191
  } catch {
43375
43192
  return false;
@@ -43385,8 +43202,8 @@ function fsyncPath(target) {
43385
43202
  }
43386
43203
  function writeJoins(joins, allowOverwrite) {
43387
43204
  const path = joinFilePath();
43388
- const directory = dirname22(path);
43389
- const temporaryPath = resolve24(directory, `.otium-join.json.${process.pid}.${randomUUID28()}.tmp`);
43205
+ const directory = dirname23(path);
43206
+ const temporaryPath = resolve25(directory, `.otium-join.json.${process.pid}.${randomUUID28()}.tmp`);
43390
43207
  const payload = { v: JOIN_FILE_VERSION, joins };
43391
43208
  let fd;
43392
43209
  try {
@@ -43407,18 +43224,18 @@ function writeJoins(joins, allowOverwrite) {
43407
43224
  } catch (error2) {
43408
43225
  if (fd !== undefined)
43409
43226
  closeSync5(fd);
43410
- if (existsSync38(temporaryPath))
43227
+ if (existsSync39(temporaryPath))
43411
43228
  unlinkSync23(temporaryPath);
43412
43229
  throw error2;
43413
43230
  }
43414
43231
  return path;
43415
43232
  }
43416
- function saveJoinWhileLocked(join43, options = {}) {
43233
+ function saveJoinWhileLocked(join44, options = {}) {
43417
43234
  const path = joinFilePath();
43418
- const directory = dirname22(path);
43419
- const normalized = normalizedJoin(join43);
43235
+ const directory = dirname23(path);
43236
+ const normalized = normalizedJoin(join44);
43420
43237
  mkdirSync33(directory, { recursive: true });
43421
- if (!existsSync38(path))
43238
+ if (!existsSync39(path))
43422
43239
  return writeJoins([normalized], false);
43423
43240
  if (lstatSync(path).isSymbolicLink()) {
43424
43241
  throw new Error(`refusing to replace symlinked Otium join file at ${path}`);
@@ -43446,13 +43263,13 @@ function saveJoinWhileLocked(join43, options = {}) {
43446
43263
  const next = conflict ? existing.map((persisted) => persisted.cellId === normalized.cellId ? normalized : persisted) : [...existing, normalized];
43447
43264
  return writeJoins(next, true);
43448
43265
  }
43449
- function saveJoin(join43, options = {}) {
43450
- return withJoinCredentialLock(() => saveJoinWhileLocked(join43, options));
43266
+ function saveJoin(join44, options = {}) {
43267
+ return withJoinCredentialLock(() => saveJoinWhileLocked(join44, options));
43451
43268
  }
43452
43269
  function removeJoin(cellId) {
43453
43270
  return withJoinCredentialLock(() => {
43454
43271
  const path = joinFilePath();
43455
- if (!existsSync38(path))
43272
+ if (!existsSync39(path))
43456
43273
  return false;
43457
43274
  if (lstatSync(path).isSymbolicLink()) {
43458
43275
  throw new Error(`refusing to remove symlinked Otium join file at ${path}`);
@@ -43460,7 +43277,7 @@ function removeJoin(cellId) {
43460
43277
  if (cellId) {
43461
43278
  let remaining;
43462
43279
  try {
43463
- remaining = readPersistedJoins(path).filter((join43) => join43.cellId !== cellId);
43280
+ remaining = readPersistedJoins(path).filter((join44) => join44.cellId !== cellId);
43464
43281
  } catch {
43465
43282
  return false;
43466
43283
  }
@@ -43472,7 +43289,7 @@ function removeJoin(cellId) {
43472
43289
  }
43473
43290
  }
43474
43291
  unlinkSync23(path);
43475
- fsyncPath(dirname22(path));
43292
+ fsyncPath(dirname23(path));
43476
43293
  return true;
43477
43294
  });
43478
43295
  }
@@ -43493,7 +43310,7 @@ function loadJoins() {
43493
43310
  logger.warn("otium: OTIUM_CENTRAL_URL, OTIUM_CELL_ID, OTIUM_CELL_SECRET must be set together \u2014 ignoring partial env");
43494
43311
  }
43495
43312
  const path = joinFilePath();
43496
- if (!existsSync38(path))
43313
+ if (!existsSync39(path))
43497
43314
  return [];
43498
43315
  try {
43499
43316
  return readPersistedJoins(path);
@@ -43513,7 +43330,7 @@ var init_join = __esm(async () => {
43513
43330
  // ../../adapters/otium/src/peer-files.ts
43514
43331
  import { randomUUID as randomUUID29 } from "crypto";
43515
43332
  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";
43333
+ import { basename as basename13, extname as extname6, join as join44 } from "path";
43517
43334
  function fileType(mimeType) {
43518
43335
  if (mimeType.startsWith("image/"))
43519
43336
  return "image";
@@ -43579,7 +43396,7 @@ function recordFile(args) {
43579
43396
  function insertLocalFile(args) {
43580
43397
  const id = randomUUID29();
43581
43398
  mkdirSync34(PEER_FILES_DIR, { recursive: true });
43582
- const path = join43(PEER_FILES_DIR, `${id}-${safeFilename(args.filename)}`);
43399
+ const path = join44(PEER_FILES_DIR, `${id}-${safeFilename(args.filename)}`);
43583
43400
  copyFileSync5(args.sourcePath, path);
43584
43401
  return recordFile({ id, path, sizeBytes: statSync21(path).size, ...args });
43585
43402
  }
@@ -43625,7 +43442,7 @@ function installPeerFileHooks() {
43625
43442
  var PEER_FILES_DIR;
43626
43443
  var init_peer_files = __esm(async () => {
43627
43444
  await init_src();
43628
- PEER_FILES_DIR = join43(DATA_DIR, "otium-peer-files");
43445
+ PEER_FILES_DIR = join44(DATA_DIR, "otium-peer-files");
43629
43446
  db.exec(`
43630
43447
  CREATE TABLE IF NOT EXISTS otium_peer_files (
43631
43448
  id TEXT PRIMARY KEY,
@@ -44018,14 +43835,14 @@ var init_store2 = __esm(async () => {
44018
43835
  // ../../adapters/otium/src/workspace-scope.ts
44019
43836
  import { createHash as createHash13 } from "crypto";
44020
43837
  import { mkdirSync as mkdirSync35, readFileSync as readFileSync30, writeFileSync as writeFileSync26 } from "fs";
44021
- import { dirname as dirname23, resolve as resolve25 } from "path";
43838
+ import { dirname as dirname24, resolve as resolve26 } from "path";
44022
43839
  function surfaceScopeFor(central, workspaceId) {
44023
43840
  const digest = createHash13("sha256").update(`${central.trim().replace(/\/+$/, "")}
44024
43841
  ${workspaceId.trim()}`).digest("hex");
44025
43842
  return `ws_${digest.slice(0, 24)}`;
44026
43843
  }
44027
43844
  function scopeCachePath() {
44028
- return resolve25(DATA_DIR, "otium-workspace.json");
43845
+ return resolve26(DATA_DIR, "otium-workspace.json");
44029
43846
  }
44030
43847
  function readCache() {
44031
43848
  try {
@@ -44037,19 +43854,19 @@ function readCache() {
44037
43854
  return {};
44038
43855
  }
44039
43856
  }
44040
- function cachedSurfaceScope(join44) {
44041
- const record = readCache()[join44.cellId];
44042
- if (!record || record.central !== join44.central || !record.workspaceId)
43857
+ function cachedSurfaceScope(join45) {
43858
+ const record = readCache()[join45.cellId];
43859
+ if (!record || record.central !== join45.central || !record.workspaceId)
44043
43860
  return null;
44044
43861
  return surfaceScopeFor(record.central, record.workspaceId);
44045
43862
  }
44046
- function cacheSurfaceScope(join44, workspaceId) {
44047
- const scope = surfaceScopeFor(join44.central, workspaceId);
43863
+ function cacheSurfaceScope(join45, workspaceId) {
43864
+ const scope = surfaceScopeFor(join45.central, workspaceId);
44048
43865
  const path = scopeCachePath();
44049
43866
  try {
44050
- mkdirSync35(dirname23(path), { recursive: true });
43867
+ mkdirSync35(dirname24(path), { recursive: true });
44051
43868
  const cache = readCache();
44052
- cache[join44.cellId] = { central: join44.central, workspaceId, scope };
43869
+ cache[join45.cellId] = { central: join45.central, workspaceId, scope };
44053
43870
  writeFileSync26(path, `${JSON.stringify(cache, null, 2)}
44054
43871
  `, { mode: 384 });
44055
43872
  } catch (err2) {
@@ -44057,23 +43874,23 @@ function cacheSurfaceScope(join44, workspaceId) {
44057
43874
  }
44058
43875
  return scope;
44059
43876
  }
44060
- async function resolveSurfaceScope(join44) {
44061
- const cached = cachedSurfaceScope(join44);
43877
+ async function resolveSurfaceScope(join45) {
43878
+ const cached = cachedSurfaceScope(join45);
44062
43879
  if (cached)
44063
43880
  return cached;
44064
43881
  try {
44065
- const workspaceId = await peerWorkspaceIdForCell(join44.cellId);
43882
+ const workspaceId = await peerWorkspaceIdForCell(join45.cellId);
44066
43883
  if (!workspaceId)
44067
43884
  return null;
44068
- return cacheSurfaceScope(join44, workspaceId);
43885
+ return cacheSurfaceScope(join45, workspaceId);
44069
43886
  } catch (err2) {
44070
43887
  logger.warn({ err: err2 }, "otium: workspace scope unresolved (will retry on the next contact)");
44071
43888
  return null;
44072
43889
  }
44073
43890
  }
44074
43891
  function surfaceScopeForCell(cellId) {
44075
- const join44 = attachedOtiumCells().find((candidate) => candidate.cellId === cellId);
44076
- return join44 ? cachedSurfaceScope(join44) : null;
43892
+ const join45 = attachedOtiumCells().find((candidate) => candidate.cellId === cellId);
43893
+ return join45 ? cachedSurfaceScope(join45) : null;
44077
43894
  }
44078
43895
  function unscopedRoomsAddressable() {
44079
43896
  return attachedOtiumCells().length < 2;
@@ -45086,7 +44903,7 @@ import {
45086
44903
  import {
45087
44904
  chmodSync as chmodSync8,
45088
44905
  closeSync as closeSync6,
45089
- existsSync as existsSync39,
44906
+ existsSync as existsSync40,
45090
44907
  fsyncSync as fsyncSync3,
45091
44908
  linkSync as linkSync2,
45092
44909
  mkdirSync as mkdirSync36,
@@ -45096,7 +44913,7 @@ import {
45096
44913
  unlinkSync as unlinkSync24,
45097
44914
  writeFileSync as writeFileSync27
45098
44915
  } from "fs";
45099
- import { dirname as dirname24, resolve as resolve26 } from "path";
44916
+ import { dirname as dirname25, resolve as resolve27 } from "path";
45100
44917
  function parseEnrollmentInvite(code) {
45101
44918
  let parsed;
45102
44919
  try {
@@ -45117,11 +44934,11 @@ function parseEnrollmentInvite(code) {
45117
44934
  return { v: 2, central, token };
45118
44935
  }
45119
44936
  function pendingEnrollmentPath() {
45120
- return resolve26(DATA_DIR, "otium-enrollment-pending.json");
44937
+ return resolve27(DATA_DIR, "otium-enrollment-pending.json");
45121
44938
  }
45122
44939
  function isEnrollmentPending(invite) {
45123
44940
  const path = pendingEnrollmentPath();
45124
- if (!existsSync39(path))
44941
+ if (!existsSync40(path))
45125
44942
  return false;
45126
44943
  try {
45127
44944
  const saved = JSON.parse(readFileSync31(path, "utf8"));
@@ -45132,7 +44949,7 @@ function isEnrollmentPending(invite) {
45132
44949
  }
45133
44950
  function loadOrCreatePending(invite, nodeName) {
45134
44951
  const path = pendingEnrollmentPath();
45135
- if (existsSync39(path)) {
44952
+ if (existsSync40(path)) {
45136
44953
  const saved = JSON.parse(readFileSync31(path, "utf8"));
45137
44954
  if (saved.central === invite.central && saved.token === invite.token)
45138
44955
  return saved;
@@ -45146,8 +44963,8 @@ function loadOrCreatePending(invite, nodeName) {
45146
44963
  publicKey: pair.publicKey.export({ format: "der", type: "spki" }).toString("base64url"),
45147
44964
  ...nodeName ? { nodeName } : {}
45148
44965
  };
45149
- mkdirSync36(dirname24(path), { recursive: true });
45150
- const temporaryPath = resolve26(dirname24(path), `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
44966
+ mkdirSync36(dirname25(path), { recursive: true });
44967
+ const temporaryPath = resolve27(dirname25(path), `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
45151
44968
  let fd;
45152
44969
  try {
45153
44970
  fd = openSync6(temporaryPath, "wx", 384);
@@ -45159,7 +44976,7 @@ function loadOrCreatePending(invite, nodeName) {
45159
44976
  linkSync2(temporaryPath, path);
45160
44977
  unlinkSync24(temporaryPath);
45161
44978
  chmodSync8(path, 384);
45162
- const directoryFd = openSync6(dirname24(path), "r");
44979
+ const directoryFd = openSync6(dirname25(path), "r");
45163
44980
  try {
45164
44981
  fsyncSync3(directoryFd);
45165
44982
  } finally {
@@ -45168,9 +44985,9 @@ function loadOrCreatePending(invite, nodeName) {
45168
44985
  } catch (error2) {
45169
44986
  if (fd !== undefined)
45170
44987
  closeSync6(fd);
45171
- if (existsSync39(temporaryPath))
44988
+ if (existsSync40(temporaryPath))
45172
44989
  unlinkSync24(temporaryPath);
45173
- if (error2.code === "EEXIST" && existsSync39(path)) {
44990
+ if (error2.code === "EEXIST" && existsSync40(path)) {
45174
44991
  const saved = JSON.parse(readFileSync31(path, "utf8"));
45175
44992
  if (saved.central === invite.central && saved.token === invite.token)
45176
44993
  return saved;
@@ -45181,8 +44998,8 @@ function loadOrCreatePending(invite, nodeName) {
45181
44998
  }
45182
44999
  function replacePendingEnrollment(pending2) {
45183
45000
  const path = pendingEnrollmentPath();
45184
- const directory = dirname24(path);
45185
- const temporaryPath = resolve26(directory, `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
45001
+ const directory = dirname25(path);
45002
+ const temporaryPath = resolve27(directory, `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
45186
45003
  let fd;
45187
45004
  try {
45188
45005
  fd = openSync6(temporaryPath, "wx", 384);
@@ -45201,18 +45018,18 @@ function replacePendingEnrollment(pending2) {
45201
45018
  } catch (error2) {
45202
45019
  if (fd !== undefined)
45203
45020
  closeSync6(fd);
45204
- if (existsSync39(temporaryPath))
45021
+ if (existsSync40(temporaryPath))
45205
45022
  unlinkSync24(temporaryPath);
45206
45023
  throw error2;
45207
45024
  }
45208
45025
  }
45209
- function recordClaimedCredential(pending2, join44) {
45026
+ function recordClaimedCredential(pending2, join45) {
45210
45027
  withJoinCredentialLock(() => {
45211
45028
  const current3 = JSON.parse(readFileSync31(pendingEnrollmentPath(), "utf8"));
45212
45029
  if (current3.central !== pending2.central || current3.token !== pending2.token || current3.idempotencyKey !== pending2.idempotencyKey || current3.publicKey !== pending2.publicKey) {
45213
45030
  throw new Error("pending Otium enrollment changed while its claim was in flight");
45214
45031
  }
45215
- const claimed = { digest: joinCredentialDigest(join44), cellId: join44.cellId };
45032
+ const claimed = { digest: joinCredentialDigest(join45), cellId: join45.cellId };
45216
45033
  if (current3.claimed && (current3.claimed.digest !== claimed.digest || current3.claimed.cellId !== claimed.cellId)) {
45217
45034
  throw new Error("central returned different credentials for an idempotent enrollment claim");
45218
45035
  }
@@ -45270,36 +45087,36 @@ async function claimEnrollment(invite, nodeName) {
45270
45087
  const secret = openCredential(credential, pending2.privateKey);
45271
45088
  if (!secret.startsWith("rcs_"))
45272
45089
  throw new Error("central returned an invalid runtime credential");
45273
- const join44 = {
45090
+ const join45 = {
45274
45091
  v: 2,
45275
45092
  central: invite.central,
45276
45093
  relay: String(response.relayUrl),
45277
45094
  cellId: String(response.cell?.id),
45278
45095
  secret
45279
45096
  };
45280
- if (!join44.relay || !join44.cellId || join44.cellId === "undefined") {
45097
+ if (!join45.relay || !join45.cellId || join45.cellId === "undefined") {
45281
45098
  throw new Error("central returned an incomplete enrollment response");
45282
45099
  }
45283
- assertSecureRelayUrl(join44.relay);
45284
- recordClaimedCredential(pending2, join44);
45285
- return join44;
45100
+ assertSecureRelayUrl(join45.relay);
45101
+ recordClaimedCredential(pending2, join45);
45102
+ return join45;
45286
45103
  }
45287
- function commitEnrollment(join44, options = {}) {
45104
+ function commitEnrollment(join45, options = {}) {
45288
45105
  return withJoinCredentialLock(() => {
45289
45106
  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)) {
45107
+ const pending2 = existsSync40(pendingPath) ? JSON.parse(readFileSync31(pendingPath, "utf8")) : null;
45108
+ const digest = joinCredentialDigest(join45);
45109
+ if (pending2 && (!pending2.claimed || pending2.claimed.digest !== digest || pending2.claimed.cellId !== join45.cellId)) {
45293
45110
  throw new Error(`pending Otium enrollment at ${pendingPath} does not match these credentials`);
45294
45111
  }
45295
- const path = saveJoinWhileLocked(join44, options);
45296
- if (!isJoinPersisted(join44)) {
45112
+ const path = saveJoinWhileLocked(join45, options);
45113
+ if (!isJoinPersisted(join45)) {
45297
45114
  throw new Error("Otium join credentials were not durably persisted");
45298
45115
  }
45299
45116
  if (!pending2)
45300
45117
  return path;
45301
45118
  unlinkSync24(pendingPath);
45302
- const directoryFd = openSync6(dirname24(pendingPath), "r");
45119
+ const directoryFd = openSync6(dirname25(pendingPath), "r");
45303
45120
  try {
45304
45121
  fsyncSync3(directoryFd);
45305
45122
  } finally {
@@ -45887,24 +45704,24 @@ function refreshDefaultSurfaceScope() {
45887
45704
  setSurfaceScopeRequired(scopes.length > 1);
45888
45705
  }
45889
45706
  function startOtiumNodeRuntime(options) {
45890
- const { join: join44 } = options;
45891
- attachOtiumCentralCell(join44);
45707
+ const { join: join45 } = options;
45708
+ attachOtiumCentralCell(join45);
45892
45709
  const releaseGlobals = acquireGlobalOtiumServices();
45893
45710
  let stopped = false;
45894
- logger.info({ central: join44.central, cellId: join44.cellId }, "otium: worker mode enabled");
45895
- mountedScopes.set(join44.cellId, cachedSurfaceScope(join44));
45711
+ logger.info({ central: join45.central, cellId: join45.cellId }, "otium: worker mode enabled");
45712
+ mountedScopes.set(join45.cellId, cachedSurfaceScope(join45));
45896
45713
  refreshDefaultSurfaceScope();
45897
- selfPeerNodeForCell(join44.cellId).then((self) => {
45714
+ selfPeerNodeForCell(join45.cellId).then((self) => {
45898
45715
  if (self) {
45899
45716
  logger.info({ nodeName: self.nodeName, baseUrl: self.baseUrl }, "otium: attached to workspace");
45900
45717
  }
45901
45718
  }).catch((err2) => {
45902
45719
  logger.warn({ err: err2 }, "otium: self check against central failed (will retry per request)");
45903
45720
  });
45904
- resolveSurfaceScope(join44).then((scope) => {
45721
+ resolveSurfaceScope(join45).then((scope) => {
45905
45722
  if (!scope || stopped)
45906
45723
  return;
45907
- mountedScopes.set(join44.cellId, scope);
45724
+ mountedScopes.set(join45.cellId, scope);
45908
45725
  refreshDefaultSurfaceScope();
45909
45726
  if (mountedScopes.size === 1)
45910
45727
  stampUnscopedOtiumTopics(scope);
@@ -45913,14 +45730,14 @@ function startOtiumNodeRuntime(options) {
45913
45730
  });
45914
45731
  return {
45915
45732
  name: "otium",
45916
- join: join44,
45733
+ join: join45,
45917
45734
  stop: () => {
45918
45735
  if (stopped)
45919
45736
  return;
45920
45737
  stopped = true;
45921
- mountedScopes.delete(join44.cellId);
45738
+ mountedScopes.delete(join45.cellId);
45922
45739
  refreshDefaultSurfaceScope();
45923
- detachOtiumCentralCell(join44.cellId);
45740
+ detachOtiumCentralCell(join45.cellId);
45924
45741
  releaseGlobals();
45925
45742
  }
45926
45743
  };
@@ -46015,14 +45832,14 @@ __export(exports_node_runtime, {
46015
45832
  function sameCredentials(left, right) {
46016
45833
  return left.central === right.central && left.relay === right.relay && left.secret === right.secret;
46017
45834
  }
46018
- function attachOtiumWorkspace(join44) {
46019
- const current3 = mounted.get(join44.cellId);
45835
+ function attachOtiumWorkspace(join45) {
45836
+ const current3 = mounted.get(join45.cellId);
46020
45837
  if (current3) {
46021
- if (sameCredentials(current3.join, join44))
45838
+ if (sameCredentials(current3.join, join45))
46022
45839
  return false;
46023
- detachOtiumWorkspace(join44.cellId);
45840
+ detachOtiumWorkspace(join45.cellId);
46024
45841
  }
46025
- mounted.set(join44.cellId, startOtiumNodeRuntime({ join: join44 }));
45842
+ mounted.set(join45.cellId, startOtiumNodeRuntime({ join: join45 }));
46026
45843
  return true;
46027
45844
  }
46028
45845
  function detachOtiumWorkspace(cellId) {
@@ -46037,7 +45854,7 @@ function mountedOtiumWorkspaces() {
46037
45854
  return [...mounted.values()].map((runtime2) => runtime2.join);
46038
45855
  }
46039
45856
  function reconcileOtiumWorkspaces(joins = loadJoins()) {
46040
- const wanted = new Map(joins.map((join44) => [join44.cellId, join44]));
45857
+ const wanted = new Map(joins.map((join45) => [join45.cellId, join45]));
46041
45858
  const detached = [];
46042
45859
  for (const cellId of [...mounted.keys()]) {
46043
45860
  if (wanted.has(cellId))
@@ -46046,8 +45863,8 @@ function reconcileOtiumWorkspaces(joins = loadJoins()) {
46046
45863
  detached.push(cellId);
46047
45864
  }
46048
45865
  const attached = [];
46049
- for (const [cellId, join44] of wanted) {
46050
- if (attachOtiumWorkspace(join44))
45866
+ for (const [cellId, join45] of wanted) {
45867
+ if (attachOtiumWorkspace(join45))
46051
45868
  attached.push(cellId);
46052
45869
  }
46053
45870
  if (attached.length > 0 || detached.length > 0) {
@@ -46070,9 +45887,9 @@ async function handleOtiumAdapterControlRequest(req) {
46070
45887
  if (req.method === "GET") {
46071
45888
  return Response.json({
46072
45889
  ok: true,
46073
- workspaces: mountedOtiumWorkspaces().map((join44) => ({
46074
- cellId: join44.cellId,
46075
- central: join44.central
45890
+ workspaces: mountedOtiumWorkspaces().map((join45) => ({
45891
+ cellId: join45.cellId,
45892
+ central: join45.central
46076
45893
  }))
46077
45894
  });
46078
45895
  }
@@ -46093,8 +45910,8 @@ function mountConfiguredOtiumNodeRuntime() {
46093
45910
  const joins = loadJoins();
46094
45911
  if (joins.length === 0)
46095
45912
  return null;
46096
- for (const join44 of joins)
46097
- attachOtiumWorkspace(join44);
45913
+ for (const join45 of joins)
45914
+ attachOtiumWorkspace(join45);
46098
45915
  registerNodeRequestHandler("otium-adapter-control", handleOtiumAdapterControlRequest);
46099
45916
  let stopped = false;
46100
45917
  return {
@@ -46206,11 +46023,11 @@ async function joinCommand(args) {
46206
46023
  process.exitCode = 1;
46207
46024
  return;
46208
46025
  }
46209
- let join44;
46026
+ let join45;
46210
46027
  let productionEnrollment = false;
46211
46028
  if (args.includes("--legacy")) {
46212
46029
  try {
46213
- join44 = parseInviteCode(code);
46030
+ join45 = parseInviteCode(code);
46214
46031
  } catch (err2) {
46215
46032
  console.error(`invalid legacy invite code: ${err2 instanceof Error ? err2.message : err2}`);
46216
46033
  process.exitCode = 1;
@@ -46236,7 +46053,7 @@ async function joinCommand(args) {
46236
46053
  return;
46237
46054
  }
46238
46055
  }
46239
- join44 = await claimEnrollment(invite, nodeName);
46056
+ join45 = await claimEnrollment(invite, nodeName);
46240
46057
  productionEnrollment = true;
46241
46058
  } catch (err2) {
46242
46059
  console.error(`enrollment failed: ${err2 instanceof Error ? err2.message : err2}`);
@@ -46247,16 +46064,16 @@ async function joinCommand(args) {
46247
46064
  let path;
46248
46065
  try {
46249
46066
  const saveOptions = { replaceExisting: args.includes("--replace") };
46250
- path = productionEnrollment ? commitEnrollment(join44, saveOptions) : saveJoin(join44, saveOptions);
46067
+ path = productionEnrollment ? commitEnrollment(join45, saveOptions) : saveJoin(join45, saveOptions);
46251
46068
  } catch (err2) {
46252
46069
  console.error(`could not save join credentials: ${err2 instanceof Error ? err2.message : err2}`);
46253
46070
  process.exitCode = 1;
46254
46071
  return;
46255
46072
  }
46256
46073
  console.log(`otium join credentials saved to ${path}`);
46257
- console.log(` central: ${join44.central}`);
46258
- console.log(` cellId: ${join44.cellId}`);
46259
- configureOtiumCentral(join44);
46074
+ console.log(` central: ${join45.central}`);
46075
+ console.log(` cellId: ${join45.cellId}`);
46076
+ configureOtiumCentral(join45);
46260
46077
  try {
46261
46078
  const self = await selfPeerNode();
46262
46079
  if (self) {
@@ -46298,19 +46115,19 @@ async function statusCommand() {
46298
46115
  return;
46299
46116
  }
46300
46117
  configureOtiumCentral(joins[0] ?? null);
46301
- for (const join44 of joins.slice(1))
46302
- attachOtiumCentralCell(join44);
46303
- for (const [index, join44] of joins.entries()) {
46118
+ for (const join45 of joins.slice(1))
46119
+ attachOtiumCentralCell(join45);
46120
+ for (const [index, join45] of joins.entries()) {
46304
46121
  if (index > 0)
46305
46122
  console.log("");
46306
- console.log(`cellId: ${join44.cellId}`);
46307
- console.log(`central: ${join44.central}`);
46308
- if (join44.relay)
46309
- console.log(`relay: ${join44.relay}`);
46123
+ console.log(`cellId: ${join45.cellId}`);
46124
+ console.log(`central: ${join45.central}`);
46125
+ if (join45.relay)
46126
+ console.log(`relay: ${join45.relay}`);
46310
46127
  try {
46311
- const self = await selfPeerNodeForCell(join44.cellId);
46128
+ const self = await selfPeerNodeForCell(join45.cellId);
46312
46129
  if (self) {
46313
- console.log(`node: ${self.nodeName ?? join44.cellId}${self.isPrimary ? " (primary)" : ""}`);
46130
+ console.log(`node: ${self.nodeName ?? join45.cellId}${self.isPrimary ? " (primary)" : ""}`);
46314
46131
  console.log(`baseUrl: ${self.baseUrl}`);
46315
46132
  } else {
46316
46133
  console.warn(" warning: central answered but this cell has no visible assignment yet \u2014 check the workspace assignment");
@@ -46346,13 +46163,13 @@ function resolveTunnelTargets(joins, relayOverride) {
46346
46163
  const targets = [];
46347
46164
  const skippedNoRelay = [];
46348
46165
  const envRelay = process.env.OTIUM_RELAY_URL?.trim();
46349
- for (const join44 of joins) {
46350
- const relayUrl = relayOverride?.trim() || join44.relay || envRelay;
46166
+ for (const join45 of joins) {
46167
+ const relayUrl = relayOverride?.trim() || join45.relay || envRelay;
46351
46168
  if (!relayUrl) {
46352
- skippedNoRelay.push(join44.cellId);
46169
+ skippedNoRelay.push(join45.cellId);
46353
46170
  continue;
46354
46171
  }
46355
- targets.push({ cellId: join44.cellId, relayUrl, secret: join44.secret });
46172
+ targets.push({ cellId: join45.cellId, relayUrl, secret: join45.secret });
46356
46173
  }
46357
46174
  return { targets, skippedNoRelay };
46358
46175
  }
@@ -46439,8 +46256,8 @@ async function runOtiumSidecar(options) {
46439
46256
  for (const tunnel of tunnels.values())
46440
46257
  tunnel.start();
46441
46258
  let resolveCompleted;
46442
- const completed = new Promise((resolve27) => {
46443
- resolveCompleted = resolve27;
46259
+ const completed = new Promise((resolve28) => {
46260
+ resolveCompleted = resolve28;
46444
46261
  });
46445
46262
  onShutdown("otium-sidecar-server", 130, () => server?.stop(true));
46446
46263
  onShutdown("otium-sidecar-tunnel", 120, () => {
@@ -46589,9 +46406,9 @@ async function runOtiumCli(args = process.argv.slice(2)) {
46589
46406
  if (joins.length === 0)
46590
46407
  throw new Error("not joined to an Otium workspace");
46591
46408
  if (!targetCellId && joins.length > 1) {
46592
- throw new Error(`this node is joined to ${joins.length} workspaces; name one to leave: ${joins.map((join44) => join44.cellId).join(", ")}`);
46409
+ throw new Error(`this node is joined to ${joins.length} workspaces; name one to leave: ${joins.map((join45) => join45.cellId).join(", ")}`);
46593
46410
  }
46594
- if (targetCellId && !joins.some((join44) => join44.cellId === targetCellId)) {
46411
+ if (targetCellId && !joins.some((join45) => join45.cellId === targetCellId)) {
46595
46412
  throw new Error(`not joined as ${targetCellId}`);
46596
46413
  }
46597
46414
  removeJoin2(targetCellId);
@@ -47244,7 +47061,7 @@ async function runCanonicalNode(port) {
47244
47061
  });
47245
47062
  console.log(`negotium node listening on 127.0.0.1:${node.port} (ctrl-c to stop)`);
47246
47063
  await node.completed;
47247
- await new Promise((resolve27) => setImmediate(resolve27));
47064
+ await new Promise((resolve28) => setImmediate(resolve28));
47248
47065
  process.exit(0);
47249
47066
  }
47250
47067
  async function stopAdapter(name) {
@@ -47378,4 +47195,4 @@ switch (command) {
47378
47195
  }
47379
47196
  }
47380
47197
 
47381
- //# debugId=09EB65295EA6123364756E2164756E21
47198
+ //# debugId=A4A68C691DEC1BCD64756E2164756E21