doer-agent 0.8.6 → 0.8.8

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.
@@ -15,7 +15,9 @@ export function createDefaultAgentSettingsConfig() {
15
15
  personality: "pragmatic",
16
16
  },
17
17
  codex: {
18
- model: "gpt-5.5",
18
+ providerModels: { openai: "gpt-5.5" },
19
+ modelProvider: null,
20
+ customProvider: null,
19
21
  reasoningEffort: "medium",
20
22
  serviceTier: null,
21
23
  authMode: "api_key",
@@ -63,6 +65,50 @@ function normalizeServiceTier(value) {
63
65
  }
64
66
  return normalized;
65
67
  }
68
+ function normalizeCodexCustomProvider(value, fallback = null) {
69
+ if (value === null) {
70
+ return null;
71
+ }
72
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
73
+ return fallback;
74
+ }
75
+ const raw = value;
76
+ const id = typeof raw.id === "string" ? raw.id.trim() : "";
77
+ const baseUrl = typeof raw.baseUrl === "string" ? raw.baseUrl.trim() : "";
78
+ if (!id || !baseUrl) {
79
+ return fallback;
80
+ }
81
+ const name = typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : id;
82
+ const envKey = typeof raw.envKey === "string" && raw.envKey.trim() ? raw.envKey.trim() : `${id.toUpperCase()}_API_KEY`;
83
+ const apiKey = raw.apiKey === null
84
+ ? null
85
+ : typeof raw.apiKey === "string" && raw.apiKey.trim()
86
+ ? raw.apiKey.trim()
87
+ : fallback?.id === id
88
+ ? fallback.apiKey
89
+ : null;
90
+ return {
91
+ id,
92
+ name,
93
+ baseUrl,
94
+ envKey,
95
+ apiKey,
96
+ };
97
+ }
98
+ function normalizeCodexProviderModels(value, fallback) {
99
+ const models = { ...fallback };
100
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
101
+ return models;
102
+ }
103
+ for (const [rawKey, rawModel] of Object.entries(value)) {
104
+ const key = rawKey.trim();
105
+ const model = typeof rawModel === "string" ? rawModel.trim() : "";
106
+ if (key && model) {
107
+ models[key] = model;
108
+ }
109
+ }
110
+ return models;
111
+ }
66
112
  function normalizeCodexPersonality(value, fallback) {
67
113
  return value === "friendly" || value === "pragmatic" ? value : fallback;
68
114
  }
@@ -200,7 +246,13 @@ export function normalizeAgentSettingsConfig(value, fallback) {
200
246
  personality: normalizeCodexPersonality(general.personality, base.general.personality),
201
247
  },
202
248
  codex: {
203
- model: typeof codex.model === "string" && codex.model.trim() ? codex.model.trim() : base.codex.model,
249
+ providerModels: normalizeCodexProviderModels(codex.providerModels, base.codex.providerModels),
250
+ modelProvider: codex.modelProvider === null
251
+ ? null
252
+ : typeof codex.modelProvider === "string" && codex.modelProvider.trim()
253
+ ? codex.modelProvider.trim()
254
+ : base.codex.modelProvider,
255
+ customProvider: normalizeCodexCustomProvider(codex.customProvider, base.codex.customProvider),
204
256
  reasoningEffort: normalizeReasoningEffort(codex.reasoningEffort, base.codex.reasoningEffort),
205
257
  serviceTier: codex.serviceTier === null ? null : normalizeServiceTier(codex.serviceTier) ?? base.codex.serviceTier,
206
258
  authMode: codex.authMode === "chatgpt" ? "chatgpt" : codex.authMode === "api_key" ? "api_key" : base.codex.authMode,
@@ -275,6 +327,7 @@ function toMaskedSecret(value) {
275
327
  export async function toAgentSettingsPublic(args) {
276
328
  const realtimeKey = toMaskedSecret(args.config.realtime.apiKey);
277
329
  const gitOauth = toMaskedSecret(args.config.git.oauthToken);
330
+ const codexProviderKey = toMaskedSecret(args.config.codex.customProvider?.apiKey ?? null);
278
331
  const customInstructions = await readAgentModelInstructions(args.workspaceRoot);
279
332
  return {
280
333
  general: {
@@ -282,7 +335,19 @@ export async function toAgentSettingsPublic(args) {
282
335
  customInstructions,
283
336
  },
284
337
  codex: {
285
- model: args.config.codex.model,
338
+ providerModels: { ...args.config.codex.providerModels },
339
+ modelProvider: args.config.codex.modelProvider,
340
+ customProvider: args.config.codex.customProvider
341
+ ? {
342
+ id: args.config.codex.customProvider.id,
343
+ name: args.config.codex.customProvider.name,
344
+ baseUrl: args.config.codex.customProvider.baseUrl,
345
+ envKey: args.config.codex.customProvider.envKey,
346
+ hasApiKey: codexProviderKey.has,
347
+ apiKeyMasked: codexProviderKey.masked,
348
+ apiKeyLength: codexProviderKey.length,
349
+ }
350
+ : null,
286
351
  reasoningEffort: args.config.codex.reasoningEffort,
287
352
  serviceTier: args.config.codex.serviceTier,
288
353
  authMode: args.config.codex.authMode,
@@ -353,7 +418,9 @@ export function normalizeAgentSettingsPatch(value) {
353
418
  delete patch[flatKey];
354
419
  };
355
420
  move("personality", "general", "personality");
356
- move("codexModel", "codex", "model");
421
+ move("codexProviderModels", "codex", "providerModels");
422
+ move("codexModelProvider", "codex", "modelProvider");
423
+ move("codexCustomProvider", "codex", "customProvider");
357
424
  move("codexReasoningEffort", "codex", "reasoningEffort");
358
425
  move("codexServiceTier", "codex", "serviceTier");
359
426
  move("codexAuthMode", "codex", "authMode");
@@ -377,6 +444,9 @@ export function normalizeAgentSettingsPatch(value) {
377
444
  }
378
445
  export function buildAgentSettingsEnvPatch(config) {
379
446
  const envPatch = {};
447
+ if (config.codex.customProvider?.apiKey && config.codex.customProvider.envKey) {
448
+ envPatch[config.codex.customProvider.envKey] = config.codex.customProvider.apiKey;
449
+ }
380
450
  if (config.git.enabled) {
381
451
  if (config.git.name)
382
452
  envPatch.GIT_AUTHOR_NAME = config.git.name;
@@ -70,11 +70,21 @@ function normalizeGeneratedSkill(value) {
70
70
  function buildSkillGeneratorCodexArgs(prompt, model) {
71
71
  return ["--dangerously-bypass-approvals-and-sandbox", "--model", model, "exec", "--", prompt];
72
72
  }
73
+ function resolveCodexModel(settings) {
74
+ const providerKey = settings.codex.modelProvider || "openai";
75
+ if (providerKey === "zai") {
76
+ return settings.codex.providerModels.zai || "glm-5.2";
77
+ }
78
+ if (providerKey === "anthropic") {
79
+ return settings.codex.providerModels.anthropic || "claude-sonnet-4-6";
80
+ }
81
+ return settings.codex.providerModels[providerKey] || settings.codex.providerModels.openai || "gpt-5.5";
82
+ }
73
83
  async function generateSkillViaCodex(args) {
74
84
  const localAgentSettings = await args.readAgentSettingsConfig({ workspaceRoot: args.workspaceRoot });
75
85
  const envPatch = args.buildAgentSettingsEnvPatch(localAgentSettings);
76
86
  const prompt = buildSkillGeneratorPrompt(args.userPrompt);
77
- const result = await args.runLocalCodexCli(buildSkillGeneratorCodexArgs(prompt, localAgentSettings.codex.model || "gpt-5.5"), 120_000, envPatch);
87
+ const result = await args.runLocalCodexCli(buildSkillGeneratorCodexArgs(prompt, resolveCodexModel(localAgentSettings)), 120_000, envPatch);
78
88
  if (result.timedOut) {
79
89
  throw new Error("Codex timed out while generating the skill");
80
90
  }
@@ -1,6 +1,7 @@
1
1
  import { buildAgentSettingsEnvPatch, readAgentModelInstructions, resolveAgentModelInstructionsFilePath, } from "./agent-settings.js";
2
2
  import { buildCustomMcpConfigArgs, buildDaemonMcpConfigArgs, buildMobileMcpConfigArgs, buildThreadsMcpConfigArgs } from "./agent-codex-cli.js";
3
3
  import { CodexAppServerClient } from "./codex-app-server-client.js";
4
+ import { startCodexChatBridge } from "./codex-chat-bridge.js";
4
5
  function toTomlStringLiteral(value) {
5
6
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
6
7
  }
@@ -10,9 +11,47 @@ function buildConfigArg(key, tomlValue) {
10
11
  function buildFeatureArg(enabled, name) {
11
12
  return [enabled ? "--enable" : "--disable", name];
12
13
  }
14
+ function resolveCodexModel(settings) {
15
+ const providerKey = settings.codex.modelProvider || "openai";
16
+ if (providerKey === "zai") {
17
+ return settings.codex.providerModels.zai || "glm-5.2";
18
+ }
19
+ if (providerKey === "anthropic") {
20
+ return settings.codex.providerModels.anthropic || "claude-sonnet-4-6";
21
+ }
22
+ return settings.codex.providerModels[providerKey] || settings.codex.providerModels.openai || "gpt-5.5";
23
+ }
24
+ function resolveCodexModelContextWindow(settings) {
25
+ if (settings.codex.modelProvider === "zai") {
26
+ return 258400;
27
+ }
28
+ if (settings.codex.modelProvider === "anthropic") {
29
+ const model = resolveCodexModel(settings);
30
+ return model.startsWith("claude-haiku-") ? 200000 : 1000000;
31
+ }
32
+ return null;
33
+ }
34
+ function buildModelProviderConfigArgs(settings) {
35
+ const provider = settings.codex.customProvider;
36
+ const providerId = settings.codex.modelProvider;
37
+ if (!provider || !providerId || provider.id !== providerId) {
38
+ return [];
39
+ }
40
+ return [
41
+ ...buildConfigArg("model_provider", toTomlStringLiteral(provider.id)),
42
+ ...buildConfigArg(`model_providers.${provider.id}.name`, toTomlStringLiteral(provider.name)),
43
+ ...buildConfigArg(`model_providers.${provider.id}.base_url`, toTomlStringLiteral(provider.baseUrl)),
44
+ ...buildConfigArg(`model_providers.${provider.id}.env_key`, toTomlStringLiteral(provider.envKey)),
45
+ ...buildConfigArg(`model_providers.${provider.id}.wire_api`, toTomlStringLiteral("responses")),
46
+ ];
47
+ }
13
48
  async function buildCodexAppServerArgs(args) {
49
+ const codexModel = resolveCodexModel(args.settings);
50
+ const modelContextWindow = resolveCodexModelContextWindow(args.settings);
14
51
  const configArgs = [
15
- ...buildConfigArg("model", toTomlStringLiteral(args.settings.codex.model)),
52
+ ...buildConfigArg("model", toTomlStringLiteral(codexModel)),
53
+ ...(modelContextWindow ? buildConfigArg("model_context_window", String(modelContextWindow)) : []),
54
+ ...buildModelProviderConfigArgs(args.settings),
16
55
  ...buildConfigArg("model_reasoning_effort", toTomlStringLiteral(args.settings.codex.reasoningEffort)),
17
56
  ...(args.settings.codex.serviceTier
18
57
  ? buildConfigArg("service_tier", toTomlStringLiteral(args.settings.codex.serviceTier))
@@ -56,6 +95,36 @@ async function buildCodexAppServerArgs(args) {
56
95
  "stdio://",
57
96
  ];
58
97
  }
98
+ async function resolveAppServerSettings(args) {
99
+ const provider = args.settings.codex.customProvider;
100
+ const providerId = args.settings.codex.modelProvider;
101
+ const gatewayProviderIds = new Set(["zai", "anthropic"]);
102
+ if (!providerId || provider?.id !== providerId || !gatewayProviderIds.has(providerId)) {
103
+ return { settings: args.settings, proxy: null };
104
+ }
105
+ const proxy = await startCodexChatBridge({
106
+ providerApiKey: provider.apiKey ?? "",
107
+ providerId: provider.id,
108
+ providerName: provider.name,
109
+ targetBaseUrl: provider.baseUrl,
110
+ onLog: args.onLog,
111
+ });
112
+ return {
113
+ proxy,
114
+ settings: {
115
+ ...args.settings,
116
+ codex: {
117
+ ...args.settings.codex,
118
+ customProvider: {
119
+ ...provider,
120
+ baseUrl: proxy.baseUrl,
121
+ envKey: proxy.envKey,
122
+ apiKey: proxy.apiKey,
123
+ },
124
+ },
125
+ },
126
+ };
127
+ }
59
128
  async function buildCodexAppServerEnv(args) {
60
129
  return {
61
130
  ...process.env,
@@ -69,18 +138,21 @@ async function buildCodexAppServerEnv(args) {
69
138
  }
70
139
  export function createCodexAppServerManager(args) {
71
140
  let client = null;
141
+ let providerProxy = null;
72
142
  let createPromise = null;
73
143
  let generation = 0;
74
144
  const notificationListeners = new Set();
75
145
  const createClient = async () => {
76
146
  const settings = await args.readAgentSettingsConfig({ workspaceRoot: args.workspaceRoot });
147
+ const resolved = await resolveAppServerSettings({ settings, onLog: args.onLog });
148
+ providerProxy = resolved.proxy;
77
149
  const appServerArgs = await buildCodexAppServerArgs({
78
150
  agentId: args.agentId,
79
151
  agentToken: args.agentToken,
80
152
  workspaceRoot: args.workspaceRoot,
81
153
  agentProjectDir: args.agentProjectDir,
82
154
  serverBaseUrl: args.serverBaseUrl,
83
- settings,
155
+ settings: resolved.settings,
84
156
  userId: args.userId,
85
157
  });
86
158
  const env = await buildCodexAppServerEnv({
@@ -89,10 +161,10 @@ export function createCodexAppServerManager(args) {
89
161
  workspaceRoot: args.workspaceRoot,
90
162
  serverBaseUrl: args.serverBaseUrl,
91
163
  resolveCodexHomePath: args.resolveCodexHomePath,
92
- settings,
164
+ settings: resolved.settings,
93
165
  userId: args.userId,
94
166
  });
95
- args.onLog?.(`starting codex app-server model=${settings.codex.model} reasoningEffort=${settings.codex.reasoningEffort} personality=${settings.general.personality} computerUse=${settings.codex.computerUseEnabled} browserUse=${settings.codex.browserUseEnabled} mcpServers=${settings.mcp.servers.filter((server) => server.enabled).length}`);
167
+ args.onLog?.(`starting codex app-server model=${resolveCodexModel(resolved.settings)} reasoningEffort=${resolved.settings.codex.reasoningEffort} personality=${resolved.settings.general.personality} computerUse=${resolved.settings.codex.computerUseEnabled} browserUse=${resolved.settings.codex.browserUseEnabled} mcpServers=${resolved.settings.mcp.servers.filter((server) => server.enabled).length}`);
96
168
  return new CodexAppServerClient({
97
169
  cwd: args.workspaceRoot,
98
170
  args: appServerArgs,
@@ -144,7 +216,9 @@ export function createCodexAppServerManager(args) {
144
216
  async restart(reason) {
145
217
  generation += 1;
146
218
  const activeClient = client;
219
+ const activeProxy = providerProxy;
147
220
  client = null;
221
+ providerProxy = null;
148
222
  createPromise = null;
149
223
  if (!activeClient) {
150
224
  args.onLog?.(`codex app-server restart requested before start reason=${reason}`);
@@ -152,12 +226,20 @@ export function createCodexAppServerManager(args) {
152
226
  }
153
227
  args.onLog?.(`restarting codex app-server reason=${reason}`);
154
228
  await activeClient.stop();
229
+ await activeProxy?.stop().catch((error) => {
230
+ args.onLog?.(`failed to stop provider proxy: ${error instanceof Error ? error.message : String(error)}`);
231
+ });
155
232
  },
156
233
  async stop() {
157
234
  const activeClient = client;
235
+ const activeProxy = providerProxy;
158
236
  client = null;
237
+ providerProxy = null;
159
238
  createPromise = null;
160
239
  await activeClient?.stop();
240
+ await activeProxy?.stop().catch((error) => {
241
+ args.onLog?.(`failed to stop provider proxy: ${error instanceof Error ? error.message : String(error)}`);
242
+ });
161
243
  },
162
244
  };
163
245
  }