negotium 0.2.8 → 0.2.10

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 (35) hide show
  1. package/dist/agent-helpers.js +21 -20
  2. package/dist/agent-helpers.js.map +6 -6
  3. package/dist/background-bash.js +2 -1
  4. package/dist/background-bash.js.map +3 -3
  5. package/dist/browser-runtime.js +2 -1
  6. package/dist/browser-runtime.js.map +3 -3
  7. package/dist/{chunk-knv045mn.js → chunk-dcfmsrnb.js} +2 -1
  8. package/dist/{chunk-knv045mn.js.map → chunk-dcfmsrnb.js.map} +3 -3
  9. package/dist/hosted-agent.js +3 -2
  10. package/dist/hosted-agent.js.map +4 -4
  11. package/dist/main.js +522 -269
  12. package/dist/main.js.map +11 -9
  13. package/dist/mcp-factories.js +21 -20
  14. package/dist/mcp-factories.js.map +6 -6
  15. package/dist/prompts.js +2 -1
  16. package/dist/prompts.js.map +3 -3
  17. package/dist/query-runtime.js +2 -1
  18. package/dist/query-runtime.js.map +3 -3
  19. package/dist/registry.js +9 -5
  20. package/dist/registry.js.map +4 -4
  21. package/dist/rollout.js +1 -1
  22. package/dist/runtime/src/agents/maestro-registry.ts +32 -2
  23. package/dist/runtime/src/agents/mcp-tools/inline-assets.ts +193 -0
  24. package/dist/runtime/src/agents/mcp-tools/publish-html.ts +183 -0
  25. package/dist/runtime/src/mcp-runtime-host.ts +1 -0
  26. package/dist/runtime/src/platform/config.ts +12 -0
  27. package/dist/runtime/src/runtime/turn-runner.ts +34 -11
  28. package/dist/runtime/src/version.ts +1 -1
  29. package/dist/runtime-helpers.js +2 -1
  30. package/dist/runtime-helpers.js.map +3 -3
  31. package/dist/types/packages/core/src/platform/config.d.ts +7 -0
  32. package/dist/types/packages/core/src/version.d.ts +1 -1
  33. package/dist/vault.js +2 -1
  34. package/dist/vault.js.map +3 -3
  35. package/package.json +2 -2
package/dist/main.js CHANGED
@@ -253,7 +253,7 @@ function resolveDefaultModel(agent, registryDefaultModel) {
253
253
  function codexAuthFilePath() {
254
254
  return process.env.NEGOTIUM_CODEX_AUTH_FILE || join(process.env.CODEX_HOME || join(homedir(), ".codex"), "auth.json");
255
255
  }
256
- var HOME, PROJECT_ROOT, STATE_DIR_ENV, STATE_DIR, WORKSPACE_DIR, TOPIC_WORKSPACE_DIR, SHARED_WIKI_DIR, CRON_WORKSPACE_DIR, BROWSER_DIR, BROWSER_PROFILES_DIR, BINARIES_DIR, SECRETS_DIR, DM_WORKSPACE_DIR, SESSION_WORKSPACE_DIR, CLAUDE_EXECUTABLE_ENV, CLAUDE_EXECUTABLE, DEFAULT_OUTPUT_LANGUAGE = "English", BROWSER_RS_VERSION = "v0.1.17", BROWSER_RS_MIN_SECURE_VERSION = "0.1.15", BROWSER_RS_BIN, PLAYWRIGHT_MCP_BIN, TSX_BIN, TSX_LOADER, TSCONFIG_PATH, SESSION_COMM_SERVER, TASK_SERVER, BROWSER_MCP_SSE_PROXY_SERVER, CANONICAL_MCP_PROXY_SERVER, WIKI_SERVER, TOKEN_STATS_SERVER, COMPACTION_LOG_SERVER, SYSTEM_HEALTH_SERVER, AGENT_HEALTH_SERVER, BACKGROUND_BASH_SERVER, VAULT_SERVER, BG_BASH_BASE_PORT, BG_BASH_MAX_PORT, RUNTIME_MCP_SECRET, NODE_CONTROL_TOKEN, VAULT_MASTER_KEY, NEGOTIUM_PORT, hostname, DATA_DIR, LOG_DIR, UPLOADS_DIR, VAULT_DIR, SESSIONS_DB, DEBUG_FILE, USERS_LOG_DIR, RUN_DIR, PROGRESS_DIR, DM_CMD_DIR, DM_RESP_DIR, SESSION_INBOX_DIR, SESSION_ASKS_DIR, PLAYWRIGHT_BASE_PORT, PLAYWRIGHT_MAX_PORT, PLAYWRIGHT_PORTS_DIR, ACTIVE_QUERY_STALE_MS, AGENTS_PROMPTS_DIR, RESOURCES_DIR, FILE_TAG_REGEX, MODEL_SONNET = "claude-sonnet-5", MODEL_OPUS = "claude-opus-5", MODEL_FABLE = "claude-fable-5", FALLBACK_AGENT, SESSION_AGENT, GATEWAY_AGENT, FALLBACK_MODEL, SESSION_MODEL, GATEWAY_MODEL, FFMPEG_BIN, FFPROBE_BIN, PYTHON_BIN, FASTER_WHISPER_WRAPPER, WHISPER_MODEL, TESSERACT_BIN, PDFTOTEXT_BIN, _envMaxTellDepth, MAX_TELL_DEPTH;
256
+ var HOME, PROJECT_ROOT, STATE_DIR_ENV, STATE_DIR, WORKSPACE_DIR, TOPIC_WORKSPACE_DIR, SHARED_WIKI_DIR, CRON_WORKSPACE_DIR, BROWSER_DIR, BROWSER_PROFILES_DIR, BINARIES_DIR, SECRETS_DIR, DM_WORKSPACE_DIR, SESSION_WORKSPACE_DIR, CLAUDE_EXECUTABLE_ENV, CLAUDE_EXECUTABLE, DEFAULT_OUTPUT_LANGUAGE = "English", BROWSER_RS_VERSION = "v0.1.17", BROWSER_RS_MIN_SECURE_VERSION = "0.1.15", SNIPPETS_API_URL, BROWSER_RS_BIN, PLAYWRIGHT_MCP_BIN, TSX_BIN, TSX_LOADER, TSCONFIG_PATH, SESSION_COMM_SERVER, TASK_SERVER, BROWSER_MCP_SSE_PROXY_SERVER, CANONICAL_MCP_PROXY_SERVER, WIKI_SERVER, TOKEN_STATS_SERVER, COMPACTION_LOG_SERVER, SYSTEM_HEALTH_SERVER, AGENT_HEALTH_SERVER, BACKGROUND_BASH_SERVER, VAULT_SERVER, BG_BASH_BASE_PORT, BG_BASH_MAX_PORT, RUNTIME_MCP_SECRET, NODE_CONTROL_TOKEN, VAULT_MASTER_KEY, NEGOTIUM_PORT, hostname, DATA_DIR, LOG_DIR, UPLOADS_DIR, VAULT_DIR, SESSIONS_DB, DEBUG_FILE, USERS_LOG_DIR, RUN_DIR, PROGRESS_DIR, DM_CMD_DIR, DM_RESP_DIR, SESSION_INBOX_DIR, SESSION_ASKS_DIR, PLAYWRIGHT_BASE_PORT, PLAYWRIGHT_MAX_PORT, PLAYWRIGHT_PORTS_DIR, ACTIVE_QUERY_STALE_MS, AGENTS_PROMPTS_DIR, RESOURCES_DIR, FILE_TAG_REGEX, MODEL_SONNET = "claude-sonnet-5", MODEL_OPUS = "claude-opus-5", MODEL_FABLE = "claude-fable-5", FALLBACK_AGENT, SESSION_AGENT, GATEWAY_AGENT, FALLBACK_MODEL, SESSION_MODEL, GATEWAY_MODEL, FFMPEG_BIN, FFPROBE_BIN, PYTHON_BIN, FASTER_WHISPER_WRAPPER, WHISPER_MODEL, TESSERACT_BIN, PDFTOTEXT_BIN, _envMaxTellDepth, MAX_TELL_DEPTH;
257
257
  var init_config = __esm(() => {
258
258
  init_config_helpers();
259
259
  init_logger();
@@ -274,6 +274,7 @@ var init_config = __esm(() => {
274
274
  SESSION_WORKSPACE_DIR = resolve(STATE_DIR, "data", "sessions");
275
275
  CLAUDE_EXECUTABLE_ENV = envText("NEGOTIUM_CLAUDE_EXECUTABLE");
276
276
  CLAUDE_EXECUTABLE = CLAUDE_EXECUTABLE_ENV ? resolve(CLAUDE_EXECUTABLE_ENV) : undefined;
277
+ SNIPPETS_API_URL = (envText("NEGOTIUM_SNIPPETS_API_URL") ?? envText("SNIPPETS_API_URL") ?? "").replace(/\/+$/, "");
277
278
  BROWSER_RS_BIN = resolveBrowserRsBin(envText("NEGOTIUM_BROWSER_RS_BIN"));
278
279
  PLAYWRIGHT_MCP_BIN = resolveBrowserMcpBin(envText("NEGOTIUM_BROWSER_MCP_BIN"));
279
280
  TSX_BIN = resolveDependencyBin("tsx");
@@ -1560,7 +1561,7 @@ var exports_version = {};
1560
1561
  __export(exports_version, {
1561
1562
  NEGOTIUM_VERSION: () => NEGOTIUM_VERSION
1562
1563
  });
1563
- var NEGOTIUM_VERSION = "0.2.8";
1564
+ var NEGOTIUM_VERSION = "0.2.10";
1564
1565
 
1565
1566
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
1566
1567
  import { spawn } from "child_process";
@@ -3633,6 +3634,9 @@ function existingCreatedAt(path) {
3633
3634
  return;
3634
3635
  }
3635
3636
  }
3637
+ function nonEmptyTurnText(text) {
3638
+ return text.trim().length > 0 ? text : BLANK_TURN_PLACEHOLDER;
3639
+ }
3636
3640
  function writeRollout(options) {
3637
3641
  const sessionId = options.reuseSessionId ?? randomUUID3();
3638
3642
  assertUuidLike("sessionId", sessionId);
@@ -3640,8 +3644,8 @@ function writeRollout(options) {
3640
3644
  const path = maestroSessionPath(sessionId);
3641
3645
  mkdirSync6(maestroSessionsDir(), { recursive: true });
3642
3646
  const messages = extractChatPairs(options.entries).flatMap((pair) => [
3643
- { role: "user", content: pair.userText },
3644
- { role: "assistant", content: pair.assistantText }
3647
+ { role: "user", content: nonEmptyTurnText(pair.userText) },
3648
+ { role: "assistant", content: nonEmptyTurnText(pair.assistantText) }
3645
3649
  ]);
3646
3650
  const lines = [
3647
3651
  {
@@ -3661,7 +3665,7 @@ function writeRollout(options) {
3661
3665
  });
3662
3666
  return { sessionId, rolloutPath: path };
3663
3667
  }
3664
- var ALIAS_MAP2, VALID_MODELS, VALID_EFFORTS3, maestroRegistry, maestroRegistryOperations;
3668
+ var ALIAS_MAP2, VALID_MODELS, VALID_EFFORTS3, maestroRegistry, BLANK_TURN_PLACEHOLDER = "(no text content in this turn)", maestroRegistryOperations;
3665
3669
  var init_maestro_registry = __esm(() => {
3666
3670
  init_shared();
3667
3671
  init_logger();
@@ -18530,10 +18534,9 @@ function startAiTurn(params) {
18530
18534
  ].join(`
18531
18535
  `);
18532
18536
  }
18537
+ const turnReminders = [];
18533
18538
  if (params.from === FROM_AUTO_CONTINUE) {
18534
- systemPrompt += `
18535
-
18536
- <system-reminder>\uC774 \uD134\uC740 \uC124\uC815 \uC790\uB3D9 \uC870\uC815(effort/model/agent) \uD6C4 \uC790\uB3D9 \uC7AC\uAC1C\uB41C \uD134\uC774\uB2E4. effort/model/agent \uB09C\uC774\uB3C4 \uC7AC\uD3C9\uAC00 \uBC0F \uC124\uC815 \uBCC0\uACBD \uC5C6\uC774 \uC989\uC2DC \uC791\uC5C5\uC744 \uC2DC\uC791\uD560 \uAC83.</system-reminder>`;
18539
+ turnReminders.push("<system-reminder>\uC774 \uD134\uC740 \uC124\uC815 \uC790\uB3D9 \uC870\uC815(effort/model/agent) \uD6C4 \uC790\uB3D9 \uC7AC\uAC1C\uB41C \uD134\uC774\uB2E4. effort/model/agent \uB09C\uC774\uB3C4 \uC7AC\uD3C9\uAC00 \uBC0F \uC124\uC815 \uBCC0\uACBD \uC5C6\uC774 \uC989\uC2DC \uC791\uC5C5\uC744 \uC2DC\uC791\uD560 \uAC83.</system-reminder>");
18537
18540
  }
18538
18541
  if (!silent && !sessionRetried) {
18539
18542
  const promptsToRecord = userConversationPromptsToRecord(renderedUserPrompts, loggedUserMessageCount, agentPrompt);
@@ -18616,26 +18619,24 @@ function startAiTurn(params) {
18616
18619
  logger.warn({ topicId, err: err2 }, "ai: ensureBgBash failed \u2014 proceeding without background bash tools");
18617
18620
  }
18618
18621
  }
18619
- let effectiveSystemPrompt = systemPrompt;
18620
18622
  if (playwrightRequested && !browserProfileOwner) {
18621
- const ownerOnlyNote = "<system-reminder>Browser tools are unavailable in this turn because browser profiles are private to the topic owner. Do not attempt browser calls or ask to reuse the owner's login state.</system-reminder>";
18622
- effectiveSystemPrompt = `${effectiveSystemPrompt}
18623
-
18624
- ${ownerOnlyNote}`;
18623
+ turnReminders.push("<system-reminder>Browser tools are unavailable in this turn because browser profiles are private to the topic owner. Do not attempt browser calls or ask to reuse the owner's login state.</system-reminder>");
18625
18624
  }
18626
18625
  if (consumePlaywrightUnavailable(userId, topic.title)) {
18627
- const playwrightNote = "<system-reminder>Playwright browser tools are UNAVAILABLE this turn. The `mcp__playwright__*` tools have been removed from this turn's catalog because the long-lived browser MCP could not be prepared. Do not attempt to call browser tools. If browser interaction is required, ask the user to retry shortly or use a non-browser alternative.</system-reminder>";
18628
- effectiveSystemPrompt = `${effectiveSystemPrompt}
18629
-
18630
- ${playwrightNote}`;
18626
+ turnReminders.push("<system-reminder>Playwright browser tools are UNAVAILABLE this turn. The `mcp__playwright__*` tools have been removed from this turn's catalog because the long-lived browser MCP could not be prepared. Do not attempt to call browser tools. If browser interaction is required, ask the user to retry shortly or use a non-browser alternative.</system-reminder>");
18631
18627
  }
18628
+ const effectivePrompt = turnReminders.length > 0 ? `${agentPrompt}
18629
+
18630
+ ${turnReminders.join(`
18631
+
18632
+ `)}` : agentPrompt;
18632
18633
  try {
18633
18634
  yield* runAgent({
18634
18635
  agent: agentKind,
18635
- prompt: agentPrompt,
18636
+ prompt: effectivePrompt,
18636
18637
  attachments: promptAttachments,
18637
18638
  cwd: workspaceCwd,
18638
- systemPrompt: effectiveSystemPrompt,
18639
+ systemPrompt,
18639
18640
  sessionId,
18640
18641
  userId,
18641
18642
  vaultUserId,
@@ -22366,6 +22367,253 @@ var init_node_host = __esm(async () => {
22366
22367
  ]);
22367
22368
  });
22368
22369
 
22370
+ // ../../packages/core/src/agents/mcp-tools/inline-assets.ts
22371
+ import { existsSync as existsSync24, readFileSync as readFileSync20, statSync as statSync13 } from "fs";
22372
+ import { extname as extname2, isAbsolute as isAbsolute6, resolve as resolve16 } from "path";
22373
+ function isNonLocal(ref) {
22374
+ return /^(?:[a-z][a-z0-9+.-]*:|\/\/|#)/i.test(ref.trim());
22375
+ }
22376
+ function stripSuffix(ref) {
22377
+ return ref.replace(/[?#].*$/, "");
22378
+ }
22379
+ function inlineLocalAssets(html, options) {
22380
+ const { baseDir, maxAssetBytes, maxTotalBytes } = options;
22381
+ const inlined = [];
22382
+ const unresolved = [];
22383
+ const skipped = [];
22384
+ const cache = new Map;
22385
+ let budget = maxTotalBytes - Buffer.byteLength(html, "utf-8");
22386
+ function toDataUri(rawRef) {
22387
+ const ref = rawRef.trim();
22388
+ if (!ref || isNonLocal(ref))
22389
+ return null;
22390
+ const cached = cache.get(ref);
22391
+ if (cached !== undefined)
22392
+ return cached;
22393
+ const relPath = stripSuffix(ref);
22394
+ if (!relPath)
22395
+ return null;
22396
+ const abs = isAbsolute6(relPath) ? relPath : resolve16(baseDir, relPath);
22397
+ let bytes;
22398
+ try {
22399
+ if (!existsSync24(abs) || !statSync13(abs).isFile()) {
22400
+ unresolved.push(ref);
22401
+ cache.set(ref, null);
22402
+ return null;
22403
+ }
22404
+ bytes = statSync13(abs).size;
22405
+ } catch {
22406
+ unresolved.push(ref);
22407
+ cache.set(ref, null);
22408
+ return null;
22409
+ }
22410
+ if (bytes > maxAssetBytes) {
22411
+ skipped.push({ ref, bytes, reason: "asset-too-large" });
22412
+ cache.set(ref, null);
22413
+ return null;
22414
+ }
22415
+ const encodedCost = Math.ceil(bytes / 3) * 4;
22416
+ if (encodedCost > budget) {
22417
+ skipped.push({ ref, bytes, reason: "document-too-large" });
22418
+ cache.set(ref, null);
22419
+ return null;
22420
+ }
22421
+ let uri;
22422
+ try {
22423
+ const mime = MIME_BY_EXT[extname2(abs).toLowerCase()] ?? "application/octet-stream";
22424
+ uri = `data:${mime};base64,${readFileSync20(abs).toString("base64")}`;
22425
+ } catch {
22426
+ unresolved.push(ref);
22427
+ cache.set(ref, null);
22428
+ return null;
22429
+ }
22430
+ budget -= uri.length;
22431
+ inlined.push({ ref, bytes });
22432
+ cache.set(ref, uri);
22433
+ return uri;
22434
+ }
22435
+ function rewriteSrcset(value) {
22436
+ return value.split(",").map((candidate) => {
22437
+ const trimmed = candidate.trim();
22438
+ if (!trimmed)
22439
+ return candidate;
22440
+ const [url, ...descriptor] = trimmed.split(/\s+/);
22441
+ const uri = url ? toDataUri(url) : null;
22442
+ if (!uri)
22443
+ return candidate;
22444
+ return [uri, ...descriptor].join(" ");
22445
+ }).join(", ");
22446
+ }
22447
+ let out = html;
22448
+ out = out.replace(/(\b(?:src|poster)\s*=\s*)(["'])([^"']*)\2/gi, (match, prefix, quote, ref) => {
22449
+ const uri = toDataUri(ref);
22450
+ return uri ? `${prefix}${quote}${uri}${quote}` : match;
22451
+ });
22452
+ out = out.replace(/(\bsrcset\s*=\s*)(["'])([^"']*)\2/gi, (match, prefix, quote, value) => {
22453
+ const rewritten = rewriteSrcset(value);
22454
+ return rewritten === value ? match : `${prefix}${quote}${rewritten}${quote}`;
22455
+ });
22456
+ out = out.replace(/<link\b[^>]*>/gi, (tag) => tag.replace(/(\bhref\s*=\s*)(["'])([^"']*)\2/i, (match, prefix, quote, ref) => {
22457
+ const uri = toDataUri(ref);
22458
+ return uri ? `${prefix}${quote}${uri}${quote}` : match;
22459
+ }));
22460
+ out = out.replace(/url\(\s*(["']?)([^"')]+)\1\s*\)/gi, (match, quote, ref) => {
22461
+ const uri = toDataUri(ref);
22462
+ return uri ? `url(${quote}${uri}${quote})` : match;
22463
+ });
22464
+ return { html: out, inlined, unresolved, skipped };
22465
+ }
22466
+ var MIME_BY_EXT;
22467
+ var init_inline_assets = __esm(() => {
22468
+ MIME_BY_EXT = {
22469
+ ".apng": "image/apng",
22470
+ ".avif": "image/avif",
22471
+ ".bmp": "image/bmp",
22472
+ ".css": "text/css",
22473
+ ".gif": "image/gif",
22474
+ ".ico": "image/x-icon",
22475
+ ".jpeg": "image/jpeg",
22476
+ ".jpg": "image/jpeg",
22477
+ ".mp3": "audio/mpeg",
22478
+ ".mp4": "video/mp4",
22479
+ ".otf": "font/otf",
22480
+ ".png": "image/png",
22481
+ ".svg": "image/svg+xml",
22482
+ ".ttf": "font/ttf",
22483
+ ".wav": "audio/wav",
22484
+ ".webm": "video/webm",
22485
+ ".webp": "image/webp",
22486
+ ".woff": "font/woff",
22487
+ ".woff2": "font/woff2"
22488
+ };
22489
+ });
22490
+
22491
+ // ../../packages/core/src/agents/mcp-tools/publish-html.ts
22492
+ import { z as z5 } from "zod";
22493
+ function formatPublishBytes(bytes) {
22494
+ if (bytes < 1024)
22495
+ return `${bytes}B`;
22496
+ if (bytes < 1024 * 1024)
22497
+ return `${(bytes / 1024).toFixed(0)}KB`;
22498
+ return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
22499
+ }
22500
+ function parseSnippetId(input) {
22501
+ const trimmed = input.trim();
22502
+ const fromUrl = trimmed.match(/\/snippets\/([A-Za-z0-9-]+)/)?.[1];
22503
+ const id = fromUrl ?? trimmed;
22504
+ return /^[A-Za-z0-9-]+$/.test(id) ? id : null;
22505
+ }
22506
+ async function snippetRequest(baseUrl, path, init = {}) {
22507
+ try {
22508
+ const response = await fetch(`${baseUrl}${path}`, {
22509
+ ...init,
22510
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
22511
+ });
22512
+ const body = await response.json().catch(() => null);
22513
+ if (body)
22514
+ return body;
22515
+ return { ok: false, error: `snippet service returned ${response.status}` };
22516
+ } catch (error) {
22517
+ const message = error instanceof Error ? error.message : "snippet service unreachable";
22518
+ return { ok: false, error: message };
22519
+ }
22520
+ }
22521
+ function createPublishHtmlToolDefinitions(ctx, options = {}) {
22522
+ const apiUrl = (options.apiUrl ?? SNIPPETS_API_URL)?.replace(/\/+$/, "");
22523
+ if (!apiUrl)
22524
+ return [];
22525
+ const getCtx = typeof ctx === "function" ? ctx : () => ctx;
22526
+ return [
22527
+ {
22528
+ name: "publish_html",
22529
+ description: [
22530
+ "Publish an HTML document and return a public, shareable https link that",
22531
+ "stays live for 72 hours. Use this when the user should be able to open,",
22532
+ "keep, or send the result \u2014 a report, dashboard, or styled table \u2014 rather",
22533
+ "than only glance at it in the side panel.",
22534
+ "Pass a complete standalone document and inline the CSS and JS.",
22535
+ "For charts and diagrams prefer inline <svg>: it stays sharp at any zoom",
22536
+ "and is far smaller than an image file.",
22537
+ "Images may be remote URLs, data: URIs, or paths to local files you",
22538
+ "created \u2014 local files are embedded automatically, so the page keeps",
22539
+ "working after your workspace is gone."
22540
+ ].join(" "),
22541
+ schema: {
22542
+ html: z5.string().describe("Complete standalone HTML document to publish. Inline the CSS and JS."),
22543
+ base_dir: z5.string().optional().describe("Directory that relative image paths resolve against. Defaults to the working directory.")
22544
+ },
22545
+ async handler(input) {
22546
+ const html = input?.html ?? "";
22547
+ if (!html.trim())
22548
+ return errorResult("html is empty \u2014 provide an HTML document.");
22549
+ const baseDir = input.base_dir?.trim() || getCtx().cwd || process.cwd();
22550
+ const report = inlineLocalAssets(html, {
22551
+ baseDir,
22552
+ maxAssetBytes: MAX_PUBLISHED_ASSET_BYTES,
22553
+ maxTotalBytes: MAX_PUBLISHED_BYTES
22554
+ });
22555
+ const bytes = Buffer.byteLength(report.html, "utf-8");
22556
+ if (bytes > MAX_PUBLISHED_BYTES) {
22557
+ return errorResult(`Document is ${formatPublishBytes(bytes)} after embedding images, over the ${formatPublishBytes(MAX_PUBLISHED_BYTES)} limit. Use fewer or smaller images, or switch charts to inline <svg>.`);
22558
+ }
22559
+ const result = await snippetRequest(apiUrl, "/snippets", {
22560
+ method: "POST",
22561
+ headers: { "content-type": "text/html; charset=utf-8" },
22562
+ body: report.html
22563
+ });
22564
+ if (!result.ok || !result.url) {
22565
+ return errorResult(result.error ?? "Failed to publish.");
22566
+ }
22567
+ const notes = [];
22568
+ if (report.inlined.length > 0) {
22569
+ const total = report.inlined.reduce((sum, a) => sum + a.bytes, 0);
22570
+ notes.push(`Embedded ${report.inlined.length} local file(s), ${formatPublishBytes(total)}.`);
22571
+ }
22572
+ if (report.unresolved.length > 0) {
22573
+ notes.push(`WARNING \u2014 these look like local files but were not found, so they will render broken: ${report.unresolved.join(", ")}. Check the paths (relative to ${baseDir}) and republish.`);
22574
+ }
22575
+ for (const s of report.skipped) {
22576
+ notes.push(s.reason === "asset-too-large" ? `WARNING \u2014 ${s.ref} is ${formatPublishBytes(s.bytes)}, over the ${formatPublishBytes(MAX_PUBLISHED_ASSET_BYTES)} per-file limit, and was left as a broken link.` : `WARNING \u2014 ${s.ref} was left out because the document hit the ${formatPublishBytes(MAX_PUBLISHED_BYTES)} limit.`);
22577
+ }
22578
+ const payload = {
22579
+ url: result.url,
22580
+ snippetId: result.snippetId,
22581
+ expiresAt: result.expiresAt,
22582
+ sizeBytes: bytes,
22583
+ ...report.inlined.length > 0 ? { embeddedFiles: report.inlined.length } : {}
22584
+ };
22585
+ return textResult([JSON.stringify(payload, null, 2), ...notes].join(`
22586
+
22587
+ `));
22588
+ }
22589
+ },
22590
+ {
22591
+ name: "unpublish_html",
22592
+ description: "Delete a previously published HTML document before its 72-hour expiry. Accepts the URL returned by publish_html, or its id.",
22593
+ schema: {
22594
+ snippet: z5.string().describe("URL returned by publish_html, or the bare id.")
22595
+ },
22596
+ async handler(input) {
22597
+ const id = parseSnippetId(input?.snippet ?? "");
22598
+ if (!id)
22599
+ return errorResult("Could not read a published document id from that value.");
22600
+ const result = await snippetRequest(apiUrl, `/snippets/${id}`, { method: "DELETE" });
22601
+ if (!result.ok)
22602
+ return errorResult(result.error ?? "Failed to delete.");
22603
+ return textResult(`Published document ${id} deleted.`);
22604
+ }
22605
+ }
22606
+ ];
22607
+ }
22608
+ var REQUEST_TIMEOUT_MS = 30000, MAX_PUBLISHED_BYTES, MAX_PUBLISHED_ASSET_BYTES;
22609
+ var init_publish_html = __esm(() => {
22610
+ init_common();
22611
+ init_inline_assets();
22612
+ init_config();
22613
+ MAX_PUBLISHED_BYTES = 10 * 1024 * 1024;
22614
+ MAX_PUBLISHED_ASSET_BYTES = 5 * 1024 * 1024;
22615
+ });
22616
+
22369
22617
  // ../../packages/core/src/mcp-runtime-host.ts
22370
22618
  var exports_mcp_runtime_host = {};
22371
22619
  __export(exports_mcp_runtime_host, {
@@ -22397,6 +22645,7 @@ __export(exports_mcp_runtime_host, {
22397
22645
  createSubagentManagementToolDefinitions: () => createSubagentManagementToolDefinitions,
22398
22646
  createSpawnSubagentToolDefinition: () => createSpawnSubagentToolDefinition,
22399
22647
  createSelfConfigToolDefinitions: () => createSelfConfigToolDefinitions,
22648
+ createPublishHtmlToolDefinitions: () => createPublishHtmlToolDefinitions,
22400
22649
  createPrepareSubagentToolDefinition: () => createPrepareSubagentToolDefinition,
22401
22650
  createAskUserToolDefinition: () => createAskUserToolDefinition,
22402
22651
  canSpawnSubagentsFromTopic: () => canSpawnSubagentsFromTopic,
@@ -22420,6 +22669,7 @@ __export(exports_mcp_runtime_host, {
22420
22669
  });
22421
22670
  var init_mcp_runtime_host = __esm(async () => {
22422
22671
  init_common();
22672
+ init_publish_html();
22423
22673
  init_visuals2();
22424
22674
  init_runtime_spec();
22425
22675
  init_config();
@@ -22449,7 +22699,7 @@ var init_mcp_runtime_host = __esm(async () => {
22449
22699
  });
22450
22700
 
22451
22701
  // ../../packages/mcp/src/node-tools.ts
22452
- import { z as z5 } from "zod";
22702
+ import { z as z6 } from "zod";
22453
22703
  function resolveTopicForUser(ctx, ref) {
22454
22704
  const trimmed = ref.trim();
22455
22705
  if (!trimmed)
@@ -22473,11 +22723,11 @@ function describeTopic(topic) {
22473
22723
  }
22474
22724
  function registerNodeTools(server, ctx) {
22475
22725
  server.tool("register_topic", "Create a new topic (agent room) on this negotium node, owned by the calling user. " + "Returns the new topic's id, title, agent, and model. Use the session-comm tell_session " + "tool to hand the new topic work.", {
22476
- title: z5.string().describe("Unique title for the new topic."),
22477
- agent: z5.enum(["claude", "codex", "maestro"]).optional().describe("AI backend for the room. Defaults to maestro."),
22478
- model: z5.string().optional().describe("Model override, must be valid for the agent."),
22479
- effort: z5.enum(EFFORT_VALUES).optional().describe("Reasoning effort override for the room."),
22480
- description: z5.string().optional().describe("Short description of the topic's purpose.")
22726
+ title: z6.string().describe("Unique title for the new topic."),
22727
+ agent: z6.enum(["claude", "codex", "maestro"]).optional().describe("AI backend for the room. Defaults to maestro."),
22728
+ model: z6.string().optional().describe("Model override, must be valid for the agent."),
22729
+ effort: z6.enum(EFFORT_VALUES).optional().describe("Reasoning effort override for the room."),
22730
+ description: z6.string().optional().describe("Short description of the topic's purpose.")
22481
22731
  }, async ({ title, agent, model, effort, description }) => {
22482
22732
  try {
22483
22733
  const topic = registerTopic({
@@ -22512,7 +22762,7 @@ function registerNodeTools(server, ctx) {
22512
22762
  `));
22513
22763
  });
22514
22764
  server.tool("abort_topic", "Abort the running turn in another topic on this node. Fire-and-forget: also queues an abort " + "signal in the topic's inbox so a turn that has not started yet is cancelled too. Returns whether an active turn was aborted.", {
22515
- topic: z5.string().describe("Target topic title or id.")
22765
+ topic: z6.string().describe("Target topic title or id.")
22516
22766
  }, async ({ topic }) => {
22517
22767
  const resolved = resolveTopicForUser(ctx, topic);
22518
22768
  if ("error" in resolved)
@@ -22537,7 +22787,7 @@ function registerNodeTools(server, ctx) {
22537
22787
  return textResult(queued ? `No active turn in "${target.title}"; abort signal queued in its inbox.` : `No active turn in "${target.title}", and the abort signal could NOT be queued ` + `(its inbox is locked). The topic will run its queued work; retry shortly.`);
22538
22788
  });
22539
22789
  server.tool("restart_topic", "Reset another topic's AI context while preserving the topic and its visible message history. " + "The next message starts a fresh provider session. Only the topic owner can restart it.", {
22540
- topic: z5.string().describe("Target topic title or id.")
22790
+ topic: z6.string().describe("Target topic title or id.")
22541
22791
  }, async ({ topic }) => {
22542
22792
  const resolved = resolveTopicForUser(ctx, topic);
22543
22793
  if ("error" in resolved)
@@ -22553,8 +22803,8 @@ function registerNodeTools(server, ctx) {
22553
22803
  return result.isError ? errorResult(`Error: ${result.text}`) : textResult(result.text);
22554
22804
  });
22555
22805
  server.tool("delete_topic", "Delete a topic owned by the calling user after archiving its conversation history. Deletion is blocked " + "when the archive cannot be written; pass force: true only as an explicit escape hatch that accepts losing history.", {
22556
- topic: z5.string().describe("Target topic title or id."),
22557
- force: z5.boolean().optional().describe("Delete even if archiving the conversation history fails. Default false.")
22806
+ topic: z6.string().describe("Target topic title or id."),
22807
+ force: z6.boolean().optional().describe("Delete even if archiving the conversation history fails. Default false.")
22558
22808
  }, async ({ topic, force }) => {
22559
22809
  const resolved = resolveTopicForUser(ctx, topic);
22560
22810
  if ("error" in resolved)
@@ -22600,7 +22850,7 @@ __export(exports_task, {
22600
22850
  createTaskMcpServer: () => createTaskMcpServer
22601
22851
  });
22602
22852
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
22603
- import { z as z6 } from "zod";
22853
+ import { z as z7 } from "zod";
22604
22854
  function createTaskMcpServer(context, host2 = defaultTaskMcpHost) {
22605
22855
  const scopeKey = context.topic ? taskScopeKey({ topicId: context.topicId, session: context.topic }) : "";
22606
22856
  const server = new McpServer({ name: "task", version: "1.0.0" });
@@ -22610,13 +22860,13 @@ function createTaskMcpServer(context, host2 = defaultTaskMcpHost) {
22610
22860
  }
22611
22861
  return null;
22612
22862
  };
22613
- const statusEnum = z6.enum(TASK_STATUS_VALUES);
22863
+ const statusEnum = z7.enum(TASK_STATUS_VALUES);
22614
22864
  server.tool("task_create", "Add one or more tasks to this topic's shared Otium task list. Use this instead of provider-native todo/task tools.", {
22615
- tasks: z6.array(z6.object({
22616
- subject: z6.string().min(1).describe("Short imperative task description"),
22617
- active_form: z6.string().optional().describe("Label to show while the task is in progress"),
22618
- blocked_by: z6.array(z6.string()).optional().describe("Task ids blocking this task"),
22619
- owner: z6.string().optional().describe("Owner/agent label for multi-agent work")
22865
+ tasks: z7.array(z7.object({
22866
+ subject: z7.string().min(1).describe("Short imperative task description"),
22867
+ active_form: z7.string().optional().describe("Label to show while the task is in progress"),
22868
+ blocked_by: z7.array(z7.string()).optional().describe("Task ids blocking this task"),
22869
+ owner: z7.string().optional().describe("Owner/agent label for multi-agent work")
22620
22870
  })).min(1)
22621
22871
  }, async ({ tasks: inputs }) => {
22622
22872
  const guard = requireContext();
@@ -22640,13 +22890,13 @@ ${renderTaskList(tasks2)}`);
22640
22890
  }
22641
22891
  });
22642
22892
  server.tool("task_update", "Update task status/content in this topic's shared Otium task list. Batch related status changes in one call.", {
22643
- updates: z6.array(z6.object({
22644
- id: z6.string().describe("Target task id"),
22893
+ updates: z7.array(z7.object({
22894
+ id: z7.string().describe("Target task id"),
22645
22895
  status: statusEnum.optional().describe("New status"),
22646
- subject: z6.string().min(1).optional().describe("Replacement task description"),
22647
- active_form: z6.string().optional().describe("Replacement in-progress label; empty string clears it"),
22648
- blocked_by: z6.array(z6.string()).optional().describe("Replacement blocker id list"),
22649
- owner: z6.string().optional().describe("Replacement owner; empty string clears it")
22896
+ subject: z7.string().min(1).optional().describe("Replacement task description"),
22897
+ active_form: z7.string().optional().describe("Replacement in-progress label; empty string clears it"),
22898
+ blocked_by: z7.array(z7.string()).optional().describe("Replacement blocker id list"),
22899
+ owner: z7.string().optional().describe("Replacement owner; empty string clears it")
22650
22900
  })).min(1)
22651
22901
  }, async ({ updates }) => {
22652
22902
  const guard = requireContext();
@@ -22680,7 +22930,7 @@ Missing ids ignored: ${missing.join(", ")}` : "";
22680
22930
  return mcpError(`task_list failed: ${errMsg(error)}`);
22681
22931
  }
22682
22932
  });
22683
- server.tool("task_get", "Read one task from this topic's shared Otium task list as JSON.", { id: z6.string().describe("Task id") }, async ({ id }) => {
22933
+ server.tool("task_get", "Read one task from this topic's shared Otium task list as JSON.", { id: z7.string().describe("Task id") }, async ({ id }) => {
22684
22934
  const guard = requireContext();
22685
22935
  if (guard)
22686
22936
  return guard;
@@ -22694,8 +22944,8 @@ Missing ids ignored: ${missing.join(", ")}` : "";
22694
22944
  }
22695
22945
  });
22696
22946
  server.tool("task_delete", "Delete tasks from this topic's shared Otium task list. Use all=true only when starting a new plan.", {
22697
- ids: z6.array(z6.string()).optional().describe("Task ids to delete"),
22698
- all: z6.boolean().optional().describe("Delete the whole list")
22947
+ ids: z7.array(z7.string()).optional().describe("Task ids to delete"),
22948
+ all: z7.boolean().optional().describe("Delete the whole list")
22699
22949
  }, async ({ ids, all }) => {
22700
22950
  const guard = requireContext();
22701
22951
  if (guard)
@@ -22732,7 +22982,7 @@ __export(exports_token_stats2, {
22732
22982
  createTokenStatsMcpServer: () => createTokenStatsMcpServer
22733
22983
  });
22734
22984
  import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
22735
- import { z as z7 } from "zod";
22985
+ import { z as z8 } from "zod";
22736
22986
  function createTokenStatsMcpServer(context, host2 = defaultTokenStatsMcpHost) {
22737
22987
  if (!context.userId)
22738
22988
  throw new Error("token-stats MCP requires userId");
@@ -22748,9 +22998,9 @@ function createTokenStatsMcpServer(context, host2 = defaultTokenStatsMcpHost) {
22748
22998
  "groupBy: 'session'\uC73C\uB85C \uC124\uC815\uD558\uBA74 \uC138\uC158\uBCC4 \uD1A0\uD070 \uC0AC\uC6A9\uB7C9 \uB7AD\uD0B9\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4."
22749
22999
  ].join(`
22750
23000
  `), {
22751
- from: z7.string().optional().describe("\uC2DC\uC791 \uC2DC\uAC01 ISO 8601. \uC608: '2026-03-28T09:00:00Z'"),
22752
- to: z7.string().optional().describe("\uC885\uB8CC \uC2DC\uAC01 ISO 8601. \uC0DD\uB7B5 \uC2DC \uD604\uC7AC"),
22753
- groupBy: z7.enum(["hour", "session"]).optional().describe("\uC9D1\uACC4 \uAE30\uC900. \uAE30\uBCF8\uAC12: hour")
23001
+ from: z8.string().optional().describe("\uC2DC\uC791 \uC2DC\uAC01 ISO 8601. \uC608: '2026-03-28T09:00:00Z'"),
23002
+ to: z8.string().optional().describe("\uC885\uB8CC \uC2DC\uAC01 ISO 8601. \uC0DD\uB7B5 \uC2DC \uD604\uC7AC"),
23003
+ groupBy: z8.enum(["hour", "session"]).optional().describe("\uC9D1\uACC4 \uAE30\uC900. \uAE30\uBCF8\uAC12: hour")
22754
23004
  }, async ({ from, to, groupBy = "hour" }) => {
22755
23005
  const { total, byHour, bySession, estimatedCostUsd } = host2.getStats(context.userId, from, to);
22756
23006
  const lines = [
@@ -22957,10 +23207,10 @@ __export(exports_agent_health, {
22957
23207
  createAgentHealthMcpServer: () => createAgentHealthMcpServer
22958
23208
  });
22959
23209
  import { spawn as spawn6 } from "child_process";
22960
- import { existsSync as existsSync24, readdirSync as readdirSync8 } from "fs";
23210
+ import { existsSync as existsSync25, readdirSync as readdirSync8 } from "fs";
22961
23211
  import { join as join33 } from "path";
22962
23212
  import { McpServer as McpServer4 } from "@modelcontextprotocol/sdk/server/mcp.js";
22963
- import { z as z8 } from "zod";
23213
+ import { z as z9 } from "zod";
22964
23214
  function signalProcessTree2(child, signal) {
22965
23215
  try {
22966
23216
  if (child.pid && process.platform !== "win32")
@@ -22974,7 +23224,7 @@ function signalProcessTree2(child, signal) {
22974
23224
  }
22975
23225
  }
22976
23226
  function spawnCapture(cmd, args, timeoutMs, signal, env) {
22977
- return new Promise((resolve16, reject) => {
23227
+ return new Promise((resolve17, reject) => {
22978
23228
  const child = spawn6(cmd, args, {
22979
23229
  env: env ?? process.env,
22980
23230
  stdio: ["ignore", "pipe", "pipe"],
@@ -23032,7 +23282,7 @@ function spawnCapture(cmd, args, timeoutMs, signal, env) {
23032
23282
  if (killTimer)
23033
23283
  clearTimeout(killTimer);
23034
23284
  if (code === 0)
23035
- resolve16({ stdout, stderr });
23285
+ resolve17({ stdout, stderr });
23036
23286
  else
23037
23287
  reject(new Error(stderr.trim() || `${cmd} exited with ${code ?? exitSignal}`));
23038
23288
  });
@@ -23110,11 +23360,11 @@ async function checkMaestro(model, timeoutMs, signal) {
23110
23360
  const remaining = deadline - Date.now();
23111
23361
  if (remaining <= 0)
23112
23362
  throw new Error("timeout");
23113
- const eventResult = await new Promise((resolve16, reject) => {
23363
+ const eventResult = await new Promise((resolve17, reject) => {
23114
23364
  const timeout = setTimeout(() => reject(new Error("timeout")), remaining);
23115
23365
  const onAbort = () => reject(new Error("aborted"));
23116
23366
  signal.addEventListener("abort", onAbort, { once: true });
23117
- activeIterator.next().then(resolve16, reject).finally(() => {
23367
+ activeIterator.next().then(resolve17, reject).finally(() => {
23118
23368
  clearTimeout(timeout);
23119
23369
  signal.removeEventListener("abort", onAbort);
23120
23370
  });
@@ -23144,7 +23394,7 @@ function getDefaultModel(registry) {
23144
23394
  }
23145
23395
  async function withCheckSlot(operation) {
23146
23396
  if (activeChecks >= MAX_CONCURRENT_CHECKS) {
23147
- await new Promise((resolve16) => checkWaiters.push(resolve16));
23397
+ await new Promise((resolve17) => checkWaiters.push(resolve17));
23148
23398
  }
23149
23399
  activeChecks += 1;
23150
23400
  try {
@@ -23163,9 +23413,9 @@ function createAgentHealthMcpServer(context) {
23163
23413
  await originalClose();
23164
23414
  };
23165
23415
  server.tool("check_agent", "\uC2E4\uC81C API \uD638\uCD9C\uB85C \uD2B9\uC815 agent+model \uC870\uD569\uC774 \uC815\uC0C1 \uB3D9\uC791\uD558\uB294\uC9C0 \uCCB4\uD06C\uD569\uB2C8\uB2E4.", {
23166
- agent: z8.enum(["claude", "codex", "maestro"]).describe("\uCCB4\uD06C\uD560 agent"),
23167
- model: z8.string().optional().describe("\uCCB4\uD06C\uD560 \uBAA8\uB378 (\uC5C6\uC73C\uBA74 agent\uBCC4 default \uC0AC\uC6A9)"),
23168
- timeoutMs: z8.number().default(30000).describe("\uD0C0\uC784\uC544\uC6C3 (ms), \uAE30\uBCF8 30\uCD08")
23416
+ agent: z9.enum(["claude", "codex", "maestro"]).describe("\uCCB4\uD06C\uD560 agent"),
23417
+ model: z9.string().optional().describe("\uCCB4\uD06C\uD560 \uBAA8\uB378 (\uC5C6\uC73C\uBA74 agent\uBCC4 default \uC0AC\uC6A9)"),
23418
+ timeoutMs: z9.number().default(30000).describe("\uD0C0\uC784\uC544\uC6C3 (ms), \uAE30\uBCF8 30\uCD08")
23169
23419
  }, async ({ agent, model, timeoutMs }) => {
23170
23420
  const entry = AGENTS.find((candidate) => candidate.kind === agent);
23171
23421
  if (!entry)
@@ -23181,7 +23431,7 @@ function createAgentHealthMcpServer(context) {
23181
23431
  latency: ${result.latency}ms
23182
23432
  error: ${result.error}`);
23183
23433
  });
23184
- server.tool("check_all", "\uBAA8\uB4E0 agent (claude, codex, maestro)\uB97C \uAE30\uBCF8 \uBAA8\uB378\uB85C \uB3D9\uC2DC\uC5D0 \uCCB4\uD06C\uD569\uB2C8\uB2E4.", { timeoutMs: z8.number().default(30000).describe("agent\uBCC4 \uD0C0\uC784\uC544\uC6C3 (ms), \uAE30\uBCF8 30\uCD08") }, async ({ timeoutMs }) => {
23434
+ server.tool("check_all", "\uBAA8\uB4E0 agent (claude, codex, maestro)\uB97C \uAE30\uBCF8 \uBAA8\uB378\uB85C \uB3D9\uC2DC\uC5D0 \uCCB4\uD06C\uD569\uB2C8\uB2E4.", { timeoutMs: z9.number().default(30000).describe("agent\uBCC4 \uD0C0\uC784\uC544\uC6C3 (ms), \uAE30\uBCF8 30\uCD08") }, async ({ timeoutMs }) => {
23185
23435
  const userKey = context.userId ?? "unscoped";
23186
23436
  if (activeCheckAllUsers.has(userKey)) {
23187
23437
  return mcpOk("FAIL check_all is already running for this user");
@@ -23203,7 +23453,7 @@ ${results.every((result) => result.ok) ? "All agents healthy" : "Some agents fai
23203
23453
  if (!context.userId)
23204
23454
  return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC870\uD68C \uBD88\uAC00 (user context \uC5C6\uC74C)");
23205
23455
  const stateDir = join33(USERS_LOG_DIR, context.userId, "active-queries");
23206
- if (!existsSync24(stateDir))
23456
+ if (!existsSync25(stateDir))
23207
23457
  return mcpOk("\uC2E4\uD589 \uC911\uC778 \uCFFC\uB9AC \uC5C6\uC74C");
23208
23458
  let files;
23209
23459
  try {
@@ -23294,7 +23544,7 @@ __export(exports_session_comm, {
23294
23544
  createSessionCommMcpServer: () => createSessionCommMcpServer
23295
23545
  });
23296
23546
  import { McpServer as McpServer5 } from "@modelcontextprotocol/sdk/server/mcp.js";
23297
- import { z as z9 } from "zod";
23547
+ import { z as z10 } from "zod";
23298
23548
  function createSessionCommMcpServer(context, host2, options = {}) {
23299
23549
  const server = new McpServer5({ name: "session-comm", version: "2.0.0" });
23300
23550
  const required = options.requiredMcpServers?.join(", ") || "none";
@@ -23302,28 +23552,28 @@ function createSessionCommMcpServer(context, host2, options = {}) {
23302
23552
  const subagentIdentity = resolveSubagentTellIdentity(context.currentTopicId, context.subagentParentTopicId);
23303
23553
  server.tool("list_sessions", "List available sessions and remote peer sessions for inter-session communication.", {}, async () => host2.listSessions(context));
23304
23554
  server.tool("configure_mcp", `Configure optional MCP servers for the current topic. Required: ${required}. Optional: ${optional}.`, {
23305
- enabled: z9.array(z9.string()).nullable().optional()
23555
+ enabled: z10.array(z10.string()).nullable().optional()
23306
23556
  }, async ({ enabled }) => host2.configureMcp(context, enabled));
23307
23557
  server.tool("get_mcp_config", "Get the current topic MCP configuration.", {}, async () => host2.getMcpConfig(context));
23308
23558
  if (host2.getBrowserProfile) {
23309
23559
  server.tool("get_browser_profile", "Get this topic's browser profile and the profiles owned by the same user.", {}, async () => host2.getBrowserProfile(context));
23310
23560
  }
23311
23561
  if (host2.setBrowserProfile) {
23312
- server.tool("set_browser_profile", "Assign this topic to a named shared browser profile. Takes effect next turn.", { profile: z9.string() }, async ({ profile }) => host2.setBrowserProfile(context, profile));
23562
+ server.tool("set_browser_profile", "Assign this topic to a named shared browser profile. Takes effect next turn.", { profile: z10.string() }, async ({ profile }) => host2.setBrowserProfile(context, profile));
23313
23563
  }
23314
23564
  if (host2.deleteBrowserProfile) {
23315
- server.tool("delete_browser_profile", "Delete an unused named browser profile and its on-disk data. The default or an assigned profile cannot be deleted.", { profile: z9.string() }, async ({ profile }) => host2.deleteBrowserProfile(context, profile));
23565
+ server.tool("delete_browser_profile", "Delete an unused named browser profile and its on-disk data. The default or an assigned profile cannot be deleted.", { profile: z10.string() }, async ({ profile }) => host2.deleteBrowserProfile(context, profile));
23316
23566
  }
23317
23567
  server.tool("peek_session", "Inspect running and idle sessions, including pending ask_session calls.", {}, async () => host2.peekSession(context));
23318
- server.tool("set_description", "Set the current session description used as a routing hint.", { description: z9.string() }, async ({ description }) => host2.setDescription(context, description));
23568
+ server.tool("set_description", "Set the current session description used as a routing hint.", { description: z10.string() }, async ({ description }) => host2.setDescription(context, description));
23319
23569
  if (!context.replyOnly) {
23320
23570
  if (!subagentIdentity.restricted) {
23321
- server.tool("ask_session", "Ask another local or remote session a question and wait for its answer.", { to: z9.string(), message: z9.string() }, async (input) => host2.askSession(context, input));
23571
+ server.tool("ask_session", "Ask another local or remote session a question and wait for its answer.", { to: z10.string(), message: z10.string() }, async (input) => host2.askSession(context, input));
23322
23572
  }
23323
23573
  if (!subagentIdentity.restricted) {
23324
- server.tool("abort_session", "Abort the active query in another session.", { to: z9.string() }, async ({ to }) => host2.abortSession(context, to));
23574
+ server.tool("abort_session", "Abort the active query in another session.", { to: z10.string() }, async ({ to }) => host2.abortSession(context, to));
23325
23575
  }
23326
- server.tool("tell_session", "Send a one-way message to another local or remote session.", { to: z9.string(), message: z9.string() }, async (input) => {
23576
+ server.tool("tell_session", "Send a one-way message to another local or remote session.", { to: z10.string(), message: z10.string() }, async (input) => {
23327
23577
  if (subagentIdentity.restricted && !canSubagentTellTargetByName(subagentIdentity, input.to)) {
23328
23578
  return {
23329
23579
  content: [
@@ -23998,7 +24248,7 @@ async function executeVaultRun(userId, request, host2) {
23998
24248
  const stdoutState = { bytes: 0, truncated: false };
23999
24249
  const stderrState = { bytes: 0, truncated: false };
24000
24250
  const startedAt = Date.now();
24001
- return await new Promise((resolve16) => {
24251
+ return await new Promise((resolve17) => {
24002
24252
  const child = spawn7(process.env.SHELL || "/bin/sh", ["-s"], {
24003
24253
  cwd: request.cwd,
24004
24254
  detached: true,
@@ -24031,7 +24281,7 @@ async function executeVaultRun(userId, request, host2) {
24031
24281
  durationMs: Date.now() - startedAt,
24032
24282
  timedOut
24033
24283
  }, "vault credential command used");
24034
- resolve16({ ...result, usedKeys: substitution.usedKeys });
24284
+ resolve17({ ...result, usedKeys: substitution.usedKeys });
24035
24285
  };
24036
24286
  child.stdout.on("data", (chunk) => appendBounded(stdoutChunks, chunk, stdoutState, maxOutputBytes));
24037
24287
  child.stderr.on("data", (chunk) => appendBounded(stderrChunks, chunk, stderrState, maxOutputBytes));
@@ -24075,7 +24325,7 @@ __export(exports_vault, {
24075
24325
  createVaultMcpServer: () => createVaultMcpServer
24076
24326
  });
24077
24327
  import { McpServer as McpServer6 } from "@modelcontextprotocol/sdk/server/mcp.js";
24078
- import { z as z10 } from "zod";
24328
+ import { z as z11 } from "zod";
24079
24329
  function createVaultMcpServer(context, host2, executors = {}) {
24080
24330
  const server = new McpServer6({ name: "vault", version: "1.0.0" });
24081
24331
  const run = executors.run ?? executeVaultRun;
@@ -24093,9 +24343,9 @@ ${lines.join(`
24093
24343
  });
24094
24344
  if (!context.listOnly && !context.httpOnly) {
24095
24345
  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.", {
24096
- command: z10.string().min(1).max(64 * 1024),
24097
- timeout_ms: z10.number().int().min(1000).max(600000).optional(),
24098
- max_output_bytes: z10.number().int().min(1024).max(2 * 1024 * 1024).optional()
24346
+ command: z11.string().min(1).max(64 * 1024),
24347
+ timeout_ms: z11.number().int().min(1000).max(600000).optional(),
24348
+ max_output_bytes: z11.number().int().min(1024).max(2 * 1024 * 1024).optional()
24099
24349
  }, async ({ command, timeout_ms, max_output_bytes }) => {
24100
24350
  if (!context.userId)
24101
24351
  return mcpError("Vault unavailable: no user context");
@@ -24110,12 +24360,12 @@ ${lines.join(`
24110
24360
  }
24111
24361
  if (!context.listOnly)
24112
24362
  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.", {
24113
- method: z10.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("GET"),
24114
- url: z10.string().url().describe("Absolute HTTPS URL without Vault placeholders"),
24115
- headers: z10.record(z10.string(), z10.string()).optional(),
24116
- body: z10.string().optional(),
24117
- timeout_ms: z10.number().int().min(1000).max(120000).optional(),
24118
- max_response_bytes: z10.number().int().min(1024).max(1024 * 1024).optional()
24363
+ method: z11.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("GET"),
24364
+ url: z11.string().url().describe("Absolute HTTPS URL without Vault placeholders"),
24365
+ headers: z11.record(z11.string(), z11.string()).optional(),
24366
+ body: z11.string().optional(),
24367
+ timeout_ms: z11.number().int().min(1000).max(120000).optional(),
24368
+ max_response_bytes: z11.number().int().min(1024).max(1024 * 1024).optional()
24119
24369
  }, async ({ method, url, headers, body, timeout_ms, max_response_bytes }) => {
24120
24370
  if (!context.userId)
24121
24371
  return mcpError("Vault unavailable: no user context");
@@ -24147,17 +24397,17 @@ import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
24147
24397
  import { randomUUID as randomUUID21 } from "crypto";
24148
24398
  import {
24149
24399
  closeSync as closeSync3,
24150
- existsSync as existsSync25,
24400
+ existsSync as existsSync26,
24151
24401
  mkdirSync as mkdirSync24,
24152
24402
  openSync as openSync3,
24153
24403
  readdirSync as readdirSync9,
24154
- readFileSync as readFileSync20,
24404
+ readFileSync as readFileSync21,
24155
24405
  renameSync as renameSync10,
24156
- statSync as statSync13,
24406
+ statSync as statSync14,
24157
24407
  unlinkSync as unlinkSync18,
24158
24408
  writeFileSync as writeFileSync17
24159
24409
  } from "fs";
24160
- import { basename as basename7, dirname as dirname16, join as join35, resolve as resolve16 } from "path";
24410
+ import { basename as basename7, dirname as dirname16, join as join35, resolve as resolve17 } from "path";
24161
24411
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
24162
24412
  import { StdioServerTransport as StdioServerTransport2 } from "@modelcontextprotocol/sdk/server/stdio.js";
24163
24413
  import {
@@ -24204,7 +24454,7 @@ function acquireRecoveryGuard(lockPath) {
24204
24454
  return () => {
24205
24455
  closeSync3(fd);
24206
24456
  try {
24207
- if (readFileSync20(guardPath, "utf-8") === ownerToken)
24457
+ if (readFileSync21(guardPath, "utf-8") === ownerToken)
24208
24458
  unlinkSync18(guardPath);
24209
24459
  } catch {}
24210
24460
  };
@@ -24238,7 +24488,7 @@ function acquireFileLock(path) {
24238
24488
  closeSync3(fd);
24239
24489
  const releaseGuard = acquireRecoveryGuard(lockPath);
24240
24490
  try {
24241
- if (readFileSync20(lockPath, "utf-8") === ownerToken)
24491
+ if (readFileSync21(lockPath, "utf-8") === ownerToken)
24242
24492
  unlinkSync18(lockPath);
24243
24493
  } catch {} finally {
24244
24494
  releaseGuard();
@@ -24250,8 +24500,8 @@ function acquireFileLock(path) {
24250
24500
  const releaseGuard = acquireRecoveryGuard(lockPath);
24251
24501
  try {
24252
24502
  try {
24253
- const staleToken = readFileSync20(lockPath, "utf-8");
24254
- if (Date.now() - statSync13(lockPath).mtimeMs > FILE_LOCK_STALE_MS && readFileSync20(lockPath, "utf-8") === staleToken) {
24503
+ const staleToken = readFileSync21(lockPath, "utf-8");
24504
+ if (Date.now() - statSync14(lockPath).mtimeMs > FILE_LOCK_STALE_MS && readFileSync21(lockPath, "utf-8") === staleToken) {
24255
24505
  unlinkSync18(lockPath);
24256
24506
  continue;
24257
24507
  }
@@ -24314,7 +24564,7 @@ function wikiQuery(args) {
24314
24564
  continue;
24315
24565
  const fp = join35(sub, f.name);
24316
24566
  try {
24317
- const text2 = readFileSync20(fp, "utf-8");
24567
+ const text2 = readFileSync21(fp, "utf-8");
24318
24568
  scored.push({
24319
24569
  score: scoreMatch(text2),
24320
24570
  path: `${label}/${entry.name}/${f.name}`,
@@ -24327,7 +24577,7 @@ function wikiQuery(args) {
24327
24577
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
24328
24578
  const fp = join35(dir, entry.name);
24329
24579
  try {
24330
- const text2 = readFileSync20(fp, "utf-8");
24580
+ const text2 = readFileSync21(fp, "utf-8");
24331
24581
  scored.push({
24332
24582
  score: scoreMatch(text2),
24333
24583
  path: `${label}/${entry.name}`,
@@ -24433,20 +24683,20 @@ function wikiLastConversation(args) {
24433
24683
  return { content: [{ type: "text", text: "No topic specified." }] };
24434
24684
  }
24435
24685
  const name = slugify(topicNameFrom(rawTopic));
24436
- const archiveDir = resolve16(runtime().archiveDir, name);
24686
+ const archiveDir = resolve17(runtime().archiveDir, name);
24437
24687
  try {
24438
- let files = existsSync25(archiveDir) ? readdirSync9(archiveDir).filter((f) => f.endsWith(".jsonl")).sort().reverse() : [];
24688
+ let files = existsSync26(archiveDir) ? readdirSync9(archiveDir).filter((f) => f.endsWith(".jsonl")).sort().reverse() : [];
24439
24689
  if (files.length === 0) {
24440
- files = existsSync25(runtime().archiveDir) ? readdirSync9(runtime().archiveDir).filter((f) => f.startsWith(name) && f.endsWith(".jsonl")).sort().reverse() : [];
24690
+ files = existsSync26(runtime().archiveDir) ? readdirSync9(runtime().archiveDir).filter((f) => f.startsWith(name) && f.endsWith(".jsonl")).sort().reverse() : [];
24441
24691
  }
24442
24692
  if (files.length === 0) {
24443
24693
  return {
24444
24694
  content: [{ type: "text", text: `No archive found for topic: ${rawTopic}` }]
24445
24695
  };
24446
24696
  }
24447
- const actualPath = files[0].includes("/") ? files[0] : resolve16(existsSync25(archiveDir) && readdirSync9(archiveDir).includes(files[0]) ? archiveDir : runtime().archiveDir, files[0]);
24697
+ const actualPath = files[0].includes("/") ? files[0] : resolve17(existsSync26(archiveDir) && readdirSync9(archiveDir).includes(files[0]) ? archiveDir : runtime().archiveDir, files[0]);
24448
24698
  try {
24449
- const raw = readFileSync20(actualPath, "utf-8");
24699
+ const raw = readFileSync21(actualPath, "utf-8");
24450
24700
  const lines = raw.trim().split(`
24451
24701
  `).filter(Boolean);
24452
24702
  const turns2 = [];
@@ -24517,9 +24767,9 @@ function skillQuery(args) {
24517
24767
  })) {
24518
24768
  if (!entry.isDirectory())
24519
24769
  continue;
24520
- const skillFile = resolve16(runtime().skillsDir, entry.name, "skill.md");
24770
+ const skillFile = resolve17(runtime().skillsDir, entry.name, "skill.md");
24521
24771
  try {
24522
- const text2 = readFileSync20(skillFile, "utf-8");
24772
+ const text2 = readFileSync21(skillFile, "utf-8");
24523
24773
  const lower = text2.toLowerCase();
24524
24774
  let score = 0;
24525
24775
  for (const w of query2.split(/\s+/).filter(Boolean)) {
@@ -24585,12 +24835,12 @@ ${fresh.join(`
24585
24835
  `)}
24586
24836
  `;
24587
24837
  }
24588
- const skillDir = resolve16(runtime().skillsDir, name);
24838
+ const skillDir = resolve17(runtime().skillsDir, name);
24589
24839
  ensureDir(skillDir);
24590
- const skillPath = resolve16(skillDir, "skill.md");
24840
+ const skillPath = resolve17(skillDir, "skill.md");
24591
24841
  let finalContent = content;
24592
24842
  try {
24593
- const existing = readFileSync20(skillPath, "utf-8");
24843
+ const existing = readFileSync21(skillPath, "utf-8");
24594
24844
  finalContent = mergeGotchas(content, extractGotchas(existing));
24595
24845
  } catch {}
24596
24846
  writeFileSync17(skillPath, finalContent, "utf-8");
@@ -24620,7 +24870,7 @@ function saveWikiEntry(args) {
24620
24870
  let counter = 1;
24621
24871
  while (true) {
24622
24872
  try {
24623
- writeFileSync17(resolve16(runtime().summariesDir, summaryName), content, {
24873
+ writeFileSync17(resolve17(runtime().summariesDir, summaryName), content, {
24624
24874
  encoding: "utf-8",
24625
24875
  flag: "wx"
24626
24876
  });
@@ -24637,8 +24887,8 @@ function saveWikiEntry(args) {
24637
24887
  let sqliteUpdated = false;
24638
24888
  if (topicId && setTopicBrief2) {
24639
24889
  try {
24640
- const briefPath = resolve16(runtime().topicsDir, `${fileSlug}.md`);
24641
- let briefMd = existsSync25(briefPath) ? readFileSync20(briefPath, "utf-8") : undefined;
24890
+ const briefPath = resolve17(runtime().topicsDir, `${fileSlug}.md`);
24891
+ let briefMd = existsSync26(briefPath) ? readFileSync21(briefPath, "utf-8") : undefined;
24642
24892
  if (briefMd === undefined && getTopicBrief2 && !getTopicBrief2(fileSlug)?.briefMd) {
24643
24893
  const legacy = getTopicBrief2(topicId)?.briefMd;
24644
24894
  if (legacy)
@@ -24671,9 +24921,9 @@ function saveTopicBrief(args) {
24671
24921
  return { content: [{ type: "text", text: "Missing content." }] };
24672
24922
  const topicId = runtime().topicId;
24673
24923
  const fileSlug = wikiBriefStorageKey(rawTopic, topicId);
24674
- const briefPath = resolve16(runtime().topicsDir, `${fileSlug}.md`);
24924
+ const briefPath = resolve17(runtime().topicsDir, `${fileSlug}.md`);
24675
24925
  ensureDir(dirname16(briefPath));
24676
- const existed = existsSync25(briefPath);
24926
+ const existed = existsSync26(briefPath);
24677
24927
  writeFileSync17(briefPath, content, "utf-8");
24678
24928
  const setTopicBrief2 = runtime().host.setTopicBrief;
24679
24929
  let sqliteUpdated = false;
@@ -24705,17 +24955,17 @@ function indexUpsert(args) {
24705
24955
  const dateStr = date ?? today;
24706
24956
  const indexPath = (() => {
24707
24957
  if (kind === "topic")
24708
- return resolve16(runtime().wikiDir, "topic-index.md");
24958
+ return resolve17(runtime().wikiDir, "topic-index.md");
24709
24959
  if (kind === "skill")
24710
- return resolve16(runtime().wikiDir, "skill-index.md");
24711
- return resolve16(runtime().wikiDir, "article-index.md");
24960
+ return resolve17(runtime().wikiDir, "skill-index.md");
24961
+ return resolve17(runtime().wikiDir, "article-index.md");
24712
24962
  })();
24713
24963
  ensureDir(dirname16(indexPath));
24714
24964
  const releaseLock = acquireFileLock(indexPath);
24715
24965
  try {
24716
24966
  let index;
24717
24967
  try {
24718
- index = readFileSync20(indexPath, "utf-8");
24968
+ index = readFileSync21(indexPath, "utf-8");
24719
24969
  } catch {
24720
24970
  index = "";
24721
24971
  }
@@ -24789,18 +25039,18 @@ function indexUpsert(args) {
24789
25039
  }
24790
25040
  function createWikiMcpServer(context, host2) {
24791
25041
  const surface = context.surface ?? "all";
24792
- const wikiDir = resolve16(host2.wikiRoot);
25042
+ const wikiDir = resolve17(host2.wikiRoot);
24793
25043
  const current3 = {
24794
25044
  userId: context.userId,
24795
25045
  topicId: context.topicId,
24796
25046
  surface,
24797
25047
  host: host2,
24798
25048
  wikiDir,
24799
- skillsDir: resolve16(wikiDir, "skills"),
24800
- topicsDir: resolve16(wikiDir, "topic"),
24801
- summariesDir: resolve16(wikiDir, "summaries"),
24802
- articlesDir: resolve16(wikiDir, "articles"),
24803
- archiveDir: resolve16(wikiDir, "archive")
25049
+ skillsDir: resolve17(wikiDir, "skills"),
25050
+ topicsDir: resolve17(wikiDir, "topic"),
25051
+ summariesDir: resolve17(wikiDir, "summaries"),
25052
+ articlesDir: resolve17(wikiDir, "articles"),
25053
+ archiveDir: resolve17(wikiDir, "archive")
24804
25054
  };
24805
25055
  const tools = surface === "wiki" ? WIKI_TOOLS : surface === "skills" ? SKILL_TOOLS : [...WIKI_TOOLS, ...SKILL_TOOLS];
24806
25056
  for (const directory of [
@@ -25000,11 +25250,11 @@ __export(exports_activity_log, {
25000
25250
  readSentFilesForTopic: () => readSentFilesForTopic
25001
25251
  });
25002
25252
  import {
25003
- existsSync as existsSync26,
25253
+ existsSync as existsSync27,
25004
25254
  mkdirSync as mkdirSync25,
25005
25255
  readdirSync as readdirSync10,
25006
25256
  renameSync as renameSync11,
25007
- statSync as statSync14,
25257
+ statSync as statSync15,
25008
25258
  unlinkSync as unlinkSync19,
25009
25259
  writeFileSync as writeFileSync18
25010
25260
  } from "fs";
@@ -25024,7 +25274,7 @@ function writeLog(entry) {
25024
25274
  const sidShort = entry.sessionId ? entry.sessionId.slice(0, 8) : "new";
25025
25275
  const file = join36(logDir, `${entry.userId}_${safeSession}_${sidShort}.jsonl`);
25026
25276
  try {
25027
- const stat = statSync14(file);
25277
+ const stat = statSync15(file);
25028
25278
  if (stat.size >= MAX_FILE_SIZE) {
25029
25279
  const rotated = file.replace(/\.jsonl$/, `.${Date.now()}.jsonl`);
25030
25280
  renameSync11(file, rotated);
@@ -25042,7 +25292,7 @@ function rotateOldLogs() {
25042
25292
  try {
25043
25293
  const files = readdirSync10(logDir).filter((f) => f.endsWith(".jsonl")).map((f) => {
25044
25294
  try {
25045
- const stat = statSync14(join36(logDir, f));
25295
+ const stat = statSync15(join36(logDir, f));
25046
25296
  return { name: f, size: stat.size, mtimeMs: stat.mtimeMs };
25047
25297
  } catch {
25048
25298
  return null;
@@ -25084,7 +25334,7 @@ function entryMatchesTopic(e, topicName) {
25084
25334
  }
25085
25335
  function readSentFilesForTopic(userId, topicName) {
25086
25336
  const file = join36(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
25087
- if (!existsSync26(file))
25337
+ if (!existsSync27(file))
25088
25338
  return [];
25089
25339
  try {
25090
25340
  return readJsonlLines(file).map((line) => JSON.parse(line)).filter((e) => entryMatchesTopic(e, topicName));
@@ -25095,7 +25345,7 @@ function readSentFilesForTopic(userId, topicName) {
25095
25345
  }
25096
25346
  function removeSentFilesForTopic(userId, topicName) {
25097
25347
  const file = join36(resolveStorageUsersLogDir(), String(userId), "sent-files.jsonl");
25098
- if (!existsSync26(file))
25348
+ if (!existsSync27(file))
25099
25349
  return;
25100
25350
  try {
25101
25351
  const remaining = readJsonlLines(file).filter((line) => {
@@ -25839,7 +26089,7 @@ var init_storage_public = __esm(async () => {
25839
26089
  });
25840
26090
 
25841
26091
  // ../../packages/mcp/src/hosted-surfaces.ts
25842
- import { resolve as resolve17 } from "path";
26092
+ import { resolve as resolve18 } from "path";
25843
26093
  function isActiveHostedMcpSurface(surface) {
25844
26094
  return ACTIVE_HOSTED_MCP_SURFACES.includes(surface);
25845
26095
  }
@@ -25925,7 +26175,7 @@ async function buildHostedSurfaceServer(surface, context) {
25925
26175
  ...topicId ? { topicId } : {},
25926
26176
  surface
25927
26177
  }, {
25928
- wikiRoot: resolve17(WORKSPACE_DIR2, "wiki"),
26178
+ wikiRoot: resolve18(WORKSPACE_DIR2, "wiki"),
25929
26179
  getTopicBrief: storage.getTopicBrief,
25930
26180
  resolveTopicBrief: accessibleTopicBrief,
25931
26181
  setTopicBrief: storage.setTopicBrief
@@ -26054,11 +26304,11 @@ var init_sse_transport = () => {};
26054
26304
 
26055
26305
  // ../../packages/mcp/src/server.ts
26056
26306
  import { createHash as createHash9, randomUUID as randomUUID23 } from "crypto";
26057
- import { realpathSync as realpathSync6, statSync as statSync15 } from "fs";
26058
- import { basename as basename9, extname as extname2, resolve as resolve18 } from "path";
26307
+ import { realpathSync as realpathSync6, statSync as statSync16 } from "fs";
26308
+ import { basename as basename9, extname as extname3, resolve as resolve19 } from "path";
26059
26309
  import { McpServer as McpServer7 } from "@modelcontextprotocol/sdk/server/mcp.js";
26060
26310
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
26061
- import { z as z11 } from "zod";
26311
+ import { z as z12 } from "zod";
26062
26312
  function requireTopicAccess(ctx) {
26063
26313
  const topic = getTopic(ctx.topicId);
26064
26314
  if (!topic)
@@ -26069,8 +26319,8 @@ function requireTopicAccess(ctx) {
26069
26319
  return { topic };
26070
26320
  }
26071
26321
  function isPathInside2(baseDir, filePath) {
26072
- const cwd = resolve18(baseDir);
26073
- const normalized = resolve18(filePath);
26322
+ const cwd = resolve19(baseDir);
26323
+ const normalized = resolve19(filePath);
26074
26324
  try {
26075
26325
  const realCwd = realpathSync6(cwd);
26076
26326
  const real = realpathSync6(normalized);
@@ -26086,18 +26336,18 @@ function localFileInfo(ctx, filePath) {
26086
26336
  if (!isPathInside2(ctx.cwd, filePath)) {
26087
26337
  return { error: "Access denied. File must be within the topic workspace." };
26088
26338
  }
26089
- const normalizedPath = resolve18(filePath);
26339
+ const normalizedPath = resolve19(filePath);
26090
26340
  if (isSensitivePath(normalizedPath)) {
26091
26341
  return { error: "Access denied. Path matches the sensitive-file blacklist." };
26092
26342
  }
26093
26343
  try {
26094
- const stats = statSync15(normalizedPath);
26344
+ const stats = statSync16(normalizedPath);
26095
26345
  if (!stats.isFile())
26096
26346
  return { error: `${filePath} is not a file` };
26097
26347
  return {
26098
26348
  normalizedPath,
26099
26349
  name: basename9(filePath),
26100
- ext: extname2(filePath).toLowerCase(),
26350
+ ext: extname3(filePath).toLowerCase(),
26101
26351
  sizeBytes: stats.size,
26102
26352
  sizeMB: (stats.size / (1024 * 1024)).toFixed(2)
26103
26353
  };
@@ -26183,6 +26433,9 @@ function buildNegotiumMcpServer(ctx) {
26183
26433
  const handler = ctx.peerBridge && (def.name === "show_html" || def.name === "show_mermaid") ? async () => textResult("Visual queued for ordered display on the canonical hub.") : def.handler;
26184
26434
  server.tool(def.name, def.description, def.schema, handler);
26185
26435
  }
26436
+ for (const def of createPublishHtmlToolDefinitions({ cwd: ctx.cwd })) {
26437
+ server.tool(def.name, def.description, def.schema, def.handler);
26438
+ }
26186
26439
  }
26187
26440
  const selfConfigCtx = {
26188
26441
  topicId: ctx.topicId,
@@ -26275,8 +26528,8 @@ function buildNegotiumMcpServer(ctx) {
26275
26528
  }
26276
26529
  }
26277
26530
  if (ctx.fileDeliveryTools === true) {
26278
- 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: z11.string().describe("Absolute path to the file to send") }, async ({ file_path }) => deliverFile(ctx, file_path));
26279
- server.tool("send_files", "Send multiple local files to the user in the chat at once.", { file_paths: z11.array(z11.string()).describe("Array of absolute file paths to send") }, async ({ file_paths }) => {
26531
+ 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));
26532
+ 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 }) => {
26280
26533
  const results = await Promise.all(file_paths.map((filePath) => deliverFile(ctx, filePath)));
26281
26534
  const hasError = results.some((result) => ("isError" in result) && result.isError);
26282
26535
  return {
@@ -26564,37 +26817,37 @@ var init_src2 = __esm(async () => {
26564
26817
 
26565
26818
  // ../../packages/mcp-host/src/paths.ts
26566
26819
  import { homedir as homedir9 } from "os";
26567
- import { resolve as resolve19 } from "path";
26820
+ import { resolve as resolve20 } from "path";
26568
26821
  function stateDir() {
26569
26822
  const env = process.env.NEGOTIUM_STATE_DIR?.trim();
26570
- return env ? resolve19(env) : resolve19(homedir9(), ".negotium");
26823
+ return env ? resolve20(env) : resolve20(homedir9(), ".negotium");
26571
26824
  }
26572
26825
  function defaultPortsDir() {
26573
- return resolve19(stateDir(), "run", "mcp-ports");
26826
+ return resolve20(stateDir(), "run", "mcp-ports");
26574
26827
  }
26575
26828
  function defaultManifestFile() {
26576
- return resolve19(stateDir(), "data", "mcp-manifest.json");
26829
+ return resolve20(stateDir(), "data", "mcp-manifest.json");
26577
26830
  }
26578
26831
  var init_paths = () => {};
26579
26832
 
26580
26833
  // ../../packages/mcp-host/src/spec.ts
26581
- import { z as z12 } from "zod";
26834
+ import { z as z13 } from "zod";
26582
26835
  var KEY_PATTERN, portSchema, mcpServerSpecSchema;
26583
26836
  var init_spec = __esm(() => {
26584
26837
  KEY_PATTERN = /^[a-zA-Z0-9._-]+$/;
26585
- portSchema = z12.number().int().min(1).max(65535);
26586
- mcpServerSpecSchema = z12.object({
26587
- key: z12.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"),
26588
- transport: z12.enum(["stdio", "http"]),
26589
- command: z12.string().min(1),
26590
- args: z12.array(z12.string()).optional(),
26591
- env: z12.record(z12.string(), z12.string()).optional(),
26592
- cwd: z12.string().optional(),
26593
- portRange: z12.object({ base: portSchema, max: portSchema }).optional(),
26594
- scope: z12.enum(["node", "instance"]),
26595
- readyTimeoutMs: z12.number().int().positive().optional(),
26596
- idleEvictMs: z12.number().int().positive().optional(),
26597
- healthIntervalMs: z12.number().int().positive().optional()
26838
+ portSchema = z13.number().int().min(1).max(65535);
26839
+ mcpServerSpecSchema = z13.object({
26840
+ 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"),
26841
+ transport: z13.enum(["stdio", "http"]),
26842
+ command: z13.string().min(1),
26843
+ args: z13.array(z13.string()).optional(),
26844
+ env: z13.record(z13.string(), z13.string()).optional(),
26845
+ cwd: z13.string().optional(),
26846
+ portRange: z13.object({ base: portSchema, max: portSchema }).optional(),
26847
+ scope: z13.enum(["node", "instance"]),
26848
+ readyTimeoutMs: z13.number().int().positive().optional(),
26849
+ idleEvictMs: z13.number().int().positive().optional(),
26850
+ healthIntervalMs: z13.number().int().positive().optional()
26598
26851
  }).superRefine((spec, ctx) => {
26599
26852
  if (spec.transport === "http") {
26600
26853
  if (!spec.portRange) {
@@ -26615,9 +26868,9 @@ var init_spec = __esm(() => {
26615
26868
  });
26616
26869
 
26617
26870
  // ../../packages/mcp-host/src/manifest.ts
26618
- import { existsSync as existsSync27, mkdirSync as mkdirSync26, readFileSync as readFileSync21, renameSync as renameSync12, writeFileSync as writeFileSync19 } from "fs";
26871
+ import { existsSync as existsSync28, mkdirSync as mkdirSync26, readFileSync as readFileSync22, renameSync as renameSync12, writeFileSync as writeFileSync19 } from "fs";
26619
26872
  import { dirname as dirname17 } from "path";
26620
- import { z as z13 } from "zod";
26873
+ import { z as z14 } from "zod";
26621
26874
 
26622
26875
  class McpManifest {
26623
26876
  file;
@@ -26660,11 +26913,11 @@ class McpManifest {
26660
26913
  return this.entries.get(key)?.enabled ?? false;
26661
26914
  }
26662
26915
  load() {
26663
- if (!existsSync27(this.file))
26916
+ if (!existsSync28(this.file))
26664
26917
  return;
26665
26918
  let parsed;
26666
26919
  try {
26667
- parsed = manifestFileSchema.parse(JSON.parse(readFileSync21(this.file, "utf8")));
26920
+ parsed = manifestFileSchema.parse(JSON.parse(readFileSync22(this.file, "utf8")));
26668
26921
  } catch (e) {
26669
26922
  throw new Error(`Invalid MCP manifest at ${this.file}: ${e instanceof Error ? e.message : String(e)}`);
26670
26923
  }
@@ -26691,13 +26944,13 @@ var manifestEntrySchema, manifestFileSchema;
26691
26944
  var init_manifest = __esm(() => {
26692
26945
  init_paths();
26693
26946
  init_spec();
26694
- manifestEntrySchema = z13.object({
26947
+ manifestEntrySchema = z14.object({
26695
26948
  spec: mcpServerSpecSchema,
26696
- enabled: z13.boolean()
26949
+ enabled: z14.boolean()
26697
26950
  });
26698
- manifestFileSchema = z13.object({
26699
- version: z13.literal(1),
26700
- servers: z13.array(manifestEntrySchema)
26951
+ manifestFileSchema = z14.object({
26952
+ version: z14.literal(1),
26953
+ servers: z14.array(manifestEntrySchema)
26701
26954
  });
26702
26955
  });
26703
26956
 
@@ -26714,7 +26967,7 @@ import { execFileSync as execFileSync8, spawn as spawn8 } from "child_process";
26714
26967
  import {
26715
26968
  mkdirSync as mkdirSync27,
26716
26969
  readdirSync as readdirSync11,
26717
- readFileSync as readFileSync22,
26970
+ readFileSync as readFileSync23,
26718
26971
  renameSync as renameSync13,
26719
26972
  unlinkSync as unlinkSync20,
26720
26973
  writeFileSync as writeFileSync20
@@ -26736,16 +26989,16 @@ function resolveInstanceKey(spec, instanceKey) {
26736
26989
  return instanceKey;
26737
26990
  }
26738
26991
  function canBind(port) {
26739
- return new Promise((resolve20) => {
26992
+ return new Promise((resolve21) => {
26740
26993
  const srv = createServer3();
26741
- srv.once("error", () => resolve20(false));
26994
+ srv.once("error", () => resolve21(false));
26742
26995
  srv.listen({ port, host: "127.0.0.1", exclusive: true }, () => {
26743
- srv.close(() => resolve20(true));
26996
+ srv.close(() => resolve21(true));
26744
26997
  });
26745
26998
  });
26746
26999
  }
26747
27000
  function tcpProbe(port, timeoutMs = PROBE_TIMEOUT_MS2) {
26748
- return new Promise((resolve20) => {
27001
+ return new Promise((resolve21) => {
26749
27002
  const sock = connect({ port, host: "127.0.0.1" });
26750
27003
  let settled = false;
26751
27004
  const done = (ok3) => {
@@ -26753,7 +27006,7 @@ function tcpProbe(port, timeoutMs = PROBE_TIMEOUT_MS2) {
26753
27006
  return;
26754
27007
  settled = true;
26755
27008
  sock.destroy();
26756
- resolve20(ok3);
27009
+ resolve21(ok3);
26757
27010
  };
26758
27011
  sock.once("connect", () => done(true));
26759
27012
  sock.once("error", () => done(false));
@@ -26781,12 +27034,12 @@ function killProcessTree(pid) {
26781
27034
  function waitExit(proc, timeoutMs) {
26782
27035
  if (proc.exitCode !== null || proc.signalCode !== null)
26783
27036
  return Promise.resolve(true);
26784
- return new Promise((resolve20) => {
26785
- const timer = setTimeout(() => resolve20(false), timeoutMs);
27037
+ return new Promise((resolve21) => {
27038
+ const timer = setTimeout(() => resolve21(false), timeoutMs);
26786
27039
  timer.unref?.();
26787
27040
  const settle = () => {
26788
27041
  clearTimeout(timer);
26789
- resolve20(true);
27042
+ resolve21(true);
26790
27043
  };
26791
27044
  proc.once("exit", settle);
26792
27045
  proc.once("error", settle);
@@ -27022,7 +27275,7 @@ class McpHost {
27022
27275
  if (file.includes(".tmp-"))
27023
27276
  continue;
27024
27277
  try {
27025
- const port = Number.parseInt(readFileSync22(join37(this.portsDir, file), "utf8").trim(), 10);
27278
+ const port = Number.parseInt(readFileSync23(join37(this.portsDir, file), "utf8").trim(), 10);
27026
27279
  if (!Number.isNaN(port))
27027
27280
  claims.set(file, port);
27028
27281
  } catch {}
@@ -27129,7 +27382,7 @@ var DEFAULT_READY_TIMEOUT_MS = 20000, READY_POLL_MS = 150, KILL_GRACE_MS = 5000,
27129
27382
  fn(`[mcp-host] ${msg}`, ctx);
27130
27383
  else
27131
27384
  fn(`[mcp-host] ${msg}`);
27132
- }, delay2 = (ms) => new Promise((resolve20) => setTimeout(resolve20, ms));
27385
+ }, delay2 = (ms) => new Promise((resolve21) => setTimeout(resolve21, ms));
27133
27386
  var init_manager3 = __esm(() => {
27134
27387
  init_manifest();
27135
27388
  init_paths();
@@ -27147,16 +27400,16 @@ var init_src3 = __esm(() => {
27147
27400
  import { randomUUID as randomUUID24 } from "crypto";
27148
27401
  import {
27149
27402
  copyFileSync as copyFileSync4,
27150
- existsSync as existsSync28,
27403
+ existsSync as existsSync29,
27151
27404
  mkdirSync as mkdirSync28,
27152
- readFileSync as readFileSync23,
27405
+ readFileSync as readFileSync24,
27153
27406
  rmSync as rmSync8,
27154
- statSync as statSync16,
27407
+ statSync as statSync17,
27155
27408
  writeFileSync as writeFileSync21
27156
27409
  } from "fs";
27157
- import { basename as basename10, extname as extname3, join as join38 } from "path";
27410
+ import { basename as basename10, extname as extname4, join as join38 } from "path";
27158
27411
  function safeExtension(filename) {
27159
- const extension = extname3(basename10(filename));
27412
+ const extension = extname4(basename10(filename));
27160
27413
  return /^\.[A-Za-z0-9]{1,16}$/.test(extension) ? extension.toLowerCase() : "";
27161
27414
  }
27162
27415
  function attachmentType(mimeType) {
@@ -27185,7 +27438,7 @@ class NodeFileStore {
27185
27438
  if (!FILE_ID_RE.test(fileId))
27186
27439
  return null;
27187
27440
  try {
27188
- const parsed = JSON.parse(readFileSync23(this.#metadataPath(fileId), "utf8"));
27441
+ const parsed = JSON.parse(readFileSync24(this.#metadataPath(fileId), "utf8"));
27189
27442
  if (typeof parsed.filename !== "string" || typeof parsed.mimeType !== "string" || typeof parsed.sizeBytes !== "number" || typeof parsed.savedName !== "string") {
27190
27443
  return null;
27191
27444
  }
@@ -27208,7 +27461,7 @@ class NodeFileStore {
27208
27461
  hooks = {
27209
27462
  resolveAttachmentByFileId: (fileId) => {
27210
27463
  const metadata = this.#metadata(fileId);
27211
- if (!metadata || !existsSync28(join38(this.uploadDir, metadata.savedName)))
27464
+ if (!metadata || !existsSync29(join38(this.uploadDir, metadata.savedName)))
27212
27465
  return null;
27213
27466
  return this.#attachment(fileId, metadata);
27214
27467
  },
@@ -27217,7 +27470,7 @@ class NodeFileStore {
27217
27470
  if (!metadata)
27218
27471
  return null;
27219
27472
  const path = join38(this.uploadDir, metadata.savedName);
27220
- return existsSync28(path) ? path : null;
27473
+ return existsSync29(path) ? path : null;
27221
27474
  },
27222
27475
  storeLocalFileAsUpload: (absPath, access = {}) => this.store(absPath, access),
27223
27476
  deleteFilesForTopic: (topicId) => this.deleteForTopic(topicId)
@@ -27229,12 +27482,12 @@ class NodeFileStore {
27229
27482
  const savedName = `${fileId}${extension}`;
27230
27483
  const savedPath = join38(this.uploadDir, savedName);
27231
27484
  try {
27232
- const stats = statSync16(absPath);
27485
+ const stats = statSync17(absPath);
27233
27486
  if (!stats.isFile() || stats.size > MAX_UPLOAD_BYTES)
27234
27487
  return null;
27235
27488
  const metadata = {
27236
27489
  filename: basename10(absPath),
27237
- mimeType: MIME_BY_EXT[extension] ?? "application/octet-stream",
27490
+ mimeType: MIME_BY_EXT2[extension] ?? "application/octet-stream",
27238
27491
  sizeBytes: stats.size,
27239
27492
  savedName,
27240
27493
  ...access.ownerUserId ? { ownerUserId: access.ownerUserId } : {},
@@ -27260,7 +27513,7 @@ class NodeFileStore {
27260
27513
  if (!allowed)
27261
27514
  return null;
27262
27515
  const path = join38(this.uploadDir, metadata.savedName);
27263
- if (!existsSync28(path))
27516
+ if (!existsSync29(path))
27264
27517
  return null;
27265
27518
  return new Response(Bun.file(path), {
27266
27519
  headers: {
@@ -27283,12 +27536,12 @@ class NodeFileStore {
27283
27536
  }
27284
27537
  }
27285
27538
  }
27286
- var MAX_UPLOAD_BYTES, FILE_ID_RE, MIME_BY_EXT, nodeFileStore;
27539
+ var MAX_UPLOAD_BYTES, FILE_ID_RE, MIME_BY_EXT2, nodeFileStore;
27287
27540
  var init_files = __esm(async () => {
27288
27541
  await init_node_host();
27289
27542
  MAX_UPLOAD_BYTES = 2 * 1024 * 1024 * 1024;
27290
27543
  FILE_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
27291
- MIME_BY_EXT = {
27544
+ MIME_BY_EXT2 = {
27292
27545
  ".csv": "text/csv",
27293
27546
  ".gif": "image/gif",
27294
27547
  ".html": "text/html",
@@ -27430,14 +27683,14 @@ function createPollingSseStream(req, options) {
27430
27683
  import { randomUUID as randomUUID25, timingSafeEqual as timingSafeEqual4 } from "crypto";
27431
27684
  import {
27432
27685
  chmodSync as chmodSync5,
27433
- existsSync as existsSync29,
27686
+ existsSync as existsSync30,
27434
27687
  mkdirSync as mkdirSync29,
27435
- readFileSync as readFileSync24,
27688
+ readFileSync as readFileSync25,
27436
27689
  renameSync as renameSync14,
27437
27690
  unlinkSync as unlinkSync21,
27438
27691
  writeFileSync as writeFileSync22
27439
27692
  } from "fs";
27440
- import { dirname as dirname18, resolve as resolve20 } from "path";
27693
+ import { dirname as dirname18, resolve as resolve21 } from "path";
27441
27694
  function jsonError(status, error2) {
27442
27695
  return Response.json({ ok: false, error: error2 }, { status });
27443
27696
  }
@@ -27939,10 +28192,10 @@ function writeNodeDaemonInfo(port, startedAt) {
27939
28192
  return info;
27940
28193
  }
27941
28194
  function readNodeDaemonInfo() {
27942
- if (!existsSync29(NODE_DAEMON_INFO_PATH))
28195
+ if (!existsSync30(NODE_DAEMON_INFO_PATH))
27943
28196
  return null;
27944
28197
  try {
27945
- const value = JSON.parse(readFileSync24(NODE_DAEMON_INFO_PATH, "utf8"));
28198
+ const value = JSON.parse(readFileSync25(NODE_DAEMON_INFO_PATH, "utf8"));
27946
28199
  if (value?.schemaVersion !== 1 || value.stateDir !== STATE_DIR || !Number.isInteger(value.protocolVersion) || !Number.isInteger(value.pid) || !Number.isInteger(value.port) || value.port <= 0) {
27947
28200
  return null;
27948
28201
  }
@@ -28026,7 +28279,7 @@ var init_control = __esm(async () => {
28026
28279
  init_files()
28027
28280
  ]);
28028
28281
  NODE_RUNTIME_CONTRACT_BASE_PATH = `${NODE_CONTROL_BASE_PATH}/runtime/v1`;
28029
- NODE_DAEMON_INFO_PATH = resolve20(RUN_DIR, "node-daemon.json");
28282
+ NODE_DAEMON_INFO_PATH = resolve21(RUN_DIR, "node-daemon.json");
28030
28283
  NODE_VERSION = NEGOTIUM_VERSION;
28031
28284
  ControlRequestError = class ControlRequestError extends Error {
28032
28285
  constructor(message) {
@@ -28230,8 +28483,8 @@ var init_schedule = __esm(() => {
28230
28483
 
28231
28484
  // ../../packages/module-cron/src/scripts.ts
28232
28485
  import { spawn as spawn9 } from "child_process";
28233
- import { existsSync as existsSync30, mkdirSync as mkdirSync30, readdirSync as readdirSync12 } from "fs";
28234
- import { resolve as resolve21, sep as sep2 } from "path";
28486
+ import { existsSync as existsSync31, mkdirSync as mkdirSync30, readdirSync as readdirSync12 } from "fs";
28487
+ import { resolve as resolve22, sep as sep2 } from "path";
28235
28488
  function validateCronScriptName(script) {
28236
28489
  if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]*\.py$/.test(script) || script.includes("..") || script.includes("/") || script.includes("\\")) {
28237
28490
  return { ok: false, error: `script must be a plain .py filename under ${CRON_JOBS_DIR}` };
@@ -28242,7 +28495,7 @@ function resolveCronScriptPath(script) {
28242
28495
  const valid = validateCronScriptName(script);
28243
28496
  if (!valid.ok)
28244
28497
  throw new Error(valid.error);
28245
- const path = resolve21(CRON_JOBS_DIR, script);
28498
+ const path = resolve22(CRON_JOBS_DIR, script);
28246
28499
  if (!path.startsWith(`${CRON_JOBS_DIR}${sep2}`))
28247
28500
  throw new Error("script path escaped jobs directory");
28248
28501
  return path;
@@ -28253,7 +28506,7 @@ function listCronScripts() {
28253
28506
  }
28254
28507
  function cronScriptExists(script) {
28255
28508
  try {
28256
- return existsSync30(resolveCronScriptPath(script));
28509
+ return existsSync31(resolveCronScriptPath(script));
28257
28510
  } catch {
28258
28511
  return false;
28259
28512
  }
@@ -28285,7 +28538,7 @@ async function runCronPromptScript(options) {
28285
28538
  if (options.signal?.aborted)
28286
28539
  throw new CronScriptError("script run aborted");
28287
28540
  const scriptPath = resolveCronScriptPath(options.script);
28288
- if (!existsSync30(scriptPath))
28541
+ if (!existsSync31(scriptPath))
28289
28542
  throw new CronScriptError(`cron script not found: ${options.script}`);
28290
28543
  const timeoutMs = Math.max(5000, options.timeoutMs ?? DEFAULT_SCRIPT_TIMEOUT_MS);
28291
28544
  const outputLimitBytes = Math.max(1024, options.outputLimitBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES);
@@ -28358,7 +28611,7 @@ async function runCronPromptScript(options) {
28358
28611
  var CRON_JOBS_DIR, DEFAULT_SCRIPT_TIMEOUT_MS, DEFAULT_OUTPUT_LIMIT_BYTES, KILL_GRACE_MS2 = 2500, CronScriptError;
28359
28612
  var init_scripts = __esm(async () => {
28360
28613
  await init_cron_host();
28361
- CRON_JOBS_DIR = resolve21(process.env.NEGOTIUM_CRON_JOBS_DIR?.trim() || resolve21(WORKSPACE_DIR, "cron", "jobs"));
28614
+ CRON_JOBS_DIR = resolve22(process.env.NEGOTIUM_CRON_JOBS_DIR?.trim() || resolve22(WORKSPACE_DIR, "cron", "jobs"));
28362
28615
  DEFAULT_SCRIPT_TIMEOUT_MS = 10 * 60000;
28363
28616
  DEFAULT_OUTPUT_LIMIT_BYTES = 1024 * 1024;
28364
28617
  CronScriptError = class CronScriptError extends Error {
@@ -29486,7 +29739,7 @@ var init_scheduler = __esm(async () => {
29486
29739
  });
29487
29740
 
29488
29741
  // ../../packages/module-cron/src/module.ts
29489
- import { existsSync as existsSync31 } from "fs";
29742
+ import { existsSync as existsSync32 } from "fs";
29490
29743
  import { fileURLToPath as fileURLToPath2 } from "url";
29491
29744
  function envMs(name, fallback) {
29492
29745
  const parsed = Number.parseInt(process.env[name] ?? "", 10);
@@ -29517,7 +29770,7 @@ function createCronModule(options = {}) {
29517
29770
  if (event.type === "topic-deleted")
29518
29771
  trackCleanup(resetCronTopicContext(event.topicId));
29519
29772
  });
29520
- const mcpServerFile = options.mcpServerFile ?? (existsSync31(SOURCE_MCP_SERVER_FILE) ? SOURCE_MCP_SERVER_FILE : PACKAGED_MCP_SERVER_FILE);
29773
+ const mcpServerFile = options.mcpServerFile ?? (existsSync32(SOURCE_MCP_SERVER_FILE) ? SOURCE_MCP_SERVER_FILE : PACKAGED_MCP_SERVER_FILE);
29521
29774
  const unregisterMcp = host2.registerRuntimeMcpServer("cron-manager", {
29522
29775
  scopes: ["forum", "manager"],
29523
29776
  forumRequired: true,
@@ -29805,7 +30058,7 @@ __export(exports_src3, {
29805
30058
  });
29806
30059
  import { createServer as createServer4 } from "net";
29807
30060
  async function availableLoopbackPort() {
29808
- return new Promise((resolve22, reject) => {
30061
+ return new Promise((resolve23, reject) => {
29809
30062
  const probe = createServer4();
29810
30063
  probe.unref();
29811
30064
  probe.once("error", reject);
@@ -29816,7 +30069,7 @@ async function availableLoopbackPort() {
29816
30069
  if (error2)
29817
30070
  reject(error2);
29818
30071
  else if (port > 0)
29819
- resolve22(port);
30072
+ resolve23(port);
29820
30073
  else
29821
30074
  reject(new Error("failed to allocate a loopback port"));
29822
30075
  });
@@ -29953,8 +30206,8 @@ function startNode(opts = {}) {
29953
30206
  const manifest = new McpManifest;
29954
30207
  const stopSweeper = mcpHost.startSweeper();
29955
30208
  let resolveCompleted;
29956
- const completed = new Promise((resolve22) => {
29957
- resolveCompleted = resolve22;
30209
+ const completed = new Promise((resolve23) => {
30210
+ resolveCompleted = resolve23;
29958
30211
  });
29959
30212
  let advertised = null;
29960
30213
  try {
@@ -30031,7 +30284,7 @@ async function runNodeDaemon(opts = {}) {
30031
30284
  singleton: true
30032
30285
  });
30033
30286
  await node.completed;
30034
- await new Promise((resolve22) => setImmediate(resolve22));
30287
+ await new Promise((resolve23) => setImmediate(resolve23));
30035
30288
  process.exit(0);
30036
30289
  }
30037
30290
  var init_src5 = __esm(async () => {
@@ -30191,7 +30444,7 @@ var init_commands = __esm(() => {
30191
30444
 
30192
30445
  // ../../adapters/terminal/src/path-suggest.ts
30193
30446
  import { execFile as execFile3 } from "child_process";
30194
- import { existsSync as existsSync32, readdirSync as readdirSync13, statSync as statSync17 } from "fs";
30447
+ import { existsSync as existsSync33, readdirSync as readdirSync13, statSync as statSync18 } from "fs";
30195
30448
  import { homedir as homedir10 } from "os";
30196
30449
  import { basename as basename11, dirname as dirname19, join as join39, sep as sep3 } from "path";
30197
30450
  import { promisify as promisify3 } from "util";
@@ -30374,7 +30627,7 @@ async function warmPathSuggestions(lineText, col, loader = listFilesWithRipgrep)
30374
30627
  return false;
30375
30628
  const { dir } = resolveFragment(token.frag);
30376
30629
  try {
30377
- if (!statSync17(dir).isDirectory())
30630
+ if (!statSync18(dir).isDirectory())
30378
30631
  return false;
30379
30632
  } catch {
30380
30633
  return false;
@@ -30406,7 +30659,7 @@ function pathSuggestions(lineText, col) {
30406
30659
  let isDir = entry.isDirectory();
30407
30660
  if (!isDir && entry.isSymbolicLink()) {
30408
30661
  try {
30409
- isDir = statSync17(join39(dir, entry.name)).isDirectory();
30662
+ isDir = statSync18(join39(dir, entry.name)).isDirectory();
30410
30663
  } catch {}
30411
30664
  }
30412
30665
  return {
@@ -30440,7 +30693,7 @@ function fragmentToAbsolutePath(frag) {
30440
30693
  }
30441
30694
  function fragmentResolves(frag) {
30442
30695
  try {
30443
- return existsSync32(fragmentToAbsolutePath(frag));
30696
+ return existsSync33(fragmentToAbsolutePath(frag));
30444
30697
  } catch {
30445
30698
  return false;
30446
30699
  }
@@ -34942,8 +35195,8 @@ class TerminalApp {
34942
35195
  this.#refreshBackgroundSessions();
34943
35196
  }, 1000);
34944
35197
  this.#backgroundRefreshTimer.unref?.();
34945
- await new Promise((resolve22) => {
34946
- this.#finishRun = resolve22;
35198
+ await new Promise((resolve23) => {
35199
+ this.#finishRun = resolve23;
34947
35200
  });
34948
35201
  } finally {
34949
35202
  this.#running = false;
@@ -37606,8 +37859,8 @@ ${caption}` : voiceText : caption;
37606
37859
  return;
37607
37860
  }
37608
37861
  let release;
37609
- const ready = new Promise((resolve22) => {
37610
- release = resolve22;
37862
+ const ready = new Promise((resolve23) => {
37863
+ release = resolve23;
37611
37864
  });
37612
37865
  const entry = {
37613
37866
  messages: [msg],
@@ -37729,8 +37982,8 @@ function renderOutbound(text2, maxLen = 4096) {
37729
37982
  }
37730
37983
 
37731
37984
  // ../../adapters/telegram/src/adapter.ts
37732
- import { existsSync as existsSync33 } from "fs";
37733
- import { basename as basename12, extname as extname4 } from "path";
37985
+ import { existsSync as existsSync34 } from "fs";
37986
+ import { basename as basename12, extname as extname5 } from "path";
37734
37987
  function startTelegramAdapter(opts) {
37735
37988
  const { client, forumChatId } = opts;
37736
37989
  const userId = opts.userId ?? "local";
@@ -37840,7 +38093,7 @@ function startTelegramAdapter(opts) {
37840
38093
  attempt += 1;
37841
38094
  const waitMs = Math.min(5000, 250 * 2 ** Math.min(attempt - 1, 5));
37842
38095
  logger.warn({ err: err2, attempt, waitMs }, "telegram adapter: getMe failed while resolving onboarding identity; retrying");
37843
- await new Promise((resolve22) => setTimeout(resolve22, waitMs));
38096
+ await new Promise((resolve23) => setTimeout(resolve23, waitMs));
37844
38097
  }
37845
38098
  }
37846
38099
  return;
@@ -38217,7 +38470,7 @@ function startTelegramAdapter(opts) {
38217
38470
  logger.warn({ path, chatId }, "telegram adapter: blocked sensitive file path");
38218
38471
  return { ref: null, delivered: false, error: "path matches the sensitive-file blacklist" };
38219
38472
  }
38220
- if (!existsSync33(path)) {
38473
+ if (!existsSync34(path)) {
38221
38474
  const sent = await client.sendMessage(chatId, `File: ${path}`, base).catch(() => null);
38222
38475
  const messageId = sentMessageId(sent);
38223
38476
  return {
@@ -38226,7 +38479,7 @@ function startTelegramAdapter(opts) {
38226
38479
  error: "file not found on disk"
38227
38480
  };
38228
38481
  }
38229
- const ext = extname4(path).slice(1).toLowerCase();
38482
+ const ext = extname5(path).slice(1).toLowerCase();
38230
38483
  const fileOptions = {
38231
38484
  filename: basename12(file.filename) || basename12(path),
38232
38485
  ...file.mimeType ? { contentType: file.mimeType } : {}
@@ -38317,14 +38570,14 @@ function startTelegramAdapter(opts) {
38317
38570
  const sendQueues = new Map;
38318
38571
  function enqueueSend(topicId, task) {
38319
38572
  const prev = sendQueues.get(topicId) ?? Promise.resolve();
38320
- const next = prev.then(() => new Promise((resolve22) => {
38573
+ const next = prev.then(() => new Promise((resolve23) => {
38321
38574
  const timer = setTimeout(() => {
38322
38575
  logger.warn({ topicId, sendTimeoutMs }, "telegram adapter: send timed out \u2014 abandoning it and continuing the queue");
38323
- resolve22();
38576
+ resolve23();
38324
38577
  }, sendTimeoutMs);
38325
38578
  task().catch((err2) => logger.warn({ err: err2, topicId }, "telegram adapter: send task failed")).finally(() => {
38326
38579
  clearTimeout(timer);
38327
- resolve22();
38580
+ resolve23();
38328
38581
  });
38329
38582
  }));
38330
38583
  sendQueues.set(topicId, next);
@@ -38568,7 +38821,7 @@ ${suffix}`;
38568
38821
  continue;
38569
38822
  }
38570
38823
  if (retryDelayMs > 0) {
38571
- await new Promise((resolve22) => setTimeout(resolve22, retryDelayMs));
38824
+ await new Promise((resolve23) => setTimeout(resolve23, retryDelayMs));
38572
38825
  }
38573
38826
  try {
38574
38827
  await client.editMessageText(text2, editOptions);
@@ -39213,8 +39466,8 @@ async function runTelegramCli(args = process.argv.slice(2)) {
39213
39466
  onShutdown("telegram-channel", 100, () => channel.stop());
39214
39467
  onShutdown("telegram-singleton", 90, () => singleton.stop());
39215
39468
  let resolveCompleted;
39216
- const completed = new Promise((resolve22) => {
39217
- resolveCompleted = resolve22;
39469
+ const completed = new Promise((resolve23) => {
39470
+ resolveCompleted = resolve23;
39218
39471
  });
39219
39472
  onShutdown("telegram-completed", -100, resolveCompleted);
39220
39473
  process.stdout.write(`negotium Telegram adapter connected to canonical node pid ${initialNode.info?.pid ?? "unknown"}
@@ -39401,22 +39654,22 @@ import { createHash as createHash10, randomUUID as randomUUID27 } from "crypto";
39401
39654
  import {
39402
39655
  chmodSync as chmodSync6,
39403
39656
  closeSync as closeSync4,
39404
- existsSync as existsSync34,
39657
+ existsSync as existsSync35,
39405
39658
  fsyncSync as fsyncSync2,
39406
39659
  linkSync,
39407
39660
  lstatSync,
39408
39661
  mkdirSync as mkdirSync32,
39409
39662
  openSync as openSync4,
39410
- readFileSync as readFileSync25,
39663
+ readFileSync as readFileSync26,
39411
39664
  renameSync as renameSync15,
39412
39665
  rmSync as rmSync9,
39413
- statSync as statSync18,
39666
+ statSync as statSync19,
39414
39667
  unlinkSync as unlinkSync22,
39415
39668
  writeFileSync as writeFileSync23
39416
39669
  } from "fs";
39417
- import { dirname as dirname21, resolve as resolve22 } from "path";
39670
+ import { dirname as dirname21, resolve as resolve23 } from "path";
39418
39671
  function joinFilePath() {
39419
- return resolve22(DATA_DIR, "otium-join.json");
39672
+ return resolve23(DATA_DIR, "otium-join.json");
39420
39673
  }
39421
39674
  function isHttpUrl(value) {
39422
39675
  return /^https?:\/\//.test(value);
@@ -39471,7 +39724,7 @@ function parseInviteCode(code) {
39471
39724
  return normalizeJoin(parsed);
39472
39725
  }
39473
39726
  function joinLockPath() {
39474
- return resolve22(DATA_DIR, ".otium-join.lock");
39727
+ return resolve23(DATA_DIR, ".otium-join.lock");
39475
39728
  }
39476
39729
  function processIsAlive(pid) {
39477
39730
  if (!Number.isSafeInteger(pid) || pid <= 0)
@@ -39485,7 +39738,7 @@ function processIsAlive(pid) {
39485
39738
  }
39486
39739
  function withJoinCredentialLock(operation) {
39487
39740
  const lockPath = joinLockPath();
39488
- const ownerPath = resolve22(lockPath, "owner.json");
39741
+ const ownerPath = resolve23(lockPath, "owner.json");
39489
39742
  const owner = { pid: process.pid, token: randomUUID27() };
39490
39743
  mkdirSync32(dirname21(lockPath), { recursive: true });
39491
39744
  for (let attempt = 0;; attempt += 1) {
@@ -39511,11 +39764,11 @@ function withJoinCredentialLock(operation) {
39511
39764
  throw error2;
39512
39765
  let current3 = null;
39513
39766
  try {
39514
- current3 = JSON.parse(readFileSync25(ownerPath, "utf8"));
39767
+ current3 = JSON.parse(readFileSync26(ownerPath, "utf8"));
39515
39768
  } catch {}
39516
39769
  let ageMs;
39517
39770
  try {
39518
- ageMs = Date.now() - statSync18(lockPath).mtimeMs;
39771
+ ageMs = Date.now() - statSync19(lockPath).mtimeMs;
39519
39772
  } catch (statError) {
39520
39773
  if (statError.code === "ENOENT")
39521
39774
  continue;
@@ -39541,7 +39794,7 @@ function withJoinCredentialLock(operation) {
39541
39794
  return operation();
39542
39795
  } finally {
39543
39796
  try {
39544
- const current3 = JSON.parse(readFileSync25(ownerPath, "utf8"));
39797
+ const current3 = JSON.parse(readFileSync26(ownerPath, "utf8"));
39545
39798
  if (current3.pid === owner.pid && current3.token === owner.token) {
39546
39799
  rmSync9(lockPath, { recursive: true, force: true });
39547
39800
  }
@@ -39564,9 +39817,9 @@ function joinCredentialDigest(join41) {
39564
39817
  return createHash10("sha256").update(JSON.stringify(normalizedJoin(join41))).digest("base64url");
39565
39818
  }
39566
39819
  function readPersistedJoin(path = joinFilePath()) {
39567
- if (!existsSync34(path))
39820
+ if (!existsSync35(path))
39568
39821
  return null;
39569
- const parsed = JSON.parse(readFileSync25(path, "utf-8"));
39822
+ const parsed = JSON.parse(readFileSync26(path, "utf-8"));
39570
39823
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
39571
39824
  throw new Error("persisted join credentials are not a JSON object");
39572
39825
  }
@@ -39585,7 +39838,7 @@ function saveJoinWhileLocked(join41, options = {}) {
39585
39838
  const directory = dirname21(path);
39586
39839
  const normalized = normalizedJoin(join41);
39587
39840
  mkdirSync32(directory, { recursive: true });
39588
- if (existsSync34(path)) {
39841
+ if (existsSync35(path)) {
39589
39842
  if (lstatSync(path).isSymbolicLink()) {
39590
39843
  throw new Error(`refusing to replace symlinked Otium join file at ${path}`);
39591
39844
  }
@@ -39617,7 +39870,7 @@ function saveJoinWhileLocked(join41, options = {}) {
39617
39870
  throw new Error(`this node is already joined${existing ? ` as ${existing.cellId}` : " with an invalid join file"}; pass --replace to replace its credentials`);
39618
39871
  }
39619
39872
  }
39620
- const temporaryPath = resolve22(directory, `.otium-join.json.${process.pid}.${randomUUID27()}.tmp`);
39873
+ const temporaryPath = resolve23(directory, `.otium-join.json.${process.pid}.${randomUUID27()}.tmp`);
39621
39874
  let fd;
39622
39875
  try {
39623
39876
  fd = openSync4(temporaryPath, "wx", 384);
@@ -39642,7 +39895,7 @@ function saveJoinWhileLocked(join41, options = {}) {
39642
39895
  } catch (error2) {
39643
39896
  if (fd !== undefined)
39644
39897
  closeSync4(fd);
39645
- if (existsSync34(temporaryPath))
39898
+ if (existsSync35(temporaryPath))
39646
39899
  unlinkSync22(temporaryPath);
39647
39900
  throw error2;
39648
39901
  }
@@ -39654,7 +39907,7 @@ function saveJoin(join41, options = {}) {
39654
39907
  function removeJoin() {
39655
39908
  return withJoinCredentialLock(() => {
39656
39909
  const path = joinFilePath();
39657
- if (!existsSync34(path))
39910
+ if (!existsSync35(path))
39658
39911
  return false;
39659
39912
  if (lstatSync(path).isSymbolicLink()) {
39660
39913
  throw new Error(`refusing to remove symlinked Otium join file at ${path}`);
@@ -39686,10 +39939,10 @@ function loadJoin() {
39686
39939
  logger.warn("otium: OTIUM_CENTRAL_URL, OTIUM_CELL_ID, OTIUM_CELL_SECRET must be set together \u2014 ignoring partial env");
39687
39940
  }
39688
39941
  const path = joinFilePath();
39689
- if (!existsSync34(path))
39942
+ if (!existsSync35(path))
39690
39943
  return null;
39691
39944
  try {
39692
- const parsed = JSON.parse(readFileSync25(path, "utf-8"));
39945
+ const parsed = JSON.parse(readFileSync26(path, "utf-8"));
39693
39946
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
39694
39947
  return null;
39695
39948
  return normalizeJoin(parsed);
@@ -39713,11 +39966,11 @@ var exports_join_status = {};
39713
39966
  __export(exports_join_status, {
39714
39967
  hasConfiguredOtiumJoin: () => hasConfiguredOtiumJoin
39715
39968
  });
39716
- import { existsSync as existsSync35 } from "fs";
39717
- import { resolve as resolve23 } from "path";
39969
+ import { existsSync as existsSync36 } from "fs";
39970
+ import { resolve as resolve24 } from "path";
39718
39971
  function hasConfiguredOtiumJoin() {
39719
39972
  const envJoin = Boolean(process.env.OTIUM_CENTRAL_URL?.trim() && process.env.OTIUM_CELL_ID?.trim() && process.env.OTIUM_CELL_SECRET?.trim());
39720
- return envJoin || existsSync35(resolve23(DATA_DIR, "otium-join.json"));
39973
+ return envJoin || existsSync36(resolve24(DATA_DIR, "otium-join.json"));
39721
39974
  }
39722
39975
  var init_join_status = __esm(() => {
39723
39976
  init_config_public();
@@ -40524,7 +40777,7 @@ function createTurnForwarder(opts) {
40524
40777
  logger.warn({ requestId, seq, type: event.type, attempt, error: result.error }, "otium: peer event delivery to hub failed");
40525
40778
  if (attempt < PEER_EVENT_MAX_ATTEMPTS) {
40526
40779
  const delayMs = retryBaseMs * 2 ** (attempt - 1);
40527
- await new Promise((resolve24) => setTimeout(resolve24, delayMs));
40780
+ await new Promise((resolve25) => setTimeout(resolve25, delayMs));
40528
40781
  }
40529
40782
  }
40530
40783
  forwarder.deliveryBlocked = true;
@@ -40666,8 +40919,8 @@ var init_event_backflow = __esm(async () => {
40666
40919
 
40667
40920
  // ../../adapters/otium/src/peer-files.ts
40668
40921
  import { randomUUID as randomUUID29 } from "crypto";
40669
- import { copyFileSync as copyFileSync5, mkdirSync as mkdirSync33, rmSync as rmSync10, statSync as statSync19 } from "fs";
40670
- import { basename as basename13, extname as extname5, join as join41 } from "path";
40922
+ import { copyFileSync as copyFileSync5, mkdirSync as mkdirSync33, rmSync as rmSync10, statSync as statSync20 } from "fs";
40923
+ import { basename as basename13, extname as extname6, join as join41 } from "path";
40671
40924
  function fileType(mimeType) {
40672
40925
  if (mimeType.startsWith("image/"))
40673
40926
  return "image";
@@ -40697,7 +40950,7 @@ function safeFilename(filename) {
40697
40950
  return value || "upload";
40698
40951
  }
40699
40952
  function mimeFromPath(path) {
40700
- const extension = extname5(path).toLowerCase();
40953
+ const extension = extname6(path).toLowerCase();
40701
40954
  const known = {
40702
40955
  ".png": "image/png",
40703
40956
  ".jpg": "image/jpeg",
@@ -40739,7 +40992,7 @@ function insertLocalFile(args) {
40739
40992
  mkdirSync33(PEER_FILES_DIR, { recursive: true });
40740
40993
  const path = join41(PEER_FILES_DIR, `${id}-${safeFilename(args.filename)}`);
40741
40994
  copyFileSync5(args.sourcePath, path);
40742
- return recordFile({ id, path, sizeBytes: statSync19(path).size, ...args });
40995
+ return recordFile({ id, path, sizeBytes: statSync20(path).size, ...args });
40743
40996
  }
40744
40997
  function deletePeerFilesForTopic(topicId) {
40745
40998
  const rows = db.query("SELECT path FROM otium_peer_files WHERE topic_id = ?").all(topicId);
@@ -42407,17 +42660,17 @@ import {
42407
42660
  import {
42408
42661
  chmodSync as chmodSync7,
42409
42662
  closeSync as closeSync5,
42410
- existsSync as existsSync36,
42663
+ existsSync as existsSync37,
42411
42664
  fsyncSync as fsyncSync3,
42412
42665
  linkSync as linkSync2,
42413
42666
  mkdirSync as mkdirSync34,
42414
42667
  openSync as openSync5,
42415
- readFileSync as readFileSync26,
42668
+ readFileSync as readFileSync27,
42416
42669
  renameSync as renameSync16,
42417
42670
  unlinkSync as unlinkSync23,
42418
42671
  writeFileSync as writeFileSync24
42419
42672
  } from "fs";
42420
- import { dirname as dirname22, resolve as resolve24 } from "path";
42673
+ import { dirname as dirname22, resolve as resolve25 } from "path";
42421
42674
  function parseEnrollmentInvite(code) {
42422
42675
  let parsed;
42423
42676
  try {
@@ -42438,14 +42691,14 @@ function parseEnrollmentInvite(code) {
42438
42691
  return { v: 2, central, token };
42439
42692
  }
42440
42693
  function pendingEnrollmentPath() {
42441
- return resolve24(DATA_DIR, "otium-enrollment-pending.json");
42694
+ return resolve25(DATA_DIR, "otium-enrollment-pending.json");
42442
42695
  }
42443
42696
  function isEnrollmentPending(invite) {
42444
42697
  const path = pendingEnrollmentPath();
42445
- if (!existsSync36(path))
42698
+ if (!existsSync37(path))
42446
42699
  return false;
42447
42700
  try {
42448
- const saved = JSON.parse(readFileSync26(path, "utf8"));
42701
+ const saved = JSON.parse(readFileSync27(path, "utf8"));
42449
42702
  return saved.central === invite.central && saved.token === invite.token;
42450
42703
  } catch {
42451
42704
  return false;
@@ -42453,8 +42706,8 @@ function isEnrollmentPending(invite) {
42453
42706
  }
42454
42707
  function loadOrCreatePending(invite, nodeName) {
42455
42708
  const path = pendingEnrollmentPath();
42456
- if (existsSync36(path)) {
42457
- const saved = JSON.parse(readFileSync26(path, "utf8"));
42709
+ if (existsSync37(path)) {
42710
+ const saved = JSON.parse(readFileSync27(path, "utf8"));
42458
42711
  if (saved.central === invite.central && saved.token === invite.token)
42459
42712
  return saved;
42460
42713
  throw new Error(`another Otium enrollment is pending at ${path}`);
@@ -42468,7 +42721,7 @@ function loadOrCreatePending(invite, nodeName) {
42468
42721
  ...nodeName ? { nodeName } : {}
42469
42722
  };
42470
42723
  mkdirSync34(dirname22(path), { recursive: true });
42471
- const temporaryPath = resolve24(dirname22(path), `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
42724
+ const temporaryPath = resolve25(dirname22(path), `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
42472
42725
  let fd;
42473
42726
  try {
42474
42727
  fd = openSync5(temporaryPath, "wx", 384);
@@ -42489,10 +42742,10 @@ function loadOrCreatePending(invite, nodeName) {
42489
42742
  } catch (error2) {
42490
42743
  if (fd !== undefined)
42491
42744
  closeSync5(fd);
42492
- if (existsSync36(temporaryPath))
42745
+ if (existsSync37(temporaryPath))
42493
42746
  unlinkSync23(temporaryPath);
42494
- if (error2.code === "EEXIST" && existsSync36(path)) {
42495
- const saved = JSON.parse(readFileSync26(path, "utf8"));
42747
+ if (error2.code === "EEXIST" && existsSync37(path)) {
42748
+ const saved = JSON.parse(readFileSync27(path, "utf8"));
42496
42749
  if (saved.central === invite.central && saved.token === invite.token)
42497
42750
  return saved;
42498
42751
  }
@@ -42503,7 +42756,7 @@ function loadOrCreatePending(invite, nodeName) {
42503
42756
  function replacePendingEnrollment(pending2) {
42504
42757
  const path = pendingEnrollmentPath();
42505
42758
  const directory = dirname22(path);
42506
- const temporaryPath = resolve24(directory, `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
42759
+ const temporaryPath = resolve25(directory, `.otium-enrollment-pending.json.${process.pid}.${randomUUID31()}.tmp`);
42507
42760
  let fd;
42508
42761
  try {
42509
42762
  fd = openSync5(temporaryPath, "wx", 384);
@@ -42522,14 +42775,14 @@ function replacePendingEnrollment(pending2) {
42522
42775
  } catch (error2) {
42523
42776
  if (fd !== undefined)
42524
42777
  closeSync5(fd);
42525
- if (existsSync36(temporaryPath))
42778
+ if (existsSync37(temporaryPath))
42526
42779
  unlinkSync23(temporaryPath);
42527
42780
  throw error2;
42528
42781
  }
42529
42782
  }
42530
42783
  function recordClaimedCredential(pending2, join42) {
42531
42784
  withJoinCredentialLock(() => {
42532
- const current3 = JSON.parse(readFileSync26(pendingEnrollmentPath(), "utf8"));
42785
+ const current3 = JSON.parse(readFileSync27(pendingEnrollmentPath(), "utf8"));
42533
42786
  if (current3.central !== pending2.central || current3.token !== pending2.token || current3.idempotencyKey !== pending2.idempotencyKey || current3.publicKey !== pending2.publicKey) {
42534
42787
  throw new Error("pending Otium enrollment changed while its claim was in flight");
42535
42788
  }
@@ -42608,7 +42861,7 @@ async function claimEnrollment(invite, nodeName) {
42608
42861
  function commitEnrollment(join42, options = {}) {
42609
42862
  return withJoinCredentialLock(() => {
42610
42863
  const pendingPath = pendingEnrollmentPath();
42611
- const pending2 = existsSync36(pendingPath) ? JSON.parse(readFileSync26(pendingPath, "utf8")) : null;
42864
+ const pending2 = existsSync37(pendingPath) ? JSON.parse(readFileSync27(pendingPath, "utf8")) : null;
42612
42865
  const digest = joinCredentialDigest(join42);
42613
42866
  if (pending2 && (!pending2.claimed || pending2.claimed.digest !== digest || pending2.claimed.cellId !== join42.cellId)) {
42614
42867
  throw new Error(`pending Otium enrollment at ${pendingPath} does not match these credentials`);
@@ -43868,8 +44121,8 @@ async function runOtiumSidecar(options) {
43868
44121
  }) : null;
43869
44122
  tunnel?.start();
43870
44123
  let resolveCompleted;
43871
- const completed = new Promise((resolve25) => {
43872
- resolveCompleted = resolve25;
44124
+ const completed = new Promise((resolve26) => {
44125
+ resolveCompleted = resolve26;
43873
44126
  });
43874
44127
  onShutdown("otium-sidecar-server", 130, () => server?.stop(true));
43875
44128
  onShutdown("otium-sidecar-tunnel", 120, () => tunnel?.stop());
@@ -44677,7 +44930,7 @@ async function runCanonicalNode(port) {
44677
44930
  });
44678
44931
  console.log(`negotium node listening on 127.0.0.1:${node.port} (ctrl-c to stop)`);
44679
44932
  await node.completed;
44680
- await new Promise((resolve25) => setImmediate(resolve25));
44933
+ await new Promise((resolve26) => setImmediate(resolve26));
44681
44934
  process.exit(0);
44682
44935
  }
44683
44936
  async function stopAdapter(name) {
@@ -44808,4 +45061,4 @@ switch (command) {
44808
45061
  }
44809
45062
  }
44810
45063
 
44811
- //# debugId=111CDA5CA492CAD164756E2164756E21
45064
+ //# debugId=17BAA8882823716C64756E2164756E21