@compyle/unagent 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +547 -0
  3. package/dist/agents/agent-as-tool.d.ts +4 -0
  4. package/dist/agents/agent-as-tool.d.ts.map +1 -0
  5. package/dist/agents/agent-as-tool.js +105 -0
  6. package/dist/agents/agent-as-tool.js.map +1 -0
  7. package/dist/agents/define-agent.d.ts +101 -0
  8. package/dist/agents/define-agent.d.ts.map +1 -0
  9. package/dist/agents/define-agent.js +866 -0
  10. package/dist/agents/define-agent.js.map +1 -0
  11. package/dist/agents/index.d.ts +3 -0
  12. package/dist/agents/index.d.ts.map +1 -0
  13. package/dist/agents/index.js +3 -0
  14. package/dist/agents/index.js.map +1 -0
  15. package/dist/bridge/node-bridge.d.ts +2 -0
  16. package/dist/bridge/node-bridge.d.ts.map +1 -0
  17. package/dist/bridge/node-bridge.js +772 -0
  18. package/dist/bridge/node-bridge.js.map +1 -0
  19. package/dist/client/base-client.d.ts +86 -0
  20. package/dist/client/base-client.d.ts.map +1 -0
  21. package/dist/client/base-client.js +254 -0
  22. package/dist/client/base-client.js.map +1 -0
  23. package/dist/client/claude.d.ts +66 -0
  24. package/dist/client/claude.d.ts.map +1 -0
  25. package/dist/client/claude.js +846 -0
  26. package/dist/client/claude.js.map +1 -0
  27. package/dist/client/codex/codex-client.d.ts +78 -0
  28. package/dist/client/codex/codex-client.d.ts.map +1 -0
  29. package/dist/client/codex/codex-client.js +906 -0
  30. package/dist/client/codex/codex-client.js.map +1 -0
  31. package/dist/client/codex/codex-home.d.ts +6 -0
  32. package/dist/client/codex/codex-home.d.ts.map +1 -0
  33. package/dist/client/codex/codex-home.js +26 -0
  34. package/dist/client/codex/codex-home.js.map +1 -0
  35. package/dist/client/codex/config/config-loader.d.ts +8 -0
  36. package/dist/client/codex/config/config-loader.d.ts.map +1 -0
  37. package/dist/client/codex/config/config-loader.js +137 -0
  38. package/dist/client/codex/config/config-loader.js.map +1 -0
  39. package/dist/client/codex/config/config-types.d.ts +43 -0
  40. package/dist/client/codex/config/config-types.d.ts.map +1 -0
  41. package/dist/client/codex/config/config-types.js +2 -0
  42. package/dist/client/codex/config/config-types.js.map +1 -0
  43. package/dist/client/codex/config/config-writer.d.ts +24 -0
  44. package/dist/client/codex/config/config-writer.d.ts.map +1 -0
  45. package/dist/client/codex/config/config-writer.js +353 -0
  46. package/dist/client/codex/config/config-writer.js.map +1 -0
  47. package/dist/client/codex/config/index.d.ts +7 -0
  48. package/dist/client/codex/config/index.d.ts.map +1 -0
  49. package/dist/client/codex/config/index.js +6 -0
  50. package/dist/client/codex/config/index.js.map +1 -0
  51. package/dist/client/codex/config/mcp-config-generator.d.ts +7 -0
  52. package/dist/client/codex/config/mcp-config-generator.d.ts.map +1 -0
  53. package/dist/client/codex/config/mcp-config-generator.js +140 -0
  54. package/dist/client/codex/config/mcp-config-generator.js.map +1 -0
  55. package/dist/client/codex/config/tools-config-writer.d.ts +2 -0
  56. package/dist/client/codex/config/tools-config-writer.d.ts.map +1 -0
  57. package/dist/client/codex/config/tools-config-writer.js +21 -0
  58. package/dist/client/codex/config/tools-config-writer.js.map +1 -0
  59. package/dist/client/codex/index.d.ts +4 -0
  60. package/dist/client/codex/index.d.ts.map +1 -0
  61. package/dist/client/codex/index.js +3 -0
  62. package/dist/client/codex/index.js.map +1 -0
  63. package/dist/client/config-utils.d.ts +4 -0
  64. package/dist/client/config-utils.d.ts.map +1 -0
  65. package/dist/client/config-utils.js +32 -0
  66. package/dist/client/config-utils.js.map +1 -0
  67. package/dist/client/extra-headers.d.ts +12 -0
  68. package/dist/client/extra-headers.d.ts.map +1 -0
  69. package/dist/client/extra-headers.js +102 -0
  70. package/dist/client/extra-headers.js.map +1 -0
  71. package/dist/client/field-mappings.d.ts +3 -0
  72. package/dist/client/field-mappings.d.ts.map +1 -0
  73. package/dist/client/field-mappings.js +40 -0
  74. package/dist/client/field-mappings.js.map +1 -0
  75. package/dist/client/transformers/content-utils.d.ts +34 -0
  76. package/dist/client/transformers/content-utils.d.ts.map +1 -0
  77. package/dist/client/transformers/content-utils.js +237 -0
  78. package/dist/client/transformers/content-utils.js.map +1 -0
  79. package/dist/events/event-bus.d.ts +19 -0
  80. package/dist/events/event-bus.d.ts.map +1 -0
  81. package/dist/events/event-bus.js +134 -0
  82. package/dist/events/event-bus.js.map +1 -0
  83. package/dist/events/event-types.d.ts +53 -0
  84. package/dist/events/event-types.d.ts.map +1 -0
  85. package/dist/events/event-types.js +6 -0
  86. package/dist/events/event-types.js.map +1 -0
  87. package/dist/events/index.d.ts +4 -0
  88. package/dist/events/index.d.ts.map +1 -0
  89. package/dist/events/index.js +4 -0
  90. package/dist/events/index.js.map +1 -0
  91. package/dist/index.d.ts +12 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +11 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/messages/index.d.ts +3 -0
  96. package/dist/messages/index.d.ts.map +1 -0
  97. package/dist/messages/index.js +3 -0
  98. package/dist/messages/index.js.map +1 -0
  99. package/dist/messages/message-types.d.ts +60 -0
  100. package/dist/messages/message-types.d.ts.map +1 -0
  101. package/dist/messages/message-types.js +6 -0
  102. package/dist/messages/message-types.js.map +1 -0
  103. package/dist/messages/message-utils.d.ts +51 -0
  104. package/dist/messages/message-utils.d.ts.map +1 -0
  105. package/dist/messages/message-utils.js +343 -0
  106. package/dist/messages/message-utils.js.map +1 -0
  107. package/dist/tools/define-tool.d.ts +6 -0
  108. package/dist/tools/define-tool.d.ts.map +1 -0
  109. package/dist/tools/define-tool.js +83 -0
  110. package/dist/tools/define-tool.js.map +1 -0
  111. package/dist/tools/index.d.ts +6 -0
  112. package/dist/tools/index.d.ts.map +1 -0
  113. package/dist/tools/index.js +6 -0
  114. package/dist/tools/index.js.map +1 -0
  115. package/dist/tools/mcp-stdio-proxy.d.ts +2 -0
  116. package/dist/tools/mcp-stdio-proxy.d.ts.map +1 -0
  117. package/dist/tools/mcp-stdio-proxy.js +31 -0
  118. package/dist/tools/mcp-stdio-proxy.js.map +1 -0
  119. package/dist/tools/tool-call-context.d.ts +8 -0
  120. package/dist/tools/tool-call-context.d.ts.map +1 -0
  121. package/dist/tools/tool-call-context.js +44 -0
  122. package/dist/tools/tool-call-context.js.map +1 -0
  123. package/dist/tools/tool-registry.d.ts +14 -0
  124. package/dist/tools/tool-registry.d.ts.map +1 -0
  125. package/dist/tools/tool-registry.js +47 -0
  126. package/dist/tools/tool-registry.js.map +1 -0
  127. package/dist/tools/tool-result-converter.d.ts +3 -0
  128. package/dist/tools/tool-result-converter.d.ts.map +1 -0
  129. package/dist/tools/tool-result-converter.js +38 -0
  130. package/dist/tools/tool-result-converter.js.map +1 -0
  131. package/dist/tools/tool-service-manager.d.ts +12 -0
  132. package/dist/tools/tool-service-manager.d.ts.map +1 -0
  133. package/dist/tools/tool-service-manager.js +118 -0
  134. package/dist/tools/tool-service-manager.js.map +1 -0
  135. package/dist/tools/tool-stdio-mcp-service.d.ts +29 -0
  136. package/dist/tools/tool-stdio-mcp-service.d.ts.map +1 -0
  137. package/dist/tools/tool-stdio-mcp-service.js +185 -0
  138. package/dist/tools/tool-stdio-mcp-service.js.map +1 -0
  139. package/dist/tools/tool-types.d.ts +29 -0
  140. package/dist/tools/tool-types.d.ts.map +1 -0
  141. package/dist/tools/tool-types.js +2 -0
  142. package/dist/tools/tool-types.js.map +1 -0
  143. package/package.json +58 -0
@@ -0,0 +1,906 @@
1
+ import * as fs from "fs";
2
+ import { BaseClient, } from "../base-client.js";
3
+ import { ClientType, } from "../../events/index.js";
4
+ import { MessageRole, createAssistantMessage, createToolCallResultMessage, normalizeMessage, } from "../../messages/index.js";
5
+ import { loadConfigFromToml, writeCodexConfigUpdates, writeSkillsConfig, } from "./config/index.js";
6
+ import { extractCustomTools, isRegisteredTool } from "../../tools/define-tool.js";
7
+ import { getAgentToolServiceCommand, resolveToolServiceOwner } from "../../tools/tool-service-manager.js";
8
+ import { stringifyContent, buildToolUseFromRecord, ToolCallTracker, } from "../transformers/content-utils.js";
9
+ import { convertSnakeCaseToCamelCase } from "../config-utils.js";
10
+ import { CODEX_FIELD_MAPPINGS } from "../field-mappings.js";
11
+ import { resolveCodexHomeDir } from "./codex-home.js";
12
+ const DEFAULT_CUSTOM_TOOL_TIMEOUT_SEC = 5184000;
13
+ export class CodexClient extends BaseClient {
14
+ codex;
15
+ thread;
16
+ toolCallTracker = new ToolCallTracker();
17
+ streamBuffer = "";
18
+ isStreamingAssistant = false;
19
+ codexConfig;
20
+ codexOptions;
21
+ threadOptions = {};
22
+ customTools = [];
23
+ customToolsInitialized = false;
24
+ userProvidedCodexHome = false;
25
+ codexHomeDir;
26
+ constructor(config) {
27
+ super(ClientType.CODEX, config);
28
+ const resolvedEnv = { ...(config.env ?? {}) };
29
+ const normalizedConfig = convertSnakeCaseToCamelCase({ ...config, env: resolvedEnv }, CODEX_FIELD_MAPPINGS);
30
+ const codexHomeKey = resolveString(normalizedConfig.codexHomeKey);
31
+ const explicitCodexHome = resolveString(resolvedEnv.CODEX_HOME);
32
+ const codexHomeResolution = resolveCodexHomeDir(explicitCodexHome, codexHomeKey);
33
+ this.userProvidedCodexHome = codexHomeResolution.userProvided;
34
+ this.codexHomeDir = codexHomeResolution.codexHomeDir;
35
+ if (!explicitCodexHome) {
36
+ resolvedEnv.CODEX_HOME = this.codexHomeDir;
37
+ }
38
+ this.codexConfig = normalizedConfig;
39
+ const agents = this.codexConfig.agents;
40
+ const systemPrompt = resolveString(this.codexConfig.systemPrompt) ?? resolveAgentSystemPrompt(agents);
41
+ const model = resolveString(this.codexConfig.model);
42
+ const addDirs = resolveStringArray(this.codexConfig.addDirs);
43
+ const workingDirectory = resolveString(this.codexConfig.cwd ?? this.codexConfig.workingDirectory);
44
+ const mcpServers = resolveMcpServers(this.codexConfig.mcpServers);
45
+ const skillDir = resolveString(this.codexConfig.skillDir);
46
+ const allowWriteWithUserCodexHome = this.codexConfig.allowConfigWriteWithUserCodexHome === true;
47
+ const codexHome = this.userProvidedCodexHome && !allowWriteWithUserCodexHome
48
+ ? undefined
49
+ : this.codexHomeDir;
50
+ const sandboxMode = mapPermissionModeToSandboxMode(this.codexConfig.permissionMode);
51
+ const codexOptions = {};
52
+ const threadOptions = {};
53
+ if (sandboxMode) {
54
+ threadOptions.sandboxMode = sandboxMode;
55
+ }
56
+ if (this.codexConfig.apiKey) {
57
+ codexOptions.apiKey = this.codexConfig.apiKey;
58
+ }
59
+ if (this.codexConfig.baseURL) {
60
+ codexOptions.baseUrl = this.codexConfig.baseURL;
61
+ }
62
+ codexOptions.env = resolvedEnv;
63
+ const sdkConfig = isRecord(this.codexConfig.config)
64
+ ? this.codexConfig.config
65
+ : undefined;
66
+ if (sdkConfig && Object.keys(sdkConfig).length > 0) {
67
+ codexOptions.config = sdkConfig;
68
+ }
69
+ const modelProviderOverride = buildModelProviderOverride(sdkConfig, resolvedEnv);
70
+ const configUpdates = {};
71
+ if (modelProviderOverride) {
72
+ if (codexHome) {
73
+ configUpdates.modelProviders = {
74
+ [modelProviderOverride.name]: modelProviderOverride.config,
75
+ };
76
+ configUpdates.modelProvider = modelProviderOverride.name;
77
+ }
78
+ else if (this.userProvidedCodexHome) {
79
+ console.warn("Model provider headers must be configured in config.toml. " +
80
+ "Set allowConfigWriteWithUserCodexHome=true or update CODEX_HOME/config.toml manually.");
81
+ }
82
+ }
83
+ if (systemPrompt) {
84
+ if (codexHome) {
85
+ configUpdates.developerInstructions = systemPrompt;
86
+ }
87
+ else if (!this.userProvidedCodexHome) {
88
+ console.warn("Codex system prompt must be configured in config.toml developer_instructions. " +
89
+ "Set env.CODEX_HOME to write it automatically.");
90
+ }
91
+ }
92
+ if (model) {
93
+ threadOptions.model = model;
94
+ if (codexHome) {
95
+ configUpdates.model = model;
96
+ }
97
+ }
98
+ if (addDirs.length > 0) {
99
+ threadOptions.additionalDirectories = [...addDirs];
100
+ if (codexHome) {
101
+ configUpdates.writableRoots = [...addDirs];
102
+ }
103
+ }
104
+ if (workingDirectory) {
105
+ threadOptions.workingDirectory = workingDirectory;
106
+ }
107
+ if (mcpServers && codexHome) {
108
+ configUpdates.mcpServers = mcpServers;
109
+ }
110
+ else if (mcpServers && Object.keys(mcpServers).length > 0) {
111
+ console.warn("MCP servers must be configured in config.toml. " +
112
+ "Set allowConfigWriteWithUserCodexHome=true or update CODEX_HOME/config.toml manually.");
113
+ }
114
+ if (codexHome && Object.keys(configUpdates).length > 0) {
115
+ writeCodexConfigUpdates(configUpdates, codexHome);
116
+ }
117
+ if (skillDir && codexHome) {
118
+ writeSkillsConfig(skillDir, codexHome);
119
+ }
120
+ else if (skillDir && this.userProvidedCodexHome && !allowWriteWithUserCodexHome) {
121
+ console.warn("Skills must be linked at CODEX_HOME/skills. " +
122
+ "Set allowConfigWriteWithUserCodexHome=true or create the symlink manually.");
123
+ }
124
+ const customTools = safeExtractCustomTools(this.codexConfig.tools);
125
+ this.customTools = customTools;
126
+ if (codexHome && this.codexConfig.loadConfigFromFile !== false) {
127
+ try {
128
+ const fileConfig = loadConfigFromToml(codexHome);
129
+ if (fileConfig) {
130
+ const modelName = resolveModelName(model, fileConfig.model);
131
+ if (modelName && !threadOptions.model) {
132
+ threadOptions.model = modelName;
133
+ }
134
+ }
135
+ }
136
+ catch (error) {
137
+ console.warn(`Failed to load config from ${codexHome}:`, error);
138
+ }
139
+ }
140
+ this.codexOptions = codexOptions;
141
+ this.threadOptions = threadOptions;
142
+ }
143
+ async getCodex() {
144
+ if (this.codex) {
145
+ return this.codex;
146
+ }
147
+ const allowWriteWithUserCodexHome = this.codexConfig.allowConfigWriteWithUserCodexHome === true;
148
+ const canWriteCustomTools = !this.userProvidedCodexHome || allowWriteWithUserCodexHome;
149
+ if (this.customTools.length > 0 && !this.customToolsInitialized && canWriteCustomTools) {
150
+ const toolServiceOwner = resolveToolServiceOwner(this.codexConfig) ?? this;
151
+ const toolServiceCommand = await getAgentToolServiceCommand(toolServiceOwner, this.customTools, {
152
+ name: "custom-tools",
153
+ version: "1.0.0",
154
+ });
155
+ this.customToolsInitialized = true;
156
+ const mcpServersConfig = {
157
+ "custom-tools": {
158
+ command: toolServiceCommand.command,
159
+ args: toolServiceCommand.args,
160
+ ...(toolServiceCommand.env ? { env: toolServiceCommand.env } : {}),
161
+ tool_timeout_sec: DEFAULT_CUSTOM_TOOL_TIMEOUT_SEC,
162
+ },
163
+ };
164
+ let codexHome = this.codexOptions.env?.CODEX_HOME;
165
+ if (!codexHome) {
166
+ const fallbackKey = resolveString(this.codexConfig.codexHomeKey);
167
+ codexHome = resolveCodexHomeDir(undefined, fallbackKey).codexHomeDir;
168
+ this.codexOptions.env = {
169
+ ...(this.codexOptions.env || {}),
170
+ CODEX_HOME: codexHome,
171
+ };
172
+ }
173
+ writeCodexConfigUpdates({ mcpServers: mcpServersConfig }, codexHome);
174
+ }
175
+ else if (this.customTools.length > 0 && !this.customToolsInitialized && this.userProvidedCodexHome) {
176
+ console.warn("Custom tools require MCP server configuration in config.toml. " +
177
+ "Set allowConfigWriteWithUserCodexHome=true or update CODEX_HOME/config.toml manually.");
178
+ }
179
+ const { Codex } = await import("@openai/codex-sdk");
180
+ this.codex = new Codex(this.codexOptions);
181
+ return this.codex;
182
+ }
183
+ buildThreadOptions() {
184
+ return {
185
+ ...this.threadOptions,
186
+ skipGitRepoCheck: true,
187
+ };
188
+ }
189
+ resetEventState() {
190
+ this.streamBuffer = "";
191
+ this.isStreamingAssistant = false;
192
+ }
193
+ isTextMessageItem(item) {
194
+ const type = item?.type;
195
+ return type === "message" || type === "text" || type === "agent_message";
196
+ }
197
+ createStartMessage(message) {
198
+ return { ...message, content: [] };
199
+ }
200
+ createChunkMessage(role, delta) {
201
+ if (role === MessageRole.USER) {
202
+ return {
203
+ type: MessageRole.USER,
204
+ content: [{ type: "text", content: delta }],
205
+ };
206
+ }
207
+ return createAssistantMessage({
208
+ text: delta,
209
+ model: this.threadOptions.model,
210
+ });
211
+ }
212
+ buildFallbackMessage(item) {
213
+ const content = item?.content ??
214
+ item?.text ??
215
+ item?.value?.[0]?.text?.value ??
216
+ item?.text?.value ??
217
+ stringifyContent(item);
218
+ return createAssistantMessage({
219
+ text: typeof content === "string" ? content : stringifyContent(content),
220
+ model: this.threadOptions.model,
221
+ });
222
+ }
223
+ async run(params) {
224
+ const events = [];
225
+ let sessionStarted = false;
226
+ this.toolCallTracker.reset();
227
+ this.resetEventState();
228
+ const { controller, cleanup } = this.createAbortController(params.signal);
229
+ try {
230
+ const codex = await this.getCodex();
231
+ this.thread = codex.startThread(this.buildThreadOptions());
232
+ const { events: codexEvents } = await this.thread.runStreamed(params.query, {
233
+ signal: controller.signal,
234
+ });
235
+ const messageContents = [];
236
+ for await (const codexEvent of codexEvents) {
237
+ if (!sessionStarted && codexEvent?.type === "thread.started") {
238
+ const threadId = typeof codexEvent.thread_id === "string"
239
+ ? codexEvent.thread_id
240
+ : undefined;
241
+ await this.emitSessionStart(params, events, threadId);
242
+ await this.emitUserItemEvents(params.query, events);
243
+ sessionStarted = true;
244
+ continue;
245
+ }
246
+ if (typeof codexEvent.type === "string" &&
247
+ (codexEvent.type.startsWith("thread.") || codexEvent.type.startsWith("turn."))) {
248
+ continue;
249
+ }
250
+ if (codexEvent.type === "item.updated") {
251
+ continue;
252
+ }
253
+ if (codexEvent.type === "delta") {
254
+ const delta = typeof codexEvent.delta === "string"
255
+ ? codexEvent.delta
256
+ : stringifyContent(codexEvent.delta ?? "");
257
+ if (!this.isStreamingAssistant) {
258
+ this.isStreamingAssistant = true;
259
+ this.streamBuffer = "";
260
+ await this.emitItemStart({ type: MessageRole.ASSISTANT, content: [] }, events);
261
+ }
262
+ this.streamBuffer += delta;
263
+ await this.emitItemChunk(this.createChunkMessage(MessageRole.ASSISTANT, delta), events);
264
+ continue;
265
+ }
266
+ if (codexEvent.type === "item.started" || codexEvent.type === "item.completed") {
267
+ const codexItem = codexEvent.item;
268
+ if (codexEvent.type === "item.completed" &&
269
+ (codexItem.type === "agent_message" || codexItem.type === "message")) {
270
+ messageContents.push(codexItem);
271
+ }
272
+ const message = this.convertToMessage(codexItem, codexEvent.type);
273
+ if (codexEvent.type === "item.completed" &&
274
+ this.isStreamingAssistant &&
275
+ this.isTextMessageItem(codexItem)) {
276
+ const finalMessage = message ??
277
+ (this.streamBuffer
278
+ ? createAssistantMessage({
279
+ text: this.streamBuffer,
280
+ model: this.threadOptions.model,
281
+ })
282
+ : this.buildFallbackMessage(codexItem));
283
+ await this.emitItemEnd(finalMessage, events);
284
+ this.isStreamingAssistant = false;
285
+ this.streamBuffer = "";
286
+ continue;
287
+ }
288
+ const finalMessage = message ?? this.buildFallbackMessage(codexItem);
289
+ await this.emitItemStart(this.createStartMessage(finalMessage), events);
290
+ await this.emitItemChunk(finalMessage, events);
291
+ await this.emitItemEnd(finalMessage, events);
292
+ continue;
293
+ }
294
+ const fallbackMessage = this.buildFallbackMessage(codexEvent);
295
+ await this.emitItemStart(this.createStartMessage(fallbackMessage), events);
296
+ await this.emitItemChunk(fallbackMessage, events);
297
+ await this.emitItemEnd(fallbackMessage, events);
298
+ }
299
+ if (this.isStreamingAssistant && this.streamBuffer) {
300
+ const finalMessage = createAssistantMessage({
301
+ text: this.streamBuffer,
302
+ model: this.threadOptions.model,
303
+ });
304
+ await this.emitItemEnd(finalMessage, events);
305
+ this.isStreamingAssistant = false;
306
+ this.streamBuffer = "";
307
+ }
308
+ if (this.isAbortRequested()) {
309
+ await this.emitSessionEnd(events, "aborted");
310
+ return {
311
+ success: false,
312
+ error: new Error("Aborted"),
313
+ events,
314
+ };
315
+ }
316
+ await this.emitSessionEnd(events);
317
+ return {
318
+ success: true,
319
+ data: { messageContents },
320
+ events,
321
+ };
322
+ }
323
+ catch (error) {
324
+ if (this.isAbortRequested()) {
325
+ await this.emitSessionEnd(events, "aborted");
326
+ return {
327
+ success: false,
328
+ error: error,
329
+ events,
330
+ };
331
+ }
332
+ await this.emitErrorEvent("run", error, events);
333
+ await this.emitSessionEnd(events, "error");
334
+ return {
335
+ success: false,
336
+ error: error,
337
+ events,
338
+ };
339
+ }
340
+ finally {
341
+ cleanup();
342
+ }
343
+ }
344
+ async runStream(params) {
345
+ this.toolCallTracker.reset();
346
+ this.resetEventState();
347
+ const { controller, cleanup } = this.createAbortController(params.signal);
348
+ try {
349
+ const codex = await this.getCodex();
350
+ this.thread = codex.startThread(this.buildThreadOptions());
351
+ const { events: codexEvents } = await this.thread.runStreamed(params.query, {
352
+ signal: controller.signal,
353
+ });
354
+ const eventStream = this.createEventStream(codexEvents, params, params.query);
355
+ return {
356
+ events: this.wrapEventStream(eventStream, cleanup),
357
+ };
358
+ }
359
+ catch (error) {
360
+ const aborted = this.isAbortRequested();
361
+ cleanup();
362
+ if (aborted) {
363
+ const sessionEndEvent = await this.emitSessionEnd(undefined, "aborted");
364
+ return {
365
+ events: (async function* () {
366
+ yield sessionEndEvent;
367
+ })(),
368
+ };
369
+ }
370
+ const errorEvent = await this.emitErrorEvent("runStream", error);
371
+ const sessionEndEvent = await this.emitSessionEnd(undefined, "error");
372
+ return {
373
+ events: (async function* () {
374
+ yield errorEvent;
375
+ yield sessionEndEvent;
376
+ })(),
377
+ };
378
+ }
379
+ }
380
+ async resume(params) {
381
+ this.toolCallTracker.reset();
382
+ this.resetEventState();
383
+ const { controller, cleanup } = this.createAbortController();
384
+ try {
385
+ const codex = await this.getCodex();
386
+ this.thread = codex.resumeThread(params.sessionId);
387
+ const query = params.query || "Pick up where you left off";
388
+ const { events: codexEvents } = await this.thread.runStreamed(query, {
389
+ signal: controller.signal,
390
+ });
391
+ const sessionParams = { query: params.query || "[resuming session]", sessionId: params.sessionId };
392
+ const eventStream = this.createEventStream(codexEvents, sessionParams, params.query);
393
+ return {
394
+ events: this.wrapEventStream(eventStream, cleanup),
395
+ };
396
+ }
397
+ catch (error) {
398
+ const aborted = this.isAbortRequested();
399
+ cleanup();
400
+ if (aborted) {
401
+ const sessionEndEvent = await this.emitSessionEnd(undefined, "aborted");
402
+ return {
403
+ events: (async function* () {
404
+ yield sessionEndEvent;
405
+ })(),
406
+ };
407
+ }
408
+ const errorEvent = await this.emitErrorEvent("resume", error, undefined, {
409
+ threadId: params.sessionId,
410
+ });
411
+ const sessionEndEvent = await this.emitSessionEnd(undefined, "error");
412
+ return {
413
+ events: (async function* () {
414
+ yield errorEvent;
415
+ yield sessionEndEvent;
416
+ })(),
417
+ };
418
+ }
419
+ }
420
+ async *createEventStream(codexEvents, sessionParams, userQuery) {
421
+ let endReason;
422
+ let sessionStarted = false;
423
+ try {
424
+ for await (const codexEvent of codexEvents) {
425
+ if (!sessionStarted && codexEvent?.type === "thread.started") {
426
+ const threadId = typeof codexEvent.thread_id === "string"
427
+ ? codexEvent.thread_id
428
+ : undefined;
429
+ const sessionStartEvent = await this.emitSessionStart(sessionParams, undefined, threadId);
430
+ yield sessionStartEvent;
431
+ const userEvents = await this.emitUserItemEvents(userQuery);
432
+ for (const event of userEvents) {
433
+ yield event;
434
+ }
435
+ sessionStarted = true;
436
+ continue;
437
+ }
438
+ if (typeof codexEvent.type === "string" &&
439
+ (codexEvent.type.startsWith("thread.") || codexEvent.type.startsWith("turn."))) {
440
+ continue;
441
+ }
442
+ if (codexEvent.type === "item.updated") {
443
+ continue;
444
+ }
445
+ if (codexEvent.type === "delta") {
446
+ const delta = typeof codexEvent.delta === "string"
447
+ ? codexEvent.delta
448
+ : stringifyContent(codexEvent.delta ?? "");
449
+ if (!this.isStreamingAssistant) {
450
+ this.isStreamingAssistant = true;
451
+ this.streamBuffer = "";
452
+ const startEvent = await this.emitItemStart({ type: MessageRole.ASSISTANT, content: [] });
453
+ yield startEvent;
454
+ }
455
+ this.streamBuffer += delta;
456
+ const chunkEvent = await this.emitItemChunk(this.createChunkMessage(MessageRole.ASSISTANT, delta));
457
+ yield chunkEvent;
458
+ continue;
459
+ }
460
+ if (codexEvent.type === "item.started" || codexEvent.type === "item.completed") {
461
+ const codexItem = codexEvent.item;
462
+ const message = this.convertToMessage(codexItem, codexEvent.type);
463
+ if (codexEvent.type === "item.completed" &&
464
+ this.isStreamingAssistant &&
465
+ this.isTextMessageItem(codexItem)) {
466
+ const finalMessage = message ??
467
+ (this.streamBuffer
468
+ ? createAssistantMessage({
469
+ text: this.streamBuffer,
470
+ model: this.threadOptions.model,
471
+ })
472
+ : this.buildFallbackMessage(codexItem));
473
+ const endEvent = await this.emitItemEnd(finalMessage);
474
+ yield endEvent;
475
+ this.isStreamingAssistant = false;
476
+ this.streamBuffer = "";
477
+ continue;
478
+ }
479
+ const finalMessage = message ?? this.buildFallbackMessage(codexItem);
480
+ const startEvent = await this.emitItemStart(this.createStartMessage(finalMessage));
481
+ yield startEvent;
482
+ const chunkEvent = await this.emitItemChunk(finalMessage);
483
+ yield chunkEvent;
484
+ const endEvent = await this.emitItemEnd(finalMessage);
485
+ yield endEvent;
486
+ continue;
487
+ }
488
+ const fallbackMessage = this.buildFallbackMessage(codexEvent);
489
+ const startEvent = await this.emitItemStart(this.createStartMessage(fallbackMessage));
490
+ yield startEvent;
491
+ const chunkEvent = await this.emitItemChunk(fallbackMessage);
492
+ yield chunkEvent;
493
+ const endEvent = await this.emitItemEnd(fallbackMessage);
494
+ yield endEvent;
495
+ }
496
+ if (this.isStreamingAssistant && this.streamBuffer) {
497
+ const finalMessage = createAssistantMessage({
498
+ text: this.streamBuffer,
499
+ model: this.threadOptions.model,
500
+ });
501
+ const endEvent = await this.emitItemEnd(finalMessage);
502
+ yield endEvent;
503
+ this.isStreamingAssistant = false;
504
+ this.streamBuffer = "";
505
+ }
506
+ }
507
+ catch (error) {
508
+ if (this.isAbortRequested()) {
509
+ endReason = "aborted";
510
+ }
511
+ else {
512
+ const errorEvent = {
513
+ type: "error",
514
+ ...this.createBaseEvent(),
515
+ error: error,
516
+ context: { stage: "streamIteration" },
517
+ };
518
+ await this.emit(errorEvent);
519
+ yield errorEvent;
520
+ endReason = "error";
521
+ }
522
+ }
523
+ if (!endReason && this.isAbortRequested()) {
524
+ endReason = "aborted";
525
+ }
526
+ const sessionEndEvent = await this.emitSessionEnd(undefined, endReason);
527
+ yield sessionEndEvent;
528
+ }
529
+ getThread() {
530
+ return this.thread;
531
+ }
532
+ resetThread() {
533
+ this.thread = undefined;
534
+ }
535
+ getSessionDir() {
536
+ return this.codexHomeDir;
537
+ }
538
+ convertToMessage(codexItem, eventType) {
539
+ let msg = null;
540
+ switch (codexItem.type) {
541
+ case "message":
542
+ case "text":
543
+ case "agent_message":
544
+ const textContent = codexItem.text || codexItem.content;
545
+ if (textContent) {
546
+ msg = createAssistantMessage({ text: textContent, model: this.threadOptions.model });
547
+ }
548
+ break;
549
+ case "reasoning":
550
+ if (codexItem.content || codexItem.text) {
551
+ msg = createAssistantMessage({
552
+ thinking: {
553
+ content: codexItem.content || codexItem.text,
554
+ },
555
+ model: this.threadOptions.model,
556
+ });
557
+ }
558
+ break;
559
+ case "mcp_tool_call": {
560
+ const innerItem = codexItem.item ?? codexItem;
561
+ const itemRecord = innerItem;
562
+ const status = innerItem.status ?? codexItem.status;
563
+ const toolLabel = typeof innerItem.tool === "string" && innerItem.tool.length > 0
564
+ ? innerItem.tool
565
+ : undefined;
566
+ const serverLabel = typeof innerItem.server === "string" && innerItem.server.length > 0
567
+ ? innerItem.server
568
+ : undefined;
569
+ const fallbackName = toolLabel
570
+ ? (serverLabel ? `${serverLabel}.${toolLabel}` : toolLabel)
571
+ : serverLabel ?? "mcp_tool_call";
572
+ const isCompletedEvent = eventType === "item.completed";
573
+ const isCompletedStatus = status === "completed" || status === "error";
574
+ if (isCompletedEvent || isCompletedStatus) {
575
+ const isError = status === "error" ||
576
+ innerItem.is_error ||
577
+ innerItem.isError ||
578
+ codexItem.is_error ||
579
+ codexItem.isError ||
580
+ Boolean(innerItem.error ?? codexItem.error);
581
+ let outputSource = innerItem.result ??
582
+ innerItem.output ??
583
+ innerItem.content ??
584
+ innerItem.text ??
585
+ codexItem.result ??
586
+ codexItem.output ??
587
+ codexItem.content ??
588
+ codexItem.text ??
589
+ "";
590
+ if (outputSource &&
591
+ typeof outputSource === "object" &&
592
+ "content" in outputSource) {
593
+ const outputRecord = outputSource;
594
+ outputSource = outputRecord.content ?? outputSource;
595
+ }
596
+ const output = stringifyContent(outputSource);
597
+ const { id: toolUseId } = this.toolCallTracker.resolveToolResult(itemRecord, fallbackName);
598
+ msg = createToolCallResultMessage([
599
+ {
600
+ tool_use_id: toolUseId,
601
+ content: output,
602
+ isError,
603
+ },
604
+ ]);
605
+ break;
606
+ }
607
+ const argsValue = innerItem.arguments ?? innerItem.input ?? innerItem.args ?? {};
608
+ const args = typeof argsValue === "string"
609
+ ? argsValue
610
+ : stringifyContent(argsValue ?? {});
611
+ const { id, name } = this.toolCallTracker.registerToolUse(itemRecord, fallbackName);
612
+ const toolUse = {
613
+ id,
614
+ name,
615
+ args,
616
+ };
617
+ msg = createAssistantMessage({ toolCalls: [toolUse], model: this.threadOptions.model });
618
+ break;
619
+ }
620
+ case "command_execution":
621
+ case "item.started": {
622
+ const innerItem = codexItem.item ?? codexItem;
623
+ const command = innerItem.command ?? codexItem.command;
624
+ const itemRecord = innerItem;
625
+ const fallbackName = command
626
+ ? "Bash"
627
+ : typeof innerItem.type === "string"
628
+ ? innerItem.type
629
+ : "tool";
630
+ const status = innerItem.status ?? codexItem.status;
631
+ const exitCode = typeof innerItem.exit_code === "number"
632
+ ? innerItem.exit_code
633
+ : typeof innerItem.exitCode === "number"
634
+ ? innerItem.exitCode
635
+ : undefined;
636
+ const isCompletedEvent = eventType === "item.completed";
637
+ const isCompletedStatus = status === "completed" || status === "error";
638
+ if (isCompletedEvent || isCompletedStatus) {
639
+ const isError = status === "error" ||
640
+ innerItem.is_error ||
641
+ innerItem.isError ||
642
+ codexItem.is_error ||
643
+ codexItem.isError ||
644
+ (exitCode !== undefined && exitCode !== 0);
645
+ let output = innerItem.aggregated_output ??
646
+ innerItem.output ??
647
+ innerItem.result ??
648
+ innerItem.stdout ??
649
+ innerItem.stderr ??
650
+ innerItem.content ??
651
+ innerItem.text ??
652
+ codexItem.content ??
653
+ codexItem.text ??
654
+ "";
655
+ if (command && output === command) {
656
+ output = "";
657
+ }
658
+ if (output !== undefined && output !== null && typeof output !== "string") {
659
+ output = stringifyContent(output);
660
+ }
661
+ else if (!output) {
662
+ output = "";
663
+ }
664
+ const { id: toolUseId } = this.toolCallTracker.resolveToolResult(itemRecord, fallbackName);
665
+ msg = createToolCallResultMessage([
666
+ {
667
+ tool_use_id: toolUseId,
668
+ content: output,
669
+ isError,
670
+ },
671
+ ]);
672
+ break;
673
+ }
674
+ if (command) {
675
+ const { id } = this.toolCallTracker.registerToolUse(itemRecord, "Bash");
676
+ const toolUse = {
677
+ id,
678
+ name: "Bash",
679
+ args: JSON.stringify({ command }),
680
+ };
681
+ msg = createAssistantMessage({ toolCalls: [toolUse], model: this.threadOptions.model });
682
+ }
683
+ else if (innerItem.type && !innerItem.type.includes('.')) {
684
+ const { id, name } = this.toolCallTracker.registerToolUse(itemRecord, fallbackName);
685
+ const toolUse = {
686
+ id,
687
+ name,
688
+ args: JSON.stringify(innerItem),
689
+ };
690
+ msg = createAssistantMessage({ toolCalls: [toolUse], model: this.threadOptions.model });
691
+ }
692
+ break;
693
+ }
694
+ case "function_call":
695
+ case "tool_call":
696
+ case "tool_use":
697
+ case "apply_patch": {
698
+ const toolRecord = codexItem;
699
+ const { id, name } = this.toolCallTracker.registerToolUse(toolRecord);
700
+ const toolUse = buildToolUseFromRecord(toolRecord, id);
701
+ toolUse.id = id;
702
+ toolUse.name = name;
703
+ msg = createAssistantMessage({ toolCalls: [toolUse], model: this.threadOptions.model });
704
+ break;
705
+ }
706
+ case "function_output":
707
+ case "tool_output":
708
+ case "tool_result":
709
+ const outputRecord = codexItem;
710
+ const { id: outputToolUseId } = this.toolCallTracker.resolveToolResult(outputRecord);
711
+ msg = createToolCallResultMessage([
712
+ {
713
+ tool_use_id: outputToolUseId,
714
+ content: stringifyContent(codexItem.content || codexItem.output || codexItem.text || ""),
715
+ isError: codexItem.is_error || codexItem.isError || false,
716
+ },
717
+ ]);
718
+ break;
719
+ default: {
720
+ if (codexItem.type && !codexItem.type.includes('.')) {
721
+ const fallbackName = typeof codexItem.type === "string" ? codexItem.type : "tool";
722
+ const { id: itemId } = this.toolCallTracker.resolveToolResult(codexItem, fallbackName);
723
+ const isError = codexItem.status === "error" || codexItem.is_error || codexItem.isError || false;
724
+ const result = stringifyContent(codexItem.content || codexItem.output || codexItem.text || codexItem);
725
+ msg = createToolCallResultMessage([
726
+ {
727
+ tool_use_id: itemId,
728
+ content: result,
729
+ isError,
730
+ },
731
+ ]);
732
+ break;
733
+ }
734
+ const defaultText = codexItem.content || codexItem.text;
735
+ if (defaultText) {
736
+ msg = createAssistantMessage({
737
+ text: defaultText,
738
+ model: this.threadOptions.model,
739
+ });
740
+ }
741
+ break;
742
+ }
743
+ }
744
+ const parentToolUseId = typeof codexItem?.parent_tool_use_id === "string"
745
+ ? codexItem.parent_tool_use_id
746
+ : typeof codexItem?.parentToolUseId === "string"
747
+ ? codexItem.parentToolUseId
748
+ : undefined;
749
+ if (msg && parentToolUseId) {
750
+ msg = { ...msg, parent_tool_use_id: parentToolUseId };
751
+ }
752
+ return msg ? normalizeMessage(this.applyParentToolUseId(msg)) : null;
753
+ }
754
+ cleanup() {
755
+ if (!this.userProvidedCodexHome && this.codexHomeDir) {
756
+ try {
757
+ fs.rmSync(this.codexHomeDir, { recursive: true, force: true });
758
+ }
759
+ catch (error) {
760
+ console.warn(`[CodexClient] Failed to remove CODEX_HOME at ${this.codexHomeDir}:`, error);
761
+ }
762
+ }
763
+ }
764
+ [Symbol.dispose]() {
765
+ this.cleanup();
766
+ }
767
+ }
768
+ function resolveModelName(explicitModel, fileModel) {
769
+ if (explicitModel) {
770
+ return explicitModel;
771
+ }
772
+ if (!fileModel) {
773
+ return undefined;
774
+ }
775
+ if (typeof fileModel === "string") {
776
+ return fileModel;
777
+ }
778
+ if (typeof fileModel === "object" && fileModel !== null && "name" in fileModel) {
779
+ const name = fileModel.name;
780
+ return typeof name === "string" && name.length > 0 ? name : undefined;
781
+ }
782
+ return undefined;
783
+ }
784
+ function resolveAgentSystemPrompt(agents) {
785
+ if (!isRecord(agents)) {
786
+ return undefined;
787
+ }
788
+ const entries = Object.entries(agents);
789
+ if (entries.length !== 1) {
790
+ return undefined;
791
+ }
792
+ const agent = entries[0]?.[1];
793
+ if (!isRecord(agent)) {
794
+ return undefined;
795
+ }
796
+ const otherConfig = isRecord(agent.otherConfig)
797
+ ? agent.otherConfig
798
+ : undefined;
799
+ const codexPrompt = resolveString(otherConfig?.developer_instructions);
800
+ if (codexPrompt) {
801
+ return codexPrompt;
802
+ }
803
+ return resolveString(agent.systemPrompt ??
804
+ agent.prompt);
805
+ }
806
+ function mapPermissionModeToSandboxMode(permissionMode) {
807
+ if (!permissionMode) {
808
+ return undefined;
809
+ }
810
+ switch (permissionMode) {
811
+ case "acceptEdits":
812
+ return "workspace-write";
813
+ case "bypassPermissions":
814
+ return "danger-full-access";
815
+ case "auto":
816
+ case "default":
817
+ case "dontAsk":
818
+ case "plan":
819
+ return "read-only";
820
+ default:
821
+ return undefined;
822
+ }
823
+ }
824
+ function resolveString(value) {
825
+ return typeof value === "string" && value.length > 0 ? value : undefined;
826
+ }
827
+ function resolveStringArray(value) {
828
+ if (!value) {
829
+ return [];
830
+ }
831
+ if (Array.isArray(value)) {
832
+ return value.filter((item) => typeof item === "string" && item.length > 0);
833
+ }
834
+ if (typeof value === "string" && value.length > 0) {
835
+ return [value];
836
+ }
837
+ return [];
838
+ }
839
+ function resolveMcpServers(value) {
840
+ if (!isRecord(value)) {
841
+ return undefined;
842
+ }
843
+ return value;
844
+ }
845
+ function buildModelProviderOverride(sdkConfig, env) {
846
+ if (!isRecord(sdkConfig)) {
847
+ return undefined;
848
+ }
849
+ const providersValue = sdkConfig.model_providers;
850
+ if (!isRecord(providersValue)) {
851
+ return undefined;
852
+ }
853
+ const openaiProvider = providersValue.openai;
854
+ if (!isRecord(openaiProvider)) {
855
+ return undefined;
856
+ }
857
+ const httpHeaders = openaiProvider.http_headers;
858
+ const envHttpHeaders = openaiProvider.env_http_headers;
859
+ const hasHttpHeaders = isRecord(httpHeaders) && Object.keys(httpHeaders).length > 0;
860
+ const hasEnvHttpHeaders = isRecord(envHttpHeaders) && Object.keys(envHttpHeaders).length > 0;
861
+ if (!hasHttpHeaders && !hasEnvHttpHeaders) {
862
+ return undefined;
863
+ }
864
+ const providerName = "openai-custom";
865
+ const providerConfig = {
866
+ name: "OpenAI custom",
867
+ wire_api: "responses",
868
+ requires_openai_auth: true,
869
+ supports_websockets: true,
870
+ };
871
+ const baseUrl = env.OPENAI_BASE_URL ?? process.env.OPENAI_BASE_URL;
872
+ if (typeof baseUrl === "string" && baseUrl.length > 0) {
873
+ providerConfig.base_url = baseUrl;
874
+ }
875
+ else {
876
+ console.warn("OPENAI_BASE_URL is not set; openai-custom provider base_url is unset.");
877
+ }
878
+ if (hasHttpHeaders) {
879
+ providerConfig.http_headers = httpHeaders;
880
+ }
881
+ if (hasEnvHttpHeaders) {
882
+ providerConfig.env_http_headers = envHttpHeaders;
883
+ }
884
+ return { name: providerName, config: providerConfig };
885
+ }
886
+ function isRecord(value) {
887
+ return typeof value === "object" && value !== null;
888
+ }
889
+ function safeExtractCustomTools(tools) {
890
+ if (tools === undefined || tools === null) {
891
+ return [];
892
+ }
893
+ if (Array.isArray(tools)) {
894
+ if (tools.every((tool) => typeof tool === "string")) {
895
+ return [];
896
+ }
897
+ if (tools.every(isRegisteredTool)) {
898
+ return extractCustomTools(tools);
899
+ }
900
+ }
901
+ if (isRegisteredTool(tools)) {
902
+ return extractCustomTools(tools);
903
+ }
904
+ throw new Error("Tools must be RegisteredTool instances created by defineTool");
905
+ }
906
+ //# sourceMappingURL=codex-client.js.map