@zergai/ztc 0.4.0-beta.1 → 0.4.1-dev.36919234045
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.
- package/README.md +8 -1
- package/THIRD_PARTY_NOTICES.md +4 -0
- package/dist/App.js +2 -2
- package/dist/agent/agent.js +41 -5
- package/dist/agent/backends/anthropic.js +5 -10
- package/dist/agent/backends/gemini.js +2 -0
- package/dist/agent/backends/index.d.ts +1 -0
- package/dist/agent/backends/index.js +10 -1
- package/dist/agent/backends/openai.d.ts +0 -6
- package/dist/agent/backends/openai.js +5 -2
- package/dist/agent/backends/openai_compatible.d.ts +2 -0
- package/dist/agent/backends/openai_compatible.js +18 -8
- package/dist/agent/backends/provider_ids.d.ts +1 -0
- package/dist/agent/backends/provider_ids.js +4 -0
- package/dist/agent/backends/types.d.ts +1 -0
- package/dist/agent/backends/zrouter_stream.d.ts +5 -1
- package/dist/agent/backends/zrouter_stream.js +141 -37
- package/dist/agent/commands/compact.js +6 -5
- package/dist/agent/commands/config.js +4 -1
- package/dist/agent/commands/context.js +6 -1
- package/dist/agent/commands/effort.js +14 -6
- package/dist/agent/commands/model.js +12 -2
- package/dist/agent/commands/profile.js +21 -2
- package/dist/agent/commands/provider_selection.js +14 -1
- package/dist/agent/commands/settings.js +45 -7
- package/dist/agent/commands/types.d.ts +19 -0
- package/dist/agent/factory.js +14 -68
- package/dist/agent/subagent.d.ts +0 -10
- package/dist/agent/subagent.js +22 -0
- package/dist/agent/tool_response_store.d.ts +1 -1
- package/dist/agent/tool_response_store.js +4 -2
- package/dist/agent/tools/pane.d.ts +0 -7
- package/dist/agent/tools/pane.js +5 -4
- package/dist/app/command_context.js +33 -10
- package/dist/app/model_catalog_cache.d.ts +5 -0
- package/dist/app/model_catalog_cache.js +138 -0
- package/dist/app/restart.d.ts +1 -1
- package/dist/app/restart.js +16 -5
- package/dist/app/selection_completion.d.ts +17 -2
- package/dist/app/selection_completion.js +74 -6
- package/dist/app/session_manager.js +13 -8
- package/dist/cli_args.js +2 -16
- package/dist/cli_main.js +25 -6
- package/dist/cli_ratatui.js +70 -33
- package/dist/config/defaults.d.ts +2 -1
- package/dist/config/defaults.js +9 -2
- package/dist/config/resolved.d.ts +2 -2
- package/dist/config.d.ts +35 -3
- package/dist/config.js +227 -29
- package/dist/headless.js +2 -0
- package/dist/onboarding/first_run.js +5 -1
- package/dist/panes/model_catalog.d.ts +3 -3
- package/dist/panes/model_catalog.js +28 -4
- package/dist/panes/pane_registry.js +6 -2
- package/dist/panes/types.d.ts +2 -0
- package/dist/panes/ztc_launch.js +12 -0
- package/dist/session.d.ts +6 -2
- package/dist/session.js +65 -10
- package/dist/ui/views/completion_menu.js +10 -5
- package/dist/ui/views/status_bar.js +9 -5
- package/dist/ui/widgets/resume_panel.d.ts +2 -0
- package/dist/ui/widgets/resume_panel.js +17 -15
- package/dist/utils/compaction.d.ts +1 -1
- package/dist/utils/compaction.js +2 -2
- package/dist/utils/context.d.ts +4 -4
- package/dist/utils/context.js +14 -12
- package/dist/utils/deepseek.d.ts +2 -2
- package/dist/utils/deepseek.js +13 -5
- package/dist/utils/model_cache.d.ts +15 -0
- package/dist/utils/model_cache.js +143 -0
- package/dist/utils/models.d.ts +4 -2
- package/dist/utils/models.js +12 -16
- package/dist/utils/reasoning.d.ts +8 -1
- package/dist/utils/reasoning.js +31 -11
- package/dist/utils/router_catalog.d.ts +47 -0
- package/dist/utils/router_catalog.js +222 -0
- package/dist/zlm-embed.mjs +1180 -514
- package/node_modules/@zerg/model-contracts/LICENSE.md +375 -0
- package/node_modules/@zerg/model-contracts/README.md +78 -0
- package/node_modules/@zerg/model-contracts/index.d.ts +18 -0
- package/node_modules/@zerg/model-contracts/index.js +220 -0
- package/node_modules/@zerg/model-contracts/package.json +8 -0
- package/node_modules/glob/node_modules/brace-expansion/index.js +78 -22
- package/node_modules/glob/node_modules/brace-expansion/package.json +1 -1
- package/node_modules/readdir-glob/node_modules/brace-expansion/index.js +78 -22
- package/node_modules/readdir-glob/node_modules/brace-expansion/package.json +1 -1
- package/package.json +9 -6
- package/packages/model-contracts/LICENSE.md +375 -0
- package/packages/model-contracts/README.md +78 -0
- package/packages/model-contracts/index.d.ts +18 -0
- package/packages/model-contracts/index.js +220 -0
- package/packages/model-contracts/package.json +8 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { routerModelIds, routerModelsForProvider, resolveRouterModel } from '../utils/router_catalog.js';
|
|
2
|
+
import { reasoningCommandChoices, reasoningEnabled } from '../utils/reasoning.js';
|
|
1
3
|
import { MODEL_CATALOG } from '../panes/model_catalog.js';
|
|
2
4
|
import { canRouteThroughRouter, canonicalProvider, PROVIDER_CHOICES } from '../agent/commands/provider_choices.js';
|
|
3
5
|
import { resolveSlashUsage } from '../agent/commands/usage_spec.js';
|
|
@@ -42,6 +44,11 @@ function commandUsage(text, commands) {
|
|
|
42
44
|
function parseSelectionInput(text, commands) {
|
|
43
45
|
if (commands.length && /^\/[\w-]*$/.test(text))
|
|
44
46
|
return { kind: 'command', prefix: '/', query: text.slice(1) };
|
|
47
|
+
const reasoning = text.match(/^\/(effort|thinking)\s+([^\s]*)\s*$/i);
|
|
48
|
+
if (reasoning && commands.some(command => command.name === reasoning[1].toLowerCase())) {
|
|
49
|
+
const command = reasoning[1].toLowerCase();
|
|
50
|
+
return { kind: 'reasoning', command, prefix: `/${command} `, query: reasoning[2] };
|
|
51
|
+
}
|
|
45
52
|
const model = text.match(/^\/(model|models)\s+([^\n]*)$/i);
|
|
46
53
|
if (!model)
|
|
47
54
|
return commandUsage(text, commands);
|
|
@@ -98,6 +105,9 @@ function commandSelections(commands) {
|
|
|
98
105
|
/** Static preview for renderers without a completion controller. Explicit null hides it. */
|
|
99
106
|
export function commandCompletionView(text, commands) {
|
|
100
107
|
const parsed = parseSelectionInput(text, commands);
|
|
108
|
+
if (parsed?.kind === 'reasoning')
|
|
109
|
+
return { kind: 'usage', candidates: [], selected: 0, loading: false,
|
|
110
|
+
usage: `/${parsed.command} [${parsed.command === 'effort' ? 'level|default' : 'setting'}]` };
|
|
101
111
|
if (parsed?.kind !== 'command' && parsed?.kind !== 'usage')
|
|
102
112
|
return null;
|
|
103
113
|
return { kind: parsed.kind, query: parsed.query, usage: parsed.usage,
|
|
@@ -126,23 +136,31 @@ export class SelectionCompletion {
|
|
|
126
136
|
requestController = null;
|
|
127
137
|
commands = [];
|
|
128
138
|
currentModel;
|
|
139
|
+
routerCatalog;
|
|
140
|
+
reasoning = {};
|
|
129
141
|
constructor(changed = () => { }) {
|
|
130
142
|
this.changed = changed;
|
|
131
143
|
}
|
|
132
|
-
update(text, provider, loadModels, commands = [], currentModel) {
|
|
144
|
+
update(text, provider, loadModels, commands = [], currentModel, loadCatalog, activeRouted = false, reasoning = {}) {
|
|
133
145
|
this.commands = commands;
|
|
134
|
-
this.currentModel = currentModel;
|
|
135
146
|
const parsed = parseSelectionInput(text, commands);
|
|
136
147
|
const catalogProvider = parsed?.modelProvider ?? provider;
|
|
148
|
+
const routed = parsed?.routed ?? ((parsed?.includeModels || parsed?.kind === 'reasoning') && activeRouted ? true : undefined);
|
|
137
149
|
if (provider !== this.provider || catalogProvider !== this.catalogProvider
|
|
138
|
-
||
|
|
150
|
+
|| routed !== this.catalogRoute || parsed?.kind !== this.parsed?.kind
|
|
151
|
+
|| reasoning.scope !== this.reasoning.scope || (parsed?.kind === 'reasoning' && currentModel !== this.currentModel)) {
|
|
139
152
|
this.requestController?.abort();
|
|
140
153
|
this.generation++;
|
|
141
154
|
this.requested = false;
|
|
142
155
|
this.loading = false;
|
|
143
156
|
this.unavailable = false;
|
|
144
157
|
this.discovered = undefined;
|
|
158
|
+
this.routerCatalog = undefined;
|
|
145
159
|
}
|
|
160
|
+
this.currentModel = currentModel;
|
|
161
|
+
this.reasoning = reasoning;
|
|
162
|
+
if (reasoning.catalog)
|
|
163
|
+
this.routerCatalog = reasoning.catalog;
|
|
146
164
|
if (text !== this.text || provider !== this.provider) {
|
|
147
165
|
this.selected = 0;
|
|
148
166
|
this.firstVisible = 0;
|
|
@@ -153,9 +171,28 @@ export class SelectionCompletion {
|
|
|
153
171
|
this.text = text;
|
|
154
172
|
this.provider = provider;
|
|
155
173
|
this.catalogProvider = catalogProvider;
|
|
156
|
-
this.catalogRoute =
|
|
174
|
+
this.catalogRoute = routed;
|
|
157
175
|
this.parsed = parsed;
|
|
158
176
|
this.filter();
|
|
177
|
+
if ((parsed?.routerOnly || routed) && !this.requested && loadCatalog) {
|
|
178
|
+
this.requested = true;
|
|
179
|
+
this.loading = true;
|
|
180
|
+
this.requestController = new AbortController();
|
|
181
|
+
const generation = this.generation;
|
|
182
|
+
void loadCatalog(this.requestController.signal).then(catalog => {
|
|
183
|
+
if (generation !== this.generation)
|
|
184
|
+
return;
|
|
185
|
+
this.routerCatalog = catalog;
|
|
186
|
+
this.discovered = routerModelIds(catalog, catalogProvider);
|
|
187
|
+
}).catch(() => { if (generation === this.generation)
|
|
188
|
+
this.unavailable = true; }).finally(() => {
|
|
189
|
+
if (generation !== this.generation)
|
|
190
|
+
return;
|
|
191
|
+
this.loading = false;
|
|
192
|
+
this.filter();
|
|
193
|
+
this.changed();
|
|
194
|
+
});
|
|
195
|
+
}
|
|
159
196
|
if ((parsed?.kind === 'model' || parsed?.includeModels) && !this.requested && loadModels) {
|
|
160
197
|
this.requested = true;
|
|
161
198
|
this.loading = true;
|
|
@@ -181,9 +218,35 @@ export class SelectionCompletion {
|
|
|
181
218
|
}
|
|
182
219
|
filter() {
|
|
183
220
|
const selectedValue = this.candidates[this.selected]?.value;
|
|
184
|
-
|
|
221
|
+
if (this.parsed?.kind === 'reasoning') {
|
|
222
|
+
const catalog = this.routerCatalog;
|
|
223
|
+
const contract = this.catalogRoute
|
|
224
|
+
? catalog?.version === 1 && !catalog.authRequired
|
|
225
|
+
? resolveRouterModel(catalog, this.provider, this.currentModel ?? '')?.capabilities?.reasoning ?? []
|
|
226
|
+
: catalog?.version === 0 && !catalog.authRequired ? this.reasoning.contract : []
|
|
227
|
+
: this.reasoning.contract;
|
|
228
|
+
const command = this.parsed.command;
|
|
229
|
+
const current = command === 'effort' ? this.reasoning.current
|
|
230
|
+
: this.reasoning.current === undefined ? undefined : reasoningEnabled(this.reasoning.current) ? 'on' : 'off';
|
|
231
|
+
this.candidates = fuzzySelections(reasoningCommandChoices(command, this.provider, this.currentModel ?? '', contract)
|
|
232
|
+
.map(value => ({ value, label: value, current: value === current,
|
|
233
|
+
description: value === 'default' ? 'Inherit model/provider defaults' : undefined })), this.parsed.query);
|
|
234
|
+
const previousIndex = this.candidates.findIndex(candidate => candidate.value === selectedValue);
|
|
235
|
+
this.selected = previousIndex >= 0 ? previousIndex : 0;
|
|
236
|
+
this.revealSelection();
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const routed = this.catalogRoute && this.routerCatalog?.version === 1;
|
|
240
|
+
const models = (routed ? routerModelsForProvider(this.routerCatalog, this.catalogProvider).map(row => ({
|
|
241
|
+
value: row.id, label: row.display_name || row.id,
|
|
242
|
+
description: [row.capabilities?.tools === false ? 'Chat only' : '', row.available === false ? 'Access unavailable' : '', this.routerCatalog?.cached ? 'Cached catalog' : ''].filter(Boolean).join(' · '),
|
|
243
|
+
})) : modelSelections(this.catalogProvider, this.discovered))
|
|
185
244
|
.map(model => ({ ...model, current: this.catalogProvider === this.provider && model.value === this.currentModel }));
|
|
186
|
-
const
|
|
245
|
+
const routerProviders = this.parsed?.routerOnly && this.routerCatalog
|
|
246
|
+
? [...new Map(this.routerCatalog.models.filter(row => this.routerCatalog?.version === 0 || row.capabilities?.chat).map(row => [row.owned_by, {
|
|
247
|
+
id: row.owned_by, label: row.provider_display_name || row.owned_by, description: this.routerCatalog?.cached ? 'Cached catalog' : 'Via Zerg Router', aliases: [],
|
|
248
|
+
}])).values()] : PROVIDER_CHOICES;
|
|
249
|
+
const providers = routerProviders.filter(p => !this.parsed?.routerOnly || canRouteThroughRouter(p.id))
|
|
187
250
|
.map(p => ({ value: p.id, label: p.label, description: p.description, aliases: p.aliases,
|
|
188
251
|
current: p.id === this.provider, needsArgument: true }));
|
|
189
252
|
const source = this.parsed?.kind === 'command' ? commandSelections(this.commands)
|
|
@@ -218,6 +281,7 @@ export class SelectionCompletion {
|
|
|
218
281
|
return null;
|
|
219
282
|
return { candidates: this.candidates, selected: this.selected, firstVisible: this.firstVisible, loading: this.loading, unavailable: this.unavailable,
|
|
220
283
|
kind: this.parsed.kind, query: this.parsed.query, usage: this.parsed.usage,
|
|
284
|
+
command: this.parsed.command, model: this.parsed.kind === 'reasoning' ? this.currentModel : undefined,
|
|
221
285
|
provider: this.parsed.kind === 'model' ? this.catalogProvider : undefined,
|
|
222
286
|
routed: this.parsed.kind === 'model' ? this.catalogRoute : undefined,
|
|
223
287
|
mixed: this.parsed.includeModels };
|
|
@@ -255,6 +319,10 @@ export class SelectionCompletion {
|
|
|
255
319
|
if (this.parsed.kind === 'provider' && candidate.needsArgument) {
|
|
256
320
|
return { handled: true, text: `${completed} ` };
|
|
257
321
|
}
|
|
322
|
+
if (key.return && (this.parsed.kind === 'model' || this.parsed.kind === 'reasoning' || (this.parsed.includeModels && !candidate.needsArgument))) {
|
|
323
|
+
this.dismissedInput = this.text;
|
|
324
|
+
return { handled: true, text: completed, submit: true };
|
|
325
|
+
}
|
|
258
326
|
if (key.return && completed === this.text)
|
|
259
327
|
return { handled: false };
|
|
260
328
|
this.dismissedInput = completed;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { saveSession, saveSessionToFile, saveMuxStateOnly, cleanupOldSessions } from '../session.js';
|
|
8
8
|
import { pruneZtcTempDirs } from '../utils/ztc_cleanup.js';
|
|
9
9
|
import { configStore } from '../config.js';
|
|
10
|
-
import { estimateConversationTokens, formatTokenCount, getContextStatus, contextStatusFromUsed, resolveAutoCompact, } from '../utils/context.js';
|
|
10
|
+
import { estimateConversationTokens, formatTokenCount, getContextStatus, getContextWindowSize, contextStatusFromUsed, resolveAutoCompact, } from '../utils/context.js';
|
|
11
11
|
import { compactConversation, describeCompaction, compactMessages, smartSummarizer, localSummarizer } from '../utils/compaction.js';
|
|
12
12
|
import { stripEphemeralMessages } from '../utils/ephemeral.js';
|
|
13
13
|
/**
|
|
@@ -47,7 +47,8 @@ export class SessionManager {
|
|
|
47
47
|
async writeCurrentSession(options) {
|
|
48
48
|
const messages = this.deps.getMessages();
|
|
49
49
|
const muxState = this.deps.getMuxState ? await this.deps.getMuxState() : undefined;
|
|
50
|
-
const runtimeState =
|
|
50
|
+
const runtimeState = { ...this.deps.getRuntimeState?.(), authMode: configStore.getResolvedAuthMode(),
|
|
51
|
+
routerOrigin: configStore.getResolvedAuthMode() === 'direct' ? undefined : configStore.get().zergProxyUrl };
|
|
51
52
|
const hostedSettings = this.deps.getHostedSettings ? this.deps.getHostedSettings() : undefined;
|
|
52
53
|
const hasRuntimeState = runtimeState !== undefined && (runtimeState.mode !== undefined
|
|
53
54
|
|| runtimeState.planFile !== undefined
|
|
@@ -153,8 +154,8 @@ export class SessionManager {
|
|
|
153
154
|
// /context); fall back to the char/4 estimate only before the first API call.
|
|
154
155
|
const realUsed = this.deps.getContextTokens?.() ?? 0;
|
|
155
156
|
const status = realUsed > 0
|
|
156
|
-
? contextStatusFromUsed(realUsed, model, this.deps.getContextWindow?.())
|
|
157
|
-
: getContextStatus(messages, model);
|
|
157
|
+
? contextStatusFromUsed(realUsed, model, (configStore.getModelContextWindow(model) !== undefined ? configStore.getModelContextWindow(model) : this.deps.getContextWindow?.()))
|
|
158
|
+
: getContextStatus(messages, model, configStore.getModelContextWindow(model));
|
|
158
159
|
const threshold = opts?.threshold ?? configStore.getAutoCompactThreshold();
|
|
159
160
|
const targetPercent = configStore.getCompactionTargetPercent();
|
|
160
161
|
const triggerMode = configStore.getCompactionTriggerMode();
|
|
@@ -164,7 +165,7 @@ export class SessionManager {
|
|
|
164
165
|
const decision = resolveAutoCompact({
|
|
165
166
|
used: realUsed > 0 ? realUsed : estimateConversationTokens(messages),
|
|
166
167
|
model,
|
|
167
|
-
windowOverride: this.deps.getContextWindow?.(),
|
|
168
|
+
windowOverride: (configStore.getModelContextWindow(model) !== undefined ? configStore.getModelContextWindow(model) : this.deps.getContextWindow?.()),
|
|
168
169
|
thresholdPercent: threshold,
|
|
169
170
|
triggerMode,
|
|
170
171
|
tokenBudget: configStore.getContextTokenBudget(),
|
|
@@ -199,7 +200,7 @@ export class SessionManager {
|
|
|
199
200
|
return false;
|
|
200
201
|
}
|
|
201
202
|
this.deps.setMessages(result.messages);
|
|
202
|
-
const newStatus = getContextStatus(result.messages, model);
|
|
203
|
+
const newStatus = getContextStatus(result.messages, model, configStore.getModelContextWindow(model));
|
|
203
204
|
const verb = describeCompaction(result);
|
|
204
205
|
this.deps.addMessage({
|
|
205
206
|
role: 'system',
|
|
@@ -228,13 +229,17 @@ export class SessionManager {
|
|
|
228
229
|
const summarizer = this.buildSummarizer();
|
|
229
230
|
this.deps.debugLog('[SessionManager.forceCompact] Forcing aggressive compaction');
|
|
230
231
|
try {
|
|
231
|
-
|
|
232
|
+
// A provider can reject request structure before its token window fills.
|
|
233
|
+
// Forced recovery must shrink even when the history already fits 30% of
|
|
234
|
+
// that window; otherwise the next request repeats the same rejection.
|
|
235
|
+
const targetTokens = Math.max(1, Math.min(Math.floor((getContextWindowSize(model, configStore.getModelContextWindow(model)) || estimateConversationTokens(messages)) * 0.3), Math.floor(estimateConversationTokens(messages) / 2)));
|
|
236
|
+
const result = await compactMessages(messages, model, summarizer, 30, 5, targetTokens);
|
|
232
237
|
if (result.removed === 0) {
|
|
233
238
|
this.deps.debugLog('[SessionManager.forceCompact] Nothing to compact — all messages are recent or system');
|
|
234
239
|
return false;
|
|
235
240
|
}
|
|
236
241
|
this.deps.setMessages(result.messages);
|
|
237
|
-
const newStatus = getContextStatus(result.messages, model);
|
|
242
|
+
const newStatus = getContextStatus(result.messages, model, configStore.getModelContextWindow(model));
|
|
238
243
|
this.deps.debugLog(`[SessionManager.forceCompact] Compacted ${result.removed} msgs: ${formatTokenCount(result.tokensBefore)} → ${formatTokenCount(result.tokensAfter)} (${Math.round(newStatus.percentage)}%)`);
|
|
239
244
|
return true;
|
|
240
245
|
}
|
package/dist/cli_args.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { CODEX_REASONING_EFFORTS } from './config/types.js';
|
|
2
|
-
import { getSupportedCodexReasoningEfforts, unsupportedEffortMessage } from './utils/models.js';
|
|
3
1
|
import { validateProfileName } from './config/profiles.js';
|
|
4
2
|
const BOOLEAN_FLAGS = new Set([
|
|
5
3
|
'--no-pin-task-header', '--help', '-h', '--version', '-v', '--list-sessions', '--plan', '--desktop', '--mux', '--no-mux', '--new-window', '--fullscreen', '--pane', '--human-sim-pane', '--zlicky', '--computer-use', '--ml', '--sfx-list', '--voyager-loop', '--mirror', '--open-mirror', '--mirror-open', '--no-browser-open', '--navigator-hidden', '--headless-mirror', '--headless-mirror-open', '--headless-interactive', '--headless', '--json', '--json-stream', '--quiet', '-q', '--timestamps', '--verbose', '--colony',
|
|
6
4
|
]);
|
|
7
5
|
const REQUIRED_VALUE_FLAGS = new Set([
|
|
8
|
-
'--cwd', '--name', '--session-id', '--session-file', '--mode', '--agent-domain', '--domain-paths', '--embedded', '--embedded-rom', '--embedded-workflow', '--embedded-runtime-tick-ms', '--embedded-runtime-frame-budget', '--bench', '--bench-workflow', '--num-zergs', '--provider', '--model', '--profile', '--effort', '--thinking', '--emulation', '--prompt', '--max-iterations', '--timeout', '--output', '--voyager-task', '--voyager-max-rounds', '--mirror-port', '--navigator-group', '--connect-zerg',
|
|
6
|
+
'--cwd', '--name', '--session-id', '--session-file', '--mode', '--agent-domain', '--domain-paths', '--embedded', '--embedded-rom', '--embedded-workflow', '--embedded-runtime-tick-ms', '--embedded-runtime-frame-budget', '--bench', '--bench-workflow', '--num-zergs', '--provider', '--model', '--auth-mode', '--router-url', '--profile', '--effort', '--thinking', '--emulation', '--prompt', '--max-iterations', '--timeout', '--output', '--voyager-task', '--voyager-max-rounds', '--mirror-port', '--navigator-group', '--connect-zerg',
|
|
9
7
|
]);
|
|
10
8
|
const OPTIONAL_VALUE_FLAGS = new Set(['--resume', '--continue', '-c', '--sfx']);
|
|
11
9
|
/** Separate prompt text from options before the legacy flag consumers see it. */
|
|
@@ -96,19 +94,7 @@ export function validateCliArgs(args) {
|
|
|
96
94
|
return { ok: false, message: error.message, error: 'invalid_profile', flag: token, value: next };
|
|
97
95
|
}
|
|
98
96
|
}
|
|
99
|
-
|
|
100
|
-
if (!CODEX_REASONING_EFFORTS.includes(next)) {
|
|
101
|
-
return { ok: false, message: `--effort must be one of: ${CODEX_REASONING_EFFORTS.join(', ')}.`, error: 'invalid_effort', flag: token, value: next };
|
|
102
|
-
}
|
|
103
|
-
const modelIndex = args.indexOf('--model');
|
|
104
|
-
const providerIndex = args.indexOf('--provider');
|
|
105
|
-
const model = modelIndex >= 0 ? args[modelIndex + 1] : undefined;
|
|
106
|
-
const provider = providerIndex >= 0 ? args[providerIndex + 1] : undefined;
|
|
107
|
-
const supported = getSupportedCodexReasoningEfforts(model, provider);
|
|
108
|
-
if (supported && !supported.includes(next)) {
|
|
109
|
-
return { ok: false, message: unsupportedEffortMessage(model, next, supported), error: 'unsupported_effort', flag: token, value: next };
|
|
110
|
-
}
|
|
111
|
-
}
|
|
97
|
+
// Model-specific effort validation runs after configuration and Router definitions load.
|
|
112
98
|
i += 1;
|
|
113
99
|
continue;
|
|
114
100
|
}
|
package/dist/cli_main.js
CHANGED
|
@@ -298,6 +298,8 @@ Options:
|
|
|
298
298
|
--bench-workflow <id>
|
|
299
299
|
Run a bench workflow after opening the workspace
|
|
300
300
|
--provider <name> LLM provider override (anthropic, claude_code, openai, codex_app_server, codex_legacy, gemini, inception, kimi, kimi_code, etc.; codex is a legacy alias)
|
|
301
|
+
--auth-mode <mode> Override routing for this process (auto, direct, zrouter)
|
|
302
|
+
--router-url <url> Router origin for this process; matching login is required
|
|
301
303
|
--profile <name> Use named settings in ~/.ztc/profiles/<name>/config.json
|
|
302
304
|
(default: ~/.ztc/config.json). Sessions stay shared.
|
|
303
305
|
--model <name> LLM model override (e.g., claude-sonnet-4-20250514, gpt-4o)
|
|
@@ -1043,9 +1045,20 @@ async function main() {
|
|
|
1043
1045
|
const { initMcpServers } = await import('./mcp/client.js');
|
|
1044
1046
|
await initMcpServers(mcpServers);
|
|
1045
1047
|
}
|
|
1048
|
+
const authModeIndex = args.indexOf('--auth-mode');
|
|
1049
|
+
const routerUrlIndex = args.indexOf('--router-url');
|
|
1050
|
+
if (authModeIndex >= 0 || routerUrlIndex >= 0) {
|
|
1051
|
+
const authMode = authModeIndex >= 0 ? args[authModeIndex + 1] : configStore.getAuthMode();
|
|
1052
|
+
if (!['auto', 'direct', 'zrouter'].includes(authMode))
|
|
1053
|
+
throw new Error('Invalid --auth-mode');
|
|
1054
|
+
configStore.setCliOverride('authMode', authMode);
|
|
1055
|
+
if (routerUrlIndex >= 0)
|
|
1056
|
+
configStore.setCliOverride('zergProxyUrl', args[routerUrlIndex + 1]);
|
|
1057
|
+
}
|
|
1046
1058
|
// Apply --provider / --model CLI overrides (never persisted to config.json)
|
|
1059
|
+
const cliRoute = configStore.getResolvedAuthMode();
|
|
1047
1060
|
const resolvedCliOverride = cliModel
|
|
1048
|
-
? resolveModelAndProvider(cliModel, cliProvider || undefined)
|
|
1061
|
+
? resolveModelAndProvider(cliModel, cliProvider || undefined, cliRoute)
|
|
1049
1062
|
: undefined;
|
|
1050
1063
|
const effectiveCliProvider = (resolvedCliOverride?.provider || cliProvider);
|
|
1051
1064
|
const effectiveCliModel = resolvedCliOverride?.model || cliModel;
|
|
@@ -1069,11 +1082,17 @@ async function main() {
|
|
|
1069
1082
|
const thinkingIndex = args.indexOf('--thinking');
|
|
1070
1083
|
if (effortIndex >= 0 || thinkingIndex >= 0) {
|
|
1071
1084
|
const { parseReasoningSelection } = await import('./utils/reasoning.js');
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1085
|
+
try {
|
|
1086
|
+
const level = parseReasoningSelection(configStore.getProvider(), configStore.getModel(), configStore.getCodexEffort(), {
|
|
1087
|
+
effort: effortIndex >= 0 ? args[effortIndex + 1] : undefined,
|
|
1088
|
+
thinking: thinkingIndex >= 0 ? args[thinkingIndex + 1] : undefined,
|
|
1089
|
+
}, configStore.getReasoningContract());
|
|
1090
|
+
configStore.setCliOverride('codexEffort', level ?? undefined);
|
|
1091
|
+
}
|
|
1092
|
+
catch (error) {
|
|
1093
|
+
console.error(`[ztc] ${error instanceof Error ? error.message : String(error)}`);
|
|
1094
|
+
process.exit(2);
|
|
1095
|
+
}
|
|
1077
1096
|
}
|
|
1078
1097
|
// Background-refresh superuser status so pre-existing logins get the field backfilled.
|
|
1079
1098
|
// Skip for colony children (they inherit the parent status) and explicitly offline
|
package/dist/cli_ratatui.js
CHANGED
|
@@ -13,7 +13,7 @@ import { randomUUID } from 'crypto';
|
|
|
13
13
|
import { appendFileSync, existsSync, writeFileSync } from 'fs';
|
|
14
14
|
import { tmpdir } from 'os';
|
|
15
15
|
import { join } from 'path';
|
|
16
|
-
import { execFile,
|
|
16
|
+
import { execFile, spawnSync } from 'child_process';
|
|
17
17
|
import { promisify } from 'util';
|
|
18
18
|
import { configStore } from './config.js';
|
|
19
19
|
import { withActiveProfileArg } from './config/profiles.js';
|
|
@@ -79,6 +79,7 @@ import { createAgentWithConfig, AgentRunLifecycle } from './app/agent_runner.js'
|
|
|
79
79
|
import { clearWorkerExecutor } from './worker/index.js';
|
|
80
80
|
import { isPhantomCompletion } from './app/phantom_completion.js';
|
|
81
81
|
import { SelectionCompletion } from './app/selection_completion.js';
|
|
82
|
+
import { routerCatalogScope } from './utils/router_catalog.js';
|
|
82
83
|
import { shouldAttachParentMirrorClient, shouldMarkMirrorServerAsColonyChild } from './app/mirror_startup.js';
|
|
83
84
|
import { buildDesktopUpdateRelaunchContext, buildHotRestartLaunch, prepareDesktopUpdateAuthorization, reduceDesktopUpdateOwnerHandshake, requestColonyCellRestart, } from './app/restart.js';
|
|
84
85
|
export { buildDesktopUpdateRelaunchContext, buildHotRestartArgs, requestColonyCellRestart, } from './app/restart.js';
|
|
@@ -1031,6 +1032,8 @@ export class RatatuiApp {
|
|
|
1031
1032
|
if (options?.resumeSession) {
|
|
1032
1033
|
this.sessionId = options.resumeSession.metadata.id;
|
|
1033
1034
|
this.resumedSession = options.resumeSession;
|
|
1035
|
+
if (!options.resumeSession.hosted_settings)
|
|
1036
|
+
configStore.restoreSessionSelection(options.resumeSession.metadata);
|
|
1034
1037
|
this.resumedPaneSpecs = mergeSessionPaneSpecs(options.resumeSession.mux_state, options.resumeSession.pane_specs);
|
|
1035
1038
|
this.sessionName = options.resumeSession.metadata.name;
|
|
1036
1039
|
this.hostedSessionSettings = options.resumeSession.hosted_settings
|
|
@@ -1084,7 +1087,7 @@ export class RatatuiApp {
|
|
|
1084
1087
|
});
|
|
1085
1088
|
// Initialize context status to show meter from the start
|
|
1086
1089
|
const model = configStore.get().model;
|
|
1087
|
-
const total = getContextWindowSize(model);
|
|
1090
|
+
const total = getContextWindowSize(model, configStore.getModelContextWindow(model));
|
|
1088
1091
|
this.controller.dispatch({
|
|
1089
1092
|
type: 'SET_CONTEXT',
|
|
1090
1093
|
length: 0,
|
|
@@ -1580,7 +1583,7 @@ export class RatatuiApp {
|
|
|
1580
1583
|
this.mirrorServer?.setSuperuser(configStore.isSuperuser());
|
|
1581
1584
|
}
|
|
1582
1585
|
/** Change the active config while retaining this renderer's session and transcript. */
|
|
1583
|
-
async switchConfigProfileInPlace(name) {
|
|
1586
|
+
async switchConfigProfileInPlace(name, duplicateSource) {
|
|
1584
1587
|
if (this.isHostedRuntime()) {
|
|
1585
1588
|
throw new Error('Hosted thread settings cannot be replaced by a local config profile.');
|
|
1586
1589
|
}
|
|
@@ -1620,7 +1623,7 @@ export class RatatuiApp {
|
|
|
1620
1623
|
await preloadSkillsManifest();
|
|
1621
1624
|
};
|
|
1622
1625
|
try {
|
|
1623
|
-
|
|
1626
|
+
const activate = async () => {
|
|
1624
1627
|
touchedRuntime = true;
|
|
1625
1628
|
applyColonyOptIn();
|
|
1626
1629
|
mcpChanged = previousMcp !== JSON.stringify(configStore.get().mcpServers ?? {})
|
|
@@ -1636,7 +1639,11 @@ export class RatatuiApp {
|
|
|
1636
1639
|
this.hookLifecycle.ensureWatcher();
|
|
1637
1640
|
if (this.shouldPersistSession())
|
|
1638
1641
|
await this.saveCurrentSession(true);
|
|
1639
|
-
}
|
|
1642
|
+
};
|
|
1643
|
+
if (duplicateSource === undefined)
|
|
1644
|
+
await configStore.switchProfile(name, activate);
|
|
1645
|
+
else
|
|
1646
|
+
await configStore.duplicateProfile(name, duplicateSource, activate);
|
|
1640
1647
|
}
|
|
1641
1648
|
catch (error) {
|
|
1642
1649
|
this.overseerModelOverride = previousOverseerOverride;
|
|
@@ -1722,6 +1729,10 @@ export class RatatuiApp {
|
|
|
1722
1729
|
setAgentState: (state) => this.controller.setAgentState(state),
|
|
1723
1730
|
reloadAgent: (options) => this.reloadAgentInPlace(options?.defer),
|
|
1724
1731
|
switchConfigProfile: (name) => this.switchConfigProfileInPlace(name),
|
|
1732
|
+
duplicateProfile: async (name, source) => {
|
|
1733
|
+
await this.switchConfigProfileInPlace(name, source);
|
|
1734
|
+
return name;
|
|
1735
|
+
},
|
|
1725
1736
|
setRuntimeModelOverride: (provider, model) => {
|
|
1726
1737
|
configStore.setCliOverride('provider', provider);
|
|
1727
1738
|
configStore.setCliOverride('model', model);
|
|
@@ -3370,7 +3381,7 @@ export class RatatuiApp {
|
|
|
3370
3381
|
sessionName: this.resumedSession.metadata.name,
|
|
3371
3382
|
messageCount: this.resumedSession.messages.length,
|
|
3372
3383
|
updatedAt: this.resumedSession.metadata.updatedAt,
|
|
3373
|
-
contextPercentage: getContextStatus(this.resumedSession.messages, model).percentage,
|
|
3384
|
+
contextPercentage: getContextStatus(this.resumedSession.messages, model, configStore.getModelContextWindow(model)).percentage,
|
|
3374
3385
|
mode: launchMode,
|
|
3375
3386
|
credentialsReady,
|
|
3376
3387
|
});
|
|
@@ -3862,14 +3873,14 @@ export class RatatuiApp {
|
|
|
3862
3873
|
}
|
|
3863
3874
|
createZtcPaneSpec(options) {
|
|
3864
3875
|
const resolved = options?.model
|
|
3865
|
-
? resolveModelAndProvider(options.model, options.provider)
|
|
3876
|
+
? resolveModelAndProvider(options.model, options.provider, configStore.getResolvedAuthMode())
|
|
3866
3877
|
: { model: undefined, provider: options?.provider };
|
|
3867
3878
|
return createZtcLaunchSpec({
|
|
3868
3879
|
sessionId: options?.sessionId,
|
|
3869
3880
|
sessionMode: options?.sessionMode,
|
|
3870
3881
|
cwd: options?.cwd ?? (this.shellCwd || process.cwd()),
|
|
3871
3882
|
title: options?.title || 'ZTC',
|
|
3872
|
-
providerOverride: isPaneProvider(resolved.provider) ? resolved.provider : undefined,
|
|
3883
|
+
providerOverride: isPaneProvider(resolved.provider, configStore.getResolvedAuthMode()) ? resolved.provider : undefined,
|
|
3873
3884
|
modelOverride: resolved.model,
|
|
3874
3885
|
extraArgs: [],
|
|
3875
3886
|
});
|
|
@@ -3882,7 +3893,7 @@ export class RatatuiApp {
|
|
|
3882
3893
|
const configProvider = configStore.getProvider();
|
|
3883
3894
|
const providerOverride = launchSpec?.kind === 'ztc' ? launchSpec.providerOverride : undefined;
|
|
3884
3895
|
const modelOverride = launchSpec?.kind === 'ztc' ? launchSpec.modelOverride : undefined;
|
|
3885
|
-
const configuredPaneProvider = isPaneProvider(configProvider) ? configProvider : undefined;
|
|
3896
|
+
const configuredPaneProvider = isPaneProvider(configProvider, configStore.getResolvedAuthMode()) ? configProvider : undefined;
|
|
3886
3897
|
const effectiveProvider = isZtc
|
|
3887
3898
|
? providerOverride
|
|
3888
3899
|
|| (!modelOverride ? configuredPaneProvider : undefined)
|
|
@@ -4284,13 +4295,18 @@ export class RatatuiApp {
|
|
|
4284
4295
|
// --- Shared validation ---
|
|
4285
4296
|
let nextOverride = null;
|
|
4286
4297
|
if (command.override) {
|
|
4287
|
-
const
|
|
4288
|
-
|
|
4298
|
+
const route = configStore.getResolvedAuthMode();
|
|
4299
|
+
const resolved = resolveModelAndProvider(command.override.model, command.override.provider, route);
|
|
4300
|
+
const catalog = configStore.getRoutedModelCatalog();
|
|
4301
|
+
const knownModel = catalog?.version === 1
|
|
4302
|
+
? catalog.models.some(row => row.owned_by === resolved.provider && row.id === resolved.model && row.capabilities?.chat)
|
|
4303
|
+
: isCatalogModelForProvider(resolved.provider || '', resolved.model);
|
|
4304
|
+
if (!isPaneProvider(resolved.provider, route) || !resolved.model || !knownModel) {
|
|
4289
4305
|
const error = 'Unsupported pane model selection.';
|
|
4290
4306
|
this.publishPaneSystemMessage(command.pane_id, error);
|
|
4291
4307
|
return { ok: false, error };
|
|
4292
4308
|
}
|
|
4293
|
-
if (!configStore.getApiKey(resolved.provider) && !configStore.isAuthenticated()) {
|
|
4309
|
+
if (route !== 'zrouter' && !configStore.getApiKey(resolved.provider) && !configStore.isAuthenticated()) {
|
|
4294
4310
|
const error = `no API key configured for provider ${resolved.provider}`;
|
|
4295
4311
|
this.publishPaneSystemMessage(command.pane_id, error);
|
|
4296
4312
|
return { ok: false, error };
|
|
@@ -4352,7 +4368,7 @@ export class RatatuiApp {
|
|
|
4352
4368
|
const effectiveProvider = nextOverride?.provider || configStore.getProvider();
|
|
4353
4369
|
this.childPaneModels.set(command.pane_id, effectiveModel);
|
|
4354
4370
|
pane.effectiveModel = effectiveModel;
|
|
4355
|
-
pane.effectiveProvider = isPaneProvider(effectiveProvider) ? effectiveProvider : undefined;
|
|
4371
|
+
pane.effectiveProvider = isPaneProvider(effectiveProvider, configStore.getResolvedAuthMode()) ? effectiveProvider : undefined;
|
|
4356
4372
|
this.mirrorServer?.setPaneModel(command.pane_id, effectiveModel, effectiveProvider);
|
|
4357
4373
|
if (pane.launchSpec?.kind === 'ztc') {
|
|
4358
4374
|
pane.launchSpec.providerOverride = nextOverride?.provider;
|
|
@@ -4377,8 +4393,8 @@ export class RatatuiApp {
|
|
|
4377
4393
|
return { ok: false, error };
|
|
4378
4394
|
}
|
|
4379
4395
|
const level = (effort ?? null);
|
|
4380
|
-
if (level !== null && !isCodexReasoningEffortSupported(command.provider, command.model, level)) {
|
|
4381
|
-
const supported = getSupportedCodexReasoningEfforts(command.model, command.provider) ?? [];
|
|
4396
|
+
if (level !== null && !isCodexReasoningEffortSupported(command.provider, command.model, level, configStore.getReasoningWireSurface(command.provider, command.model))) {
|
|
4397
|
+
const supported = getSupportedCodexReasoningEfforts(command.model, command.provider, configStore.getReasoningWireSurface(command.provider, command.model)) ?? [];
|
|
4382
4398
|
const error = unsupportedEffortMessage(command.model, level, supported);
|
|
4383
4399
|
this.publishPaneSystemMessage(command.pane_id, error);
|
|
4384
4400
|
return { ok: false, error };
|
|
@@ -4897,7 +4913,8 @@ export class RatatuiApp {
|
|
|
4897
4913
|
// the static table alone would revert the denominator (e.g. 400K -> 200K for
|
|
4898
4914
|
// would revert the denominator to a stale static fallback until the next
|
|
4899
4915
|
// token_usage event corrects it.
|
|
4900
|
-
const
|
|
4916
|
+
const advertisedWindow = configStore.getModelContextWindow(model);
|
|
4917
|
+
const windowOverride = advertisedWindow !== undefined ? advertisedWindow : this.controller.getState().contextStatus?.total;
|
|
4901
4918
|
const status = contextStatusFromUsed(used, model, windowOverride);
|
|
4902
4919
|
this.controller.dispatch({ type: 'SET_CONTEXT', length: used, estimated, status });
|
|
4903
4920
|
const agentState = this.controller.getState().agentState;
|
|
@@ -5616,7 +5633,16 @@ export class RatatuiApp {
|
|
|
5616
5633
|
? this.inputState.segments[0].text : '';
|
|
5617
5634
|
if (textCursorIndex(this.inputState.segments, this.inputState.cursor) !== text.length)
|
|
5618
5635
|
text = '';
|
|
5619
|
-
|
|
5636
|
+
let routed = configStore.getAuthMode() === 'zrouter';
|
|
5637
|
+
try {
|
|
5638
|
+
routed = configStore.resolveLLMBackendConfig().useProxy;
|
|
5639
|
+
}
|
|
5640
|
+
catch { /* Discovery can still show its isolated cache. */ }
|
|
5641
|
+
const reasoningCommand = /^\/(?:effort|thinking)\s/i.test(text);
|
|
5642
|
+
const connection = reasoningCommand && routed ? configStore.getRouterCatalogConnection() : undefined;
|
|
5643
|
+
this.selectionCompletion.update(text, configStore.getProvider(), (signal, provider, authMode) => buildModelsController(configStore).list(provider, { signal, authMode }), commands, configStore.getModel(), buildModelsController(configStore).catalog, routed, reasoningCommand ? { catalog: routed ? configStore.getRoutedModelCatalog() : undefined,
|
|
5644
|
+
current: configStore.getCodexEffort(), contract: configStore.getReasoningContract(),
|
|
5645
|
+
scope: connection ? routerCatalogScope(connection) : undefined } : {});
|
|
5620
5646
|
return this.selectionCompletion.view();
|
|
5621
5647
|
}
|
|
5622
5648
|
handleInput(input, key) {
|
|
@@ -5708,8 +5734,15 @@ export class RatatuiApp {
|
|
|
5708
5734
|
this.updateSelectionCompletion();
|
|
5709
5735
|
const selection = this.selectionCompletion.handleKey({ ...key, return: key.return || input === '\r' || input === '\n' });
|
|
5710
5736
|
if (selection.handled) {
|
|
5711
|
-
if (selection.text !== undefined)
|
|
5712
|
-
|
|
5737
|
+
if (selection.text !== undefined) {
|
|
5738
|
+
if (selection.submit && !this.queueEdit) {
|
|
5739
|
+
this.clearComposerAfterSubmit(selection.text);
|
|
5740
|
+
void this.handleSubmit(selection.text);
|
|
5741
|
+
}
|
|
5742
|
+
else {
|
|
5743
|
+
this.setInputToText(selection.text, selection.text.length);
|
|
5744
|
+
}
|
|
5745
|
+
}
|
|
5713
5746
|
else
|
|
5714
5747
|
this.render();
|
|
5715
5748
|
return;
|
|
@@ -7420,7 +7453,7 @@ export class RatatuiApp {
|
|
|
7420
7453
|
// exact same math /context and auto-compact use, so all surfaces agree.
|
|
7421
7454
|
// event.contextWindow (when the provider reports one, e.g. Codex's
|
|
7422
7455
|
// modelContextWindow) overrides the static window guess.
|
|
7423
|
-
const status = contextStatusFromUsed(used, model, event.contextWindow);
|
|
7456
|
+
const status = contextStatusFromUsed(used, model, event.contextWindow ?? configStore.getModelContextWindow(model));
|
|
7424
7457
|
const total = status.total;
|
|
7425
7458
|
const percentage = status.percentage;
|
|
7426
7459
|
const previousContextTokens = this.controller.getState().agentState.contextTokens;
|
|
@@ -10389,33 +10422,37 @@ export class RatatuiApp {
|
|
|
10389
10422
|
try {
|
|
10390
10423
|
debugLog(`[${options.label}] Saving session before relaunch...`);
|
|
10391
10424
|
await this.saveCurrentSession();
|
|
10392
|
-
// Clean up
|
|
10393
|
-
this.sessionManager?.stopAutoSave();
|
|
10394
|
-
if (this.dictationCleanup) {
|
|
10395
|
-
this.dictationCleanup();
|
|
10396
|
-
this.dictationCleanup = null;
|
|
10397
|
-
}
|
|
10398
|
-
void shutdownLSPManager();
|
|
10399
10425
|
if (this.mirrorServer) {
|
|
10400
10426
|
this.mirrorServer.publishSessionEnd({ reloadOnReconnect: true });
|
|
10401
10427
|
await stopMirrorStreamServer();
|
|
10402
10428
|
this.mirrorServer = null;
|
|
10403
10429
|
}
|
|
10404
|
-
//
|
|
10405
|
-
|
|
10430
|
+
// execve skips exit handlers. Release the old session's registry entry,
|
|
10431
|
+
// timers, backends and renderer before replacing the process. Persistence
|
|
10432
|
+
// already happened above, while the renderer could still snapshot panes.
|
|
10433
|
+
await this.exit(false, { announce: false, persistSession: false, terminateProcess: false });
|
|
10406
10434
|
// Build relaunch args: preserve runtime flags but force the target session
|
|
10407
10435
|
// as the only resume target.
|
|
10408
|
-
const { command, args, shell } = buildHotRestartLaunch(withActiveProfileArg(process.argv.slice(1)), options.sessionId, process.execPath);
|
|
10409
10436
|
const spawnCwd = existsSync(options.cwd) ? options.cwd : process.cwd();
|
|
10437
|
+
const { command, args, shell } = buildHotRestartLaunch(withActiveProfileArg(process.argv.slice(1)), options.sessionId, process.execPath, spawnCwd);
|
|
10410
10438
|
debugLog(`[${options.label}] Spawning new process: ${command} ${args.join(' ')} (cwd=${spawnCwd})`);
|
|
10411
|
-
|
|
10439
|
+
// Keep the foreground job alive. Spawning and exiting lets the shell
|
|
10440
|
+
// reclaim the terminal, so the orphaned renderer fails with raw-mode EIO.
|
|
10441
|
+
// Node 20 lacks execve; its fallback must wait until the replacement ends.
|
|
10442
|
+
const runtime = process;
|
|
10443
|
+
if (runtime.execve) {
|
|
10444
|
+
process.chdir(spawnCwd);
|
|
10445
|
+
runtime.execve(command, [command, ...args], process.env);
|
|
10446
|
+
}
|
|
10447
|
+
const result = spawnSync(command, args, {
|
|
10412
10448
|
stdio: 'inherit',
|
|
10413
10449
|
cwd: spawnCwd,
|
|
10414
10450
|
env: process.env,
|
|
10415
10451
|
shell,
|
|
10416
10452
|
});
|
|
10417
|
-
|
|
10418
|
-
|
|
10453
|
+
if (result.error)
|
|
10454
|
+
throw result.error;
|
|
10455
|
+
process.exit(result.status ?? 1);
|
|
10419
10456
|
}
|
|
10420
10457
|
catch (err) {
|
|
10421
10458
|
debugLog(`[${options.label}] Failed: ${err.message}`);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ReasoningWireSurface } from '@zerg/model-contracts';
|
|
1
2
|
import type { CodexReasoningEffort, ZTCConfig } from './types.js';
|
|
2
3
|
export declare const DEFAULT_OPENAI_MODEL = "gpt-5.4-nano";
|
|
3
4
|
export declare const DEFAULT_CODEX_MODEL = "gpt-5.6-sol";
|
|
@@ -14,7 +15,7 @@ export declare const DEFAULT_CLARIFAI_MODEL = "https://clarifai.com/openai/chat-
|
|
|
14
15
|
* undefined (use the provider default). Used both when building an agent
|
|
15
16
|
* (factory) and to display the active effort in the model picker.
|
|
16
17
|
*/
|
|
17
|
-
export declare function resolveCodexEffort(config: ZTCConfig, provider?: string, model?: string): CodexReasoningEffort | undefined;
|
|
18
|
+
export declare function resolveCodexEffort(config: ZTCConfig, provider?: string, model?: string, contract?: ReasoningWireSurface | import('../utils/router_catalog.js').RouterModelCapabilities): CodexReasoningEffort | undefined;
|
|
18
19
|
/**
|
|
19
20
|
* The highest-priority saved effort when the active model rejects it, so the
|
|
20
21
|
* UI can say the choice is not applied instead of silently falling back.
|
package/dist/config/defaults.js
CHANGED
|
@@ -20,14 +20,19 @@ export const DEFAULT_CLARIFAI_MODEL = 'https://clarifai.com/openai/chat-completi
|
|
|
20
20
|
* undefined (use the provider default). Used both when building an agent
|
|
21
21
|
* (factory) and to display the active effort in the model picker.
|
|
22
22
|
*/
|
|
23
|
-
export function resolveCodexEffort(config, provider, model) {
|
|
23
|
+
export function resolveCodexEffort(config, provider, model, contract = config.authMode === 'zrouter' || provider === 'openai_compatible' ? 'chat_tools' : 'native_api') {
|
|
24
24
|
const candidates = [
|
|
25
25
|
model ? config.codexEffortByModel?.[model] : undefined,
|
|
26
26
|
provider ? config.codexEffortByProvider?.[provider] : undefined,
|
|
27
27
|
config.codexEffort,
|
|
28
28
|
];
|
|
29
29
|
for (const candidate of candidates) {
|
|
30
|
-
if (candidate
|
|
30
|
+
if (!candidate)
|
|
31
|
+
continue;
|
|
32
|
+
const supported = typeof contract === 'string'
|
|
33
|
+
? isCodexReasoningEffortSupported(provider, model, candidate, contract)
|
|
34
|
+
: contract.reasoning?.choices.includes(candidate);
|
|
35
|
+
if (candidate && supported)
|
|
31
36
|
return candidate;
|
|
32
37
|
}
|
|
33
38
|
return undefined;
|
|
@@ -143,6 +148,8 @@ export function getDefaultApiKeyForProvider(provider) {
|
|
|
143
148
|
return process.env.DEEPSEEK_API_KEY;
|
|
144
149
|
case 'modal':
|
|
145
150
|
return undefined; // Modal credentials belong to the Router platform.
|
|
151
|
+
case 'fireworks':
|
|
152
|
+
return process.env.FIREWORKS_API_KEY;
|
|
146
153
|
default:
|
|
147
154
|
return (process.env.ANTHROPIC_API_KEY
|
|
148
155
|
|| process.env.OPENAI_API_KEY
|
|
@@ -35,8 +35,8 @@ export interface AuthContext {
|
|
|
35
35
|
* `apiKey` is the DIRECT provider key (what a direct backend would use).
|
|
36
36
|
* `authToken` is the platform/proxy bearer. When `useProxy` is true the active
|
|
37
37
|
* backend streams through `proxyUrl` with `authToken`; the direct `apiKey` is
|
|
38
|
-
* still surfaced
|
|
39
|
-
*
|
|
38
|
+
* still surfaced for direct-provider construction. Auto-mode preferences are
|
|
39
|
+
* resolved before `useProxy` is returned.
|
|
40
40
|
*/
|
|
41
41
|
export interface ResolvedLLMConfig {
|
|
42
42
|
provider: string;
|