doer-agent 0.8.6 → 0.8.7

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,18 @@ 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
+ return settings.codex.providerModels[providerKey] || settings.codex.providerModels.openai || "gpt-5.5";
79
+ }
73
80
  async function generateSkillViaCodex(args) {
74
81
  const localAgentSettings = await args.readAgentSettingsConfig({ workspaceRoot: args.workspaceRoot });
75
82
  const envPatch = args.buildAgentSettingsEnvPatch(localAgentSettings);
76
83
  const prompt = buildSkillGeneratorPrompt(args.userPrompt);
77
- const result = await args.runLocalCodexCli(buildSkillGeneratorCodexArgs(prompt, localAgentSettings.codex.model || "gpt-5.5"), 120_000, envPatch);
84
+ const result = await args.runLocalCodexCli(buildSkillGeneratorCodexArgs(prompt, resolveCodexModel(localAgentSettings)), 120_000, envPatch);
78
85
  if (result.timedOut) {
79
86
  throw new Error("Codex timed out while generating the skill");
80
87
  }
@@ -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,37 @@ 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
+ return settings.codex.providerModels[providerKey] || settings.codex.providerModels.openai || "gpt-5.5";
20
+ }
21
+ function resolveCodexModelContextWindow(settings) {
22
+ return settings.codex.modelProvider === "zai" ? 258400 : null;
23
+ }
24
+ function buildModelProviderConfigArgs(settings) {
25
+ const provider = settings.codex.customProvider;
26
+ const providerId = settings.codex.modelProvider;
27
+ if (!provider || !providerId || provider.id !== providerId) {
28
+ return [];
29
+ }
30
+ return [
31
+ ...buildConfigArg("model_provider", toTomlStringLiteral(provider.id)),
32
+ ...buildConfigArg(`model_providers.${provider.id}.name`, toTomlStringLiteral(provider.name)),
33
+ ...buildConfigArg(`model_providers.${provider.id}.base_url`, toTomlStringLiteral(provider.baseUrl)),
34
+ ...buildConfigArg(`model_providers.${provider.id}.env_key`, toTomlStringLiteral(provider.envKey)),
35
+ ...buildConfigArg(`model_providers.${provider.id}.wire_api`, toTomlStringLiteral("responses")),
36
+ ];
37
+ }
13
38
  async function buildCodexAppServerArgs(args) {
39
+ const codexModel = resolveCodexModel(args.settings);
40
+ const modelContextWindow = resolveCodexModelContextWindow(args.settings);
14
41
  const configArgs = [
15
- ...buildConfigArg("model", toTomlStringLiteral(args.settings.codex.model)),
42
+ ...buildConfigArg("model", toTomlStringLiteral(codexModel)),
43
+ ...(modelContextWindow ? buildConfigArg("model_context_window", String(modelContextWindow)) : []),
44
+ ...buildModelProviderConfigArgs(args.settings),
16
45
  ...buildConfigArg("model_reasoning_effort", toTomlStringLiteral(args.settings.codex.reasoningEffort)),
17
46
  ...(args.settings.codex.serviceTier
18
47
  ? buildConfigArg("service_tier", toTomlStringLiteral(args.settings.codex.serviceTier))
@@ -56,6 +85,34 @@ async function buildCodexAppServerArgs(args) {
56
85
  "stdio://",
57
86
  ];
58
87
  }
88
+ async function resolveAppServerSettings(args) {
89
+ const provider = args.settings.codex.customProvider;
90
+ if (args.settings.codex.modelProvider !== "zai" || provider?.id !== "zai") {
91
+ return { settings: args.settings, proxy: null };
92
+ }
93
+ const proxy = await startCodexChatBridge({
94
+ providerApiKey: provider.apiKey ?? "",
95
+ providerId: provider.id,
96
+ providerName: provider.name,
97
+ targetBaseUrl: provider.baseUrl,
98
+ onLog: args.onLog,
99
+ });
100
+ return {
101
+ proxy,
102
+ settings: {
103
+ ...args.settings,
104
+ codex: {
105
+ ...args.settings.codex,
106
+ customProvider: {
107
+ ...provider,
108
+ baseUrl: proxy.baseUrl,
109
+ envKey: proxy.envKey,
110
+ apiKey: proxy.apiKey,
111
+ },
112
+ },
113
+ },
114
+ };
115
+ }
59
116
  async function buildCodexAppServerEnv(args) {
60
117
  return {
61
118
  ...process.env,
@@ -69,18 +126,21 @@ async function buildCodexAppServerEnv(args) {
69
126
  }
70
127
  export function createCodexAppServerManager(args) {
71
128
  let client = null;
129
+ let providerProxy = null;
72
130
  let createPromise = null;
73
131
  let generation = 0;
74
132
  const notificationListeners = new Set();
75
133
  const createClient = async () => {
76
134
  const settings = await args.readAgentSettingsConfig({ workspaceRoot: args.workspaceRoot });
135
+ const resolved = await resolveAppServerSettings({ settings, onLog: args.onLog });
136
+ providerProxy = resolved.proxy;
77
137
  const appServerArgs = await buildCodexAppServerArgs({
78
138
  agentId: args.agentId,
79
139
  agentToken: args.agentToken,
80
140
  workspaceRoot: args.workspaceRoot,
81
141
  agentProjectDir: args.agentProjectDir,
82
142
  serverBaseUrl: args.serverBaseUrl,
83
- settings,
143
+ settings: resolved.settings,
84
144
  userId: args.userId,
85
145
  });
86
146
  const env = await buildCodexAppServerEnv({
@@ -89,10 +149,10 @@ export function createCodexAppServerManager(args) {
89
149
  workspaceRoot: args.workspaceRoot,
90
150
  serverBaseUrl: args.serverBaseUrl,
91
151
  resolveCodexHomePath: args.resolveCodexHomePath,
92
- settings,
152
+ settings: resolved.settings,
93
153
  userId: args.userId,
94
154
  });
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}`);
155
+ 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
156
  return new CodexAppServerClient({
97
157
  cwd: args.workspaceRoot,
98
158
  args: appServerArgs,
@@ -144,7 +204,9 @@ export function createCodexAppServerManager(args) {
144
204
  async restart(reason) {
145
205
  generation += 1;
146
206
  const activeClient = client;
207
+ const activeProxy = providerProxy;
147
208
  client = null;
209
+ providerProxy = null;
148
210
  createPromise = null;
149
211
  if (!activeClient) {
150
212
  args.onLog?.(`codex app-server restart requested before start reason=${reason}`);
@@ -152,12 +214,20 @@ export function createCodexAppServerManager(args) {
152
214
  }
153
215
  args.onLog?.(`restarting codex app-server reason=${reason}`);
154
216
  await activeClient.stop();
217
+ await activeProxy?.stop().catch((error) => {
218
+ args.onLog?.(`failed to stop provider proxy: ${error instanceof Error ? error.message : String(error)}`);
219
+ });
155
220
  },
156
221
  async stop() {
157
222
  const activeClient = client;
223
+ const activeProxy = providerProxy;
158
224
  client = null;
225
+ providerProxy = null;
159
226
  createPromise = null;
160
227
  await activeClient?.stop();
228
+ await activeProxy?.stop().catch((error) => {
229
+ args.onLog?.(`failed to stop provider proxy: ${error instanceof Error ? error.message : String(error)}`);
230
+ });
161
231
  },
162
232
  };
163
233
  }