negotium 0.23.1 → 0.23.2

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 (36) hide show
  1. package/dist/agent-helpers.js +136 -30
  2. package/dist/agent-helpers.js.map +12 -11
  3. package/dist/{chunk-pr8p6pap.js → chunk-6cnpkzkb.js} +7 -2
  4. package/dist/{chunk-pr8p6pap.js.map → chunk-6cnpkzkb.js.map} +3 -3
  5. package/dist/{chunk-z4r7tt2p.js → chunk-cez5m5hm.js} +1 -1
  6. package/dist/{chunk-w6xavjz2.js → chunk-s34sv3d2.js} +20 -8
  7. package/dist/chunk-s34sv3d2.js.map +12 -0
  8. package/dist/hosted-agent.js +97 -13
  9. package/dist/hosted-agent.js.map +8 -7
  10. package/dist/main.js +162 -39
  11. package/dist/main.js.map +14 -13
  12. package/dist/mcp-factories.js +136 -30
  13. package/dist/mcp-factories.js.map +12 -11
  14. package/dist/prompts.js +9 -10
  15. package/dist/prompts.js.map +3 -3
  16. package/dist/registry.js +5 -5
  17. package/dist/registry.js.map +3 -3
  18. package/dist/rollout.js +2 -2
  19. package/dist/runtime/src/agents/codex-native-multi-agent.ts +28 -6
  20. package/dist/runtime/src/agents/codex-provider.ts +14 -1
  21. package/dist/runtime/src/agents/codex-registry.ts +6 -7
  22. package/dist/runtime/src/agents/codex-tier-resolver.ts +74 -0
  23. package/dist/runtime/src/agents/explicit-agent-switch.ts +6 -1
  24. package/dist/runtime/src/agents/model-catalog.ts +34 -23
  25. package/dist/runtime/src/runtime/turn-event-stream.ts +13 -1
  26. package/dist/runtime/src/storage/token-stats.ts +2 -1
  27. package/dist/runtime/src/version.ts +1 -1
  28. package/dist/storage.js +2 -1
  29. package/dist/storage.js.map +3 -3
  30. package/dist/types/packages/core/src/agents/codex-native-multi-agent.d.ts +9 -0
  31. package/dist/types/packages/core/src/agents/codex-tier-resolver.d.ts +10 -0
  32. package/dist/types/packages/core/src/agents/model-catalog.d.ts +2 -2
  33. package/dist/types/packages/core/src/version.d.ts +1 -1
  34. package/package.json +2 -2
  35. package/dist/chunk-w6xavjz2.js.map +0 -12
  36. /package/dist/{chunk-z4r7tt2p.js.map → chunk-cez5m5hm.js.map} +0 -0
@@ -10,16 +10,15 @@ const VALID_EFFORTS = new Set<EffortLevel>(CODEX_EFFORT_VALUES);
10
10
 
11
11
  // Codex CLI's own bundled default became gpt-6-astra as of v0.153.4
12
12
  // (2026-09-05); before that it was gpt-5.6-sol (`codex exec` 2026-07-10 →
13
- // "model: gpt-5.6-sol"). We deliberately default to gpt-6-luna — the
13
+ // "model: gpt-5.6-sol"). We deliberately default to the luna tier — the
14
14
  // cheapest/fastest GPT-6 tier — for a general always-on assistant where most
15
15
  // queries are light. Heavier work escalates to terra/sol/astra via set_model. The
16
- // GPT-5.6 Sol/Luna ids are retired aliases of gpt-6-sol/gpt-6-luna (see
17
- // canonicalModelId), so a stored gpt-5.6-luna still runs as gpt-6-luna. This
18
- // value is passed explicitly to the SDK (see event-processor resolveDefaultModel),
19
- // so the footer and the actual model stay in sync.
16
+ // GPT-5.6 Sol/Luna/Terra ids are retired aliases of their GPT-6 successors (see
17
+ // canonicalModelId), so a stored gpt-5.6-luna normalizes to luna. The provider
18
+ // resolves the tier immediately before passing it to the SDK.
20
19
  export const codexRegistry: AgentRegistry = {
21
20
  kind: "codex",
22
- defaultModel: "gpt-6-luna",
21
+ defaultModel: "luna",
23
22
  // defaultEffort intentionally omitted — Codex SDK treats absence as
24
23
  // "reasoning off". Setting "high"/etc. would silently flip on reasoning.
25
24
 
@@ -29,7 +28,7 @@ export const codexRegistry: AgentRegistry = {
29
28
 
30
29
  validateModel(s) {
31
30
  // Codex doesn't publish a closed model list and OpenAI ships new IDs
32
- // (gpt-6-astra, gpt-6-sol, gpt-5.6-terra, gpt-6-luna, o3, ...) frequently. Best-effort: accept any
31
+ // (gpt-6-astra, gpt-6-sol, gpt-6.1-terra, gpt-6-luna, o3, ...) frequently. Best-effort: accept any
33
32
  // non-empty string. Bad IDs surface at SDK call time with a clear error.
34
33
  return typeof s === "string" && s.length > 0;
35
34
  },
@@ -0,0 +1,74 @@
1
+ import {
2
+ bundledCodexModelCachePath,
3
+ readCompatibleCodexModelCache,
4
+ } from "#agents/codex-native-multi-agent";
5
+ import { hostedCodexAuthFilePath } from "#agents/execution-host";
6
+ import { logger } from "#platform/logger";
7
+
8
+ export const CODEX_TIER_FALLBACKS = {
9
+ astra: "gpt-6-astra",
10
+ sol: "gpt-6-sol",
11
+ luna: "gpt-6-luna",
12
+ terra: "gpt-6.1-terra",
13
+ } as const;
14
+
15
+ export type CodexTier = keyof typeof CODEX_TIER_FALLBACKS;
16
+
17
+ const warnedCachePaths = new Set<string>();
18
+ const warnedUnpricedSlugs = new Set<string>();
19
+ const pricedSlugs = new Set([
20
+ "gpt-6-astra",
21
+ "gpt-6-sol",
22
+ "gpt-6-luna",
23
+ "gpt-6.1-terra",
24
+ "gpt-5.6-sol",
25
+ "gpt-5.6-luna",
26
+ "gpt-5.6-terra",
27
+ ]);
28
+
29
+ /** Choose Codex's highest-priority visible model for each supported tier. */
30
+ export function codexTiersFromCache(cache: unknown): Partial<Record<CodexTier, string>> {
31
+ const result: Partial<Record<CodexTier, string>> = {};
32
+ const priorities: Partial<Record<CodexTier, number>> = {};
33
+ if (!cache || typeof cache !== "object" || !("models" in cache)) return result;
34
+ const models = (cache as { models?: unknown }).models;
35
+ if (!Array.isArray(models)) return result;
36
+
37
+ for (const model of models) {
38
+ if (!model || typeof model !== "object") continue;
39
+ const { slug, visibility, priority } = model as Record<string, unknown>;
40
+ if (typeof slug !== "string" || visibility !== "list") continue;
41
+ if (typeof priority !== "number" || !Number.isFinite(priority)) continue;
42
+ const suffix = slug.slice(slug.lastIndexOf("-") + 1);
43
+ if (!Object.hasOwn(CODEX_TIER_FALLBACKS, suffix)) continue;
44
+ const tier = suffix as CodexTier;
45
+ if (priorities[tier] === undefined || priority < priorities[tier]) {
46
+ priorities[tier] = priority;
47
+ result[tier] = slug;
48
+ }
49
+ }
50
+ return result;
51
+ }
52
+
53
+ export function resolveCodexTierModel(
54
+ tier: CodexTier,
55
+ cachePath = process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE ??
56
+ bundledCodexModelCachePath(hostedCodexAuthFilePath()),
57
+ ): string {
58
+ try {
59
+ const selected = codexTiersFromCache(readCompatibleCodexModelCache(cachePath).parsed)[tier];
60
+ if (selected) {
61
+ if (!pricedSlugs.has(selected) && !warnedUnpricedSlugs.has(selected)) {
62
+ warnedUnpricedSlugs.add(selected);
63
+ logger.warn({ tier, model: selected }, "Codex tier model has no token-stats price");
64
+ }
65
+ return selected;
66
+ }
67
+ } catch (error) {
68
+ if (!warnedCachePaths.has(cachePath)) {
69
+ warnedCachePaths.add(cachePath);
70
+ logger.warn({ err: error, cachePath }, "Codex tier cache unavailable; using fallback ids");
71
+ }
72
+ }
73
+ return CODEX_TIER_FALLBACKS[tier];
74
+ }
@@ -21,9 +21,14 @@ function agentAliases(agent: AgentKind): string[] {
21
21
  return [
22
22
  "codex",
23
23
  "코덱스",
24
+ "astra",
25
+ "sol",
26
+ "luna",
27
+ "terra",
24
28
  "gpt-6-luna",
25
29
  "gpt-6-sol",
26
30
  "gpt-5.6-luna",
31
+ "gpt-6.1-terra",
27
32
  "gpt-5.6-terra",
28
33
  "gpt-5.6-sol",
29
34
  "gpt-6-astra",
@@ -55,7 +60,7 @@ export function hasExplicitAgentSwitchRequest(
55
60
  ): boolean {
56
61
  if (!prompt?.trim()) return false;
57
62
  const text = prompt.toLowerCase().replace(/\s+/g, " ").trim();
58
- const target = `(?:${agentAliases(agent).map(escapeRegExp).join("|")})`;
63
+ const target = `(?<![A-Za-z0-9_-])(?:${agentAliases(agent).map(escapeRegExp).join("|")})(?![A-Za-z0-9_-])`;
59
64
  const switchVerb =
60
65
  "(?:바꿔|바꿔줘|변경|변경해|전환|전환해|설정|설정해|써줘|사용|가|switch|change|set|use)";
61
66
  const switchSubject = "(?:agent|runtime|model|에이전트|런타임|모델)";
@@ -21,9 +21,14 @@ export const MODEL_OWNER: Record<string, AgentKind> = {
21
21
  opus: "claude",
22
22
  haiku: "claude",
23
23
  fable: "claude",
24
+ astra: "codex",
25
+ sol: "codex",
26
+ luna: "codex",
27
+ terra: "codex",
24
28
  "gpt-6-astra": "codex",
25
29
  "gpt-6-sol": "codex",
26
30
  "gpt-6-luna": "codex",
31
+ "gpt-6.1-terra": "codex",
27
32
  "gpt-5.6-luna": "codex",
28
33
  "gpt-5.6-terra": "codex",
29
34
  "gpt-5.6-sol": "codex",
@@ -100,7 +105,7 @@ const CLAUDE_COMMUNITY_SESSION =
100
105
  */
101
106
  export const SELECTABLE_MODELS: readonly SelectableModel[] = [
102
107
  {
103
- model: "gpt-6-astra",
108
+ model: "astra",
104
109
  agent: "codex",
105
110
  description: "Newest OpenAI flagship; Codex's highest-capability route.",
106
111
  intelligenceTier: "fable",
@@ -111,7 +116,7 @@ export const SELECTABLE_MODELS: readonly SelectableModel[] = [
111
116
  estimatedUsage: `Released 2026-09-03; became Codex CLI's bundled default in v0.153.4 (2026-09-05). Local message-count/quota-weight ranges not yet published as of the last catalog check — expect a quota weight above gpt-6-sol given the higher per-token cost. ${CODEX_COMMUNITY_WEEKLY}`,
112
117
  },
113
118
  {
114
- model: "gpt-6-sol",
119
+ model: "sol",
115
120
  agent: "codex",
116
121
  description: "GPT-6 generation Sol route (2026-09-22) for demanding coding and agentic work.",
117
122
  intelligenceTier: "opus",
@@ -122,7 +127,7 @@ export const SELECTABLE_MODELS: readonly SelectableModel[] = [
122
127
  estimatedUsage: `Released 2026-09-22; local message-count/quota-weight ranges not yet published. ${CODEX_COMMUNITY_WEEKLY}`,
123
128
  },
124
129
  {
125
- model: "gpt-6-luna",
130
+ model: "luna",
126
131
  agent: "codex",
127
132
  description: "Default Codex route (GPT-6 Luna, 2026-09-22): fast, high-volume, lowest cost.",
128
133
  intelligenceTier: "sonnet",
@@ -133,14 +138,16 @@ export const SELECTABLE_MODELS: readonly SelectableModel[] = [
133
138
  estimatedUsage: `Released 2026-09-22; local message-count/quota-weight ranges not yet published. ${CODEX_COMMUNITY_WEEKLY}`,
134
139
  },
135
140
  {
136
- model: "gpt-5.6-terra",
141
+ model: "terra",
137
142
  agent: "codex",
138
- description: "High-capability Codex route for complex coding and reasoning.",
143
+ description:
144
+ "GPT-6.1 Terra route (2026-10-26) for complex coding, visual reasoning, and computer use.",
139
145
  intelligenceTier: "opus",
140
- routingSummary: "complex coding and reasoning; 2.5x Codex quota cost",
146
+ routingSummary: "capable coding and computer use at a lower cost than GPT-5.6 Terra",
141
147
  accessCost: CODEX_PRO_20X_COST,
142
- marginalTokenCost: "Codex credits: $2.50/M uncached input, $0.25/M cached input, $15/M output",
143
- estimatedUsage: `Official Pro 20x range: 400–2,200 local messages per 5 hours; quota weight 2.5x the retired GPT-5.6 Luna. ${CODEX_COMMUNITY_WEEKLY}`,
148
+ marginalTokenCost:
149
+ "Codex credits (OpenAI API rate): $1/M uncached input, $0.05/M cached input, $5/M output",
150
+ estimatedUsage: `Released 2026-10-26; local message-count/quota-weight ranges not yet published. ${CODEX_COMMUNITY_WEEKLY}`,
144
151
  },
145
152
  {
146
153
  model: "fable",
@@ -249,12 +256,14 @@ export const SELECTABLE_MODELS: readonly SelectableModel[] = [
249
256
  ];
250
257
 
251
258
  const SELECTABLE_MODEL_ALIASES: Readonly<Record<string, string>> = {
252
- // GPT-6 Sol/Luna (2026-09-22) replace the GPT-5.6 tiers of the same name at half the price.
253
- // Only the GPT-6 route is selectable; the retired ids resolve to it, so stored topic configs,
254
- // cron rows and explicit requests keep working without a second, duplicate Sol/Luna. Terra has
255
- // no GPT-6 successor and stays as `gpt-5.6-terra`.
256
- "gpt-5.6-sol": "gpt-6-sol",
257
- "gpt-5.6-luna": "gpt-6-luna",
259
+ // Known concrete IDs normalize to stable tier names before persistence.
260
+ "gpt-6-astra": "astra",
261
+ "gpt-5.6-sol": "sol",
262
+ "gpt-6-sol": "sol",
263
+ "gpt-5.6-luna": "luna",
264
+ "gpt-6-luna": "luna",
265
+ "gpt-5.6-terra": "terra",
266
+ "gpt-6.1-terra": "terra",
258
267
  kimi: "kimi-k3",
259
268
  "kimi-pro": "kimi-k3",
260
269
  "kimi-code": "kimi-k2.7-code",
@@ -272,15 +281,17 @@ const SELECTABLE_MODEL_ALIASES: Readonly<Record<string, string>> = {
272
281
  // ownership check and, for Codex specifically (whose `validateModel` accepts
273
282
  // any non-empty string), reaches the provider unrejected despite belonging
274
283
  // to a different agent.
275
- const CANONICAL_MODEL_CASING: Readonly<Record<string, string>> = Object.fromEntries(
276
- SELECTABLE_MODELS.map((candidate) => [candidate.model.toLowerCase(), candidate.model]),
277
- );
284
+ function canonicalModelCasing(): Readonly<Record<string, string>> {
285
+ return Object.fromEntries(
286
+ SELECTABLE_MODELS.map((candidate) => [candidate.model.toLowerCase(), candidate.model]),
287
+ );
288
+ }
278
289
 
279
290
  /** Normalize supported user-facing aliases before validation or persistence. */
280
291
  export function canonicalModelId(value: string): string {
281
292
  const trimmed = value.trim();
282
293
  const lower = trimmed.toLowerCase();
283
- return SELECTABLE_MODEL_ALIASES[lower] ?? CANONICAL_MODEL_CASING[lower] ?? trimmed;
294
+ return SELECTABLE_MODEL_ALIASES[lower] ?? canonicalModelCasing()[lower] ?? trimmed;
284
295
  }
285
296
 
286
297
  export function formatSelectableModel(candidate: SelectableModel): string {
@@ -344,8 +355,8 @@ export function resolveModelForAgent(
344
355
  /**
345
356
  * Default model for unattended background workers (compaction, memory
346
357
  * archiving, and similar fixed-purpose programmatic turns) — Codex prefers
347
- * the stronger `gpt-5.6-terra` here over its cheap interactive default
348
- * (`gpt-6-luna`), since nothing is waiting on the response in real time.
358
+ * the stronger `terra` tier here over its cheap interactive default
359
+ * (`luna`), since nothing is waiting on the response in real time.
349
360
  * Claude and Maestro keep their normal registry defaults (`sonnet` /
350
361
  * `deepseek-pro`).
351
362
  */
@@ -353,7 +364,7 @@ export function resolveWorkerDefaultModel(
353
364
  agent: AgentKind,
354
365
  registry: { defaultModel: string },
355
366
  ): string {
356
- return agent === "codex" ? "gpt-5.6-terra" : registry.defaultModel;
367
+ return agent === "codex" ? "terra" : registry.defaultModel;
357
368
  }
358
369
 
359
370
  /**
@@ -409,14 +420,14 @@ export function resolveCompactionExecution(
409
420
  export const FALLBACK_ORDER: Record<AgentKind, { agent: AgentKind; model: string }[]> = {
410
421
  claude: [
411
422
  { agent: "maestro", model: "deepseek-pro" },
412
- { agent: "codex", model: "gpt-6-luna" },
423
+ { agent: "codex", model: "luna" },
413
424
  ],
414
425
  codex: [
415
426
  { agent: "maestro", model: "deepseek-pro" },
416
427
  { agent: "claude", model: "sonnet" },
417
428
  ],
418
429
  maestro: [
419
- { agent: "codex", model: "gpt-6-luna" },
430
+ { agent: "codex", model: "luna" },
420
431
  { agent: "claude", model: "sonnet" },
421
432
  ],
422
433
  };
@@ -1,6 +1,11 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { realpathSync, statSync } from "node:fs";
3
3
  import { isAbsolute, resolve } from "node:path";
4
+ import {
5
+ CODEX_TIER_FALLBACKS,
6
+ type CodexTier,
7
+ resolveCodexTierModel,
8
+ } from "#agents/codex-tier-resolver";
4
9
  import { estimateTextTokens } from "#agents/compaction-support";
5
10
  import { cleanupAgentFork } from "#agents/fork";
6
11
  import { scheduleIdleArchiveForTopic } from "#agents/idle-archiver";
@@ -180,12 +185,19 @@ export async function runTurnEventStream(
180
185
  const toolSummaryOptions = {
181
186
  cwd: control.injectParams?.cwd ?? workspaceCwdFor(topicId),
182
187
  };
188
+ // Usage is billed against the real model Codex actually ran (resolved from
189
+ // a bare tier name like "terra" the same way codexProvider resolves it for
190
+ // the API call itself), not the tier name stored on the topic/turn.
191
+ const pricedModel =
192
+ agentType === "codex" && Object.hasOwn(CODEX_TIER_FALLBACKS, model)
193
+ ? resolveCodexTierModel(model as CodexTier)
194
+ : model;
183
195
  const recordEventUsage = (usage: TokenUsage): void => {
184
196
  recordUsage(userId, topicTitle, usage, {
185
197
  topicId,
186
198
  ...(control.sessionId ? { providerSessionId: control.sessionId } : {}),
187
199
  agent: agentType,
188
- model,
200
+ model: pricedModel,
189
201
  });
190
202
  };
191
203
  const broadcastContextProgress = (force = false): void => {
@@ -124,7 +124,8 @@ const TOKEN_PRICES: Record<string, TokenPrices> = {
124
124
  "codex:gpt-6-astra": { input: 10, cacheWrite: 12.5, cacheRead: 1, output: 50 },
125
125
  "codex:gpt-6-sol": { input: 2, cacheRead: 0.2, output: 10 },
126
126
  "codex:gpt-6-luna": { input: 0.1, cacheRead: 0.01, output: 0.5 },
127
- // GPT-5.6 Sol/Luna are retired aliases of gpt-6-sol/gpt-6-luna, but historical usage rows still
127
+ "codex:gpt-6.1-terra": { input: 1, cacheRead: 0.05, output: 5 },
128
+ // GPT-5.6 Sol/Luna/Terra are retired aliases, but historical usage rows still
128
129
  // carry the old ids, so their prices stay for cost accounting.
129
130
  "codex:gpt-5.6-sol": { input: 5, cacheRead: 0.5, output: 30 },
130
131
  "codex:gpt-5.6-terra": { input: 2.5, cacheRead: 0.25, output: 15 },
@@ -1 +1 @@
1
- export const NEGOTIUM_VERSION = "0.23.1";
1
+ export const NEGOTIUM_VERSION = "0.23.2";
package/dist/storage.js CHANGED
@@ -4364,6 +4364,7 @@ var TOKEN_PRICES = {
4364
4364
  "codex:gpt-6-astra": { input: 10, cacheWrite: 12.5, cacheRead: 1, output: 50 },
4365
4365
  "codex:gpt-6-sol": { input: 2, cacheRead: 0.2, output: 10 },
4366
4366
  "codex:gpt-6-luna": { input: 0.1, cacheRead: 0.01, output: 0.5 },
4367
+ "codex:gpt-6.1-terra": { input: 1, cacheRead: 0.05, output: 5 },
4367
4368
  "codex:gpt-5.6-sol": { input: 5, cacheRead: 0.5, output: 30 },
4368
4369
  "codex:gpt-5.6-terra": { input: 2.5, cacheRead: 0.25, output: 15 },
4369
4370
  "codex:gpt-5.6-luna": { input: 1, cacheRead: 0.1, output: 6 },
@@ -5186,4 +5187,4 @@ export {
5186
5187
  ConversationLogDivergedError
5187
5188
  };
5188
5189
 
5189
- //# debugId=0E6C4F3F0C7EF59464756E2164756E21
5190
+ //# debugId=B9550B6DC8305D3264756E2164756E21