negotium 0.1.20 → 0.1.21

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 (34) hide show
  1. package/dist/agent-helpers.js +119 -9
  2. package/dist/agent-helpers.js.map +10 -8
  3. package/dist/cron.js +108 -35
  4. package/dist/cron.js.map +16 -15
  5. package/dist/hosted-agent.js +2 -2
  6. package/dist/hosted-agent.js.map +2 -2
  7. package/dist/main.js +116 -38
  8. package/dist/main.js.map +16 -15
  9. package/dist/prompts.js +98 -9
  10. package/dist/prompts.js.map +6 -5
  11. package/dist/runtime/scripts/browser-passkey-policy.mjs +36 -0
  12. package/dist/runtime/scripts/mcp-patchright-http.mjs +14 -4
  13. package/dist/runtime/src/agents/archiver.ts +0 -14
  14. package/dist/runtime/src/agents/idle-archiver.ts +21 -0
  15. package/dist/runtime/src/agents/mcp-tools/self-config.ts +1 -1
  16. package/dist/runtime/src/agents/mcp-tools/spawn-subagent.ts +6 -1
  17. package/dist/runtime/src/agents/memory-archive-policy.ts +34 -0
  18. package/dist/runtime/src/agents/model-catalog.ts +84 -18
  19. package/dist/runtime/src/platform/playwright/manager.ts +4 -2
  20. package/dist/runtime/src/prompts/builders.ts +36 -7
  21. package/dist/runtime/src/runtime/turn-runner.ts +2 -0
  22. package/dist/runtime/src/storage/topic-archive.ts +5 -2
  23. package/dist/runtime/src/topics/lifecycle.ts +2 -1
  24. package/dist/runtime/src/topics/session.ts +2 -0
  25. package/dist/runtime/src/version.ts +1 -1
  26. package/dist/storage.js +23 -3
  27. package/dist/storage.js.map +5 -4
  28. package/dist/types/packages/core/src/agents/idle-archiver.d.ts +2 -0
  29. package/dist/types/packages/core/src/agents/memory-archive-policy.d.ts +14 -0
  30. package/dist/types/packages/core/src/agents/model-catalog.d.ts +77 -0
  31. package/dist/types/packages/core/src/prompts/builders.d.ts +5 -1
  32. package/dist/types/packages/core/src/storage/topic-archive.d.ts +1 -0
  33. package/dist/types/packages/core/src/version.d.ts +1 -1
  34. package/package.json +2 -2
package/dist/main.js CHANGED
@@ -3707,7 +3707,7 @@ var init_claude_provider = __esm(async () => {
3707
3707
  });
3708
3708
 
3709
3709
  // ../../packages/core/src/version.ts
3710
- var NEGOTIUM_VERSION = "0.1.20";
3710
+ var NEGOTIUM_VERSION = "0.1.21";
3711
3711
 
3712
3712
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
3713
3713
  import { spawn as spawn3 } from "child_process";
@@ -6079,11 +6079,18 @@ __export(exports_model_catalog, {
6079
6079
  selectableModel: () => selectableModel,
6080
6080
  resolveModelForAgent: () => resolveModelForAgent,
6081
6081
  modelOwner: () => modelOwner,
6082
+ formatSelectableModel: () => formatSelectableModel,
6082
6083
  SELECTABLE_MODELS: () => SELECTABLE_MODELS,
6083
6084
  MODEL_OWNER: () => MODEL_OWNER,
6085
+ MODEL_COST_ROUTING_SUMMARY: () => MODEL_COST_ROUTING_SUMMARY,
6086
+ MODEL_COST_RESEARCHED_AT: () => MODEL_COST_RESEARCHED_AT,
6084
6087
  FALLBACK_ORDER: () => FALLBACK_ORDER,
6085
6088
  AGENT_DISPLAY_NAME: () => AGENT_DISPLAY_NAME
6086
6089
  });
6090
+ function formatSelectableModel(candidate) {
6091
+ const tier = `${candidate.intelligenceTier[0].toUpperCase()}${candidate.intelligenceTier.slice(1)}`;
6092
+ return `${candidate.agent} / \`${candidate.model}\` [${tier}-level]: ${candidate.routingSummary}`;
6093
+ }
6087
6094
  function selectableModel(value) {
6088
6095
  const normalized = value.trim().toLowerCase();
6089
6096
  return SELECTABLE_MODELS.find((candidate) => candidate.model === normalized);
@@ -6106,7 +6113,7 @@ function resolveModelForAgent(agent, requested, registry) {
6106
6113
  return defaultModel;
6107
6114
  return registry.validateModel(requested) ? requested : defaultModel;
6108
6115
  }
6109
- var MODEL_OWNER, SELECTABLE_MODELS, FALLBACK_ORDER, AGENT_DISPLAY_NAME;
6116
+ 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;
6110
6117
  var init_model_catalog = __esm(() => {
6111
6118
  init_config();
6112
6119
  MODEL_OWNER = {
@@ -6126,47 +6133,72 @@ var init_model_catalog = __esm(() => {
6126
6133
  {
6127
6134
  model: "gpt-5.6-sol",
6128
6135
  agent: "codex",
6129
- description: "Latest frontier agentic coding model."
6136
+ description: "Highest-capability Codex route for the hardest agentic coding work.",
6137
+ intelligenceTier: "fable",
6138
+ routingSummary: "hardest coding work; 5x Codex quota cost",
6139
+ accessCost: CODEX_PRO_20X_COST,
6140
+ marginalTokenCost: "Codex credits: $5/M uncached input, $0.50/M cached input, $30/M output",
6141
+ estimatedUsage: `Official Pro 20x range: 300\u20131,800 local messages per 5 hours; quota weight 5x Luna. ${CODEX_COMMUNITY_WEEKLY}`
6130
6142
  },
6131
6143
  {
6132
6144
  model: "gpt-5.6-terra",
6133
6145
  agent: "codex",
6134
- description: "Balanced agentic coding model for everyday work."
6146
+ description: "High-capability Codex route for complex coding and reasoning.",
6147
+ intelligenceTier: "opus",
6148
+ routingSummary: "complex coding and reasoning; 2.5x Codex quota cost",
6149
+ accessCost: CODEX_PRO_20X_COST,
6150
+ marginalTokenCost: "Codex credits: $2.50/M uncached input, $0.25/M cached input, $15/M output",
6151
+ estimatedUsage: `Official Pro 20x range: 400\u20132,200 local messages per 5 hours; quota weight 2.5x Luna. ${CODEX_COMMUNITY_WEEKLY}`
6135
6152
  },
6136
6153
  {
6137
6154
  model: "gpt-5.6-luna",
6138
6155
  agent: "codex",
6139
- description: "Fast and affordable agentic coding model."
6140
- },
6141
- {
6142
- model: "gpt-5.5",
6143
- agent: "codex",
6144
- description: "Frontier model for complex coding, research, and real-world work."
6156
+ description: "Default Codex route with strong everyday coding intelligence.",
6157
+ intelligenceTier: "sonnet",
6158
+ routingSummary: "everyday coding default; lowest Codex quota cost (1x)",
6159
+ accessCost: CODEX_PRO_20X_COST,
6160
+ marginalTokenCost: "Codex credits: $1/M uncached input, $0.10/M cached input, $6/M output",
6161
+ estimatedUsage: `Official Pro 20x range: 1,000\u20135,600 local messages per 5 hours; lowest Codex quota weight (1x). ${CODEX_COMMUNITY_WEEKLY}`
6145
6162
  },
6146
6163
  {
6147
6164
  model: "fable",
6148
6165
  agent: "claude",
6149
- description: "Fable 5 \xB7 Most capable for your hardest and longest-running tasks \xB7 $10/$50 per Mtok"
6166
+ description: "Highest-capability Claude route for the hardest and longest-running tasks.",
6167
+ intelligenceTier: "fable",
6168
+ routingSummary: "hardest long-running work; highest Claude cost; explicit request only",
6169
+ accessCost: CLAUDE_MAX_20X_COST,
6170
+ marginalTokenCost: "Claude API/extra usage: $10/M input, $12.50/M cache write, $1/M cache read, $50/M output",
6171
+ estimatedUsage: `${CLAUDE_COMMUNITY_SESSION}; Fable drains weighted quota fastest, so use only on explicit user request. No stable per-model token cap is published.`
6150
6172
  },
6151
6173
  {
6152
6174
  model: "opus",
6153
6175
  agent: "claude",
6154
- description: "Opus 4.8 with 1M context \xB7 Best for everyday, complex tasks \xB7 $5/$25 per Mtok"
6176
+ description: "High-capability Claude route for complex reasoning and tool-heavy work.",
6177
+ intelligenceTier: "opus",
6178
+ routingSummary: "complex reasoning and tool-heavy work; about 2.5x Sonnet marginal cost",
6179
+ accessCost: CLAUDE_MAX_20X_COST,
6180
+ marginalTokenCost: "Claude API/extra usage: $5/M input, $6.25/M cache write, $0.50/M cache read, $25/M output",
6181
+ estimatedUsage: `${CLAUDE_COMMUNITY_SESSION}; Opus uses the shared all-model weekly pool more quickly than Sonnet. No stable per-model token cap is published.`
6155
6182
  },
6156
6183
  {
6157
6184
  model: "sonnet",
6158
6185
  agent: "claude",
6159
- description: "Sonnet 5 \xB7 Efficient for routine tasks \xB7 $2/$10 per Mtok \xB7 promo through Aug 31"
6186
+ description: "Default Claude route for capable, efficient everyday work.",
6187
+ intelligenceTier: "sonnet",
6188
+ routingSummary: "capable everyday default; lowest Claude model cost",
6189
+ accessCost: CLAUDE_MAX_20X_COST,
6190
+ 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",
6191
+ 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.`
6160
6192
  },
6161
6193
  {
6162
6194
  model: "deepseek-pro",
6163
6195
  agent: "maestro",
6164
- description: "Sonnet-level performance at lower cost \xB7 Best for everyday tasks over coding"
6165
- },
6166
- {
6167
- model: "deepseek-flash",
6168
- agent: "maestro",
6169
- description: "DeepSeek V4 Flash \xB7 Fast and low-cost for lightweight everyday tasks"
6196
+ description: "API-priced Sonnet-level route for cost-efficient everyday work.",
6197
+ intelligenceTier: "sonnet",
6198
+ routingSummary: "cost-efficient everyday work; pay-per-token and cheapest route",
6199
+ accessCost: "DeepSeek V4 Pro pay-as-you-go API; no monthly subscription required",
6200
+ marginalTokenCost: "DeepSeek API: $0.435/M uncached input, $0.003625/M cached input, $0.87/M output",
6201
+ estimatedUsage: "No subscription token cap; pay per token. Official account concurrency limit is 500 requests."
6170
6202
  }
6171
6203
  ];
6172
6204
  FALLBACK_ORDER = {
@@ -7120,6 +7152,7 @@ async function spawnPlaywright(instanceKey, userId, reservedPort, browserBin = P
7120
7152
  String(port),
7121
7153
  "--host",
7122
7154
  "127.0.0.1",
7155
+ "--headed",
7123
7156
  "--user-data-dir",
7124
7157
  userDataDir
7125
7158
  ];
@@ -8446,7 +8479,7 @@ function createSelfConfigToolDefinitions(ctx) {
8446
8479
  return [
8447
8480
  {
8448
8481
  name: "set_model",
8449
- description: "Set the model for THIS topic. Persists and applies from the NEXT turn. Claude: 'sonnet' / 'opus' / 'fable'. Codex: 'gpt-5.6-luna' (default) / 'gpt-5.6-terra' / 'gpt-5.6-sol'. Maestro: 'deepseek-pro' / 'deepseek-flash' / 'deepseek'. The model must match the topic's current agent's accepted list. Switch agent first via set_agent only when crossing the claude/codex/maestro runtime boundary. Fails if the user locked this setting. NEVER use 'fable' unless the user explicitly requests it.",
8482
+ description: "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.",
8450
8483
  schema: { model: z2.string().describe("Model id valid for the topic's current agent.") },
8451
8484
  async handler({ model }) {
8452
8485
  return mcpResult(setSelfConfigModel(getCtx(), model));
@@ -8843,7 +8876,7 @@ function loadAgentPrompt(filename) {
8843
8876
  prompt: match[2].trim()
8844
8877
  };
8845
8878
  }
8846
- function buildRuntimeToolSection(agentKind, canSpawnSubagents = false, visualTools = false, fileDeliveryTools = false) {
8879
+ function buildRuntimeToolSection(agentKind, canSpawnSubagents = false, visualTools = false, fileDeliveryTools = false, currentModel, currentEffort) {
8847
8880
  const runtimeNamespace = "mcp__runtime";
8848
8881
  const taskNamespace = "mcp__task";
8849
8882
  const visualToolLine = agentKind === "codex" ? `To display charts, tables, or interactive HTML results to the user, call the \`show_html\` function in the \`${runtimeNamespace}\` namespace with { html: "<complete HTML string>", title?: "optional title" }.` : `To display charts, tables, or interactive HTML results to the user, call the MCP tool "${runtimeNamespace}__show_html" with { html: "<complete HTML string>", title?: "optional title" }.`;
@@ -8901,20 +8934,26 @@ function buildRuntimeToolSection(agentKind, canSpawnSubagents = false, visualToo
8901
8934
  "Do not use session communication to make another topic perform destructive changes without the user's clear intent.",
8902
8935
  ...spawnSubagentSection
8903
8936
  ];
8937
+ const modelCatalog = SELECTABLE_MODELS.map((candidate) => `- ${formatSelectableModel(candidate)}`);
8904
8938
  const topicConfig = [
8905
8939
  "",
8906
8940
  "## Topic Configuration (model / agent / effort)",
8941
+ `Current execution: agent=\`${agentKind}\`, model=\`${currentModel ?? "unknown (call get_model)"}\`, effort=\`${currentEffort ?? "provider default/off"}\`.`,
8907
8942
  "The user's configured agent/model/effort is intentional. Preserve it by default.",
8908
8943
  `When the user explicitly asks to change the model, agent backend, or reasoning effort for THIS topic, call "${runtimeNamespace}__set_model", "${runtimeNamespace}__set_agent", or "${runtimeNamespace}__set_effort". The change applies from your NEXT turn. After calling, briefly confirm and the system will continue with the new setting.`,
8909
8944
  "`set_effort` is available but discouraged; use it only when the user explicitly requests an effort change.",
8910
- "`set_model` may be called autonomously only when the current model is clearly below the task's required capability, such as complex algorithm design, proof-level math, or broad multi-file refactoring. In that case, move up one step within the same agent and end the turn. Do not use vague task complexity as a trigger.",
8945
+ "`set_model` may be called autonomously only when the current model is clearly below the task's required capability, such as complex algorithm design, proof-level math, or broad multi-file refactoring. Choose the best-fit model directly from the same-agent catalog; model selection is not a mandatory one-step ladder. End the turn after changing it. Do not use vague task complexity as a trigger.",
8911
8946
  "`set_agent` autonomous calls are forbidden. Only switch agent when the user explicitly asks to switch runtime, e.g. \u201Ccodex\uB85C \uAC00\u201D, \u201Cclaude\uB85C \uBC14\uAFD4\u201D.",
8912
8947
  "Never use `fable` unless the user explicitly requests it; it is expensive.",
8913
8948
  "",
8914
- "Accepted values:",
8915
- "- claude models: `sonnet` (default), `opus`, `fable`; efforts: `low`, `medium`, `high`, `xhigh`, `max`.",
8916
- "- codex models: `gpt-5.6-luna` (default), `gpt-5.6-terra`, `gpt-5.6-sol`; efforts: `low`, `medium`, `high`, `xhigh`, `max`.",
8917
- "- maestro models: `deepseek-pro` (default), `deepseek-flash`, `deepseek`; efforts: `low`, `medium`, `high`, `xhigh`, `max`.",
8949
+ "Model catalog (capability/cost routing guidance):",
8950
+ MODEL_COST_ROUTING_SUMMARY,
8951
+ ...modelCatalog,
8952
+ "",
8953
+ "Accepted effort values:",
8954
+ "- claude: `low`, `medium`, `high`, `xhigh`, `max`.",
8955
+ "- codex: `low`, `medium`, `high`, `xhigh`, `max`.",
8956
+ "- maestro: `low`, `medium`, `high`, `xhigh`, `max`.",
8918
8957
  "Agent guidance when the user explicitly asks to switch: `codex` for deepest reasoning and complex code/math; `claude` for tool-heavy MCP/file automation; `maestro` for inexpensive fast drafts and lighter experiments."
8919
8958
  ];
8920
8959
  if (agentKind !== "claude") {
@@ -8937,7 +8976,7 @@ function buildTopicSystemPrompt(opts) {
8937
8976
  TOPIC_TITLE: opts.topicTitle,
8938
8977
  WORKSPACE_CWD: opts.workspaceCwd,
8939
8978
  UPLOADS_DIR: uploadsDir
8940
- }) + buildRuntimeToolSection(opts.agentKind, opts.canSpawnSubagents, opts.visualTools, opts.fileDeliveryTools);
8979
+ }) + buildRuntimeToolSection(opts.agentKind, opts.canSpawnSubagents, opts.visualTools, opts.fileDeliveryTools, opts.currentModel, opts.currentEffort);
8941
8980
  if (opts.description?.trim()) {
8942
8981
  prompt += `
8943
8982
 
@@ -8953,7 +8992,7 @@ function buildChannelSystemPrompt(opts) {
8953
8992
  TOPIC_TITLE: opts.topicTitle,
8954
8993
  WORKSPACE_CWD: opts.workspaceCwd,
8955
8994
  UPLOADS_DIR: uploadsDir
8956
- }) + buildRuntimeToolSection(opts.agentKind, false, opts.visualTools, opts.fileDeliveryTools);
8995
+ }) + buildRuntimeToolSection(opts.agentKind, false, opts.visualTools, opts.fileDeliveryTools, opts.currentModel, opts.currentEffort);
8957
8996
  }
8958
8997
  function buildManagerSystemPrompt(opts) {
8959
8998
  return `${buildTopicSystemPrompt(opts)}
@@ -9003,6 +9042,7 @@ User-uploaded files for this Channel are copied under "{{UPLOADS_DIR}}" as attac
9003
9042
  This is the shared "General" hub of the user's workspace.
9004
9043
  Act as the workspace manager: orient the user across topics, summarize what is going on, and route focused work to the right room.`, _topicSystemPromptTemplate = null, _channelSystemPromptTemplate = null, _managerSystemPromptTemplate = null, _visualDesignGuide = null;
9005
9044
  var init_builders = __esm(() => {
9045
+ init_model_catalog();
9006
9046
  init_config();
9007
9047
  init_logger();
9008
9048
  PROMPTS_DIR = resolve6(PROJECT_ROOT, "src/prompts");
@@ -9121,10 +9161,6 @@ function getArchiverDef() {
9121
9161
  }
9122
9162
  function runArchiverTurn(params) {
9123
9163
  const { userId, topicId, topicTitle, archivePath, messageCount, mode = "deleted-topic" } = params;
9124
- if (mode !== "active-topic" && messageCount < MIN_ARCHIVE_MESSAGES) {
9125
- logger.info({ userId, topicTitle, messageCount, min: MIN_ARCHIVE_MESSAGES }, "archiver: skipped \u2014 too few messages to distil");
9126
- return false;
9127
- }
9128
9164
  let archiverDef;
9129
9165
  try {
9130
9166
  archiverDef = getArchiverDef();
@@ -9332,7 +9368,7 @@ ${rolled.join(`
9332
9368
  logger.warn({ err: err2, topicTitle }, "archiver: failed to post #General notification");
9333
9369
  }
9334
9370
  }
9335
- var MAX_BRIEF_ENTRIES = 8, MIN_ARCHIVE_MESSAGES = 4, MAX_SESSION_STEPS = 20, activeArchiverSessions, _archiverDef = null;
9371
+ var MAX_BRIEF_ENTRIES = 8, MAX_SESSION_STEPS = 20, activeArchiverSessions, _archiverDef = null;
9336
9372
  var init_archiver = __esm(async () => {
9337
9373
  await init_agents();
9338
9374
  await init_bus();
@@ -9347,6 +9383,26 @@ var init_archiver = __esm(async () => {
9347
9383
  activeArchiverSessions = new Map;
9348
9384
  });
9349
9385
 
9386
+ // ../../packages/core/src/agents/memory-archive-policy.ts
9387
+ function countMemoryArchiveExchanges(rows) {
9388
+ let waitingForAssistant = false;
9389
+ let completed = 0;
9390
+ for (const row of rows) {
9391
+ const assistant = row.author_id === "ai" || Boolean(row.agent_type);
9392
+ const conversational = row.kind == null || row.kind === "message";
9393
+ if (!assistant && row.author_id !== "system" && conversational) {
9394
+ waitingForAssistant = true;
9395
+ continue;
9396
+ }
9397
+ if (assistant && conversational && waitingForAssistant) {
9398
+ completed++;
9399
+ waitingForAssistant = false;
9400
+ }
9401
+ }
9402
+ return completed;
9403
+ }
9404
+ var MIN_MEMORY_ARCHIVE_EXCHANGES = 6;
9405
+
9350
9406
  // ../../packages/core/src/storage/topic-transcript.ts
9351
9407
  function parseJsonField(value) {
9352
9408
  if (!value)
@@ -9434,8 +9490,9 @@ function archiveTopicMessages(topicId, topicTitle, options = {}) {
9434
9490
  counter++;
9435
9491
  }
9436
9492
  }
9437
- logger.info({ topicId, topicTitle, archive: path, messageCount: rows.length, lastRowid }, "archiveTopicMessages: archived topic messages");
9438
- return { path, messageCount: rows.length, lastRowid };
9493
+ const exchangeCount = countMemoryArchiveExchanges(rows);
9494
+ logger.info({ topicId, topicTitle, archive: path, messageCount: rows.length, exchangeCount, lastRowid }, "archiveTopicMessages: archived topic messages");
9495
+ return { path, messageCount: rows.length, exchangeCount, lastRowid };
9439
9496
  }
9440
9497
  var init_topic_archive = __esm(async () => {
9441
9498
  init_logger();
@@ -9653,6 +9710,7 @@ function archiveActiveTopicForMemory(topicId, userId, options) {
9653
9710
  if (!options.allowMentionOnly && topic.aiMode === "mention")
9654
9711
  return "mention-only-channel";
9655
9712
  let skipped = "empty";
9713
+ let skipMemoryTurn = false;
9656
9714
  const claim = claimTopicArchiveJob(topicId, (afterRowid) => {
9657
9715
  const pending = getMessagesForTopicAfterRowid(topicId, afterRowid);
9658
9716
  const minMessages = options.minMessages;
@@ -9661,6 +9719,8 @@ function archiveActiveTopicForMemory(topicId, userId, options) {
9661
9719
  logger.debug({ topicId, topicTitle: topic.title, pending: pending.length, minMessages }, "topic-memory-archiver: skipped below threshold");
9662
9720
  return null;
9663
9721
  }
9722
+ const exchangeCount = countMemoryArchiveExchanges(pending);
9723
+ skipMemoryTurn = options.minExchanges !== undefined && exchangeCount < options.minExchanges;
9664
9724
  const archived = (options.archiveMessages ?? archiveTopicMessages)(topicId, topic.title, {
9665
9725
  afterRowid,
9666
9726
  reason: options.reason
@@ -9678,6 +9738,18 @@ function archiveActiveTopicForMemory(topicId, userId, options) {
9678
9738
  if (claim.kind === "busy")
9679
9739
  return "busy";
9680
9740
  const { job } = claim;
9741
+ if (skipMemoryTurn) {
9742
+ settleTopicArchiveJob(topicId, job.archivePath, true);
9743
+ logger.info({
9744
+ topicId,
9745
+ topicTitle: topic.title,
9746
+ messageCount: job.messageCount,
9747
+ minExchanges: options.minExchanges,
9748
+ archive: job.archivePath,
9749
+ reason: options.reason
9750
+ }, "topic-memory-archiver: raw snapshot preserved below exchange threshold");
9751
+ return "below-threshold";
9752
+ }
9681
9753
  const memoryTopic = getTopicMemoryOrigin(topicId) ?? topic;
9682
9754
  const launched = (options.launchArchiver ?? runArchiverTurn)({
9683
9755
  userId,
@@ -11618,6 +11690,7 @@ __export(exports_turn_runner, {
11618
11690
  isPathInside: () => isPathInside,
11619
11691
  isLikelyCurrentChannelTrigger: () => isLikelyCurrentChannelTrigger,
11620
11692
  ingestAttachment: () => ingestAttachment,
11693
+ formatSelectableModel: () => formatSelectableModel,
11621
11694
  formatChannelTranscriptLine: () => formatChannelTranscriptLine,
11622
11695
  escapeRegExp: () => escapeRegExp2,
11623
11696
  escapeHtml: () => escapeHtml2,
@@ -11635,6 +11708,8 @@ __export(exports_turn_runner, {
11635
11708
  SESSION_EXPIRED_MSG: () => SESSION_EXPIRED_MSG,
11636
11709
  SELECTABLE_MODELS: () => SELECTABLE_MODELS,
11637
11710
  MODEL_OWNER: () => MODEL_OWNER,
11711
+ MODEL_COST_ROUTING_SUMMARY: () => MODEL_COST_ROUTING_SUMMARY,
11712
+ MODEL_COST_RESEARCHED_AT: () => MODEL_COST_RESEARCHED_AT,
11638
11713
  FALLBACK_ORDER: () => FALLBACK_ORDER,
11639
11714
  AGENT_DISPLAY_NAME: () => AGENT_DISPLAY_NAME
11640
11715
  });
@@ -12842,6 +12917,8 @@ function startAiTurn(params) {
12842
12917
  topicTitle: topic.title,
12843
12918
  workspaceCwd,
12844
12919
  agentKind,
12920
+ currentModel: resolvedModel,
12921
+ currentEffort: resolvedEffort,
12845
12922
  description: topic.description,
12846
12923
  canSpawnSubagents: peerBridge?.canSpawnSubagents ?? (topicRecord?.kind === "agent" && !topicRecord.isSubagent),
12847
12924
  visualTools,
@@ -13520,7 +13597,7 @@ async function deleteTopicCascadeImpl(topic, userId, options, deletingAncestorId
13520
13597
  logger.warn({ err: err2, topicId }, "deleteTopicCascade: archive failed but force=true - continuing with hard delete");
13521
13598
  }
13522
13599
  }
13523
- if (archived) {
13600
+ if (archived && archived.exchangeCount >= MIN_MEMORY_ARCHIVE_EXCHANGES) {
13524
13601
  try {
13525
13602
  const memoryTopic = getTopicMemoryOrigin(topicId) ?? topic;
13526
13603
  runArchiverTurn({
@@ -14060,7 +14137,7 @@ function createSpawnSubagentToolDefinition(ctx) {
14060
14137
  task: z3.string().describe("Self-contained task brief for the subagent. It sees nothing else \u2014 include all context."),
14061
14138
  name: z3.string().optional().describe("Short name for the subagent room. Auto-generated when omitted."),
14062
14139
  agent: z3.enum(["claude", "codex", "maestro"]).optional().describe("Agent backend override. Defaults to this room's agent."),
14063
- model: z3.string().optional().describe("Model override. Defaults to the agent's default.")
14140
+ 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.`)
14064
14141
  },
14065
14142
  async handler(input) {
14066
14143
  return spawnSubagent(ctx, input);
@@ -14568,6 +14645,7 @@ async function restartTopicSession(topicId, userId, reason = "topic-session-rest
14568
14645
  (options.archiveMemory ?? archiveActiveTopicForMemory)(topicId, userId, {
14569
14646
  reason: "reset",
14570
14647
  minMessages: 1,
14648
+ minExchanges: MIN_MEMORY_ARCHIVE_EXCHANGES,
14571
14649
  allowMentionOnly: true,
14572
14650
  skipBusyCheck: true
14573
14651
  });
@@ -32811,4 +32889,4 @@ switch (command) {
32811
32889
  }
32812
32890
  }
32813
32891
 
32814
- //# debugId=7766C8A27B2E5D3C64756E2164756E21
32892
+ //# debugId=C8F4C2CD3CFA25F564756E2164756E21