@northflare/runner 0.0.8 → 0.0.10
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/dist/components/claude-sdk-manager.d.ts +1 -1
- package/dist/components/claude-sdk-manager.d.ts.map +1 -1
- package/dist/components/claude-sdk-manager.js +30 -16
- package/dist/components/claude-sdk-manager.js.map +1 -1
- package/dist/components/codex-sdk-manager.d.ts +60 -0
- package/dist/components/codex-sdk-manager.d.ts.map +1 -0
- package/dist/components/codex-sdk-manager.js +988 -0
- package/dist/components/codex-sdk-manager.js.map +1 -0
- package/dist/components/message-handler-sse.d.ts +3 -0
- package/dist/components/message-handler-sse.d.ts.map +1 -1
- package/dist/components/message-handler-sse.js +66 -21
- package/dist/components/message-handler-sse.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/runner-sse.d.ts +4 -0
- package/dist/runner-sse.d.ts.map +1 -1
- package/dist/runner-sse.js +63 -20
- package/dist/runner-sse.js.map +1 -1
- package/dist/runner.js +3 -3
- package/dist/types/claude.d.ts +11 -1
- package/dist/types/claude.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/runner-interface.d.ts +2 -0
- package/dist/types/runner-interface.d.ts.map +1 -1
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +1 -0
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/console.d.ts.map +1 -1
- package/dist/utils/console.js +2 -1
- package/dist/utils/console.js.map +1 -1
- package/dist/utils/debug.d.ts +2 -0
- package/dist/utils/debug.d.ts.map +1 -0
- package/dist/utils/debug.js +19 -0
- package/dist/utils/debug.js.map +1 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +6 -4
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/model.d.ts +6 -0
- package/dist/utils/model.d.ts.map +1 -0
- package/dist/utils/model.js +23 -0
- package/dist/utils/model.js.map +1 -0
- package/dist/utils/status-line.d.ts +0 -8
- package/dist/utils/status-line.d.ts.map +1 -1
- package/dist/utils/status-line.js +9 -8
- package/dist/utils/status-line.js.map +1 -1
- package/dist/utils/tool-response-sanitizer.d.ts +9 -0
- package/dist/utils/tool-response-sanitizer.d.ts.map +1 -0
- package/dist/utils/tool-response-sanitizer.js +122 -0
- package/dist/utils/tool-response-sanitizer.js.map +1 -0
- package/exceptions.log +2 -0
- package/lib/codex-sdk/.prettierignore +3 -0
- package/lib/codex-sdk/.prettierrc +5 -0
- package/lib/codex-sdk/README.md +133 -0
- package/lib/codex-sdk/dist/index.d.ts +260 -0
- package/lib/codex-sdk/dist/index.js +426 -0
- package/lib/codex-sdk/eslint.config.js +21 -0
- package/lib/codex-sdk/jest.config.cjs +31 -0
- package/lib/codex-sdk/package.json +65 -0
- package/lib/codex-sdk/samples/basic_streaming.ts +90 -0
- package/lib/codex-sdk/samples/helpers.ts +8 -0
- package/lib/codex-sdk/samples/structured_output.ts +22 -0
- package/lib/codex-sdk/samples/structured_output_zod.ts +19 -0
- package/lib/codex-sdk/src/codex.ts +38 -0
- package/lib/codex-sdk/src/codexOptions.ts +10 -0
- package/lib/codex-sdk/src/events.ts +80 -0
- package/lib/codex-sdk/src/exec.ts +336 -0
- package/lib/codex-sdk/src/index.ts +39 -0
- package/lib/codex-sdk/src/items.ts +127 -0
- package/lib/codex-sdk/src/outputSchemaFile.ts +40 -0
- package/lib/codex-sdk/src/thread.ts +155 -0
- package/lib/codex-sdk/src/threadOptions.ts +18 -0
- package/lib/codex-sdk/src/turnOptions.ts +6 -0
- package/lib/codex-sdk/tests/abort.test.ts +165 -0
- package/lib/codex-sdk/tests/codexExecSpy.ts +37 -0
- package/lib/codex-sdk/tests/responsesProxy.ts +225 -0
- package/lib/codex-sdk/tests/run.test.ts +687 -0
- package/lib/codex-sdk/tests/runStreamed.test.ts +211 -0
- package/lib/codex-sdk/tsconfig.json +24 -0
- package/lib/codex-sdk/tsup.config.ts +12 -0
- package/package.json +3 -1
- package/rejections.log +5 -0
- package/src/components/claude-sdk-manager.ts +38 -17
- package/src/components/codex-sdk-manager.ts +1349 -0
- package/src/components/message-handler-sse.ts +94 -22
- package/src/index.ts +4 -2
- package/src/runner-sse.ts +75 -20
- package/src/types/claude.ts +12 -1
- package/src/types/index.ts +1 -0
- package/src/types/runner-interface.ts +3 -1
- package/src/utils/config.ts +1 -0
- package/src/utils/console.ts +4 -2
- package/src/utils/debug.ts +18 -0
- package/src/utils/logger.ts +8 -5
- package/src/utils/model.ts +29 -0
- package/src/utils/status-line.ts +9 -8
- package/src/utils/tool-response-sanitizer.ts +160 -0
- package/tests/tool-response-sanitizer.test.ts +63 -0
- package/src/utils/codex-sdk.js +0 -448
- package/src/utils/sdk-demo.js +0 -34
|
@@ -0,0 +1,988 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.CodexManager = void 0;
|
|
40
|
+
const status_line_1 = require("../utils/status-line");
|
|
41
|
+
const console_1 = require("../utils/console");
|
|
42
|
+
const expand_env_1 = require("../utils/expand-env");
|
|
43
|
+
const model_1 = require("../utils/model");
|
|
44
|
+
const jwt = __importStar(require("jsonwebtoken"));
|
|
45
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
46
|
+
const path_1 = __importDefault(require("path"));
|
|
47
|
+
const debug_1 = require("../utils/debug");
|
|
48
|
+
let codexSdkPromise = null;
|
|
49
|
+
// Use runtime dynamic import so the CommonJS build can load the ESM-only SDK.
|
|
50
|
+
const dynamicImport = new Function("specifier", "return import(specifier);");
|
|
51
|
+
async function loadCodexSdk() {
|
|
52
|
+
if (!codexSdkPromise) {
|
|
53
|
+
codexSdkPromise = dynamicImport("@northflare/codex-sdk");
|
|
54
|
+
}
|
|
55
|
+
return codexSdkPromise;
|
|
56
|
+
}
|
|
57
|
+
class CodexManager {
|
|
58
|
+
runner;
|
|
59
|
+
repositoryManager;
|
|
60
|
+
threadStates = new Map();
|
|
61
|
+
constructor(runner, repositoryManager) {
|
|
62
|
+
this.runner = runner;
|
|
63
|
+
this.repositoryManager = repositoryManager;
|
|
64
|
+
}
|
|
65
|
+
async startConversation(conversationObjectType, conversationObjectId, config, initialMessages, conversationData) {
|
|
66
|
+
if (!conversationData?.id) {
|
|
67
|
+
throw new Error("startConversation requires conversationData with a valid conversation.id");
|
|
68
|
+
}
|
|
69
|
+
const conversationId = conversationData.id;
|
|
70
|
+
const agentSessionId = this.resolveSessionId(config, conversationData);
|
|
71
|
+
const rawModel = conversationData?.model ||
|
|
72
|
+
config?.model ||
|
|
73
|
+
config?.defaultModel ||
|
|
74
|
+
"openai";
|
|
75
|
+
const { baseModel, reasoningEffort } = (0, model_1.parseModelValue)(rawModel);
|
|
76
|
+
const normalizedModel = baseModel || rawModel;
|
|
77
|
+
const metadata = {
|
|
78
|
+
instructionsInjected: false,
|
|
79
|
+
originalModelValue: rawModel,
|
|
80
|
+
};
|
|
81
|
+
if (reasoningEffort) {
|
|
82
|
+
metadata["modelReasoningEffort"] = reasoningEffort;
|
|
83
|
+
}
|
|
84
|
+
const context = {
|
|
85
|
+
conversationId,
|
|
86
|
+
agentSessionId,
|
|
87
|
+
conversationObjectType,
|
|
88
|
+
conversationObjectId,
|
|
89
|
+
taskId: conversationObjectType === "Task" ? conversationObjectId : undefined,
|
|
90
|
+
workspaceId: config.workspaceId,
|
|
91
|
+
status: "starting",
|
|
92
|
+
config,
|
|
93
|
+
startedAt: new Date(),
|
|
94
|
+
lastActivityAt: new Date(),
|
|
95
|
+
model: normalizedModel,
|
|
96
|
+
globalInstructions: conversationData.globalInstructions || "",
|
|
97
|
+
workspaceInstructions: conversationData.workspaceInstructions || "",
|
|
98
|
+
permissionsMode: conversationData.permissionsMode ||
|
|
99
|
+
config?.permissionsMode ||
|
|
100
|
+
"all",
|
|
101
|
+
provider: "openai",
|
|
102
|
+
metadata,
|
|
103
|
+
};
|
|
104
|
+
this.runner.activeConversations_.set(conversationId, context);
|
|
105
|
+
console_1.console.log(`[CodexManager] Stored conversation context`, {
|
|
106
|
+
conversationId,
|
|
107
|
+
agentSessionId,
|
|
108
|
+
model: context.model,
|
|
109
|
+
permissionsMode: context.permissionsMode,
|
|
110
|
+
});
|
|
111
|
+
const workspacePath = await this.resolveWorkspacePath(context, config);
|
|
112
|
+
context.metadata["workspacePath"] = workspacePath;
|
|
113
|
+
const githubToken = context.workspaceId
|
|
114
|
+
? await this.fetchGithubTokens(context.workspaceId)
|
|
115
|
+
: undefined;
|
|
116
|
+
const toolToken = this.generateToolToken(context);
|
|
117
|
+
let mcpServers;
|
|
118
|
+
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
119
|
+
const expandedServers = (0, expand_env_1.expandEnv)(config.mcpServers, {
|
|
120
|
+
TOOL_TOKEN: toolToken,
|
|
121
|
+
});
|
|
122
|
+
mcpServers = this.normalizeMcpServersForCodex(expandedServers);
|
|
123
|
+
console_1.console.log("[CodexManager] MCP servers configuration:", JSON.stringify(mcpServers, null, 2));
|
|
124
|
+
context.metadata["mcpServers"] = mcpServers;
|
|
125
|
+
}
|
|
126
|
+
const configOverrides = this.buildConfigOverridesFromMcp(mcpServers);
|
|
127
|
+
const envVars = await this.buildEnvVars(config, githubToken, toolToken);
|
|
128
|
+
const { Codex } = await loadCodexSdk();
|
|
129
|
+
const codex = new Codex({
|
|
130
|
+
baseUrl: config?.openaiBaseUrl ||
|
|
131
|
+
config?.codexBaseUrl ||
|
|
132
|
+
process.env["OPENAI_BASE_URL"],
|
|
133
|
+
apiKey: config?.openaiApiKey ||
|
|
134
|
+
config?.codexApiKey ||
|
|
135
|
+
process.env["CODEX_API_KEY"] ||
|
|
136
|
+
process.env["OPENAI_API_KEY"],
|
|
137
|
+
env: envVars,
|
|
138
|
+
});
|
|
139
|
+
const threadOptions = {
|
|
140
|
+
model: context.model,
|
|
141
|
+
workingDirectory: workspacePath,
|
|
142
|
+
sandboxMode: this.mapSandboxMode(context.permissionsMode),
|
|
143
|
+
networkAccessEnabled: this.shouldEnableNetwork(context.permissionsMode),
|
|
144
|
+
webSearchEnabled: true,
|
|
145
|
+
// additionalDirectories: this.getAdditionalDirectories(config),
|
|
146
|
+
configOverrides,
|
|
147
|
+
skipGitRepoCheck: true,
|
|
148
|
+
modelReasoningEffort: reasoningEffort,
|
|
149
|
+
};
|
|
150
|
+
console_1.console.log("[CodexManager] Thread options:", JSON.stringify(threadOptions, null, 2));
|
|
151
|
+
const thread = agentSessionId
|
|
152
|
+
? codex.resumeThread(agentSessionId, threadOptions)
|
|
153
|
+
: codex.startThread(threadOptions);
|
|
154
|
+
this.threadStates.set(conversationId, {
|
|
155
|
+
thread,
|
|
156
|
+
abortController: null,
|
|
157
|
+
runPromise: null,
|
|
158
|
+
});
|
|
159
|
+
context.conversation = thread;
|
|
160
|
+
// Send initial messages sequentially without waiting for completion
|
|
161
|
+
if (initialMessages?.length) {
|
|
162
|
+
for (const message of initialMessages) {
|
|
163
|
+
const text = this.normalizeToText(message.content);
|
|
164
|
+
const prompt = this.buildPromptForMessage(context, text, true);
|
|
165
|
+
this.launchTurn(context, prompt);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return context;
|
|
169
|
+
}
|
|
170
|
+
async stopConversation(_agentSessionId, context, isRunnerShutdown = false, reason) {
|
|
171
|
+
context.status = "stopping";
|
|
172
|
+
await this.abortActiveRun(context.conversationId);
|
|
173
|
+
try {
|
|
174
|
+
await this._finalizeConversation(context, false, undefined, reason || (isRunnerShutdown ? "runner_shutdown" : undefined));
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
console_1.console.error(`[CodexManager] Error finalizing conversation ${context.conversationId}:`, error);
|
|
178
|
+
}
|
|
179
|
+
finally {
|
|
180
|
+
this.threadStates.delete(context.conversationId);
|
|
181
|
+
}
|
|
182
|
+
context.status = "stopped";
|
|
183
|
+
}
|
|
184
|
+
async resumeConversation(conversationObjectType, conversationObjectId, agentSessionId, config, conversationData, resumeMessage) {
|
|
185
|
+
console_1.console.log(`[CodexManager] Resuming conversation ${agentSessionId}`);
|
|
186
|
+
const context = await this.startConversation(conversationObjectType, conversationObjectId, { ...config, sessionId: agentSessionId }, [], conversationData);
|
|
187
|
+
if (resumeMessage) {
|
|
188
|
+
const prompt = this.buildPromptForMessage(context, resumeMessage, false);
|
|
189
|
+
this.launchTurn(context, prompt);
|
|
190
|
+
}
|
|
191
|
+
return context.agentSessionId;
|
|
192
|
+
}
|
|
193
|
+
async sendUserMessage(conversationId, content, config, conversationObjectType, conversationObjectId, conversation, agentSessionIdOverride) {
|
|
194
|
+
console_1.console.log(`[CodexManager] sendUserMessage called`, {
|
|
195
|
+
conversationId,
|
|
196
|
+
hasConfig: !!config,
|
|
197
|
+
});
|
|
198
|
+
let context = this.runner.getConversationContext(conversationId);
|
|
199
|
+
if (!context && conversation) {
|
|
200
|
+
const conversationDetails = conversation;
|
|
201
|
+
const resumeSessionId = this.resolveSessionId(config, conversationDetails, agentSessionIdOverride);
|
|
202
|
+
const startConfig = {
|
|
203
|
+
...(config || {}),
|
|
204
|
+
workspaceId: conversationDetails.workspaceId || config?.workspaceId || undefined,
|
|
205
|
+
...(resumeSessionId ? { sessionId: resumeSessionId } : {}),
|
|
206
|
+
};
|
|
207
|
+
context = await this.startConversation(conversationObjectType ||
|
|
208
|
+
conversationDetails.objectType, conversationObjectId || conversationDetails.objectId, startConfig, [], conversationDetails);
|
|
209
|
+
}
|
|
210
|
+
if (!context) {
|
|
211
|
+
throw new Error(`No active or fetchable conversation found for ${conversationId}`);
|
|
212
|
+
}
|
|
213
|
+
await this.abortActiveRun(conversationId);
|
|
214
|
+
const messageText = this.normalizeToText(content);
|
|
215
|
+
const prompt = this.buildPromptForMessage(context, messageText, false);
|
|
216
|
+
this.launchTurn(context, prompt);
|
|
217
|
+
context.lastActivityAt = new Date();
|
|
218
|
+
}
|
|
219
|
+
async buildEnvVars(config, githubToken, toolToken) {
|
|
220
|
+
const envVars = {
|
|
221
|
+
...Object.fromEntries(Object.entries(process.env).filter(([, value]) => value !== undefined)),
|
|
222
|
+
};
|
|
223
|
+
if (config.codexAuth?.accessToken) {
|
|
224
|
+
envVars["OPENAI_ACCESS_TOKEN"] = config.codexAuth.accessToken;
|
|
225
|
+
}
|
|
226
|
+
else if (config.accessToken) {
|
|
227
|
+
envVars["OPENAI_ACCESS_TOKEN"] = config.accessToken;
|
|
228
|
+
}
|
|
229
|
+
if (githubToken) {
|
|
230
|
+
envVars["GITHUB_TOKEN"] = githubToken;
|
|
231
|
+
}
|
|
232
|
+
if (toolToken) {
|
|
233
|
+
envVars["TOOL_TOKEN"] = toolToken;
|
|
234
|
+
}
|
|
235
|
+
if ((0, debug_1.isRunnerDebugEnabled)()) {
|
|
236
|
+
envVars["DEBUG"] = "1";
|
|
237
|
+
}
|
|
238
|
+
const codexHome = await this.ensureCodexAuthHome(config);
|
|
239
|
+
if (codexHome) {
|
|
240
|
+
envVars["CODEX_HOME"] = codexHome;
|
|
241
|
+
}
|
|
242
|
+
return envVars;
|
|
243
|
+
}
|
|
244
|
+
async ensureCodexAuthHome(config) {
|
|
245
|
+
if (!config.codexAuth) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
const runnerId = this.runner.getRunnerId();
|
|
249
|
+
const dataDir = this.runner.config_.dataDir;
|
|
250
|
+
if (!runnerId || !dataDir) {
|
|
251
|
+
console_1.console.warn("[CodexManager] Missing runnerId or dataDir; cannot prepare Codex auth directory");
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
const codexDir = path_1.default.join(dataDir, "codex", runnerId);
|
|
255
|
+
const authPayload = {
|
|
256
|
+
OPENAI_API_KEY: null,
|
|
257
|
+
tokens: {
|
|
258
|
+
id_token: config.codexAuth.idToken,
|
|
259
|
+
access_token: config.codexAuth.accessToken,
|
|
260
|
+
refresh_token: "",
|
|
261
|
+
account_id: config.codexAuth.accountId,
|
|
262
|
+
},
|
|
263
|
+
last_refresh: config.codexAuth.lastRefresh || new Date().toISOString(),
|
|
264
|
+
};
|
|
265
|
+
try {
|
|
266
|
+
await promises_1.default.mkdir(codexDir, { recursive: true });
|
|
267
|
+
await promises_1.default.writeFile(path_1.default.join(codexDir, "auth.json"), JSON.stringify(authPayload, null, 2), "utf-8");
|
|
268
|
+
return codexDir;
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
console_1.console.error("[CodexManager] Failed to persist Codex auth configuration:", error);
|
|
272
|
+
throw new Error("Runner failed to persist Codex credentials. Check runner logs for details.");
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
buildPromptForMessage(context, text, forceInstructions) {
|
|
276
|
+
const trimmed = text ?? "";
|
|
277
|
+
const instructions = this.getInstructionPrefix(context);
|
|
278
|
+
const metadata = context.metadata || {};
|
|
279
|
+
const shouldInjectInstructions = forceInstructions || !metadata["instructionsInjected"];
|
|
280
|
+
if (instructions && shouldInjectInstructions) {
|
|
281
|
+
metadata["instructionsInjected"] = true;
|
|
282
|
+
context.metadata = metadata;
|
|
283
|
+
return `${instructions}\n\n${trimmed}`.trim();
|
|
284
|
+
}
|
|
285
|
+
return trimmed;
|
|
286
|
+
}
|
|
287
|
+
getInstructionPrefix(context) {
|
|
288
|
+
const parts = [];
|
|
289
|
+
if (context.globalInstructions) {
|
|
290
|
+
parts.push(`<global-instructions>\n${context.globalInstructions}\n</global-instructions>`);
|
|
291
|
+
}
|
|
292
|
+
if (context.workspaceInstructions) {
|
|
293
|
+
parts.push(`<workspace-instructions>\n${context.workspaceInstructions}\n</workspace-instructions>`);
|
|
294
|
+
}
|
|
295
|
+
return parts.join("\n\n");
|
|
296
|
+
}
|
|
297
|
+
launchTurn(context, prompt) {
|
|
298
|
+
const state = this.threadStates.get(context.conversationId);
|
|
299
|
+
if (!state || !state.thread) {
|
|
300
|
+
throw new Error(`Thread state missing for conversation ${context.conversationId}`);
|
|
301
|
+
}
|
|
302
|
+
const abortController = new AbortController();
|
|
303
|
+
state.abortController = abortController;
|
|
304
|
+
context.lastActivityAt = new Date();
|
|
305
|
+
const runPromise = this.streamThreadEvents(context, state.thread, prompt, abortController);
|
|
306
|
+
state.runPromise = runPromise;
|
|
307
|
+
runPromise
|
|
308
|
+
.catch((error) => {
|
|
309
|
+
if (!this.isAbortError(error)) {
|
|
310
|
+
console_1.console.error(`[CodexManager] Run failed for ${context.conversationId}:`, error);
|
|
311
|
+
this._handleConversationError(context, error);
|
|
312
|
+
}
|
|
313
|
+
})
|
|
314
|
+
.finally(() => {
|
|
315
|
+
if (state.runPromise === runPromise) {
|
|
316
|
+
state.runPromise = null;
|
|
317
|
+
}
|
|
318
|
+
if (state.abortController === abortController) {
|
|
319
|
+
state.abortController = null;
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
async streamThreadEvents(context, thread, prompt, abortController) {
|
|
324
|
+
try {
|
|
325
|
+
const { events } = await thread.runStreamed(prompt, {
|
|
326
|
+
signal: abortController.signal,
|
|
327
|
+
});
|
|
328
|
+
for await (const event of events) {
|
|
329
|
+
await this.handleThreadEvent(context, event);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
if (this.isAbortError(error)) {
|
|
334
|
+
console_1.console.log(`[CodexManager] Turn aborted for ${context.conversationId}`);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
throw error;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
async abortActiveRun(conversationId) {
|
|
341
|
+
const state = this.threadStates.get(conversationId);
|
|
342
|
+
if (!state || !state.runPromise)
|
|
343
|
+
return;
|
|
344
|
+
if (state.abortController) {
|
|
345
|
+
state.abortController.abort();
|
|
346
|
+
}
|
|
347
|
+
try {
|
|
348
|
+
await state.runPromise;
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
if (!this.isAbortError(error)) {
|
|
352
|
+
console_1.console.warn(`[CodexManager] Run aborted with error for ${conversationId}:`, error);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
async handleThreadEvent(context, event) {
|
|
357
|
+
try {
|
|
358
|
+
this.logRawThreadEvent(event);
|
|
359
|
+
switch (event.type) {
|
|
360
|
+
case "thread.started": {
|
|
361
|
+
await this.handleThreadStarted(context, event.thread_id);
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
case "turn.started": {
|
|
365
|
+
context.status = "active";
|
|
366
|
+
await this.sendAgentMessage(context, "system", {
|
|
367
|
+
subtype: "turn.started",
|
|
368
|
+
content: [
|
|
369
|
+
{
|
|
370
|
+
type: "text",
|
|
371
|
+
text: `Turn started at ${new Date().toISOString()}`,
|
|
372
|
+
},
|
|
373
|
+
],
|
|
374
|
+
});
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
case "turn.completed": {
|
|
378
|
+
await this.sendAgentMessage(context, "result", {
|
|
379
|
+
subtype: "turn.completed",
|
|
380
|
+
content: [
|
|
381
|
+
{
|
|
382
|
+
type: "usage",
|
|
383
|
+
usage: event.usage,
|
|
384
|
+
},
|
|
385
|
+
],
|
|
386
|
+
});
|
|
387
|
+
context.status = "stopped";
|
|
388
|
+
await this._finalizeConversation(context, false);
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
case "turn.failed": {
|
|
392
|
+
const error = new Error(event.error?.message || "Turn failed");
|
|
393
|
+
await this.sendAgentMessage(context, "error", {
|
|
394
|
+
subtype: "turn.failed",
|
|
395
|
+
content: [
|
|
396
|
+
{
|
|
397
|
+
type: "text",
|
|
398
|
+
text: error.message,
|
|
399
|
+
},
|
|
400
|
+
],
|
|
401
|
+
isError: true,
|
|
402
|
+
});
|
|
403
|
+
await this._handleConversationError(context, error);
|
|
404
|
+
context.status = "stopped";
|
|
405
|
+
await this._finalizeConversation(context, true, error, "turn_failed");
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
case "item.started":
|
|
409
|
+
case "item.updated":
|
|
410
|
+
case "item.completed": {
|
|
411
|
+
await this.forwardItemEvent(context, event.item, event.type.split(".")[1]);
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
case "error": {
|
|
415
|
+
const fatalError = new Error(event.message || "Unknown error");
|
|
416
|
+
await this.sendAgentMessage(context, "error", {
|
|
417
|
+
subtype: "thread.error",
|
|
418
|
+
content: [{ type: "text", text: fatalError.message }],
|
|
419
|
+
isError: true,
|
|
420
|
+
});
|
|
421
|
+
await this._handleConversationError(context, fatalError);
|
|
422
|
+
context.status = "stopped";
|
|
423
|
+
await this._finalizeConversation(context, true, fatalError, "thread_error");
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
console_1.console.error("[CodexManager] Failed to handle thread event", {
|
|
430
|
+
event,
|
|
431
|
+
error,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
async forwardItemEvent(context, item, phase) {
|
|
436
|
+
const normalized = this.normalizeItemEvent(context, item, phase);
|
|
437
|
+
if (!normalized)
|
|
438
|
+
return;
|
|
439
|
+
const { subtype, content, isError, toolCalls, type } = normalized;
|
|
440
|
+
const payload = {
|
|
441
|
+
subtype,
|
|
442
|
+
content,
|
|
443
|
+
isError,
|
|
444
|
+
};
|
|
445
|
+
if (toolCalls) {
|
|
446
|
+
payload.toolCalls = toolCalls;
|
|
447
|
+
}
|
|
448
|
+
await this.sendAgentMessage(context, type, payload);
|
|
449
|
+
}
|
|
450
|
+
normalizeItemEvent(context, item, phase) {
|
|
451
|
+
switch (item.type) {
|
|
452
|
+
case "agent_message": {
|
|
453
|
+
if (phase !== "completed")
|
|
454
|
+
return null;
|
|
455
|
+
return {
|
|
456
|
+
type: "assistant",
|
|
457
|
+
content: [
|
|
458
|
+
{
|
|
459
|
+
type: "text",
|
|
460
|
+
text: item.text || "",
|
|
461
|
+
},
|
|
462
|
+
],
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
case "reasoning": {
|
|
466
|
+
return {
|
|
467
|
+
type: "thinking",
|
|
468
|
+
content: [
|
|
469
|
+
{
|
|
470
|
+
type: "thinking",
|
|
471
|
+
thinking: item.text,
|
|
472
|
+
text: item.text,
|
|
473
|
+
phase,
|
|
474
|
+
},
|
|
475
|
+
],
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
case "command_execution": {
|
|
479
|
+
const toolUseId = this.buildToolUseId(context, item.id);
|
|
480
|
+
const timestamp = new Date().toISOString();
|
|
481
|
+
const isTerminal = phase === "completed" ||
|
|
482
|
+
item.status === "completed" ||
|
|
483
|
+
item.status === "failed";
|
|
484
|
+
if (!isTerminal) {
|
|
485
|
+
if (phase !== "started") {
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
return {
|
|
489
|
+
type: "assistant",
|
|
490
|
+
subtype: "tool_use",
|
|
491
|
+
content: [
|
|
492
|
+
{
|
|
493
|
+
toolCalls: [
|
|
494
|
+
{
|
|
495
|
+
id: toolUseId,
|
|
496
|
+
name: "codex_command",
|
|
497
|
+
arguments: {
|
|
498
|
+
command: item.command,
|
|
499
|
+
status: item.status,
|
|
500
|
+
},
|
|
501
|
+
status: item.status,
|
|
502
|
+
},
|
|
503
|
+
],
|
|
504
|
+
timestamp,
|
|
505
|
+
},
|
|
506
|
+
],
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
const exitCode = typeof item.exit_code === "number" ? item.exit_code : null;
|
|
510
|
+
const isError = item.status === "failed" || (exitCode ?? 0) !== 0;
|
|
511
|
+
return {
|
|
512
|
+
type: "tool_result",
|
|
513
|
+
subtype: "tool_result",
|
|
514
|
+
content: [
|
|
515
|
+
{
|
|
516
|
+
type: "tool_result",
|
|
517
|
+
tool_use_id: toolUseId,
|
|
518
|
+
content: {
|
|
519
|
+
kind: "codex_command_result",
|
|
520
|
+
command: item.command,
|
|
521
|
+
output: item.aggregated_output || "",
|
|
522
|
+
exitCode,
|
|
523
|
+
status: item.status,
|
|
524
|
+
},
|
|
525
|
+
timestamp,
|
|
526
|
+
},
|
|
527
|
+
],
|
|
528
|
+
isError,
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
case "file_change": {
|
|
532
|
+
if (phase === "updated")
|
|
533
|
+
return null;
|
|
534
|
+
return {
|
|
535
|
+
type: "system",
|
|
536
|
+
subtype: "file_change",
|
|
537
|
+
content: [
|
|
538
|
+
{
|
|
539
|
+
type: "file_change",
|
|
540
|
+
changes: item.changes,
|
|
541
|
+
status: item.status,
|
|
542
|
+
},
|
|
543
|
+
],
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
case "mcp_tool_call": {
|
|
547
|
+
const toolName = this.buildMcpToolName(item.server, item.tool);
|
|
548
|
+
const toolUseId = this.buildToolUseId(context, item.id);
|
|
549
|
+
if (item.status === "in_progress" || phase !== "completed") {
|
|
550
|
+
return {
|
|
551
|
+
type: "assistant",
|
|
552
|
+
subtype: "tool_use",
|
|
553
|
+
content: [
|
|
554
|
+
{
|
|
555
|
+
toolCalls: [
|
|
556
|
+
{
|
|
557
|
+
id: toolUseId,
|
|
558
|
+
name: toolName,
|
|
559
|
+
arguments: item.arguments,
|
|
560
|
+
server: item.server,
|
|
561
|
+
tool: item.tool,
|
|
562
|
+
status: item.status,
|
|
563
|
+
},
|
|
564
|
+
],
|
|
565
|
+
timestamp: new Date().toISOString(),
|
|
566
|
+
},
|
|
567
|
+
],
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
if (item.status === "failed") {
|
|
571
|
+
return {
|
|
572
|
+
type: "error",
|
|
573
|
+
subtype: "tool_result",
|
|
574
|
+
content: [
|
|
575
|
+
{
|
|
576
|
+
type: "text",
|
|
577
|
+
text: item.error?.message || "Tool call failed",
|
|
578
|
+
tool_use_id: toolUseId,
|
|
579
|
+
tool_name: toolName,
|
|
580
|
+
timestamp: new Date().toISOString(),
|
|
581
|
+
},
|
|
582
|
+
],
|
|
583
|
+
isError: true,
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
return {
|
|
587
|
+
type: "tool_result",
|
|
588
|
+
subtype: "tool_result",
|
|
589
|
+
content: [
|
|
590
|
+
{
|
|
591
|
+
type: "tool_result",
|
|
592
|
+
subtype: "tool_result",
|
|
593
|
+
tool_use_id: toolUseId,
|
|
594
|
+
content: item.result?.content || [],
|
|
595
|
+
structured_content: item.result?.structured_content,
|
|
596
|
+
metadata: {
|
|
597
|
+
server: item.server,
|
|
598
|
+
tool: item.tool,
|
|
599
|
+
name: toolName,
|
|
600
|
+
original_tool_use_id: item.id,
|
|
601
|
+
},
|
|
602
|
+
timestamp: new Date().toISOString(),
|
|
603
|
+
},
|
|
604
|
+
],
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
case "web_search": {
|
|
608
|
+
return {
|
|
609
|
+
type: "system",
|
|
610
|
+
subtype: `web_search.${phase}`,
|
|
611
|
+
content: [
|
|
612
|
+
{
|
|
613
|
+
type: "web_search",
|
|
614
|
+
query: item.query,
|
|
615
|
+
status: phase,
|
|
616
|
+
},
|
|
617
|
+
],
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
case "todo_list": {
|
|
621
|
+
return {
|
|
622
|
+
type: "system",
|
|
623
|
+
subtype: "todo_list",
|
|
624
|
+
content: [
|
|
625
|
+
{
|
|
626
|
+
type: "todo_list",
|
|
627
|
+
items: item.items,
|
|
628
|
+
phase,
|
|
629
|
+
},
|
|
630
|
+
],
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
case "error": {
|
|
634
|
+
return {
|
|
635
|
+
type: "error",
|
|
636
|
+
subtype: `item.${phase}`,
|
|
637
|
+
content: [
|
|
638
|
+
{
|
|
639
|
+
type: "text",
|
|
640
|
+
text: item.message,
|
|
641
|
+
},
|
|
642
|
+
],
|
|
643
|
+
isError: true,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
default:
|
|
647
|
+
return null;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
async sendAgentMessage(context, type, { subtype, content, toolCalls, isError, }) {
|
|
651
|
+
const normalizedContent = Array.isArray(content)
|
|
652
|
+
? content
|
|
653
|
+
: content
|
|
654
|
+
? [content]
|
|
655
|
+
: [];
|
|
656
|
+
const payload = {
|
|
657
|
+
taskId: context.taskId,
|
|
658
|
+
conversationId: context.conversationId,
|
|
659
|
+
conversationObjectType: context.conversationObjectType,
|
|
660
|
+
conversationObjectId: context.conversationObjectId,
|
|
661
|
+
agentSessionId: context.agentSessionId,
|
|
662
|
+
type,
|
|
663
|
+
subtype,
|
|
664
|
+
content: normalizedContent,
|
|
665
|
+
toolCalls,
|
|
666
|
+
isError: Boolean(isError),
|
|
667
|
+
messageId: this.generateMessageId(context),
|
|
668
|
+
timestamp: new Date().toISOString(),
|
|
669
|
+
};
|
|
670
|
+
if ((0, debug_1.isRunnerDebugEnabled)()) {
|
|
671
|
+
console_1.console.log("[CodexManager] Sending message.agent", payload);
|
|
672
|
+
}
|
|
673
|
+
await this.runner.notify("message.agent", payload);
|
|
674
|
+
}
|
|
675
|
+
async handleThreadStarted(context, threadId) {
|
|
676
|
+
if (!threadId || threadId === context.agentSessionId) {
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
const oldSessionId = context.agentSessionId;
|
|
680
|
+
context.agentSessionId = threadId;
|
|
681
|
+
await this.runner.notify("agentSessionId.changed", {
|
|
682
|
+
conversationId: context.conversationId,
|
|
683
|
+
conversationObjectType: context.conversationObjectType,
|
|
684
|
+
conversationObjectId: context.conversationObjectId,
|
|
685
|
+
oldAgentSessionId: oldSessionId,
|
|
686
|
+
newAgentSessionId: threadId,
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
mapSandboxMode(permissionsMode) {
|
|
690
|
+
const mode = (permissionsMode || "").toLowerCase();
|
|
691
|
+
if (mode === "read" || mode === "read_only") {
|
|
692
|
+
return "read-only";
|
|
693
|
+
}
|
|
694
|
+
if (mode === "workspace" || mode === "workspace-write") {
|
|
695
|
+
return "workspace-write";
|
|
696
|
+
}
|
|
697
|
+
return "danger-full-access";
|
|
698
|
+
}
|
|
699
|
+
shouldEnableNetwork(permissionsMode) {
|
|
700
|
+
const mode = (permissionsMode || "").toLowerCase();
|
|
701
|
+
return mode !== "read" && mode !== "read_only";
|
|
702
|
+
}
|
|
703
|
+
getAdditionalDirectories(config) {
|
|
704
|
+
const additionalDirs = [];
|
|
705
|
+
if (config.runnerRepoPath) {
|
|
706
|
+
additionalDirs.push(config.runnerRepoPath);
|
|
707
|
+
}
|
|
708
|
+
return additionalDirs.length ? additionalDirs : undefined;
|
|
709
|
+
}
|
|
710
|
+
buildConfigOverridesFromMcp(mcpServers) {
|
|
711
|
+
if (!mcpServers)
|
|
712
|
+
return undefined;
|
|
713
|
+
const overrides = {};
|
|
714
|
+
for (const [serverName, config] of Object.entries(mcpServers)) {
|
|
715
|
+
this.flattenOverrideObject(`mcp_servers.${serverName}`, config, overrides);
|
|
716
|
+
}
|
|
717
|
+
return Object.keys(overrides).length ? overrides : undefined;
|
|
718
|
+
}
|
|
719
|
+
normalizeMcpServersForCodex(mcpServers) {
|
|
720
|
+
const normalized = {};
|
|
721
|
+
for (const [serverName, config] of Object.entries(mcpServers)) {
|
|
722
|
+
normalized[serverName] = this.stripAuthorizationHeader(config);
|
|
723
|
+
}
|
|
724
|
+
return normalized;
|
|
725
|
+
}
|
|
726
|
+
buildMcpToolName(server, tool) {
|
|
727
|
+
const safeServer = (server || "unknown").trim() || "unknown";
|
|
728
|
+
const safeTool = (tool || "unknown").trim() || "unknown";
|
|
729
|
+
return `mcp__${safeServer}__${safeTool}`;
|
|
730
|
+
}
|
|
731
|
+
logRawThreadEvent(event) {
|
|
732
|
+
if (!(0, debug_1.isRunnerDebugEnabled)()) {
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
try {
|
|
736
|
+
console_1.console.log("[CodexManager] RAW Codex event FULL:", this.safeStringify(event));
|
|
737
|
+
const summary = {
|
|
738
|
+
type: event?.type,
|
|
739
|
+
keys: Object.keys(event || {}),
|
|
740
|
+
hasItem: Boolean(event?.item),
|
|
741
|
+
itemType: event?.item?.type,
|
|
742
|
+
hasUsage: Boolean(event?.usage),
|
|
743
|
+
threadId: event?.thread_id,
|
|
744
|
+
turnId: event?.turn_id,
|
|
745
|
+
};
|
|
746
|
+
console_1.console.log("[CodexManager] RAW Codex event summary:", summary);
|
|
747
|
+
if (event?.item) {
|
|
748
|
+
console_1.console.log("[CodexManager] RAW Codex event item:", this.safeStringify(event.item));
|
|
749
|
+
}
|
|
750
|
+
if (event?.usage) {
|
|
751
|
+
console_1.console.log("[CodexManager] RAW Codex usage:", event.usage);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
catch (error) {
|
|
755
|
+
console_1.console.warn("[CodexManager] Failed to log raw Codex event:", error);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
safeStringify(value) {
|
|
759
|
+
const seen = new WeakSet();
|
|
760
|
+
return JSON.stringify(value, (key, nested) => {
|
|
761
|
+
if (typeof nested === "function")
|
|
762
|
+
return undefined;
|
|
763
|
+
if (typeof nested === "bigint")
|
|
764
|
+
return nested.toString();
|
|
765
|
+
if (typeof nested === "object" && nested !== null) {
|
|
766
|
+
if (seen.has(nested))
|
|
767
|
+
return "[Circular]";
|
|
768
|
+
seen.add(nested);
|
|
769
|
+
}
|
|
770
|
+
return nested;
|
|
771
|
+
}, 2);
|
|
772
|
+
}
|
|
773
|
+
buildToolUseId(context, rawId) {
|
|
774
|
+
const scope = context.agentSessionId ||
|
|
775
|
+
context.conversationId ||
|
|
776
|
+
context.conversationObjectId ||
|
|
777
|
+
"codex";
|
|
778
|
+
return `${scope}:${rawId}`;
|
|
779
|
+
}
|
|
780
|
+
stripAuthorizationHeader(config) {
|
|
781
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
782
|
+
return config;
|
|
783
|
+
}
|
|
784
|
+
const normalized = { ...config };
|
|
785
|
+
if (normalized.bearer_token_env_var &&
|
|
786
|
+
normalized.headers &&
|
|
787
|
+
typeof normalized.headers === "object" &&
|
|
788
|
+
!Array.isArray(normalized.headers)) {
|
|
789
|
+
const headers = { ...normalized.headers };
|
|
790
|
+
delete headers["Authorization"];
|
|
791
|
+
if (Object.keys(headers).length === 0) {
|
|
792
|
+
delete normalized.headers;
|
|
793
|
+
}
|
|
794
|
+
else {
|
|
795
|
+
normalized.headers = headers;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
return normalized;
|
|
799
|
+
}
|
|
800
|
+
flattenOverrideObject(prefix, value, target) {
|
|
801
|
+
if (value === undefined) {
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
805
|
+
target[prefix] = value;
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
const entries = Object.entries(value);
|
|
809
|
+
if (!entries.length) {
|
|
810
|
+
target[prefix] = {};
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
for (const [key, nested] of entries) {
|
|
814
|
+
this.flattenOverrideObject(`${prefix}.${key}`, nested, target);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
async resolveWorkspacePath(context, config) {
|
|
818
|
+
let workspacePath;
|
|
819
|
+
const workspaceId = config.workspaceId;
|
|
820
|
+
if (config.runnerRepoPath) {
|
|
821
|
+
workspacePath = config.runnerRepoPath;
|
|
822
|
+
console_1.console.log(`[CodexManager] Using local workspace path ${workspacePath}`);
|
|
823
|
+
if (context.conversationObjectType === "Task") {
|
|
824
|
+
const taskHandle = await this.repositoryManager.createLocalTaskHandle(context.conversationObjectId, workspacePath);
|
|
825
|
+
context.taskHandle = taskHandle;
|
|
826
|
+
}
|
|
827
|
+
return workspacePath;
|
|
828
|
+
}
|
|
829
|
+
if (context.conversationObjectType === "Task" &&
|
|
830
|
+
config.repository &&
|
|
831
|
+
workspaceId) {
|
|
832
|
+
if (!config.repository.url) {
|
|
833
|
+
throw new Error("Repository URL is required for task conversations");
|
|
834
|
+
}
|
|
835
|
+
const taskHandle = await this.repositoryManager.createTaskWorktree(context.conversationObjectId, workspaceId, config.repository.url, config.repository.branch, config.githubToken);
|
|
836
|
+
context.taskHandle = taskHandle;
|
|
837
|
+
workspacePath = taskHandle.worktreePath;
|
|
838
|
+
return workspacePath;
|
|
839
|
+
}
|
|
840
|
+
if (config.repository && workspaceId) {
|
|
841
|
+
if (config.repository.type === "local" && config.repository.localPath) {
|
|
842
|
+
workspacePath = config.repository.localPath;
|
|
843
|
+
return workspacePath;
|
|
844
|
+
}
|
|
845
|
+
workspacePath = await this.repositoryManager.checkoutRepository(workspaceId, config.repository.url, config.repository.branch, config.githubToken);
|
|
846
|
+
return workspacePath;
|
|
847
|
+
}
|
|
848
|
+
if (workspaceId) {
|
|
849
|
+
workspacePath =
|
|
850
|
+
await this.repositoryManager.getWorkspacePath(workspaceId);
|
|
851
|
+
return workspacePath;
|
|
852
|
+
}
|
|
853
|
+
return process.cwd();
|
|
854
|
+
}
|
|
855
|
+
async fetchGithubTokens(workspaceId) {
|
|
856
|
+
try {
|
|
857
|
+
const response = await fetch(`${this.runner.config_.orchestratorUrl}/api/runner/tokens?workspaceId=${workspaceId}`, {
|
|
858
|
+
method: "GET",
|
|
859
|
+
headers: {
|
|
860
|
+
Authorization: `Bearer ${process.env["NORTHFLARE_RUNNER_TOKEN"]}`,
|
|
861
|
+
},
|
|
862
|
+
});
|
|
863
|
+
if (!response.ok) {
|
|
864
|
+
console_1.console.error(`[CodexManager] Failed to fetch GitHub tokens: ${response.status}`);
|
|
865
|
+
return undefined;
|
|
866
|
+
}
|
|
867
|
+
const data = (await response.json());
|
|
868
|
+
return data.githubToken;
|
|
869
|
+
}
|
|
870
|
+
catch (error) {
|
|
871
|
+
console_1.console.error("[CodexManager] Error fetching GitHub tokens", error);
|
|
872
|
+
return undefined;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
generateToolToken(context) {
|
|
876
|
+
if (!context.config.mcpServers)
|
|
877
|
+
return undefined;
|
|
878
|
+
const runnerToken = process.env["NORTHFLARE_RUNNER_TOKEN"];
|
|
879
|
+
const runnerUid = this.runner.getRunnerUid();
|
|
880
|
+
if (!runnerToken || !runnerUid)
|
|
881
|
+
return undefined;
|
|
882
|
+
return jwt.sign({
|
|
883
|
+
conversationId: context.conversationId,
|
|
884
|
+
runnerUid,
|
|
885
|
+
}, runnerToken, {
|
|
886
|
+
expiresIn: "60m",
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
normalizeToText(value) {
|
|
890
|
+
if (typeof value === "string")
|
|
891
|
+
return value;
|
|
892
|
+
if (value == null)
|
|
893
|
+
return "";
|
|
894
|
+
if (typeof value === "object") {
|
|
895
|
+
if (typeof value.text === "string") {
|
|
896
|
+
return value.text;
|
|
897
|
+
}
|
|
898
|
+
if (Array.isArray(value)) {
|
|
899
|
+
const texts = value
|
|
900
|
+
.map((entry) => {
|
|
901
|
+
if (entry &&
|
|
902
|
+
typeof entry === "object" &&
|
|
903
|
+
typeof entry.text === "string") {
|
|
904
|
+
return entry.text;
|
|
905
|
+
}
|
|
906
|
+
return null;
|
|
907
|
+
})
|
|
908
|
+
.filter((entry) => Boolean(entry));
|
|
909
|
+
if (texts.length) {
|
|
910
|
+
return texts.join(" ");
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
if (typeof value.content === "string") {
|
|
914
|
+
return value.content;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
try {
|
|
918
|
+
return JSON.stringify(value);
|
|
919
|
+
}
|
|
920
|
+
catch {
|
|
921
|
+
return String(value);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
normalizeSessionId(value) {
|
|
925
|
+
if (!value)
|
|
926
|
+
return undefined;
|
|
927
|
+
const trimmed = value.trim();
|
|
928
|
+
return trimmed.length ? trimmed : undefined;
|
|
929
|
+
}
|
|
930
|
+
resolveSessionId(config, conversationData, override) {
|
|
931
|
+
return (this.normalizeSessionId(override) ||
|
|
932
|
+
this.normalizeSessionId(config?.sessionId) ||
|
|
933
|
+
this.normalizeSessionId(conversationData?.agentSessionId) ||
|
|
934
|
+
this.normalizeSessionId(conversationData?.threadId) ||
|
|
935
|
+
this.normalizeSessionId(conversationData?.sessionId) ||
|
|
936
|
+
"");
|
|
937
|
+
}
|
|
938
|
+
isAbortError(error) {
|
|
939
|
+
if (!error)
|
|
940
|
+
return false;
|
|
941
|
+
return (error.name === "AbortError" ||
|
|
942
|
+
/aborted|abort/i.test(error.message || ""));
|
|
943
|
+
}
|
|
944
|
+
async _finalizeConversation(context, hadError, error, reason) {
|
|
945
|
+
if (context._finalized)
|
|
946
|
+
return;
|
|
947
|
+
context._finalized = true;
|
|
948
|
+
try {
|
|
949
|
+
await this.runner.notify("conversation.end", {
|
|
950
|
+
conversationId: context.conversationId,
|
|
951
|
+
conversationObjectType: context.conversationObjectType,
|
|
952
|
+
conversationObjectId: context.conversationObjectId,
|
|
953
|
+
agentSessionId: context.agentSessionId,
|
|
954
|
+
isError: hadError,
|
|
955
|
+
errorMessage: error?.message,
|
|
956
|
+
reason,
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
catch (notifyError) {
|
|
960
|
+
console_1.console.error("[CodexManager] Failed to send conversation.end notification", notifyError);
|
|
961
|
+
}
|
|
962
|
+
this.threadStates.delete(context.conversationId);
|
|
963
|
+
this.runner.activeConversations_.delete(context.conversationId);
|
|
964
|
+
status_line_1.statusLineManager.updateActiveCount(this.runner.activeConversations_.size);
|
|
965
|
+
}
|
|
966
|
+
async _handleConversationError(context, error) {
|
|
967
|
+
console_1.console.error(`[CodexManager] Conversation error for ${context.conversationId}:`, error);
|
|
968
|
+
await this.runner.notify("error.report", {
|
|
969
|
+
conversationId: context.conversationId,
|
|
970
|
+
conversationObjectType: context.conversationObjectType,
|
|
971
|
+
conversationObjectId: context.conversationObjectId,
|
|
972
|
+
agentSessionId: context.agentSessionId,
|
|
973
|
+
errorType: "codex_error",
|
|
974
|
+
message: error.message,
|
|
975
|
+
details: {
|
|
976
|
+
stack: error.stack,
|
|
977
|
+
timestamp: new Date(),
|
|
978
|
+
},
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
generateMessageId(context) {
|
|
982
|
+
return `${context.agentSessionId}-${Date.now()}-${Math.random()
|
|
983
|
+
.toString(36)
|
|
984
|
+
.slice(2)}`;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
exports.CodexManager = CodexManager;
|
|
988
|
+
//# sourceMappingURL=codex-sdk-manager.js.map
|