negotium 0.2.9 → 0.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/agent-helpers.js +336 -92
  2. package/dist/agent-helpers.js.map +8 -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 +505 -252
  12. package/dist/main.js.map +9 -7
  13. package/dist/mcp-factories.js +4 -3
  14. package/dist/mcp-factories.js.map +4 -4
  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 +3 -3
  20. package/dist/registry.js.map +2 -2
  21. package/dist/rollout.js +1 -1
  22. package/dist/runtime/src/agents/mcp-tools/inline-assets.ts +193 -0
  23. package/dist/runtime/src/agents/mcp-tools/publish-html.ts +183 -0
  24. package/dist/runtime/src/agents/public-helpers.ts +4 -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/version.ts +1 -1
  28. package/dist/runtime-helpers.js +2 -1
  29. package/dist/runtime-helpers.js.map +3 -3
  30. package/dist/types/packages/core/src/agents/mcp-tools/inline-assets.d.ts +24 -0
  31. package/dist/types/packages/core/src/agents/mcp-tools/publish-html.d.ts +18 -0
  32. package/dist/types/packages/core/src/agents/public-helpers.d.ts +1 -0
  33. package/dist/types/packages/core/src/platform/config.d.ts +7 -0
  34. package/dist/types/packages/core/src/version.d.ts +1 -1
  35. package/dist/vault.js +2 -1
  36. package/dist/vault.js.map +3 -3
  37. package/package.json +1 -1
@@ -331,7 +331,7 @@ function resolveDefaultModel(agent, registryDefaultModel) {
331
331
  function codexAuthFilePath() {
332
332
  return process.env.NEGOTIUM_CODEX_AUTH_FILE || join(process.env.CODEX_HOME || join(homedir(), ".codex"), "auth.json");
333
333
  }
334
- 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;
334
+ 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;
335
335
  var init_config = __esm(() => {
336
336
  init_config_helpers();
337
337
  init_logger();
@@ -352,6 +352,7 @@ var init_config = __esm(() => {
352
352
  SESSION_WORKSPACE_DIR = resolve2(STATE_DIR, "data", "sessions");
353
353
  CLAUDE_EXECUTABLE_ENV = envText("NEGOTIUM_CLAUDE_EXECUTABLE");
354
354
  CLAUDE_EXECUTABLE = CLAUDE_EXECUTABLE_ENV ? resolve2(CLAUDE_EXECUTABLE_ENV) : undefined;
355
+ SNIPPETS_API_URL = (envText("NEGOTIUM_SNIPPETS_API_URL") ?? envText("SNIPPETS_API_URL") ?? "").replace(/\/+$/, "");
355
356
  BROWSER_RS_BIN = resolveBrowserRsBin(envText("NEGOTIUM_BROWSER_RS_BIN"));
356
357
  PLAYWRIGHT_MCP_BIN = resolveBrowserMcpBin(envText("NEGOTIUM_BROWSER_MCP_BIN"));
357
358
  TSX_BIN = resolveDependencyBin("tsx");
@@ -2147,7 +2148,7 @@ var init_claude_registry = __esm(() => {
2147
2148
  });
2148
2149
 
2149
2150
  // ../../packages/core/src/version.ts
2150
- var NEGOTIUM_VERSION = "0.2.9";
2151
+ var NEGOTIUM_VERSION = "0.2.11";
2151
2152
 
2152
2153
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
2153
2154
  import { spawn as spawn2 } from "child_process";
@@ -10227,7 +10228,7 @@ var init_manager_utils = () => {};
10227
10228
  import { execFileSync as execFileSync6 } from "child_process";
10228
10229
  import { readdirSync as readdirSync3, unlinkSync as unlinkSync10 } from "fs";
10229
10230
  import { createServer } from "net";
10230
- import { resolve as resolve11, sep } from "path";
10231
+ import { resolve as resolve12, sep } from "path";
10231
10232
  function isPortInUse(port) {
10232
10233
  return new Promise((resolveProbe) => {
10233
10234
  const server = createServer();
@@ -10260,8 +10261,8 @@ async function reserveAvailableLoopbackPort(minPort, maxPort, reservedPorts, pro
10260
10261
  return null;
10261
10262
  }
10262
10263
  function killBrowserProcsForUserDataDir(userDataDir) {
10263
- const target = resolve11(userDataDir);
10264
- const profileRoot = resolve11(BROWSER_PROFILES_DIR);
10264
+ const target = resolve12(userDataDir);
10265
+ const profileRoot = resolve12(BROWSER_PROFILES_DIR);
10265
10266
  if (target !== profileRoot && !target.startsWith(`${profileRoot}${sep}`))
10266
10267
  return;
10267
10268
  let pids;
@@ -10282,7 +10283,7 @@ function killBrowserProcsForUserDataDir(userDataDir) {
10282
10283
  stdio: "pipe"
10283
10284
  }).toString().trim();
10284
10285
  const argDir = extractUserDataDirArg(cmdline);
10285
- if (!argDir || resolve11(argDir) !== target)
10286
+ if (!argDir || resolve12(argDir) !== target)
10286
10287
  continue;
10287
10288
  killProcessTreeChildren(pidNum);
10288
10289
  process.kill(pidNum, "SIGKILL");
@@ -10293,13 +10294,13 @@ function killBrowserProcsForUserDataDir(userDataDir) {
10293
10294
  }
10294
10295
  }
10295
10296
  function selectOrphanBrowserPids(procs, liveUserDataDirs, profileRoot, selfPid) {
10296
- const root = resolve11(profileRoot);
10297
- const live = new Set([...liveUserDataDirs].map((d) => resolve11(d)));
10297
+ const root = resolve12(profileRoot);
10298
+ const live = new Set([...liveUserDataDirs].map((d) => resolve12(d)));
10298
10299
  const out = [];
10299
10300
  for (const { pid, userDataDir } of procs) {
10300
10301
  if (pid === selfPid || !userDataDir)
10301
10302
  continue;
10302
- const dir = resolve11(userDataDir);
10303
+ const dir = resolve12(userDataDir);
10303
10304
  if (dir !== root && !dir.startsWith(`${root}${sep}`))
10304
10305
  continue;
10305
10306
  if (live.has(dir))
@@ -10315,7 +10316,7 @@ function reapOrphanBrowsers(liveUserDataDirs) {
10315
10316
  const daemonLease = getRuntimeProcessLease("node-daemon", Date.now(), Number.POSITIVE_INFINITY);
10316
10317
  if (!isBrowserJanitorOwner(daemonLease?.pid ?? null, process.pid))
10317
10318
  return;
10318
- const profileRoot = resolve11(BROWSER_PROFILES_DIR);
10319
+ const profileRoot = resolve12(BROWSER_PROFILES_DIR);
10319
10320
  let pids;
10320
10321
  try {
10321
10322
  pids = execFileSync6("pgrep", ["-f", "--", profileRoot], { stdio: "pipe" }).toString().trim();
@@ -10354,7 +10355,7 @@ function cleanSingletonFiles(userDataDir) {
10354
10355
  for (const f of files) {
10355
10356
  if (f.startsWith("Singleton")) {
10356
10357
  try {
10357
- unlinkSync10(resolve11(userDataDir, f));
10358
+ unlinkSync10(resolve12(userDataDir, f));
10358
10359
  logger.info({ file: f, userDataDir }, "Removed stale Singleton file");
10359
10360
  } catch (e) {
10360
10361
  logger.warn({ err: e, file: f }, "Failed to remove stale Chrome Singleton file");
@@ -10394,9 +10395,9 @@ var init_browser_processes = __esm(async () => {
10394
10395
 
10395
10396
  // ../../packages/core/src/platform/playwright/headed-launch.ts
10396
10397
  import { accessSync as accessSync2, constants as constants2 } from "fs";
10397
- import { delimiter, isAbsolute as isAbsolute3, resolve as resolve12 } from "path";
10398
+ import { delimiter, isAbsolute as isAbsolute4, resolve as resolve13 } from "path";
10398
10399
  function findExecutableOnPath(command, environment = process.env) {
10399
- const candidates = isAbsolute3(command) ? [command] : (environment.PATH ?? "").split(delimiter).filter(Boolean).map((directory) => resolve12(directory, command));
10400
+ const candidates = isAbsolute4(command) ? [command] : (environment.PATH ?? "").split(delimiter).filter(Boolean).map((directory) => resolve13(directory, command));
10400
10401
  for (const candidate of candidates) {
10401
10402
  try {
10402
10403
  accessSync2(candidate, constants2.X_OK);
@@ -10784,21 +10785,21 @@ import { execFileSync as execFileSync7, spawn as spawn5 } from "child_process";
10784
10785
  import { createHash as createHash3, randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual3 } from "crypto";
10785
10786
  import {
10786
10787
  cpSync,
10787
- existsSync as existsSync16,
10788
+ existsSync as existsSync17,
10788
10789
  mkdirSync as mkdirSync11,
10789
- readFileSync as readFileSync14,
10790
+ readFileSync as readFileSync15,
10790
10791
  rmSync as rmSync3,
10791
10792
  unlinkSync as unlinkSync11,
10792
10793
  writeFileSync as writeFileSync9
10793
10794
  } from "fs";
10794
- import { dirname as dirname11, join as join19, resolve as resolve13 } from "path";
10795
+ import { dirname as dirname11, join as join19, resolve as resolve14 } from "path";
10795
10796
  function removeDefaultProfileDataDir(userDataDir) {
10796
- const root = resolve13(BROWSER_PROFILES_DIR);
10797
- const target = resolve13(userDataDir);
10797
+ const root = resolve14(BROWSER_PROFILES_DIR);
10798
+ const target = resolve14(userDataDir);
10798
10799
  if (dirname11(target) !== root) {
10799
10800
  throw new Error(`Refusing to delete browser profile outside managed root: ${target}`);
10800
10801
  }
10801
- const existed = existsSync16(target);
10802
+ const existed = existsSync17(target);
10802
10803
  rmSync3(target, { recursive: true, force: true });
10803
10804
  return existed;
10804
10805
  }
@@ -11012,7 +11013,7 @@ function releasePort(port) {
11012
11013
  usedPorts.delete(port);
11013
11014
  }
11014
11015
  function defaultProfileDir(_ownerId, profile) {
11015
- return resolve13(BROWSER_PROFILES_DIR, profile);
11016
+ return resolve14(BROWSER_PROFILES_DIR, profile);
11016
11017
  }
11017
11018
  function resolveUserDataDir(instanceKey) {
11018
11019
  return managerHost.resolveInstanceDataDir(instanceKey);
@@ -11373,11 +11374,11 @@ async function cloneProfileForChild(opts) {
11373
11374
  killInstance(dstKey);
11374
11375
  await delay(500);
11375
11376
  }
11376
- if (!existsSync16(srcDir)) {
11377
+ if (!existsSync17(srcDir)) {
11377
11378
  return { copied: false, srcDir, dstDir, reason: "src-missing" };
11378
11379
  }
11379
11380
  try {
11380
- if (existsSync16(dstDir)) {
11381
+ if (existsSync17(dstDir)) {
11381
11382
  rmSync3(dstDir, { recursive: true, force: true });
11382
11383
  }
11383
11384
  mkdirSync11(dirname11(dstDir), { recursive: true });
@@ -11398,7 +11399,7 @@ async function cloneProfileForChild(opts) {
11398
11399
  cleanSingletonFiles(dstDir);
11399
11400
  for (const f of ["DevToolsActivePort", "LOCK"]) {
11400
11401
  try {
11401
- unlinkSync11(resolve13(dstDir, f));
11402
+ unlinkSync11(resolve14(dstDir, f));
11402
11403
  } catch {}
11403
11404
  }
11404
11405
  logger.info({ srcKey, dstKey, srcDir, dstDir }, "Cloned Playwright profile for child topic");
@@ -13034,8 +13035,8 @@ import {
13034
13035
  mkdirSync as mkdirSync15,
13035
13036
  openSync as openSync2,
13036
13037
  readdirSync as readdirSync4,
13037
- readFileSync as readFileSync15,
13038
- statSync as statSync7,
13038
+ readFileSync as readFileSync16,
13039
+ statSync as statSync8,
13039
13040
  unlinkSync as unlinkSync16,
13040
13041
  writeFileSync as writeFileSync13
13041
13042
  } from "fs";
@@ -13083,11 +13084,11 @@ function parsePendingAskFilename(fileName) {
13083
13084
  }
13084
13085
  function readPendingAskFile(path, fallback) {
13085
13086
  try {
13086
- const raw = readFileSync15(path, "utf8").trim();
13087
+ const raw = readFileSync16(path, "utf8").trim();
13087
13088
  if (!raw)
13088
13089
  return null;
13089
13090
  if (!raw.startsWith("{")) {
13090
- const ts = new Date(statSync7(path).mtimeMs).toISOString();
13091
+ const ts = new Date(statSync8(path).mtimeMs).toISOString();
13091
13092
  return {
13092
13093
  userId: fallback.userId,
13093
13094
  from: fallback.from,
@@ -13127,7 +13128,7 @@ function isStale(record, path) {
13127
13128
  if (Number.isFinite(ts))
13128
13129
  return Date.now() - ts > PENDING_ASK_TTL_MS;
13129
13130
  try {
13130
- return Date.now() - statSync7(path).mtimeMs > PENDING_ASK_TTL_MS;
13131
+ return Date.now() - statSync8(path).mtimeMs > PENDING_ASK_TTL_MS;
13131
13132
  } catch {
13132
13133
  return false;
13133
13134
  }
@@ -14214,12 +14215,12 @@ var init_errors = __esm(() => {
14214
14215
 
14215
14216
  // ../../packages/core/src/runtime/event-heartbeat.ts
14216
14217
  function nextOrHeartbeat(pending, intervalMs) {
14217
- return new Promise((resolve14, reject) => {
14218
- const timer = setTimeout(() => resolve14({ kind: "heartbeat" }), intervalMs);
14218
+ return new Promise((resolve15, reject) => {
14219
+ const timer = setTimeout(() => resolve15({ kind: "heartbeat" }), intervalMs);
14219
14220
  timer.unref?.();
14220
14221
  pending.then((result) => {
14221
14222
  clearTimeout(timer);
14222
- resolve14({ kind: "event", result });
14223
+ resolve15({ kind: "event", result });
14223
14224
  }, (error) => {
14224
14225
  clearTimeout(timer);
14225
14226
  reject(error);
@@ -14476,7 +14477,7 @@ var init_visual_html = __esm(() => {
14476
14477
 
14477
14478
  // ../../packages/core/src/runtime/visuals.ts
14478
14479
  import { realpathSync as realpathSync4 } from "fs";
14479
- import { isAbsolute as isAbsolute4, resolve as resolve14 } from "path";
14480
+ import { isAbsolute as isAbsolute5, resolve as resolve15 } from "path";
14480
14481
  function activeVisualHtmlForPrompt(html) {
14481
14482
  if (html.length <= ACTIVE_VISUAL_PROMPT_MAX_CHARS) {
14482
14483
  return { html, omittedChars: 0 };
@@ -14527,8 +14528,8 @@ function topicAllowsVisualFileId(topicId, fileId) {
14527
14528
  return topicHasAttachmentFileId(topicId, fileId) || topicHasVisualFileId(topicId, fileId);
14528
14529
  }
14529
14530
  function isPathInside(baseDir, filePath) {
14530
- const base = resolve14(baseDir);
14531
- const normalized = resolve14(filePath);
14531
+ const base = resolve15(baseDir);
14532
+ const normalized = resolve15(filePath);
14532
14533
  try {
14533
14534
  const realBase = realpathSync4(base);
14534
14535
  const real = realpathSync4(normalized);
@@ -14590,7 +14591,7 @@ function resolveVisualMediaInput(topicId, input) {
14590
14591
  }
14591
14592
  const rawPath = input.file_path.trim();
14592
14593
  const cwd = workspaceCwdFor(topicId);
14593
- const candidate = isAbsolute4(rawPath) ? rawPath : resolve14(cwd, rawPath);
14594
+ const candidate = isAbsolute5(rawPath) ? rawPath : resolve15(cwd, rawPath);
14594
14595
  if (!isPathInside(cwd, candidate)) {
14595
14596
  return { error: "file_path must be inside the topic workspace" };
14596
14597
  }
@@ -14664,8 +14665,8 @@ var init_token_stats = __esm(async () => {
14664
14665
 
14665
14666
  // ../../packages/core/src/runtime/turn-event-stream.ts
14666
14667
  import { randomUUID as randomUUID15 } from "crypto";
14667
- import { realpathSync as realpathSync5, statSync as statSync8 } from "fs";
14668
- import { isAbsolute as isAbsolute5, resolve as resolve15 } from "path";
14668
+ import { realpathSync as realpathSync5, statSync as statSync9 } from "fs";
14669
+ import { isAbsolute as isAbsolute6, resolve as resolve16 } from "path";
14669
14670
  function sessionEventMatchesCurrentExecution(topicId, queryId, agent, model) {
14670
14671
  if (getRoomQuery(topicId)?.queryId !== queryId)
14671
14672
  return false;
@@ -14980,7 +14981,7 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
14980
14981
  case "file":
14981
14982
  if (!silent && peerBridge) {
14982
14983
  const cwd = workspaceCwdFor(topicId);
14983
- const path = isAbsolute5(event.path) ? event.path : resolve15(cwd, event.path);
14984
+ const path = isAbsolute6(event.path) ? event.path : resolve16(cwd, event.path);
14984
14985
  if (!isPathInside(cwd, path)) {
14985
14986
  logger.warn({ topicId, path }, "peer output file is outside the topic workspace");
14986
14987
  break;
@@ -14988,7 +14989,7 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
14988
14989
  let safePath;
14989
14990
  try {
14990
14991
  safePath = realpathSync5(path);
14991
- if (!statSync8(safePath).isFile()) {
14992
+ if (!statSync9(safePath).isFile()) {
14992
14993
  logger.warn({ topicId, path }, "peer output path is not a regular file");
14993
14994
  break;
14994
14995
  }
@@ -15264,7 +15265,7 @@ var init_turn_session = __esm(async () => {
15264
15265
  });
15265
15266
 
15266
15267
  // ../../packages/core/src/storage/app-settings.ts
15267
- import { existsSync as existsSync17, mkdirSync as mkdirSync18, readFileSync as readFileSync16, writeFileSync as writeFileSync15 } from "fs";
15268
+ import { existsSync as existsSync18, mkdirSync as mkdirSync18, readFileSync as readFileSync17, writeFileSync as writeFileSync15 } from "fs";
15268
15269
  import { dirname as dirname14, join as join27 } from "path";
15269
15270
  function settingsFile() {
15270
15271
  return join27(resolveStorageDataDir(), "otium-settings.json");
@@ -15276,8 +15277,8 @@ function ensureSettingsLoaded() {
15276
15277
  loadedSettingsFile = path;
15277
15278
  aiName = DEFAULT_AI_NAME;
15278
15279
  try {
15279
- if (existsSync17(path)) {
15280
- const data = JSON.parse(readFileSync16(path, "utf8"));
15280
+ if (existsSync18(path)) {
15281
+ const data = JSON.parse(readFileSync17(path, "utf8"));
15281
15282
  if (typeof data.aiName === "string" && data.aiName.trim()) {
15282
15283
  aiName = data.aiName.trim();
15283
15284
  }
@@ -15369,7 +15370,7 @@ __export(exports_turn_runner, {
15369
15370
  AGENT_DISPLAY_NAME: () => AGENT_DISPLAY_NAME
15370
15371
  });
15371
15372
  import { randomUUID as randomUUID16 } from "crypto";
15372
- import { existsSync as existsSync18, mkdirSync as mkdirSync19, readdirSync as readdirSync5, statSync as statSync9 } from "fs";
15373
+ import { existsSync as existsSync19, mkdirSync as mkdirSync19, readdirSync as readdirSync5, statSync as statSync10 } from "fs";
15373
15374
  import { join as join28 } from "path";
15374
15375
  function withDefaultPlaywright(configuredMcp, isManager) {
15375
15376
  if (isManager)
@@ -15414,7 +15415,7 @@ function appendAskReplyMessage(topicId, text2, agentType) {
15414
15415
  }
15415
15416
  function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, matchesTitle, preferExact = true) {
15416
15417
  const preferred = join28(directory, preferredFilename);
15417
- if (preferExact && existsSync18(preferred))
15418
+ if (preferExact && existsSync19(preferred))
15418
15419
  return preferred;
15419
15420
  let newestLegacyId = null;
15420
15421
  let newestTitle = null;
@@ -15425,7 +15426,7 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
15425
15426
  if (!titleMatch && !legacyIdMatch)
15426
15427
  continue;
15427
15428
  const path = join28(directory, filename);
15428
- const mtimeMs = statSync9(path).mtimeMs;
15429
+ const mtimeMs = statSync10(path).mtimeMs;
15429
15430
  if (titleMatch) {
15430
15431
  if (!newestTitle || mtimeMs > newestTitle.mtimeMs) {
15431
15432
  newestTitle = { path, mtimeMs };
@@ -15439,9 +15440,9 @@ function resolveWikiMirrorPath(directory, preferredFilename, matchesLegacyId, ma
15439
15440
  }
15440
15441
  function resolveWikiMemoryMirror(wikiDir, topicId, topicTitle) {
15441
15442
  const briefFile = resolveWikiMirrorPath(join28(wikiDir, "topic"), `${wikiBriefStorageKey(topicTitle, topicId)}.md`, (filename) => isTopicBriefFile(filename, topicId), (filename) => isTopicBriefFile(filename, topicId, topicTitle));
15442
- const hasBriefFile = existsSync18(briefFile) && statSync9(briefFile).isFile();
15443
+ const hasBriefFile = existsSync19(briefFile) && statSync10(briefFile).isFile();
15443
15444
  const latestSummaryCandidate = resolveWikiMirrorPath(join28(wikiDir, "summaries"), `__missing__-${wikiSummaryFilename("0000-00-00", topicTitle, topicId)}`, (filename) => isTopicSummaryFile(filename, topicId), (filename) => isTopicSummaryFile(filename, topicId, topicTitle), false);
15444
- const latestSummaryFile = existsSync18(latestSummaryCandidate) && statSync9(latestSummaryCandidate).isFile() ? latestSummaryCandidate : null;
15445
+ const latestSummaryFile = existsSync19(latestSummaryCandidate) && statSync10(latestSummaryCandidate).isFile() ? latestSummaryCandidate : null;
15445
15446
  return { briefFile, hasBriefFile, latestSummaryFile };
15446
15447
  }
15447
15448
  async function streamAgentEvents(topicId, topicTitle, queryId, events, control, agentType, model, effort, userId, retryableSessionExpired = true, onSessionId, execution) {
@@ -16635,7 +16636,7 @@ var init_turn_runner = __esm(async () => {
16635
16636
  });
16636
16637
 
16637
16638
  // ../../packages/core/src/agents/mcp-tools/spawn-subagent.ts
16638
- import { z as z3 } from "zod";
16639
+ import { z as z4 } from "zod";
16639
16640
  function createSubagentLifecycle(host) {
16640
16641
  const runtimeOwnerId = host.runtime.ownerId;
16641
16642
  const limits = Object.freeze({
@@ -17170,7 +17171,7 @@ ${card.task}${reportInstruction}`;
17170
17171
  name: "start_subagent",
17171
17172
  description: "Start a prepared descendant subagent task.",
17172
17173
  schema: {
17173
- topic_id: z3.string().describe("Prepared subagent room id.")
17174
+ topic_id: z4.string().describe("Prepared subagent room id.")
17174
17175
  },
17175
17176
  async handler(input) {
17176
17177
  const result = ownedSubagentTree(ctx);
@@ -17190,7 +17191,7 @@ ${card.task}${reportInstruction}`;
17190
17191
  name: "delete_subagent",
17191
17192
  description: "Permanently delete one descendant subagent room managed by the current room. " + "This removes its conversation, workspace, runtime state, and topic. Keep it when follow-up work is likely.",
17192
17193
  schema: {
17193
- topic_id: z3.string().describe("Exact subagent room id returned by list_subagents; names are not accepted.")
17194
+ topic_id: z4.string().describe("Exact subagent room id returned by list_subagents; names are not accepted.")
17194
17195
  },
17195
17196
  async handler(input) {
17196
17197
  const result = ownedSubagentTree(ctx);
@@ -17214,8 +17215,8 @@ ${card.task}${reportInstruction}`;
17214
17215
  name: "grant_subagent_tell",
17215
17216
  description: "Allow one descendant subagent to tell_session another topic in this managed subagent tree. " + "Direct-parent reporting is always allowed and does not need a grant.",
17216
17217
  schema: {
17217
- subagent_topic_id: z3.string().describe("Source descendant subagent topic id."),
17218
- target_topic_id: z3.string().describe("Target topic id: this manager room or another descendant in its tree.")
17218
+ subagent_topic_id: z4.string().describe("Source descendant subagent topic id."),
17219
+ target_topic_id: z4.string().describe("Target topic id: this manager room or another descendant in its tree.")
17219
17220
  },
17220
17221
  async handler(input) {
17221
17222
  const result = ownedSubagentTree(ctx);
@@ -17241,8 +17242,8 @@ ${card.task}${reportInstruction}`;
17241
17242
  name: "revoke_subagent_tell",
17242
17243
  description: "Remove an extra tell_session connection previously granted to a descendant subagent.",
17243
17244
  schema: {
17244
- subagent_topic_id: z3.string().describe("Source descendant subagent topic id."),
17245
- target_topic_id: z3.string().describe("Granted target topic id.")
17245
+ subagent_topic_id: z4.string().describe("Source descendant subagent topic id."),
17246
+ target_topic_id: z4.string().describe("Granted target topic id.")
17246
17247
  },
17247
17248
  async handler(input) {
17248
17249
  const result = ownedSubagentTree(ctx);
@@ -17268,12 +17269,12 @@ ${card.task}${reportInstruction}`;
17268
17269
  name: "spawn_subagent",
17269
17270
  description: "Delegate a self-contained task to a subagent that works in its own new agent room. " + "Returns immediately; the subagent runs in the background and reports according to report_mode. " + "The fresh room has no parent conversation history; it receives the task plus the selected or inherited topic memory. " + "Use for parallelizable or long-running side work. Do not use provider built-in Task/Agent subagents.",
17270
17271
  schema: {
17271
- task: z3.string().describe("Self-contained task brief. The worker has topic memory but no parent conversation history, so include all task-specific context."),
17272
- name: z3.string().optional().describe("Short name for the subagent room. Auto-generated when omitted."),
17273
- agent: z3.enum(["claude", "codex", "maestro"]).optional().describe("Agent backend override. Defaults to this room's agent."),
17274
- model: z3.string().optional().describe(`Best-fit model override from the system prompt catalog. Omit agent+model to inherit ${ctx.agent}/${ctx.model ?? "default"}; overriding agent without model uses that agent's default.`),
17275
- memory_topic: z3.string().optional().describe("Optional knowledge source: an accessible topic id/title or topic/*.md brief. Defaults to the parent room's effective topic brief."),
17276
- report_mode: z3.enum(["auto", "tell", "status-only"]).optional().describe("Completion reporting: auto injects the final result, tell requires tell_session, status-only updates only the lifecycle card.")
17272
+ task: z4.string().describe("Self-contained task brief. The worker has topic memory but no parent conversation history, so include all task-specific context."),
17273
+ name: z4.string().optional().describe("Short name for the subagent room. Auto-generated when omitted."),
17274
+ agent: z4.enum(["claude", "codex", "maestro"]).optional().describe("Agent backend override. Defaults to this room's agent."),
17275
+ model: z4.string().optional().describe(`Best-fit model override from the system prompt catalog. Omit agent+model to inherit ${ctx.agent}/${ctx.model ?? "default"}; overriding agent without model uses that agent's default.`),
17276
+ memory_topic: z4.string().optional().describe("Optional knowledge source: an accessible topic id/title or topic/*.md brief. Defaults to the parent room's effective topic brief."),
17277
+ report_mode: z4.enum(["auto", "tell", "status-only"]).optional().describe("Completion reporting: auto injects the final result, tell requires tell_session, status-only updates only the lifecycle card.")
17277
17278
  },
17278
17279
  async handler(input) {
17279
17280
  return createSubagent(ctx, input, true);
@@ -17285,12 +17286,12 @@ ${card.task}${reportInstruction}`;
17285
17286
  name: "create_subagent",
17286
17287
  description: "Create a prepared subagent room and task definition without starting its AI turn. " + "Use start_subagent when the team is ready to run it.",
17287
17288
  schema: {
17288
- task: z3.string().describe("Self-contained task brief for the subagent."),
17289
- name: z3.string().optional().describe("Short room name. Auto-generated when omitted."),
17290
- agent: z3.enum(["claude", "codex", "maestro"]).optional(),
17291
- model: z3.string().optional(),
17292
- memory_topic: z3.string().optional().describe("Optional accessible topic id/title or topic/*.md brief to use as this worker's knowledge source. Defaults to the parent."),
17293
- report_mode: z3.enum(["auto", "tell", "status-only"]).optional()
17289
+ task: z4.string().describe("Self-contained task brief for the subagent."),
17290
+ name: z4.string().optional().describe("Short room name. Auto-generated when omitted."),
17291
+ agent: z4.enum(["claude", "codex", "maestro"]).optional(),
17292
+ model: z4.string().optional(),
17293
+ memory_topic: z4.string().optional().describe("Optional accessible topic id/title or topic/*.md brief to use as this worker's knowledge source. Defaults to the parent."),
17294
+ report_mode: z4.enum(["auto", "tell", "status-only"]).optional()
17294
17295
  },
17295
17296
  async handler(input) {
17296
17297
  return createSubagent(ctx, input, false);
@@ -17478,10 +17479,252 @@ await __promiseAll([
17478
17479
  init_ask_user()
17479
17480
  ]);
17480
17481
 
17481
- // ../../packages/core/src/agents/mcp-tools/self-config.ts
17482
+ // ../../packages/core/src/agents/mcp-tools/publish-html.ts
17482
17483
  init_common();
17483
17484
  import { z as z2 } from "zod";
17484
17485
 
17486
+ // ../../packages/core/src/agents/mcp-tools/inline-assets.ts
17487
+ import { existsSync as existsSync16, readFileSync as readFileSync14, statSync as statSync7 } from "fs";
17488
+ import { extname, isAbsolute as isAbsolute3, resolve as resolve11 } from "path";
17489
+ var MIME_BY_EXT = {
17490
+ ".apng": "image/apng",
17491
+ ".avif": "image/avif",
17492
+ ".bmp": "image/bmp",
17493
+ ".css": "text/css",
17494
+ ".gif": "image/gif",
17495
+ ".ico": "image/x-icon",
17496
+ ".jpeg": "image/jpeg",
17497
+ ".jpg": "image/jpeg",
17498
+ ".mp3": "audio/mpeg",
17499
+ ".mp4": "video/mp4",
17500
+ ".otf": "font/otf",
17501
+ ".png": "image/png",
17502
+ ".svg": "image/svg+xml",
17503
+ ".ttf": "font/ttf",
17504
+ ".wav": "audio/wav",
17505
+ ".webm": "video/webm",
17506
+ ".webp": "image/webp",
17507
+ ".woff": "font/woff",
17508
+ ".woff2": "font/woff2"
17509
+ };
17510
+ function isNonLocal(ref) {
17511
+ return /^(?:[a-z][a-z0-9+.-]*:|\/\/|#)/i.test(ref.trim());
17512
+ }
17513
+ function stripSuffix(ref) {
17514
+ return ref.replace(/[?#].*$/, "");
17515
+ }
17516
+ function inlineLocalAssets(html, options) {
17517
+ const { baseDir, maxAssetBytes, maxTotalBytes } = options;
17518
+ const inlined = [];
17519
+ const unresolved = [];
17520
+ const skipped = [];
17521
+ const cache = new Map;
17522
+ let budget = maxTotalBytes - Buffer.byteLength(html, "utf-8");
17523
+ function toDataUri(rawRef) {
17524
+ const ref = rawRef.trim();
17525
+ if (!ref || isNonLocal(ref))
17526
+ return null;
17527
+ const cached = cache.get(ref);
17528
+ if (cached !== undefined)
17529
+ return cached;
17530
+ const relPath = stripSuffix(ref);
17531
+ if (!relPath)
17532
+ return null;
17533
+ const abs = isAbsolute3(relPath) ? relPath : resolve11(baseDir, relPath);
17534
+ let bytes;
17535
+ try {
17536
+ if (!existsSync16(abs) || !statSync7(abs).isFile()) {
17537
+ unresolved.push(ref);
17538
+ cache.set(ref, null);
17539
+ return null;
17540
+ }
17541
+ bytes = statSync7(abs).size;
17542
+ } catch {
17543
+ unresolved.push(ref);
17544
+ cache.set(ref, null);
17545
+ return null;
17546
+ }
17547
+ if (bytes > maxAssetBytes) {
17548
+ skipped.push({ ref, bytes, reason: "asset-too-large" });
17549
+ cache.set(ref, null);
17550
+ return null;
17551
+ }
17552
+ const encodedCost = Math.ceil(bytes / 3) * 4;
17553
+ if (encodedCost > budget) {
17554
+ skipped.push({ ref, bytes, reason: "document-too-large" });
17555
+ cache.set(ref, null);
17556
+ return null;
17557
+ }
17558
+ let uri;
17559
+ try {
17560
+ const mime = MIME_BY_EXT[extname(abs).toLowerCase()] ?? "application/octet-stream";
17561
+ uri = `data:${mime};base64,${readFileSync14(abs).toString("base64")}`;
17562
+ } catch {
17563
+ unresolved.push(ref);
17564
+ cache.set(ref, null);
17565
+ return null;
17566
+ }
17567
+ budget -= uri.length;
17568
+ inlined.push({ ref, bytes });
17569
+ cache.set(ref, uri);
17570
+ return uri;
17571
+ }
17572
+ function rewriteSrcset(value) {
17573
+ return value.split(",").map((candidate) => {
17574
+ const trimmed = candidate.trim();
17575
+ if (!trimmed)
17576
+ return candidate;
17577
+ const [url, ...descriptor] = trimmed.split(/\s+/);
17578
+ const uri = url ? toDataUri(url) : null;
17579
+ if (!uri)
17580
+ return candidate;
17581
+ return [uri, ...descriptor].join(" ");
17582
+ }).join(", ");
17583
+ }
17584
+ let out = html;
17585
+ out = out.replace(/(\b(?:src|poster)\s*=\s*)(["'])([^"']*)\2/gi, (match, prefix, quote, ref) => {
17586
+ const uri = toDataUri(ref);
17587
+ return uri ? `${prefix}${quote}${uri}${quote}` : match;
17588
+ });
17589
+ out = out.replace(/(\bsrcset\s*=\s*)(["'])([^"']*)\2/gi, (match, prefix, quote, value) => {
17590
+ const rewritten = rewriteSrcset(value);
17591
+ return rewritten === value ? match : `${prefix}${quote}${rewritten}${quote}`;
17592
+ });
17593
+ out = out.replace(/<link\b[^>]*>/gi, (tag) => tag.replace(/(\bhref\s*=\s*)(["'])([^"']*)\2/i, (match, prefix, quote, ref) => {
17594
+ const uri = toDataUri(ref);
17595
+ return uri ? `${prefix}${quote}${uri}${quote}` : match;
17596
+ }));
17597
+ out = out.replace(/url\(\s*(["']?)([^"')]+)\1\s*\)/gi, (match, quote, ref) => {
17598
+ const uri = toDataUri(ref);
17599
+ return uri ? `url(${quote}${uri}${quote})` : match;
17600
+ });
17601
+ return { html: out, inlined, unresolved, skipped };
17602
+ }
17603
+
17604
+ // ../../packages/core/src/agents/mcp-tools/publish-html.ts
17605
+ init_config();
17606
+ var REQUEST_TIMEOUT_MS = 30000;
17607
+ var MAX_PUBLISHED_BYTES = 10 * 1024 * 1024;
17608
+ var MAX_PUBLISHED_ASSET_BYTES = 5 * 1024 * 1024;
17609
+ function formatPublishBytes(bytes) {
17610
+ if (bytes < 1024)
17611
+ return `${bytes}B`;
17612
+ if (bytes < 1024 * 1024)
17613
+ return `${(bytes / 1024).toFixed(0)}KB`;
17614
+ return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
17615
+ }
17616
+ function parseSnippetId(input) {
17617
+ const trimmed = input.trim();
17618
+ const fromUrl = trimmed.match(/\/snippets\/([A-Za-z0-9-]+)/)?.[1];
17619
+ const id = fromUrl ?? trimmed;
17620
+ return /^[A-Za-z0-9-]+$/.test(id) ? id : null;
17621
+ }
17622
+ async function snippetRequest(baseUrl, path, init = {}) {
17623
+ try {
17624
+ const response = await fetch(`${baseUrl}${path}`, {
17625
+ ...init,
17626
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
17627
+ });
17628
+ const body = await response.json().catch(() => null);
17629
+ if (body)
17630
+ return body;
17631
+ return { ok: false, error: `snippet service returned ${response.status}` };
17632
+ } catch (error) {
17633
+ const message = error instanceof Error ? error.message : "snippet service unreachable";
17634
+ return { ok: false, error: message };
17635
+ }
17636
+ }
17637
+ function createPublishHtmlToolDefinitions(ctx, options = {}) {
17638
+ const apiUrl = (options.apiUrl ?? SNIPPETS_API_URL)?.replace(/\/+$/, "");
17639
+ if (!apiUrl)
17640
+ return [];
17641
+ const getCtx = typeof ctx === "function" ? ctx : () => ctx;
17642
+ return [
17643
+ {
17644
+ name: "publish_html",
17645
+ description: [
17646
+ "Publish an HTML document and return a public, shareable https link that",
17647
+ "stays live for 72 hours. Use this when the user should be able to open,",
17648
+ "keep, or send the result \u2014 a report, dashboard, or styled table \u2014 rather",
17649
+ "than only glance at it in the side panel.",
17650
+ "Pass a complete standalone document and inline the CSS and JS.",
17651
+ "For charts and diagrams prefer inline <svg>: it stays sharp at any zoom",
17652
+ "and is far smaller than an image file.",
17653
+ "Images may be remote URLs, data: URIs, or paths to local files you",
17654
+ "created \u2014 local files are embedded automatically, so the page keeps",
17655
+ "working after your workspace is gone."
17656
+ ].join(" "),
17657
+ schema: {
17658
+ html: z2.string().describe("Complete standalone HTML document to publish. Inline the CSS and JS."),
17659
+ base_dir: z2.string().optional().describe("Directory that relative image paths resolve against. Defaults to the working directory.")
17660
+ },
17661
+ async handler(input) {
17662
+ const html = input?.html ?? "";
17663
+ if (!html.trim())
17664
+ return errorResult("html is empty \u2014 provide an HTML document.");
17665
+ const baseDir = input.base_dir?.trim() || getCtx().cwd || process.cwd();
17666
+ const report = inlineLocalAssets(html, {
17667
+ baseDir,
17668
+ maxAssetBytes: MAX_PUBLISHED_ASSET_BYTES,
17669
+ maxTotalBytes: MAX_PUBLISHED_BYTES
17670
+ });
17671
+ const bytes = Buffer.byteLength(report.html, "utf-8");
17672
+ if (bytes > MAX_PUBLISHED_BYTES) {
17673
+ 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>.`);
17674
+ }
17675
+ const result = await snippetRequest(apiUrl, "/snippets", {
17676
+ method: "POST",
17677
+ headers: { "content-type": "text/html; charset=utf-8" },
17678
+ body: report.html
17679
+ });
17680
+ if (!result.ok || !result.url) {
17681
+ return errorResult(result.error ?? "Failed to publish.");
17682
+ }
17683
+ const notes = [];
17684
+ if (report.inlined.length > 0) {
17685
+ const total = report.inlined.reduce((sum, a) => sum + a.bytes, 0);
17686
+ notes.push(`Embedded ${report.inlined.length} local file(s), ${formatPublishBytes(total)}.`);
17687
+ }
17688
+ if (report.unresolved.length > 0) {
17689
+ 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.`);
17690
+ }
17691
+ for (const s of report.skipped) {
17692
+ 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.`);
17693
+ }
17694
+ const payload = {
17695
+ url: result.url,
17696
+ snippetId: result.snippetId,
17697
+ expiresAt: result.expiresAt,
17698
+ sizeBytes: bytes,
17699
+ ...report.inlined.length > 0 ? { embeddedFiles: report.inlined.length } : {}
17700
+ };
17701
+ return textResult([JSON.stringify(payload, null, 2), ...notes].join(`
17702
+
17703
+ `));
17704
+ }
17705
+ },
17706
+ {
17707
+ name: "unpublish_html",
17708
+ description: "Delete a previously published HTML document before its 72-hour expiry. Accepts the URL returned by publish_html, or its id.",
17709
+ schema: {
17710
+ snippet: z2.string().describe("URL returned by publish_html, or the bare id.")
17711
+ },
17712
+ async handler(input) {
17713
+ const id = parseSnippetId(input?.snippet ?? "");
17714
+ if (!id)
17715
+ return errorResult("Could not read a published document id from that value.");
17716
+ const result = await snippetRequest(apiUrl, `/snippets/${id}`, { method: "DELETE" });
17717
+ if (!result.ok)
17718
+ return errorResult(result.error ?? "Failed to delete.");
17719
+ return textResult(`Published document ${id} deleted.`);
17720
+ }
17721
+ }
17722
+ ];
17723
+ }
17724
+ // ../../packages/core/src/agents/mcp-tools/self-config.ts
17725
+ init_common();
17726
+ import { z as z3 } from "zod";
17727
+
17485
17728
  // ../../packages/core/src/agents/api-topic-agent-switch.ts
17486
17729
  init_model_catalog();
17487
17730
  init_logger();
@@ -18027,7 +18270,7 @@ function createSelfConfigToolDefinitionsForCore(core, ctx) {
18027
18270
  {
18028
18271
  name: "set_model",
18029
18272
  description: toolDescription(core, "set_model", "Set the model for THIS topic; it persists and applies from the NEXT turn. Use a model from the system prompt's same-agent catalog. Cross-agent changes require an explicit user-requested set_agent first. Fails if the user locked this setting. NEVER use 'fable' unless explicitly requested."),
18030
- schema: { model: z2.string().describe("Model id valid for the topic's current agent.") },
18273
+ schema: { model: z3.string().describe("Model id valid for the topic's current agent.") },
18031
18274
  async handler({ model }) {
18032
18275
  return mcpResult(core.setModel(getCtx(), model));
18033
18276
  }
@@ -18043,7 +18286,7 @@ function createSelfConfigToolDefinitionsForCore(core, ctx) {
18043
18286
  {
18044
18287
  name: "set_agent",
18045
18288
  description: toolDescription(core, "set_agent", "Switch the agent backend for THIS topic between 'claude', 'codex', and 'maestro'. Clears the topic's model/effort override so the new agent starts at its defaults. Fails if the user locked this setting. Use only when the user explicitly asks to switch the runtime itself."),
18046
- schema: { agent: z2.enum(AGENT_VALUES) },
18289
+ schema: { agent: z3.enum(AGENT_VALUES) },
18047
18290
  async handler({ agent }) {
18048
18291
  return mcpResult(core.setAgent(getCtx(), agent));
18049
18292
  }
@@ -18060,7 +18303,7 @@ function createSelfConfigToolDefinitionsForCore(core, ctx) {
18060
18303
  name: "set_effort",
18061
18304
  description: toolDescription(core, "set_effort", "Set the reasoning effort for THIS topic. Claude: low/medium/high/xhigh/max. Codex: low/medium/high/xhigh. Maestro: low/medium/high/xhigh/max. Higher effort costs more and is slower. Fails if the user locked this setting."),
18062
18305
  schema: {
18063
- effort: z2.enum(EFFORT_VALUES).describe("Reasoning effort level valid for the topic's current agent.")
18306
+ effort: z3.enum(EFFORT_VALUES).describe("Reasoning effort level valid for the topic's current agent.")
18064
18307
  },
18065
18308
  async handler({ effort }) {
18066
18309
  return mcpResult(core.setEffort(getCtx(), effort));
@@ -18080,8 +18323,8 @@ function createSelfConfigToolDefinitionsForCore(core, ctx) {
18080
18323
  name: "schedule_self",
18081
18324
  description: toolDescription(core, "schedule_self", "Create the one pending durable delayed continuation allowed for THIS topic without blocking the current turn. Use it to check a long-running operation or resume work later. The future message must be self-contained. If one already exists, use get_self_schedule then update_self_schedule or cancel_self_schedule. For recurring or longer-lived schedules, use cron-manager instead."),
18082
18325
  schema: {
18083
- delay_seconds: z2.number().int().min(1).max(core.product.scheduleMaxDelaySeconds).describe(`Seconds before this topic resumes, from 1 through ${core.product.scheduleMaxDelaySeconds}${core.product.scheduleMaxDelaySeconds === 86400 ? " (24 hours)" : ""}.`),
18084
- message: z2.string().min(1).max(core.product.scheduleMaxMessageLength).describe("Self-contained instruction delivered to your future turn.")
18326
+ delay_seconds: z3.number().int().min(1).max(core.product.scheduleMaxDelaySeconds).describe(`Seconds before this topic resumes, from 1 through ${core.product.scheduleMaxDelaySeconds}${core.product.scheduleMaxDelaySeconds === 86400 ? " (24 hours)" : ""}.`),
18327
+ message: z3.string().min(1).max(core.product.scheduleMaxMessageLength).describe("Self-contained instruction delivered to your future turn.")
18085
18328
  },
18086
18329
  async handler({ delay_seconds, message }) {
18087
18330
  return mcpResult(core.scheduleContinue(getCtx(), delay_seconds, message));
@@ -18099,9 +18342,9 @@ function createSelfConfigToolDefinitionsForCore(core, ctx) {
18099
18342
  name: "update_self_schedule",
18100
18343
  description: toolDescription(core, "update_self_schedule", "Edit THIS topic's pending self-schedule. Provide its schedule_id from schedule_self/get_self_schedule and change delay_seconds, message, or both. delay_seconds is measured from this update. A schedule that already started cannot be edited."),
18101
18344
  schema: {
18102
- schedule_id: z2.string().min(1).describe("Exact ID of the pending self-schedule."),
18103
- delay_seconds: z2.number().int().min(1).max(core.product.scheduleMaxDelaySeconds).optional().describe(`New delay from now, from 1 through ${core.product.scheduleMaxDelaySeconds} seconds.`),
18104
- message: z2.string().min(1).max(core.product.scheduleMaxMessageLength).optional().describe("Replacement self-contained future instruction.")
18345
+ schedule_id: z3.string().min(1).describe("Exact ID of the pending self-schedule."),
18346
+ delay_seconds: z3.number().int().min(1).max(core.product.scheduleMaxDelaySeconds).optional().describe(`New delay from now, from 1 through ${core.product.scheduleMaxDelaySeconds} seconds.`),
18347
+ message: z3.string().min(1).max(core.product.scheduleMaxMessageLength).optional().describe("Replacement self-contained future instruction.")
18105
18348
  },
18106
18349
  async handler({
18107
18350
  schedule_id,
@@ -18118,7 +18361,7 @@ function createSelfConfigToolDefinitionsForCore(core, ctx) {
18118
18361
  name: "cancel_self_schedule",
18119
18362
  description: toolDescription(core, "cancel_self_schedule", "Cancel THIS topic's pending self-schedule by its exact schedule_id. A schedule that already started cannot be cancelled with this tool."),
18120
18363
  schema: {
18121
- schedule_id: z2.string().min(1).describe("Exact ID of the pending self-schedule.")
18364
+ schedule_id: z3.string().min(1).describe("Exact ID of the pending self-schedule.")
18122
18365
  },
18123
18366
  async handler({ schedule_id }) {
18124
18367
  return mcpResult(core.cancelSchedule(getCtx(), schedule_id));
@@ -18130,7 +18373,7 @@ function createSelfConfigToolDefinitionsForCore(core, ctx) {
18130
18373
  name: "spawn_topic",
18131
18374
  description: toolDescription(core, "spawn_topic", `Create a new forum topic that inherits all settings from THIS topic but starts with a fresh session. Limited to ${core.product.derivedTopicLimit} calls per conversation turn.`),
18132
18375
  schema: {
18133
- name: z2.string().optional().describe("New topic name. Auto-generated if omitted.")
18376
+ name: z3.string().optional().describe("New topic name. Auto-generated if omitted.")
18134
18377
  },
18135
18378
  async handler({ name }) {
18136
18379
  if (derivedCount >= core.product.derivedTopicLimit) {
@@ -18147,7 +18390,7 @@ function createSelfConfigToolDefinitionsForCore(core, ctx) {
18147
18390
  name: "fork_topic",
18148
18391
  description: toolDescription(core, "fork_topic", `Fork THIS topic by creating a new topic that inherits both settings AND conversation history. Limited to ${core.product.derivedTopicLimit} calls per conversation turn.`),
18149
18392
  schema: {
18150
- name: z2.string().optional().describe("New topic name. Auto-generated if omitted.")
18393
+ name: z3.string().optional().describe("New topic name. Auto-generated if omitted.")
18151
18394
  },
18152
18395
  async handler({ name }) {
18153
18396
  if (derivedCount >= core.product.derivedTopicLimit) {
@@ -18169,14 +18412,14 @@ await init_spawn_subagent();
18169
18412
 
18170
18413
  // ../../packages/core/src/agents/mcp-tools/visuals.ts
18171
18414
  init_common();
18172
- import { z as z4 } from "zod";
18415
+ import { z as z5 } from "zod";
18173
18416
  var VISUALS_MCP_KEY = "visuals";
18174
18417
  var showHtmlTool = {
18175
18418
  name: "show_html",
18176
18419
  description: "Display charts, tables, or interactive HTML to the user in a sandboxed WebView side panel. Pass a complete, self-contained HTML string with inline CSS/JS. Local DOM interactions are supported. Links (<a href> to http/https URLs) are safe to include: a click opens the URL in the user's system browser. Network requests, external scripts, form posts, popups, and parent-window access are blocked.",
18177
18420
  schema: {
18178
- html: z4.string().describe("Complete, self-contained HTML document or fragment to render."),
18179
- title: z4.string().optional().describe("Optional title shown above the rendered card.")
18421
+ html: z5.string().describe("Complete, self-contained HTML document or fragment to render."),
18422
+ title: z5.string().optional().describe("Optional title shown above the rendered card.")
18180
18423
  },
18181
18424
  async handler() {
18182
18425
  return textResult("HTML card displayed to the user.");
@@ -18186,9 +18429,9 @@ var showMermaidTool = {
18186
18429
  name: "show_mermaid",
18187
18430
  description: "Render a Mermaid diagram in the user's visual side panel. Pass Mermaid DSL only, not markdown fences. Use this for flowcharts, sequence diagrams, class diagrams, state diagrams, ER diagrams, timelines, and architecture diagrams.",
18188
18431
  schema: {
18189
- code: z4.string().describe("Mermaid diagram source without markdown code fences."),
18190
- title: z4.string().optional().describe("Optional title shown above the rendered card."),
18191
- theme: z4.enum(["default", "neutral", "dark", "forest"]).optional().describe("Optional Mermaid theme. Defaults to neutral.")
18432
+ code: z5.string().describe("Mermaid diagram source without markdown code fences."),
18433
+ title: z5.string().optional().describe("Optional title shown above the rendered card."),
18434
+ theme: z5.enum(["default", "neutral", "dark", "forest"]).optional().describe("Optional Mermaid theme. Defaults to neutral.")
18192
18435
  },
18193
18436
  async handler() {
18194
18437
  return textResult("Mermaid diagram displayed to the user.");
@@ -18198,10 +18441,10 @@ var showImageTool = {
18198
18441
  name: "show_image",
18199
18442
  description: "Display an image in the user's visual side panel. Provide either file_path for an image in the topic workspace, or file_id for an uploaded file already attached in this topic.",
18200
18443
  schema: {
18201
- file_path: z4.string().optional().describe("Absolute or workspace-relative path to an image file."),
18202
- file_id: z4.string().optional().describe("Full Otium uploaded file UUID for an image attached in this topic."),
18203
- title: z4.string().optional().describe("Optional title shown above the rendered card."),
18204
- alt: z4.string().optional().describe("Optional alt text for the image.")
18444
+ file_path: z5.string().optional().describe("Absolute or workspace-relative path to an image file."),
18445
+ file_id: z5.string().optional().describe("Full Otium uploaded file UUID for an image attached in this topic."),
18446
+ title: z5.string().optional().describe("Optional title shown above the rendered card."),
18447
+ alt: z5.string().optional().describe("Optional alt text for the image.")
18205
18448
  },
18206
18449
  async handler(input) {
18207
18450
  if (!input?.file_path && !input?.file_id) {
@@ -18214,9 +18457,9 @@ var showVideoTool = {
18214
18457
  name: "show_video",
18215
18458
  description: "Display a playable video in the user's visual side panel. Provide either file_path for a video in the topic workspace, or file_id for an uploaded file already attached in this topic.",
18216
18459
  schema: {
18217
- file_path: z4.string().optional().describe("Absolute or workspace-relative path to a video file."),
18218
- file_id: z4.string().optional().describe("Full Otium uploaded file UUID for a video attached in this topic."),
18219
- title: z4.string().optional().describe("Optional title shown above the rendered card.")
18460
+ file_path: z5.string().optional().describe("Absolute or workspace-relative path to a video file."),
18461
+ file_id: z5.string().optional().describe("Full Otium uploaded file UUID for a video attached in this topic."),
18462
+ title: z5.string().optional().describe("Optional title shown above the rendered card.")
18220
18463
  },
18221
18464
  async handler(input) {
18222
18465
  if (!input?.file_path && !input?.file_id) {
@@ -18290,6 +18533,7 @@ export {
18290
18533
  createSelfConfigToolDefinitionsForCore,
18291
18534
  createSelfConfigRuntime,
18292
18535
  createSelfConfigCore,
18536
+ createPublishHtmlToolDefinitions,
18293
18537
  createCodexTreeManager,
18294
18538
  createAskUserRuntime,
18295
18539
  createArchiverRuntime,
@@ -18309,4 +18553,4 @@ export {
18309
18553
  DEFAULT_SELF_CONFIG_PRODUCT
18310
18554
  };
18311
18555
 
18312
- //# debugId=8D09CAB15E720F3E64756E2164756E21
18556
+ //# debugId=893EA9DA75C8A56D64756E2164756E21