u-foo 2.3.21 → 2.3.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "u-foo",
3
- "version": "2.3.21",
3
+ "version": "2.3.23",
4
4
  "description": "Multi-Agent Workspace Protocol. Just add u. claude → uclaude, codex → ucodex.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://ufoo.dev",
@@ -1,7 +1,12 @@
1
1
  "use strict";
2
2
 
3
3
  const { randomUUID } = require("crypto");
4
- const { loadConfig } = require("../config");
4
+ const {
5
+ loadConfig,
6
+ defaultAgentModelForProvider,
7
+ defaultRouterModelForProvider,
8
+ sameModelProvider,
9
+ } = require("../config");
5
10
  const {
6
11
  resolveRuntimeConfig,
7
12
  resolveCompletionUrl,
@@ -30,6 +35,16 @@ const CODEX_DEFAULT_BASE_URL = "https://chatgpt.com/backend-api/codex";
30
35
  const CODEX_DEFAULT_USER_AGENT = "codex-tui/0.118.0 (Mac OS 26.3.1; arm64) iTerm.app/3.6.9 (codex-tui; 0.118.0)";
31
36
  const CODEX_DEFAULT_ORIGINATOR = "codex-tui";
32
37
 
38
+ function resolveConfiguredModelForProvider(config = {}, provider = "") {
39
+ if (config.routerProvider && sameModelProvider(config.routerProvider, provider)) {
40
+ return config.routerModel;
41
+ }
42
+ if (config.agentProvider && sameModelProvider(config.agentProvider, provider)) {
43
+ return config.agentModel;
44
+ }
45
+ return "";
46
+ }
47
+
33
48
  function buildOpenAiChatRequest({
34
49
  model = "",
35
50
  systemPrompt = "",
@@ -217,7 +232,11 @@ async function resolveUpstreamRuntime({
217
232
  const baseUrl = useCodexResponses
218
233
  ? String(env.UFOO_CODEX_BASE_URL || "").trim() || CODEX_DEFAULT_BASE_URL
219
234
  : String(env.OPENAI_BASE_URL || "").trim() || "https://api.openai.com/v1";
220
- const resolvedModel = String(model || config.routerModel || config.agentModel || "").trim();
235
+ const resolvedModel = String(
236
+ model
237
+ || resolveConfiguredModelForProvider(config, "codex")
238
+ || defaultRouterModelForProvider("codex")
239
+ ).trim();
221
240
  return {
222
241
  provider: "codex",
223
242
  transport: useCodexResponses ? "codex-responses" : "openai-chat",
@@ -237,7 +256,11 @@ async function resolveUpstreamRuntime({
237
256
  env,
238
257
  });
239
258
  const baseUrl = String(env.ANTHROPIC_BASE_URL || "").trim() || "https://api.anthropic.com/v1";
240
- const resolvedModel = String(model || config.routerModel || config.agentModel || "").trim();
259
+ const resolvedModel = String(
260
+ model
261
+ || resolveConfiguredModelForProvider(config, "claude")
262
+ || defaultRouterModelForProvider("claude")
263
+ ).trim();
241
264
  return {
242
265
  provider: "claude",
243
266
  transport: "anthropic-messages",
@@ -252,7 +275,7 @@ async function resolveUpstreamRuntime({
252
275
  const runtime = resolveRuntimeConfig({
253
276
  workspaceRoot: projectRoot,
254
277
  provider: normalizedProvider === "ucode" ? "" : normalizedProvider,
255
- model,
278
+ model: model || resolveConfiguredModelForProvider(config, normalizedProvider) || defaultAgentModelForProvider(config.agentProvider),
256
279
  });
257
280
  const auth = runtime.apiKey ? { apiKey: String(runtime.apiKey || "").trim() } : { headers: {} };
258
281
  return {
@@ -61,6 +61,7 @@ function createAgentViewController(options = {}) {
61
61
  let busLogLines = [];
62
62
  let busStartupAgentId = "";
63
63
  let busStartupLineCount = 0;
64
+ let busAgentReplyActive = false;
64
65
  const originalRender = screen.render.bind(screen);
65
66
  let renderFrozen = false;
66
67
 
@@ -361,6 +362,7 @@ function createAgentViewController(options = {}) {
361
362
  function resetBusView(agentId) {
362
363
  busInputValue = "";
363
364
  busInputCursor = 0;
365
+ busAgentReplyActive = false;
364
366
  busStartupAgentId = agentId || "";
365
367
  const label = getAgentLabel(agentId);
366
368
  const startupLines = staticStartupLines(agentId, label, getCols());
@@ -390,6 +392,42 @@ function createAgentViewController(options = {}) {
390
392
  if (busLogLines.length > 1000) {
391
393
  busLogLines = busLogLines.slice(-1000);
392
394
  }
395
+ if (clean.endsWith("\n")) {
396
+ busAgentReplyActive = false;
397
+ }
398
+ }
399
+
400
+ function ensureBusLinePrefix(prefix = "") {
401
+ if (busLogLines.length === 0) {
402
+ busLogLines.push(prefix);
403
+ return;
404
+ }
405
+ if (busLogLines[busLogLines.length - 1] === "") {
406
+ busLogLines[busLogLines.length - 1] = prefix;
407
+ return;
408
+ }
409
+ busLogLines.push(prefix);
410
+ }
411
+
412
+ function appendBusAgentReply(text = "") {
413
+ const clean = stripAnsi(String(text || "")).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
414
+ if (!clean) return;
415
+ for (const char of clean) {
416
+ if (char === "\n") {
417
+ busLogLines.push("");
418
+ continue;
419
+ }
420
+ if (!busAgentReplyActive) {
421
+ ensureBusLinePrefix("• ");
422
+ busAgentReplyActive = true;
423
+ } else if (busLogLines.length === 0 || busLogLines[busLogLines.length - 1] === "") {
424
+ ensureBusLinePrefix(" ");
425
+ }
426
+ busLogLines[busLogLines.length - 1] += char;
427
+ }
428
+ if (busLogLines.length > 1000) {
429
+ busLogLines = busLogLines.slice(-1000);
430
+ }
393
431
  }
394
432
 
395
433
  function getBusInputViewport(width) {
@@ -533,6 +571,7 @@ function createAgentViewController(options = {}) {
533
571
  busLogLines = [];
534
572
  busStartupAgentId = "";
535
573
  busStartupLineCount = 0;
574
+ busAgentReplyActive = false;
536
575
 
537
576
  currentView = "main";
538
577
  viewingAgent = null;
@@ -661,6 +700,7 @@ function createAgentViewController(options = {}) {
661
700
  return;
662
701
  }
663
702
  appendBusLog(`> ${text}\n`);
703
+ busAgentReplyActive = false;
664
704
  busInputValue = "";
665
705
  busInputCursor = 0;
666
706
  sendBusMessage(viewingAgent, text);
@@ -767,7 +807,7 @@ function createAgentViewController(options = {}) {
767
807
  .replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "")
768
808
  .replace(/\x1b\[(?:[?>=]?[0-9]*c|[?]?6n|5n)/g, "");
769
809
  if (agentViewUsesBus) {
770
- appendBusLog(cleaned);
810
+ appendBusAgentReply(cleaned);
771
811
  renderBusView();
772
812
  return;
773
813
  }
@@ -9,6 +9,9 @@ const {
9
9
  loadGlobalUcodeConfig,
10
10
  saveGlobalUcodeConfig,
11
11
  normalizeControllerMode,
12
+ SETTINGS_MODEL_DEFAULTS,
13
+ defaultAgentModelForProvider,
14
+ defaultRouterModelForProvider,
12
15
  } = require("../config");
13
16
  const { resolveTransport } = require("../code/nativeRunner");
14
17
  const { resolveDisplayNickname } = require("../daemon/nicknameScope");
@@ -43,17 +46,6 @@ function defaultResolveTerminalApp() {
43
46
  return "";
44
47
  }
45
48
 
46
- const SETTINGS_MODEL_DEFAULTS = Object.freeze({
47
- agent: Object.freeze({
48
- codex: "gpt-5.5",
49
- claude: "opus-4.7",
50
- }),
51
- router: Object.freeze({
52
- codex: "gpt-5.4-mini",
53
- claude: "sonnet-4.7",
54
- }),
55
- });
56
-
57
49
  function normalizeSettingsProvider(value = "", fallback = "codex-cli") {
58
50
  const text = String(value || "").trim().toLowerCase();
59
51
  if (text === "claude" || text === "claude-cli" || text === "claude-code" || text === "anthropic") {
@@ -69,13 +61,8 @@ function agentProviderKey(value = "") {
69
61
  return normalizeSettingsProvider(value) === "claude-cli" ? "claude" : "codex";
70
62
  }
71
63
 
72
- function defaultAgentModelForProvider(value = "") {
73
- return SETTINGS_MODEL_DEFAULTS.agent[agentProviderKey(value)] || SETTINGS_MODEL_DEFAULTS.agent.codex;
74
- }
75
-
76
64
  function defaultGateModelForProvider(value = "") {
77
- const key = agentProviderKey(value);
78
- return SETTINGS_MODEL_DEFAULTS.router[key] || SETTINGS_MODEL_DEFAULTS.router.codex;
65
+ return defaultRouterModelForProvider(value);
79
66
  }
80
67
 
81
68
  function collectHostLaunchRequestContext(env = process.env) {
@@ -113,7 +113,7 @@ const COMMAND_TREE = {
113
113
  loop: { desc: "Set router mode to loop", order: 5 },
114
114
  legacy: { desc: "Set router mode to legacy", order: 6 },
115
115
  shadow: { desc: "Set router mode to shadow", order: 7 },
116
- codex: { desc: "Use Codex gate model (gpt-5.4-mini)", order: 8 },
116
+ codex: { desc: "Use Codex gate model (gpt-5.3-codex-spark)", order: 8 },
117
117
  claude: { desc: "Use Claude gate model (sonnet-4.7)", order: 9 },
118
118
  },
119
119
  },
package/src/code/agent.js CHANGED
@@ -17,7 +17,7 @@ const {
17
17
  stripLeakedEscapeTags,
18
18
  } = require("./tui");
19
19
  const { stripBlessedTags } = require("../chat/text");
20
- const { loadConfig } = require("../config");
20
+ const { loadConfig, defaultAgentModelForProvider, sameModelProvider } = require("../config");
21
21
  const {
22
22
  resolveSessionId,
23
23
  normalizeSessionId,
@@ -109,12 +109,14 @@ function resolveUcodeProviderModel({
109
109
  || ""
110
110
  ).trim();
111
111
  const resolvedProvider = resolvePlannerProvider(explicitProvider || fallbackProviderFromAgent);
112
+ const configuredModel = sameModelProvider(config.ucodeProvider || config.agentProvider, resolvedProvider)
113
+ ? (config.ucodeModel || config.agentModel)
114
+ : "";
112
115
  const resolvedModel = String(
113
116
  model
114
117
  || process.env.UFOO_UCODE_MODEL
115
- || config.ucodeModel
116
- || config.agentModel
117
- || ""
118
+ || configuredModel
119
+ || defaultAgentModelForProvider(resolvedProvider)
118
120
  ).trim();
119
121
  return {
120
122
  provider: resolvedProvider,
@@ -1,5 +1,5 @@
1
1
  const { randomUUID } = require("crypto");
2
- const { loadConfig } = require("../config");
2
+ const { loadConfig, defaultAgentModelForProvider, sameModelProvider } = require("../config");
3
3
  const { runToolCall } = require("./dispatch");
4
4
  const { getReadToolDescription } = require("./prompts/toolDescriptions/read");
5
5
  const { getWriteToolDescription } = require("./prompts/toolDescriptions/write");
@@ -219,13 +219,15 @@ function resolveRuntimeConfig({ workspaceRoot = process.cwd(), provider = "", mo
219
219
  || configuredProvider
220
220
  || "openai"
221
221
  ) || "openai";
222
+ const configuredModel = sameModelProvider(config.ucodeProvider || config.agentProvider, selectedProvider)
223
+ ? (config.ucodeModel || config.agentModel)
224
+ : "";
222
225
 
223
226
  const selectedModel = String(
224
227
  model
225
228
  || process.env.UFOO_UCODE_MODEL
226
- || config.ucodeModel
227
- || config.agentModel
228
- || ""
229
+ || configuredModel
230
+ || defaultAgentModelForProvider(selectedProvider)
229
231
  ).trim();
230
232
 
231
233
  const defaultBaseUrl = selectedProvider === "anthropic"
package/src/code/tui.js CHANGED
@@ -18,6 +18,77 @@ const STATUS_INDICATORS = {
18
18
 
19
19
  const ANSI_PATTERN = /\x1B\[[0-9;?]*[ -/]*[@-~]/g;
20
20
 
21
+ function charDisplayWidth(char = "") {
22
+ if (!char) return 0;
23
+ const code = char.codePointAt(0) || 0;
24
+ if (code === 0) return 0;
25
+ if (code < 32 || (code >= 0x7f && code < 0xa0)) return 0;
26
+ if ((code >= 0x0300 && code <= 0x036f) ||
27
+ (code >= 0x1ab0 && code <= 0x1aff) ||
28
+ (code >= 0x1dc0 && code <= 0x1dff) ||
29
+ (code >= 0x20d0 && code <= 0x20ff) ||
30
+ (code >= 0xfe20 && code <= 0xfe2f)) {
31
+ return 0;
32
+ }
33
+ if ((code >= 0x1100 && code <= 0x115f) ||
34
+ code === 0x2329 ||
35
+ code === 0x232a ||
36
+ (code >= 0x2e80 && code <= 0xa4cf) ||
37
+ (code >= 0xac00 && code <= 0xd7a3) ||
38
+ (code >= 0xf900 && code <= 0xfaff) ||
39
+ (code >= 0xfe10 && code <= 0xfe19) ||
40
+ (code >= 0xfe30 && code <= 0xfe6f) ||
41
+ (code >= 0xff00 && code <= 0xff60) ||
42
+ (code >= 0xffe0 && code <= 0xffe6) ||
43
+ (code >= 0x1f300 && code <= 0x1faff)) {
44
+ return 2;
45
+ }
46
+ return 1;
47
+ }
48
+
49
+ function displayCellWidth(text = "") {
50
+ return Array.from(String(text || "").replace(ANSI_PATTERN, "")).reduce(
51
+ (sum, char) => sum + charDisplayWidth(char),
52
+ 0
53
+ );
54
+ }
55
+
56
+ function safeRead(getter, fallback = undefined) {
57
+ try {
58
+ return getter();
59
+ } catch {
60
+ return fallback;
61
+ }
62
+ }
63
+
64
+ function resolveLogContentWidth({ logBox = null, screen = null, fallback = 80 } = {}) {
65
+ const coords = safeRead(() => logBox && typeof logBox._getCoords === "function" ? logBox._getCoords() : null, null);
66
+ if (coords && Number.isFinite(coords.xl) && Number.isFinite(coords.xi)) {
67
+ return Math.max(1, coords.xl - coords.xi);
68
+ }
69
+ const width = safeRead(() => logBox && logBox.width, null);
70
+ if (typeof width === "number") return Math.max(1, width);
71
+ const screenWidth = safeRead(() => screen && screen.width, null);
72
+ if (typeof screenWidth === "number") return Math.max(1, screenWidth);
73
+ const screenCols = safeRead(() => screen && screen.cols, null);
74
+ if (typeof screenCols === "number") return Math.max(1, screenCols);
75
+ return Math.max(1, fallback);
76
+ }
77
+
78
+ function formatHighlightedUserInput(text = "", {
79
+ width = 80,
80
+ escapeText = (value) => String(value || ""),
81
+ } = {}) {
82
+ const plain = String(text || "").trim();
83
+ if (!plain) return "";
84
+ const targetWidth = Math.max(1, Math.floor(Number(width) || 80) - 1);
85
+ const prefix = " → ";
86
+ const suffix = " ";
87
+ const contentWidth = displayCellWidth(`${prefix}${plain}${suffix}`);
88
+ const pad = " ".repeat(Math.max(0, targetWidth - contentWidth));
89
+ return `{cyan-bg}{white-fg}${prefix}${escapeText(plain)}${suffix}${pad}{/white-fg}{/cyan-bg}`;
90
+ }
91
+
21
92
  // Stream buffer for smooth output
22
93
  class StreamBuffer {
23
94
  constructor(writer, options = {}) {
@@ -627,8 +698,8 @@ function runUcodeTui({
627
698
  blessed,
628
699
  currentInputHeight: 4,
629
700
  version: UCODE_VERSION,
630
- logBorder: true,
631
- logScrollbar: true,
701
+ logBorder: false,
702
+ logScrollbar: false,
632
703
  });
633
704
 
634
705
  if (completionPanel && typeof completionPanel.hide === "function") {
@@ -1027,13 +1098,12 @@ function runUcodeTui({
1027
1098
 
1028
1099
  const logUserInput = (text = "") => {
1029
1100
  activeToolMerge = null;
1030
- const plain = String(text || "").trim();
1031
- if (!plain) return;
1032
- const content = ` → ${escapeBlessed(plain)} `;
1033
- const visibleLen = plain.length + 4; // " → " + text + " "
1034
- const boxWidth = (logBox.width || 80) - 2; // subtract border/padding
1035
- const pad = boxWidth > visibleLen ? " ".repeat(boxWidth - visibleLen) : "";
1036
- logBox.log(`{cyan-bg}{white-fg}${content}${pad}{/white-fg}{/cyan-bg}`);
1101
+ const line = formatHighlightedUserInput(text, {
1102
+ width: resolveLogContentWidth({ logBox, screen, fallback: (stdout && stdout.columns) || 80 }),
1103
+ escapeText: escapeBlessed,
1104
+ });
1105
+ if (!line) return;
1106
+ logBox.log(line);
1037
1107
  logBox.log(""); // Add line break after user input
1038
1108
  screen.render();
1039
1109
  };
@@ -1911,6 +1981,9 @@ module.exports = {
1911
1981
  UCODE_BANNER_LINES,
1912
1982
  UCODE_VERSION,
1913
1983
  StreamBuffer,
1984
+ displayCellWidth,
1985
+ resolveLogContentWidth,
1986
+ formatHighlightedUserInput,
1914
1987
  buildUcodeBannerLines,
1915
1988
  buildUcodeBannerBlessedLines,
1916
1989
  parseActiveAgentsFromBusStatus,
package/src/config.js CHANGED
@@ -4,6 +4,17 @@ const path = require("path");
4
4
 
5
5
  const UCODE_FIELDS = ["ucodeProvider", "ucodeModel", "ucodeBaseUrl", "ucodeApiKey", "ucodeAgentDir"];
6
6
 
7
+ const SETTINGS_MODEL_DEFAULTS = Object.freeze({
8
+ agent: Object.freeze({
9
+ codex: "gpt-5.5",
10
+ claude: "opus-4.7",
11
+ }),
12
+ router: Object.freeze({
13
+ codex: "gpt-5.3-codex-spark",
14
+ claude: "sonnet-4.7",
15
+ }),
16
+ });
17
+
7
18
  const DEFAULT_CONFIG = {
8
19
  launchMode: "auto",
9
20
  agentProvider: "codex-cli",
@@ -15,6 +26,8 @@ const DEFAULT_CONFIG = {
15
26
  claudeOauthTokenPath: "",
16
27
  claudeOauthRefreshWindowSec: 300,
17
28
  agentModel: "",
29
+ routerProvider: "",
30
+ routerModel: "",
18
31
  autoResume: false,
19
32
  };
20
33
 
@@ -41,6 +54,33 @@ function normalizeAgentProvider(value) {
41
54
  return "codex-cli";
42
55
  }
43
56
 
57
+ function providerKey(value = "") {
58
+ const text = String(value || "").trim().toLowerCase();
59
+ if (text === "claude" || text === "claude-cli" || text === "claude-code" || text === "anthropic") return "claude";
60
+ return "codex";
61
+ }
62
+
63
+ function sameModelProvider(left = "", right = "") {
64
+ return providerKey(left) === providerKey(right);
65
+ }
66
+
67
+ function defaultAgentModelForProvider(value = "") {
68
+ return SETTINGS_MODEL_DEFAULTS.agent[providerKey(value)] || SETTINGS_MODEL_DEFAULTS.agent.codex;
69
+ }
70
+
71
+ function defaultRouterProviderForAgentProvider(value = "") {
72
+ return providerKey(value) === "claude" ? "claude" : "codex";
73
+ }
74
+
75
+ function defaultRouterModelForProvider(value = "") {
76
+ return SETTINGS_MODEL_DEFAULTS.router[providerKey(value)] || SETTINGS_MODEL_DEFAULTS.router.codex;
77
+ }
78
+
79
+ function normalizeModel(value, fallback = "") {
80
+ const text = typeof value === "string" ? value.trim() : "";
81
+ return text || fallback;
82
+ }
83
+
44
84
  function normalizeControllerMode(value) {
45
85
  const raw = String(value || "").trim().toLowerCase();
46
86
  if (raw === "shadow") return "shadow";
@@ -100,11 +140,19 @@ function loadJsonSafe(filePath) {
100
140
  function loadConfig(projectRoot) {
101
141
  try {
102
142
  const raw = loadJsonSafe(configPath(projectRoot));
143
+ const agentProvider = normalizeAgentProvider(raw.agentProvider);
144
+ const routerProvider = normalizeModel(
145
+ raw.routerProvider,
146
+ defaultRouterProviderForAgentProvider(agentProvider)
147
+ );
103
148
  return {
104
149
  ...DEFAULT_CONFIG,
105
150
  ...raw,
106
151
  launchMode: normalizeLaunchMode(raw.launchMode),
107
- agentProvider: normalizeAgentProvider(raw.agentProvider),
152
+ agentProvider,
153
+ agentModel: normalizeModel(raw.agentModel, defaultAgentModelForProvider(agentProvider)),
154
+ routerProvider,
155
+ routerModel: normalizeModel(raw.routerModel, defaultRouterModelForProvider(routerProvider)),
108
156
  controllerMode: Object.prototype.hasOwnProperty.call(raw, "controllerMode")
109
157
  ? normalizeControllerMode(raw.controllerMode)
110
158
  : DEFAULT_CONFIG.controllerMode,
@@ -121,7 +169,15 @@ function loadConfig(projectRoot) {
121
169
  ...loadGlobalUcodeConfig(),
122
170
  };
123
171
  } catch {
124
- return { ...DEFAULT_CONFIG, ...DEFAULT_UCODE_CONFIG };
172
+ const agentProvider = DEFAULT_CONFIG.agentProvider;
173
+ const routerProvider = defaultRouterProviderForAgentProvider(agentProvider);
174
+ return {
175
+ ...DEFAULT_CONFIG,
176
+ agentModel: defaultAgentModelForProvider(agentProvider),
177
+ routerProvider,
178
+ routerModel: defaultRouterModelForProvider(routerProvider),
179
+ ...DEFAULT_UCODE_CONFIG,
180
+ };
125
181
  }
126
182
  }
127
183
 
@@ -152,6 +208,9 @@ function saveConfig(projectRoot, config) {
152
208
  }
153
209
  merged.launchMode = normalizeLaunchMode(merged.launchMode);
154
210
  merged.agentProvider = normalizeAgentProvider(merged.agentProvider);
211
+ merged.agentModel = typeof merged.agentModel === "string" ? merged.agentModel.trim() : "";
212
+ merged.routerProvider = typeof merged.routerProvider === "string" ? merged.routerProvider.trim() : "";
213
+ merged.routerModel = typeof merged.routerModel === "string" ? merged.routerModel.trim() : "";
155
214
  merged.controllerMode = normalizeControllerMode(merged.controllerMode);
156
215
  merged.codexInternalThreadMode = normalizeCodexInternalThreadMode(merged.codexInternalThreadMode);
157
216
  merged.codexAuthPath = normalizeCodexAuthPath(merged.codexAuthPath);
@@ -190,12 +249,17 @@ function saveGlobalUcodeConfig(updates = {}) {
190
249
  }
191
250
 
192
251
  module.exports = {
252
+ SETTINGS_MODEL_DEFAULTS,
193
253
  loadConfig,
194
254
  saveConfig,
195
255
  loadGlobalUcodeConfig,
196
256
  saveGlobalUcodeConfig,
197
257
  normalizeLaunchMode,
198
258
  normalizeAgentProvider,
259
+ sameModelProvider,
260
+ defaultAgentModelForProvider,
261
+ defaultRouterProviderForAgentProvider,
262
+ defaultRouterModelForProvider,
199
263
  normalizeControllerMode,
200
264
  normalizeCodexInternalThreadMode,
201
265
  normalizeCodexAuthPath,
@@ -1,6 +1,11 @@
1
1
  "use strict";
2
2
 
3
- const { loadConfig } = require("../config");
3
+ const {
4
+ loadConfig,
5
+ defaultRouterProviderForAgentProvider,
6
+ defaultRouterModelForProvider,
7
+ sameModelProvider,
8
+ } = require("../config");
4
9
 
5
10
  const DEFAULT_EXECUTION_PATH = "main";
6
11
  const DEFAULT_CONFIDENCE_THRESHOLD = 0.6;
@@ -122,18 +127,22 @@ function resolveGateRouterConfig({
122
127
  loadConfigImpl = loadConfig,
123
128
  } = {}) {
124
129
  const config = loadConfigImpl(projectRoot || process.cwd());
130
+ const requestProvider = requestMeta.router_provider || env.UFOO_AGENT_ROUTER_PROVIDER;
131
+ const provider = String(
132
+ requestProvider
133
+ || config.routerProvider
134
+ || defaultRouterProviderForAgentProvider(config.agentProvider)
135
+ ).trim();
136
+ const configuredRouterModel = !requestProvider || sameModelProvider(config.routerProvider, provider)
137
+ ? config.routerModel
138
+ : "";
125
139
  return {
126
- provider: String(
127
- requestMeta.router_provider
128
- || env.UFOO_AGENT_ROUTER_PROVIDER
129
- || config.routerProvider
130
- || "ucode"
131
- ).trim(),
140
+ provider,
132
141
  model: String(
133
142
  requestMeta.router_model
134
143
  || env.UFOO_AGENT_ROUTER_MODEL
135
- || config.routerModel
136
- || ""
144
+ || configuredRouterModel
145
+ || defaultRouterModelForProvider(provider)
137
146
  ).trim(),
138
147
  timeoutMs: toPositiveNumber(
139
148
  requestMeta.router_timeout_ms
package/src/daemon/run.js CHANGED
@@ -1,14 +1,17 @@
1
1
  const path = require("path");
2
2
  const { startDaemon, stopDaemon, isRunning } = require("./index");
3
- const { loadConfig } = require("../config");
3
+ const { loadConfig, defaultAgentModelForProvider } = require("../config");
4
4
 
5
5
  function runDaemonCli(argv) {
6
6
  const cmd = argv[1] || "start";
7
7
  const projectRoot = process.cwd();
8
8
  const config = loadConfig(projectRoot);
9
- const provider = process.env.UFOO_AGENT_PROVIDER || config.agentProvider || "codex-cli";
9
+ const envProvider = process.env.UFOO_AGENT_PROVIDER;
10
+ const provider = envProvider || config.agentProvider || "codex-cli";
10
11
  const model =
11
- process.env.UFOO_AGENT_MODEL || config.agentModel || (provider === "claude-cli" ? "opus" : "");
12
+ process.env.UFOO_AGENT_MODEL
13
+ || (envProvider && envProvider !== config.agentProvider ? "" : config.agentModel)
14
+ || defaultAgentModelForProvider(provider);
12
15
  const resumeMode = process.env.UFOO_FORCE_RESUME === "1" ? "force" : "auto";
13
16
  const launchMode = config.launchMode || "terminal";
14
17