negotium 0.1.24 → 0.1.26

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 (47) hide show
  1. package/README.md +5 -2
  2. package/dist/agent-helpers.js +64 -8
  3. package/dist/agent-helpers.js.map +9 -9
  4. package/dist/background-bash.js.map +1 -1
  5. package/dist/chunk-7azk83mw.js.map +1 -1
  6. package/dist/cron.js +161 -50
  7. package/dist/cron.js.map +20 -19
  8. package/dist/hosted-agent.js +2 -2
  9. package/dist/hosted-agent.js.map +5 -5
  10. package/dist/main.js +321 -130
  11. package/dist/main.js.map +23 -22
  12. package/dist/mcp-factories.js.map +1 -1
  13. package/dist/prompts.js +22 -2
  14. package/dist/prompts.js.map +4 -4
  15. package/dist/registry.js +8 -3
  16. package/dist/registry.js.map +3 -3
  17. package/dist/runtime/src/agents/api-topic-agent-switch.ts +8 -2
  18. package/dist/runtime/src/agents/auth-check.ts +33 -3
  19. package/dist/runtime/src/agents/idle-archiver.ts +20 -1
  20. package/dist/runtime/src/agents/maestro-provider.ts +2 -1
  21. package/dist/runtime/src/agents/maestro-registry.ts +11 -6
  22. package/dist/runtime/src/agents/mcp-tools/spawn-subagent.ts +16 -10
  23. package/dist/runtime/src/agents/model-catalog.ts +47 -5
  24. package/dist/runtime/src/agents/public-helpers.ts +2 -0
  25. package/dist/runtime/src/agents/self-config-core.ts +17 -5
  26. package/dist/runtime/src/agents/topic-agent-switch.ts +4 -2
  27. package/dist/runtime/src/application/switch-topic-model.ts +3 -0
  28. package/dist/runtime/src/index.ts +1 -1
  29. package/dist/runtime/src/platform/playwright/headed-launch.ts +61 -0
  30. package/dist/runtime/src/platform/playwright/manager.ts +39 -9
  31. package/dist/runtime/src/runtime/errors.ts +2 -2
  32. package/dist/runtime/src/topics/derive.ts +7 -3
  33. package/dist/runtime/src/topics/session.ts +45 -1
  34. package/dist/runtime/src/types.ts +1 -1
  35. package/dist/runtime/src/version.ts +1 -1
  36. package/dist/runtime-helpers.js.map +1 -1
  37. package/dist/storage.js.map +1 -1
  38. package/dist/types/packages/core/src/agents/auth-check.d.ts +2 -0
  39. package/dist/types/packages/core/src/agents/idle-archiver.d.ts +4 -0
  40. package/dist/types/packages/core/src/agents/maestro-provider.d.ts +2 -1
  41. package/dist/types/packages/core/src/agents/maestro-registry.d.ts +3 -5
  42. package/dist/types/packages/core/src/agents/model-catalog.d.ts +5 -1
  43. package/dist/types/packages/core/src/agents/public-helpers.d.ts +2 -1
  44. package/dist/types/packages/core/src/types.d.ts +1 -1
  45. package/dist/types/packages/core/src/version.d.ts +1 -1
  46. package/dist/vault.js.map +1 -1
  47. package/package.json +2 -2
package/dist/cron.js CHANGED
@@ -1716,14 +1716,19 @@ import {
1716
1716
  setConversationReader,
1717
1717
  maestroRegistry as upstreamMaestroRegistry
1718
1718
  } from "maestro-agent-sdk";
1719
- var maestroRegistry;
1719
+ var DISABLED_MODEL_ALIASES, upstream, maestroRegistry;
1720
1720
  var init_maestro_registry = __esm(async () => {
1721
1721
  init_maestro_bootstrap_env();
1722
1722
  await init_conversations();
1723
+ DISABLED_MODEL_ALIASES = new Set(["deepseek", "deepseek-flash", "deepseek-v4-flash"]);
1724
+ upstream = upstreamMaestroRegistry;
1723
1725
  setConversationReader(readConversation);
1724
1726
  maestroRegistry = {
1725
- ...upstreamMaestroRegistry,
1726
- defaultModel: "deepseek-pro"
1727
+ ...upstream,
1728
+ defaultModel: "deepseek-pro",
1729
+ validateModel(model) {
1730
+ return !DISABLED_MODEL_ALIASES.has(model) && upstream.validateModel(model);
1731
+ }
1727
1732
  };
1728
1733
  });
1729
1734
 
@@ -3463,7 +3468,7 @@ var init_claude_provider = __esm(async () => {
3463
3468
  });
3464
3469
 
3465
3470
  // ../../packages/core/src/version.ts
3466
- var NEGOTIUM_VERSION = "0.1.24";
3471
+ var NEGOTIUM_VERSION = "0.1.26";
3467
3472
 
3468
3473
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
3469
3474
  import { spawn as spawn3 } from "child_process";
@@ -5445,19 +5450,25 @@ var init_ask_user = __esm(async () => {
5445
5450
  });
5446
5451
 
5447
5452
  // ../../packages/core/src/agents/model-catalog.ts
5453
+ function canonicalModelId(value) {
5454
+ const trimmed = value.trim();
5455
+ return SELECTABLE_MODEL_ALIASES[trimmed.toLowerCase()] ?? trimmed;
5456
+ }
5448
5457
  function formatSelectableModel(candidate) {
5449
5458
  const tier = `${candidate.intelligenceTier[0].toUpperCase()}${candidate.intelligenceTier.slice(1)}`;
5450
5459
  return `${candidate.agent} / \`${candidate.model}\` [${tier}-level]: ${candidate.routingSummary}`;
5451
5460
  }
5452
5461
  function selectableModel(value) {
5453
- const normalized = value.trim().toLowerCase();
5454
- return SELECTABLE_MODELS.find((candidate) => candidate.model === normalized);
5462
+ const canonical = canonicalModelId(value).toLowerCase();
5463
+ return SELECTABLE_MODELS.find((candidate) => candidate.model === canonical);
5455
5464
  }
5456
5465
  function modelOwner(model) {
5457
5466
  if (model.startsWith("claude-"))
5458
5467
  return "claude";
5459
5468
  if (model.startsWith("deepseek-"))
5460
5469
  return "maestro";
5470
+ if (model.startsWith("kimi-"))
5471
+ return "maestro";
5461
5472
  if (model.startsWith("gpt-"))
5462
5473
  return "codex";
5463
5474
  return MODEL_OWNER[model];
@@ -5466,12 +5477,13 @@ function resolveModelForAgent(agent, requested, registry) {
5466
5477
  const defaultModel = resolveDefaultModel(agent, registry.defaultModel);
5467
5478
  if (!requested)
5468
5479
  return defaultModel;
5469
- const owner = modelOwner(requested);
5480
+ const candidate = canonicalModelId(requested);
5481
+ const owner = modelOwner(candidate);
5470
5482
  if (owner && owner !== agent)
5471
5483
  return defaultModel;
5472
- return registry.validateModel(requested) ? requested : defaultModel;
5484
+ return registry.validateModel(candidate) ? candidate : defaultModel;
5473
5485
  }
5474
- var MODEL_OWNER, MODEL_COST_RESEARCHED_AT = "2026-07-19", MODEL_COST_ROUTING_SUMMARY = "Cost basis (2026-07-19): Codex Pro 20x and Claude Max 20x are each $200/month; DeepSeek Pro is pay-per-token. Relative marginal token cost: DeepSeek Pro << Codex < Claude.", CODEX_PRO_20X_COST = "ChatGPT Pro 20x subscription: $200/month", CODEX_COMMUNITY_WEEKLY = "Community plan-level observation: roughly 2\u20134B raw/cached tokens per week; fresh-input equivalent is much lower and unstable (low confidence)", CLAUDE_MAX_20X_COST = "Claude Max 20x subscription: $200/month", CLAUDE_COMMUNITY_SESSION = "Community observations vary from roughly 220\u2013250K locally displayed tokens per 5-hour session to billions of cache-heavy raw tokens per week; calibrated reports value a full weekly allowance around $680\u2013$1,900 at API rates. Recent heavy-model reports reach the weekly cap after about 4\u20135 full sessions (low confidence; not a token cap)", SELECTABLE_MODELS, FALLBACK_ORDER, AGENT_DISPLAY_NAME;
5486
+ var MODEL_OWNER, MODEL_COST_RESEARCHED_AT = "2026-07-19", MODEL_COST_ROUTING_SUMMARY = "Cost basis (2026-07-19): Codex Pro 20x and Claude Max 20x are each $200/month; Maestro models are pay-per-token. DeepSeek Pro is cheapest, Kimi K2.7 Code is the coding route, and Kimi K3 is the frontier route.", CODEX_PRO_20X_COST = "ChatGPT Pro 20x subscription: $200/month", CODEX_COMMUNITY_WEEKLY = "Community plan-level observation: roughly 2\u20134B raw/cached tokens per week; fresh-input equivalent is much lower and unstable (low confidence)", CLAUDE_MAX_20X_COST = "Claude Max 20x subscription: $200/month", CLAUDE_COMMUNITY_SESSION = "Community observations vary from roughly 220\u2013250K locally displayed tokens per 5-hour session to billions of cache-heavy raw tokens per week; calibrated reports value a full weekly allowance around $680\u2013$1,900 at API rates. Recent heavy-model reports reach the weekly cap after about 4\u20135 full sessions (low confidence; not a token cap)", SELECTABLE_MODELS, SELECTABLE_MODEL_ALIASES, FALLBACK_ORDER, AGENT_DISPLAY_NAME;
5475
5487
  var init_model_catalog = __esm(() => {
5476
5488
  init_config();
5477
5489
  MODEL_OWNER = {
@@ -5485,7 +5497,12 @@ var init_model_catalog = __esm(() => {
5485
5497
  "gpt-5.5": "codex",
5486
5498
  deepseek: "maestro",
5487
5499
  "deepseek-pro": "maestro",
5488
- "deepseek-flash": "maestro"
5500
+ "deepseek-flash": "maestro",
5501
+ kimi: "maestro",
5502
+ "kimi-pro": "maestro",
5503
+ "kimi-k3": "maestro",
5504
+ "kimi-code": "maestro",
5505
+ "kimi-k2.7-code": "maestro"
5489
5506
  };
5490
5507
  SELECTABLE_MODELS = [
5491
5508
  {
@@ -5548,6 +5565,26 @@ var init_model_catalog = __esm(() => {
5548
5565
  marginalTokenCost: "Claude API/extra usage introductory rate: $2/M input, $2.50/M cache write, $0.20/M cache read, $10/M output through 2026-08-31; then $3/M input and $15/M output",
5549
5566
  estimatedUsage: `${CLAUDE_COMMUNITY_SESSION}; Sonnet also has a separate weekly allowance and normally provides the highest Claude throughput. No stable weekly token cap is published.`
5550
5567
  },
5568
+ {
5569
+ model: "kimi-k3",
5570
+ agent: "maestro",
5571
+ description: "Frontier Kimi route for long-horizon coding and knowledge work.",
5572
+ intelligenceTier: "fable",
5573
+ routingSummary: "frontier general/coding route; 1M context; highest Maestro API cost",
5574
+ accessCost: "Moonshot AI pay-as-you-go API; no monthly subscription required",
5575
+ marginalTokenCost: "Kimi API: $3/M cache-miss input, $0.30/M cached input, $15/M output",
5576
+ estimatedUsage: "No subscription token cap; pay per token. Supports a 1M-token context window."
5577
+ },
5578
+ {
5579
+ model: "kimi-k2.7-code",
5580
+ agent: "maestro",
5581
+ description: "Coding-specialized Kimi route for repository-scale, long-horizon work.",
5582
+ intelligenceTier: "opus",
5583
+ routingSummary: "coding-specialized route; 256K context; cheaper than Kimi K3",
5584
+ accessCost: "Moonshot AI pay-as-you-go API; no monthly subscription required",
5585
+ marginalTokenCost: "Kimi API: $0.95/M cache-miss input, $0.19/M cached input, $4/M output",
5586
+ estimatedUsage: "No subscription token cap; pay per token. Always uses thinking and supports a 256K context window."
5587
+ },
5551
5588
  {
5552
5589
  model: "deepseek-pro",
5553
5590
  agent: "maestro",
@@ -5559,6 +5596,11 @@ var init_model_catalog = __esm(() => {
5559
5596
  estimatedUsage: "No subscription token cap; pay per token. Official account concurrency limit is 500 requests."
5560
5597
  }
5561
5598
  ];
5599
+ SELECTABLE_MODEL_ALIASES = {
5600
+ kimi: "kimi-k3",
5601
+ "kimi-pro": "kimi-k3",
5602
+ "kimi-code": "kimi-k2.7-code"
5603
+ };
5562
5604
  FALLBACK_ORDER = {
5563
5605
  claude: [
5564
5606
  { agent: "maestro", model: "deepseek-pro" },
@@ -5748,6 +5790,38 @@ var init_self_schedules = __esm(async () => {
5748
5790
  `);
5749
5791
  });
5750
5792
 
5793
+ // ../../packages/core/src/platform/playwright/headed-launch.ts
5794
+ import { accessSync, constants } from "fs";
5795
+ import { delimiter, isAbsolute, resolve as resolve5 } from "path";
5796
+ function findExecutableOnPath(command, environment = process.env) {
5797
+ const candidates = isAbsolute(command) ? [command] : (environment.PATH ?? "").split(delimiter).filter(Boolean).map((directory) => resolve5(directory, command));
5798
+ for (const candidate of candidates) {
5799
+ try {
5800
+ accessSync(candidate, constants.X_OK);
5801
+ return candidate;
5802
+ } catch {}
5803
+ }
5804
+ return null;
5805
+ }
5806
+ function resolveHeadedPlaywrightSpawn(command, args, options = {}) {
5807
+ const platform2 = options.platform ?? process.platform;
5808
+ const environment = options.environment ?? process.env;
5809
+ const hasDisplay = Boolean(environment.DISPLAY?.trim() || environment.WAYLAND_DISPLAY?.trim());
5810
+ if (platform2 !== "linux" || hasDisplay) {
5811
+ return { command, args: [...args], virtualDisplay: false };
5812
+ }
5813
+ const xvfbRun = (options.findExecutable ?? findExecutableOnPath)("xvfb-run", environment);
5814
+ if (!xvfbRun) {
5815
+ throw new Error("Headed Playwright on Linux requires DISPLAY/WAYLAND_DISPLAY or xvfb-run; install Xvfb and ensure xvfb-run is on PATH");
5816
+ }
5817
+ return {
5818
+ command: xvfbRun,
5819
+ args: ["-a", "-s", "-screen 0 1440x1000x24", command, ...args],
5820
+ virtualDisplay: true
5821
+ };
5822
+ }
5823
+ var init_headed_launch = () => {};
5824
+
5751
5825
  // ../../packages/core/src/storage/browser-profiles.ts
5752
5826
  function normalizeBrowserProfileName(name) {
5753
5827
  const value = name.trim().toLowerCase();
@@ -5852,7 +5926,7 @@ import {
5852
5926
  unlinkSync as unlinkSync9,
5853
5927
  writeFileSync as writeFileSync7
5854
5928
  } from "fs";
5855
- import { dirname as dirname8, join as join13, resolve as resolve5 } from "path";
5929
+ import { dirname as dirname8, join as join13, resolve as resolve6 } from "path";
5856
5930
  function makeInstanceKey(userId, topic) {
5857
5931
  if (!topic)
5858
5932
  return makeBrowserProfileInstanceKey(userId, "default");
@@ -5870,7 +5944,7 @@ function migrateLegacyTopicProfile(ownerId, topic) {
5870
5944
  const current2 = getTopicBrowserProfile(topic);
5871
5945
  if (current2 !== "default" || !hasBrowserProfileTopic(topic))
5872
5946
  return current2;
5873
- const legacyDir = resolve5(BROWSER_PROFILES_DIR, sanitizeTopicName(topic));
5947
+ const legacyDir = resolve6(BROWSER_PROFILES_DIR, sanitizeTopicName(topic));
5874
5948
  if (!existsSync13(legacyDir))
5875
5949
  return current2;
5876
5950
  const profile = legacyBrowserProfileName(topic);
@@ -6049,11 +6123,11 @@ async function killPlaywrightOnPort(port, expectedUserDataDir) {
6049
6123
  }
6050
6124
  function browserProcessMatchesExpectedProfile(cmdline, expectedUserDataDir) {
6051
6125
  const actualUserDataDir = extractUserDataDirArg(cmdline);
6052
- return actualUserDataDir !== null && resolve5(actualUserDataDir) === resolve5(expectedUserDataDir);
6126
+ return actualUserDataDir !== null && resolve6(actualUserDataDir) === resolve6(expectedUserDataDir);
6053
6127
  }
6054
6128
  function killBrowserProcsForUserDataDir(userDataDir) {
6055
- const target = resolve5(userDataDir);
6056
- if (!target.startsWith(resolve5(BROWSER_PROFILES_DIR)))
6129
+ const target = resolve6(userDataDir);
6130
+ if (!target.startsWith(resolve6(BROWSER_PROFILES_DIR)))
6057
6131
  return;
6058
6132
  let pids;
6059
6133
  try {
@@ -6073,7 +6147,7 @@ function killBrowserProcsForUserDataDir(userDataDir) {
6073
6147
  stdio: "pipe"
6074
6148
  }).toString().trim();
6075
6149
  const argDir = extractUserDataDirArg(cmdline);
6076
- if (!argDir || resolve5(argDir) !== target)
6150
+ if (!argDir || resolve6(argDir) !== target)
6077
6151
  continue;
6078
6152
  killProcessTreeChildren(pidNum);
6079
6153
  process.kill(pidNum, "SIGKILL");
@@ -6084,13 +6158,13 @@ function killBrowserProcsForUserDataDir(userDataDir) {
6084
6158
  }
6085
6159
  }
6086
6160
  function selectOrphanBrowserPids(procs, liveUserDataDirs, profileRoot, selfPid) {
6087
- const root = resolve5(profileRoot);
6088
- const live = new Set([...liveUserDataDirs].map((d) => resolve5(d)));
6161
+ const root = resolve6(profileRoot);
6162
+ const live = new Set([...liveUserDataDirs].map((d) => resolve6(d)));
6089
6163
  const out = [];
6090
6164
  for (const { pid, userDataDir } of procs) {
6091
6165
  if (pid === selfPid || !userDataDir)
6092
6166
  continue;
6093
- const dir = resolve5(userDataDir);
6167
+ const dir = resolve6(userDataDir);
6094
6168
  if (!dir.startsWith(root))
6095
6169
  continue;
6096
6170
  if (live.has(dir))
@@ -6106,7 +6180,7 @@ function reapOrphanBrowsers() {
6106
6180
  const daemonLease = getRuntimeProcessLease("node-daemon", Date.now(), Number.POSITIVE_INFINITY);
6107
6181
  if (!isBrowserJanitorOwner(daemonLease?.pid ?? null, process.pid))
6108
6182
  return;
6109
- const profileRoot = resolve5(BROWSER_PROFILES_DIR);
6183
+ const profileRoot = resolve6(BROWSER_PROFILES_DIR);
6110
6184
  let pids;
6111
6185
  try {
6112
6186
  pids = execFileSync4("pgrep", ["-f", "--", profileRoot], { stdio: "pipe" }).toString().trim();
@@ -6159,7 +6233,7 @@ function cleanSingletonFiles(userDataDir) {
6159
6233
  for (const f of files) {
6160
6234
  if (f.startsWith("Singleton")) {
6161
6235
  try {
6162
- unlinkSync9(resolve5(userDataDir, f));
6236
+ unlinkSync9(resolve6(userDataDir, f));
6163
6237
  logger.info({ file: f, userDataDir }, "Removed stale Singleton file");
6164
6238
  } catch (e) {
6165
6239
  logger.warn({ err: e, file: f }, "Failed to remove stale Chrome Singleton file");
@@ -6195,7 +6269,7 @@ function ownerDirectory(ownerId) {
6195
6269
  return `${sanitizeTopicName(ownerId).slice(0, 24)}_${digest}`;
6196
6270
  }
6197
6271
  function resolveProfileDir(ownerId, profile) {
6198
- return resolve5(BROWSER_PROFILES_DIR, "profiles", ownerDirectory(ownerId), profile);
6272
+ return resolve6(BROWSER_PROFILES_DIR, "profiles", ownerDirectory(ownerId), profile);
6199
6273
  }
6200
6274
  function resolveUserDataDir(instanceKey) {
6201
6275
  const { ownerId, profile } = parseInstanceKey(instanceKey);
@@ -6253,11 +6327,25 @@ async function spawnPlaywright(instanceKey, userId, reservedPort, browserBin = P
6253
6327
  }
6254
6328
  const command = browserBin.endsWith(".mjs") ? process.execPath : browserBin;
6255
6329
  const args = browserBin.endsWith(".mjs") ? [browserBin, ...mcpArgs] : mcpArgs;
6256
- const proc = spawn4(command, args, {
6257
- stdio: "ignore",
6258
- detached: false,
6259
- env: childEnv
6260
- });
6330
+ let spawnSpec;
6331
+ try {
6332
+ spawnSpec = resolveHeadedPlaywrightSpawn(command, args, { environment: childEnv });
6333
+ } catch (err) {
6334
+ releasePort(port);
6335
+ throw err;
6336
+ }
6337
+ let proc;
6338
+ try {
6339
+ proc = spawn4(spawnSpec.command, spawnSpec.args, {
6340
+ stdio: "ignore",
6341
+ detached: false,
6342
+ env: childEnv
6343
+ });
6344
+ } catch (err) {
6345
+ releasePort(port);
6346
+ throw err;
6347
+ }
6348
+ const spawnError = waitForChildProcessSpawnError(proc);
6261
6349
  const reapCrashedBrowser = () => {
6262
6350
  const userDataDir2 = resolveUserDataDir(instanceKey);
6263
6351
  killBrowserProcsForUserDataDir(userDataDir2);
@@ -6290,7 +6378,10 @@ async function spawnPlaywright(instanceKey, userId, reservedPort, browserBin = P
6290
6378
  lastUsedAt: now,
6291
6379
  capability
6292
6380
  });
6293
- const ready = await waitForServer(port, 1e4) && await supportsOwnerCleanup(port, capability);
6381
+ const ready = await Promise.race([
6382
+ (async () => await waitForServer(port, 1e4) && await supportsOwnerCleanup(port, capability))(),
6383
+ spawnError
6384
+ ]);
6294
6385
  if (!ready) {
6295
6386
  const exitCode = proc.exitCode;
6296
6387
  killInstance(instanceKey);
@@ -6301,7 +6392,7 @@ async function spawnPlaywright(instanceKey, userId, reservedPort, browserBin = P
6301
6392
  throw new Error(`Playwright MCP failed health check after spawn on port ${port}` + (exitCode === null ? "" : ` (exitCode=${exitCode})`));
6302
6393
  }
6303
6394
  writePortFile(instanceKey, port);
6304
- logger.info({ instanceKey, port, pid: proc.pid, ready }, "Playwright MCP started");
6395
+ logger.info({ instanceKey, port, pid: proc.pid, ready, virtualDisplay: spawnSpec.virtualDisplay }, "Playwright MCP started");
6305
6396
  return port;
6306
6397
  }
6307
6398
  async function supportsOwnerCleanup(port, capability) {
@@ -6369,10 +6460,16 @@ async function waitForServer(port, timeoutMs) {
6369
6460
  logger.warn({ port, timeoutMs }, "Playwright MCP not ready before timeout");
6370
6461
  return false;
6371
6462
  }
6463
+ function waitForChildProcessSpawnError(proc) {
6464
+ return new Promise((_resolve, reject) => {
6465
+ proc.once("error", reject);
6466
+ });
6467
+ }
6372
6468
  var BASE_PORT, MAX_PORT, MAX_IDLE_MS, instances, usedPorts, spawning, pinnedInstances, _onPlaywrightExit = null;
6373
6469
  var init_manager2 = __esm(async () => {
6374
6470
  init_config();
6375
6471
  init_logger();
6472
+ init_headed_launch();
6376
6473
  await init_browser_profiles();
6377
6474
  await init_runtime_process_leases();
6378
6475
  BASE_PORT = PLAYWRIGHT_BASE_PORT;
@@ -6997,9 +7094,9 @@ var init_runtime_turn_requests = __esm(async () => {
6997
7094
 
6998
7095
  // ../../packages/core/src/prompts/builders.ts
6999
7096
  import { readFileSync as readFileSync10 } from "fs";
7000
- import { resolve as resolve6 } from "path";
7097
+ import { resolve as resolve7 } from "path";
7001
7098
  function loadPrompt(filename, dir = SESSIONS_DIR) {
7002
- const raw = readFileSync10(resolve6(dir, filename), "utf-8");
7099
+ const raw = readFileSync10(resolve7(dir, filename), "utf-8");
7003
7100
  return raw.replace(/\{\{RESOURCES_DIR\}\}/g, RESOURCES_DIR);
7004
7101
  }
7005
7102
  function replaceVars(template, vars) {
@@ -7042,7 +7139,7 @@ function visualDesignGuide() {
7042
7139
  return _visualDesignGuide;
7043
7140
  }
7044
7141
  function loadAgentPrompt(filename) {
7045
- const raw = readFileSync10(resolve6(AGENTS_PROMPTS_DIR, filename), "utf-8");
7142
+ const raw = readFileSync10(resolve7(AGENTS_PROMPTS_DIR, filename), "utf-8");
7046
7143
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
7047
7144
  if (!match)
7048
7145
  throw new Error(`Agent prompt ${filename} is missing frontmatter`);
@@ -7242,8 +7339,8 @@ var init_builders = __esm(() => {
7242
7339
  init_model_catalog();
7243
7340
  init_config();
7244
7341
  init_logger();
7245
- PROMPTS_DIR = resolve6(PROJECT_ROOT, "src/prompts");
7246
- SESSIONS_DIR = resolve6(PROMPTS_DIR, "sessions");
7342
+ PROMPTS_DIR = resolve7(PROJECT_ROOT, "src/prompts");
7343
+ SESSIONS_DIR = resolve7(PROMPTS_DIR, "sessions");
7247
7344
  });
7248
7345
 
7249
7346
  // ../../packages/core/src/storage/api-topic-brief.ts
@@ -7934,7 +8031,17 @@ function archiveActiveTopicForMemory(topicId, userId, options) {
7934
8031
  archivePath: job.archivePath,
7935
8032
  messageCount: job.messageCount,
7936
8033
  mode: "active-topic",
7937
- onSettled: (success) => settleTopicArchiveJob(topicId, job.archivePath, success)
8034
+ onSettled: (success) => {
8035
+ let settled = false;
8036
+ try {
8037
+ (options.settleArchiveJob ?? settleTopicArchiveJob)(topicId, job.archivePath, success);
8038
+ settled = true;
8039
+ } catch (err) {
8040
+ logger.warn({ err, topicId, archive: job.archivePath }, "topic-memory-archiver: failed to settle archive job");
8041
+ } finally {
8042
+ options.onSettled?.(settled && success);
8043
+ }
8044
+ }
7938
8045
  });
7939
8046
  if (!launched) {
7940
8047
  settleTopicArchiveJob(topicId, job.archivePath, false);
@@ -8556,7 +8663,7 @@ function authRecoveryHint(agent) {
8556
8663
  case "codex":
8557
8664
  return "Please log in again with `codex login` (~/.codex/auth.json)";
8558
8665
  case "maestro":
8559
- return "Please check the DEEPSEEK_API_KEY environment variable";
8666
+ return "Please check the DEEPSEEK_API_KEY or MOONSHOT_API_KEY environment variable";
8560
8667
  }
8561
8668
  }
8562
8669
  function classifyAgentError(err, agent) {
@@ -8572,7 +8679,7 @@ function classifyAgentError(err, agent) {
8572
8679
  return status === 529 ? `${name} server is overloaded. Please try again in a moment. (529)` : `${name} server error occurred. Please try again in a moment. (500)`;
8573
8680
  }
8574
8681
  const s = stringifyError(err);
8575
- if (/401|authentication.error|invalid.*api.key|not logged|ANTHROPIC_API_KEY|DEEPSEEK_API_KEY/i.test(s)) {
8682
+ if (/401|authentication.error|invalid.*api.key|not logged|ANTHROPIC_API_KEY|DEEPSEEK_API_KEY|MOONSHOT_API_KEY/i.test(s)) {
8576
8683
  return `${name} authentication expired. ${hint}. (401)`;
8577
8684
  }
8578
8685
  if (/429|rate.limit/i.test(s))
@@ -8594,12 +8701,12 @@ var init_errors = __esm(() => {
8594
8701
 
8595
8702
  // ../../packages/core/src/runtime/event-heartbeat.ts
8596
8703
  function nextOrHeartbeat(pending, intervalMs) {
8597
- return new Promise((resolve7, reject) => {
8598
- const timer = setTimeout(() => resolve7({ kind: "heartbeat" }), intervalMs);
8704
+ return new Promise((resolve8, reject) => {
8705
+ const timer = setTimeout(() => resolve8({ kind: "heartbeat" }), intervalMs);
8599
8706
  timer.unref?.();
8600
8707
  pending.then((result) => {
8601
8708
  clearTimeout(timer);
8602
- resolve7({ kind: "event", result });
8709
+ resolve8({ kind: "event", result });
8603
8710
  }, (error) => {
8604
8711
  clearTimeout(timer);
8605
8712
  reject(error);
@@ -9026,7 +9133,7 @@ var init_visual_html = __esm(() => {
9026
9133
 
9027
9134
  // ../../packages/core/src/runtime/visuals.ts
9028
9135
  import { realpathSync as realpathSync2 } from "fs";
9029
- import { isAbsolute, resolve as resolve7 } from "path";
9136
+ import { isAbsolute as isAbsolute2, resolve as resolve8 } from "path";
9030
9137
  function activeVisualHtmlForPrompt(html) {
9031
9138
  if (html.length <= ACTIVE_VISUAL_PROMPT_MAX_CHARS) {
9032
9139
  return { html, omittedChars: 0 };
@@ -9077,8 +9184,8 @@ function topicAllowsVisualFileId(topicId, fileId) {
9077
9184
  return topicHasAttachmentFileId(topicId, fileId) || topicHasVisualFileId(topicId, fileId);
9078
9185
  }
9079
9186
  function isPathInside(baseDir, filePath) {
9080
- const base = resolve7(baseDir);
9081
- const normalized = resolve7(filePath);
9187
+ const base = resolve8(baseDir);
9188
+ const normalized = resolve8(filePath);
9082
9189
  try {
9083
9190
  const realBase = realpathSync2(base);
9084
9191
  const real = realpathSync2(normalized);
@@ -9140,7 +9247,7 @@ function resolveVisualMediaInput(topicId, input) {
9140
9247
  }
9141
9248
  const rawPath = input.file_path.trim();
9142
9249
  const cwd = workspaceCwdFor(topicId);
9143
- const candidate = isAbsolute(rawPath) ? rawPath : resolve7(cwd, rawPath);
9250
+ const candidate = isAbsolute2(rawPath) ? rawPath : resolve8(cwd, rawPath);
9144
9251
  if (!isPathInside(cwd, candidate)) {
9145
9252
  return { error: "file_path must be inside the topic workspace" };
9146
9253
  }
@@ -9836,6 +9943,7 @@ __export(exports_turn_runner, {
9836
9943
  composeAttachmentPrompt: () => composeAttachmentPrompt,
9837
9944
  classifyAgentError: () => classifyAgentError,
9838
9945
  channelTranscriptSpeaker: () => channelTranscriptSpeaker,
9946
+ canonicalModelId: () => canonicalModelId,
9839
9947
  buildVideoHtml: () => buildVideoHtml,
9840
9948
  buildMermaidHtml: () => buildMermaidHtml,
9841
9949
  buildMentionOnlyChannelPrompt: () => buildMentionOnlyChannelPrompt,
@@ -9853,7 +9961,7 @@ __export(exports_turn_runner, {
9853
9961
  });
9854
9962
  import { randomUUID as randomUUID9 } from "crypto";
9855
9963
  import { mkdirSync as mkdirSync16, realpathSync as realpathSync3, statSync as statSync6 } from "fs";
9856
- import { isAbsolute as isAbsolute2, resolve as resolve8 } from "path";
9964
+ import { isAbsolute as isAbsolute3, resolve as resolve9 } from "path";
9857
9965
  function withDefaultPlaywright(configuredMcp, isManager) {
9858
9966
  if (isManager)
9859
9967
  return configuredMcp;
@@ -10197,7 +10305,7 @@ async function streamAgentEvents(topicId, topicTitle, queryId, events, control,
10197
10305
  case "file":
10198
10306
  if (!silent && peerBridge) {
10199
10307
  const cwd = workspaceCwdFor(topicId);
10200
- const path = isAbsolute2(event.path) ? event.path : resolve8(cwd, event.path);
10308
+ const path = isAbsolute3(event.path) ? event.path : resolve9(cwd, event.path);
10201
10309
  if (!isPathInside(cwd, path)) {
10202
10310
  logger.warn({ topicId, path }, "peer output file is outside the topic workspace");
10203
10311
  break;
@@ -11796,6 +11904,7 @@ await init_api_topics();
11796
11904
  await init_conversations();
11797
11905
 
11798
11906
  // ../../packages/core/src/agents/self-config-core.ts
11907
+ init_auth_check();
11799
11908
  init_model_catalog();
11800
11909
  await init_registry();
11801
11910
  await init_bus();
@@ -11918,6 +12027,7 @@ await init_bus();
11918
12027
  await init_api_topic_config();
11919
12028
  await init_api_topics();
11920
12029
  // ../../packages/core/src/application/switch-topic-model.ts
12030
+ init_auth_check();
11921
12031
  init_model_catalog();
11922
12032
  await init_registry();
11923
12033
  await init_bus();
@@ -11967,6 +12077,7 @@ await init_runtime_leases();
11967
12077
  await init_runtime_topic_state();
11968
12078
  await init_runtime_turn_requests();
11969
12079
  await init_personal_general();
12080
+ var RESET_MEMORY_ARCHIVE_WAIT_MS = 5 * 60 * 1000;
11970
12081
  // ../../packages/core/src/application/vault-command.ts
11971
12082
  await init_vault();
11972
12083
  var VAULT_COMMAND_HELP = [
@@ -12474,8 +12585,8 @@ function computeNextCronRun(expression, after = new Date, timezone) {
12474
12585
  // ../../packages/module-cron/src/scripts.ts
12475
12586
  import { spawn as spawn5 } from "child_process";
12476
12587
  import { existsSync as existsSync16, mkdirSync as mkdirSync19, readdirSync as readdirSync6 } from "fs";
12477
- import { resolve as resolve9, sep } from "path";
12478
- var CRON_JOBS_DIR = resolve9(process.env.NEGOTIUM_CRON_JOBS_DIR?.trim() || resolve9(WORKSPACE_DIR, "cron", "jobs"));
12588
+ import { resolve as resolve10, sep } from "path";
12589
+ var CRON_JOBS_DIR = resolve10(process.env.NEGOTIUM_CRON_JOBS_DIR?.trim() || resolve10(WORKSPACE_DIR, "cron", "jobs"));
12479
12590
  var DEFAULT_SCRIPT_TIMEOUT_MS = 10 * 60000;
12480
12591
  var DEFAULT_OUTPUT_LIMIT_BYTES = 1024 * 1024;
12481
12592
  var KILL_GRACE_MS = 2500;
@@ -12498,7 +12609,7 @@ function resolveCronScriptPath(script) {
12498
12609
  const valid = validateCronScriptName(script);
12499
12610
  if (!valid.ok)
12500
12611
  throw new Error(valid.error);
12501
- const path = resolve9(CRON_JOBS_DIR, script);
12612
+ const path = resolve10(CRON_JOBS_DIR, script);
12502
12613
  if (!path.startsWith(`${CRON_JOBS_DIR}${sep}`))
12503
12614
  throw new Error("script path escaped jobs directory");
12504
12615
  return path;
@@ -13956,4 +14067,4 @@ export {
13956
14067
  CRON_CONTEXT_RETAIN_TURNS
13957
14068
  };
13958
14069
 
13959
- //# debugId=DC099CBD3DDF771164756E2164756E21
14070
+ //# debugId=8A97E8B383252AFE64756E2164756E21