funolio-agent 1.0.52 → 1.0.75
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/approval.d.ts +1 -6
- package/dist/approval.d.ts.map +1 -1
- package/dist/approval.js +2 -7
- package/dist/approval.js.map +1 -1
- package/dist/bot-manager.d.ts +5 -1
- package/dist/bot-manager.d.ts.map +1 -1
- package/dist/bot-manager.js +23 -13
- package/dist/bot-manager.js.map +1 -1
- package/dist/cli-session-epoch.d.ts +1 -1
- package/dist/cli-session-epoch.d.ts.map +1 -1
- package/dist/cli-session-epoch.js +1 -1
- package/dist/cli-session-epoch.js.map +1 -1
- package/dist/cli-session-registry.d.ts +35 -0
- package/dist/cli-session-registry.d.ts.map +1 -0
- package/dist/cli-session-registry.js +177 -0
- package/dist/cli-session-registry.js.map +1 -0
- package/dist/cli.js +62 -0
- package/dist/cli.js.map +1 -1
- package/dist/codex-app-server-manager.d.ts +129 -0
- package/dist/codex-app-server-manager.d.ts.map +1 -0
- package/dist/codex-app-server-manager.js +768 -0
- package/dist/codex-app-server-manager.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +8 -30
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/setup.d.ts +4 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +9 -25
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +77 -2
- package/dist/commands/start.js.map +1 -1
- package/dist/completion-marker.d.ts +7 -0
- package/dist/completion-marker.d.ts.map +1 -0
- package/dist/completion-marker.js +28 -0
- package/dist/completion-marker.js.map +1 -0
- package/dist/config.d.ts +6 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +15 -3
- package/dist/config.js.map +1 -1
- package/dist/context-window.d.ts.map +1 -1
- package/dist/context-window.js +8 -1
- package/dist/context-window.js.map +1 -1
- package/dist/live-activity.d.ts +29 -0
- package/dist/live-activity.d.ts.map +1 -0
- package/dist/live-activity.js +36 -0
- package/dist/live-activity.js.map +1 -0
- package/dist/local-cli-pty-manager.d.ts +51 -0
- package/dist/local-cli-pty-manager.d.ts.map +1 -1
- package/dist/local-cli-pty-manager.js +1227 -114
- package/dist/local-cli-pty-manager.js.map +1 -1
- package/dist/local-data.d.ts +41 -0
- package/dist/local-data.d.ts.map +1 -1
- package/dist/local-data.js +140 -4
- package/dist/local-data.js.map +1 -1
- package/dist/local-db.d.ts.map +1 -1
- package/dist/local-db.js +55 -1
- package/dist/local-db.js.map +1 -1
- package/dist/local-server.d.ts +25 -0
- package/dist/local-server.d.ts.map +1 -1
- package/dist/local-server.js +528 -267
- package/dist/local-server.js.map +1 -1
- package/dist/message-loop.d.ts +6 -0
- package/dist/message-loop.d.ts.map +1 -1
- package/dist/message-loop.js +247 -89
- package/dist/message-loop.js.map +1 -1
- package/dist/mqtt-client.d.ts +10 -1
- package/dist/mqtt-client.d.ts.map +1 -1
- package/dist/mqtt-client.js +14 -1
- package/dist/mqtt-client.js.map +1 -1
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +69 -29
- package/dist/oauth.js.map +1 -1
- package/dist/orchestration/orchestrator-operating-prompt.d.ts +1 -0
- package/dist/orchestration/orchestrator-operating-prompt.d.ts.map +1 -1
- package/dist/orchestration/orchestrator-operating-prompt.js +60 -0
- package/dist/orchestration/orchestrator-operating-prompt.js.map +1 -1
- package/dist/orchestration/validation.d.ts +40 -0
- package/dist/orchestration/validation.d.ts.map +1 -0
- package/dist/orchestration/validation.js +203 -0
- package/dist/orchestration/validation.js.map +1 -0
- package/dist/orchestrator.d.ts +21 -32
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +287 -725
- package/dist/orchestrator.js.map +1 -1
- package/dist/providers/claude-cli-prompt.d.ts.map +1 -1
- package/dist/providers/claude-cli-prompt.js +49 -5
- package/dist/providers/claude-cli-prompt.js.map +1 -1
- package/dist/providers/claude-cli.d.ts.map +1 -1
- package/dist/providers/claude-cli.js +56 -5
- package/dist/providers/claude-cli.js.map +1 -1
- package/dist/providers/codex-cli.d.ts.map +1 -1
- package/dist/providers/codex-cli.js +15 -10
- package/dist/providers/codex-cli.js.map +1 -1
- package/dist/response-guard.js +1 -1
- package/dist/response-guard.js.map +1 -1
- package/dist/tools/admin-tools.d.ts.map +1 -1
- package/dist/tools/admin-tools.js +8 -2
- package/dist/tools/admin-tools.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/search-conversation-history.d.ts +16 -0
- package/dist/tools/search-conversation-history.d.ts.map +1 -0
- package/dist/tools/search-conversation-history.js +324 -0
- package/dist/tools/search-conversation-history.js.map +1 -0
- package/dist/wizard-state.d.ts +7 -0
- package/dist/wizard-state.d.ts.map +1 -1
- package/dist/wizard-state.js +31 -2
- package/dist/wizard-state.js.map +1 -1
- package/dist/workflow-engine.d.ts +4 -1
- package/dist/workflow-engine.d.ts.map +1 -1
- package/dist/workflow-engine.js +190 -29
- package/dist/workflow-engine.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,768 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.__codexAppServerTestUtils = exports.CodexAppServerManager = void 0;
|
|
4
|
+
exports.getCodexAppServerManager = getCodexAppServerManager;
|
|
5
|
+
exports.runCodexAppServerTurnForTest = runCodexAppServerTurnForTest;
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
const runtime_context_1 = require("./runtime-context");
|
|
8
|
+
const sync_cli_config_1 = require("./mcp/sync-cli-config");
|
|
9
|
+
const LOCAL_ONLY_ERROR = 'Codex app-server manager is local_desktop only';
|
|
10
|
+
const DEFAULT_CODEX_SETTINGS = {
|
|
11
|
+
reasoningEffort: 'none',
|
|
12
|
+
reasoningSummary: 'detailed',
|
|
13
|
+
personality: 'friendly',
|
|
14
|
+
serviceTier: 'fast',
|
|
15
|
+
sandboxPolicy: 'danger-full-access',
|
|
16
|
+
approvalPolicy: 'never',
|
|
17
|
+
};
|
|
18
|
+
let _manager = null;
|
|
19
|
+
let _prepareCodexCliConfigChain = Promise.resolve();
|
|
20
|
+
function delay(ms) {
|
|
21
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
22
|
+
}
|
|
23
|
+
function buildAbortError() {
|
|
24
|
+
const err = new Error('Codex app-server turn aborted');
|
|
25
|
+
err.name = 'AbortError';
|
|
26
|
+
return err;
|
|
27
|
+
}
|
|
28
|
+
function throwIfNotLocalDesktop(runtimeMode) {
|
|
29
|
+
if (runtimeMode && !(0, runtime_context_1.isLocalDesktopRuntime)(runtimeMode)) {
|
|
30
|
+
throw new Error(LOCAL_ONLY_ERROR);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function sessionKey(conversationId, botId) {
|
|
34
|
+
return `${conversationId}::${botId}`;
|
|
35
|
+
}
|
|
36
|
+
function createEventLog() {
|
|
37
|
+
return {
|
|
38
|
+
schema: 'funolio.codex-app-server.event-log@1',
|
|
39
|
+
events: [],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function appendEvent(eventLog, direction, kind, payload) {
|
|
43
|
+
if (!eventLog)
|
|
44
|
+
return;
|
|
45
|
+
eventLog.events.push({
|
|
46
|
+
ts: new Date().toISOString(),
|
|
47
|
+
direction,
|
|
48
|
+
kind,
|
|
49
|
+
payload,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function serializeEventLog(eventLog) {
|
|
53
|
+
return JSON.stringify(eventLog);
|
|
54
|
+
}
|
|
55
|
+
function normalizeDetail(text) {
|
|
56
|
+
return String(text || '').replace(/\s+/g, ' ').trim();
|
|
57
|
+
}
|
|
58
|
+
function scrubCodexAppServerEnv(baseEnv, extraEnv) {
|
|
59
|
+
const env = {};
|
|
60
|
+
for (const [key, value] of Object.entries(baseEnv)) {
|
|
61
|
+
if (value == null)
|
|
62
|
+
continue;
|
|
63
|
+
if (key === 'CODEX_THREAD_ID')
|
|
64
|
+
continue;
|
|
65
|
+
env[key] = value;
|
|
66
|
+
}
|
|
67
|
+
for (const [key, value] of Object.entries(extraEnv)) {
|
|
68
|
+
if (!value)
|
|
69
|
+
continue;
|
|
70
|
+
env[key] = value;
|
|
71
|
+
}
|
|
72
|
+
return env;
|
|
73
|
+
}
|
|
74
|
+
function computeEnvFingerprint(env) {
|
|
75
|
+
return JSON.stringify(Object.entries(env)
|
|
76
|
+
.filter(([, value]) => typeof value === 'string')
|
|
77
|
+
.sort(([a], [b]) => a.localeCompare(b)));
|
|
78
|
+
}
|
|
79
|
+
function buildCodexAppServerToolEnv(input) {
|
|
80
|
+
const env = {};
|
|
81
|
+
const actorId = input.botName?.trim() || input.botId.trim();
|
|
82
|
+
if (actorId)
|
|
83
|
+
env.FUNOLIO_TOOL_ACTOR_ID = actorId;
|
|
84
|
+
if (input.projectId !== undefined && input.projectId !== null)
|
|
85
|
+
env.FUNOLIO_TOOL_PROJECT_ID = String(input.projectId);
|
|
86
|
+
if (input.todoTaskId !== undefined && input.todoTaskId !== null)
|
|
87
|
+
env.FUNOLIO_TOOL_TODO_ID = String(input.todoTaskId);
|
|
88
|
+
return env;
|
|
89
|
+
}
|
|
90
|
+
function extractLatestUserText(messages) {
|
|
91
|
+
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
|
|
92
|
+
if (lastMessage?.role === 'user') {
|
|
93
|
+
return lastMessage.content || '';
|
|
94
|
+
}
|
|
95
|
+
if (messages.length === 0)
|
|
96
|
+
return '';
|
|
97
|
+
const rendered = messages.map((message) => {
|
|
98
|
+
const label = message.role === 'user' ? 'USER'
|
|
99
|
+
: message.role === 'assistant' ? 'ASSISTANT'
|
|
100
|
+
: message.role === 'tool' ? `TOOL (${message.toolName || 'tool'})`
|
|
101
|
+
: String(message.role || 'MESSAGE').toUpperCase();
|
|
102
|
+
return `${label}:\n${message.content || ''}`;
|
|
103
|
+
});
|
|
104
|
+
return rendered.join('\n\n');
|
|
105
|
+
}
|
|
106
|
+
function buildCodexAppServerInput(messages) {
|
|
107
|
+
return [
|
|
108
|
+
{
|
|
109
|
+
type: 'text',
|
|
110
|
+
text: extractLatestUserText(messages),
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
function buildTurnUsage(tokenUsage) {
|
|
115
|
+
const last = tokenUsage?.last;
|
|
116
|
+
if (!last || typeof last !== 'object')
|
|
117
|
+
return undefined;
|
|
118
|
+
const inputTokens = Number(last.inputTokens || 0) + Number(last.cachedInputTokens || 0);
|
|
119
|
+
const outputTokens = Number(last.outputTokens || 0) + Number(last.reasoningOutputTokens || 0);
|
|
120
|
+
if (!Number.isFinite(inputTokens) || !Number.isFinite(outputTokens))
|
|
121
|
+
return undefined;
|
|
122
|
+
return { inputTokens, outputTokens };
|
|
123
|
+
}
|
|
124
|
+
function extractThreadId(payload) {
|
|
125
|
+
const id = payload?.thread?.id ?? payload?.threadId ?? null;
|
|
126
|
+
return typeof id === 'string' && id.trim() ? id.trim() : null;
|
|
127
|
+
}
|
|
128
|
+
function extractTurnId(payload) {
|
|
129
|
+
const id = payload?.turn?.id ?? payload?.turnId ?? null;
|
|
130
|
+
return typeof id === 'string' && id.trim() ? id.trim() : null;
|
|
131
|
+
}
|
|
132
|
+
function extractTurnStatus(payload) {
|
|
133
|
+
const status = payload?.turn?.status ?? null;
|
|
134
|
+
return typeof status === 'string' ? status : null;
|
|
135
|
+
}
|
|
136
|
+
function extractTurnFailureMessage(payload) {
|
|
137
|
+
const message = payload?.turn?.error?.message ?? payload?.error?.message ?? null;
|
|
138
|
+
return typeof message === 'string' && message.trim() ? message.trim() : null;
|
|
139
|
+
}
|
|
140
|
+
function isResponseMessage(value) {
|
|
141
|
+
return !!value && typeof value === 'object' && value.id !== undefined && ('result' in value || 'error' in value);
|
|
142
|
+
}
|
|
143
|
+
function isServerRequestMessage(value) {
|
|
144
|
+
return !!value && typeof value === 'object' && value.id !== undefined && typeof value.method === 'string';
|
|
145
|
+
}
|
|
146
|
+
function isNotificationMessage(value) {
|
|
147
|
+
return !!value && typeof value === 'object' && value.id === undefined && typeof value.method === 'string';
|
|
148
|
+
}
|
|
149
|
+
function isTurnMatch(activeTurn, params) {
|
|
150
|
+
if (!activeTurn)
|
|
151
|
+
return false;
|
|
152
|
+
if (activeTurn.threadId && params?.threadId && activeTurn.threadId !== params.threadId)
|
|
153
|
+
return false;
|
|
154
|
+
if (activeTurn.turnId && params?.turnId && activeTurn.turnId !== params.turnId)
|
|
155
|
+
return false;
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
function safeJsonParse(line) {
|
|
159
|
+
try {
|
|
160
|
+
return JSON.parse(line);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function describeStartupStatus(params) {
|
|
167
|
+
const name = typeof params?.name === 'string' ? params.name : 'MCP server';
|
|
168
|
+
const status = typeof params?.status === 'string' ? params.status : 'unknown';
|
|
169
|
+
const error = typeof params?.error === 'string' && params.error.trim() ? ` (${params.error.trim()})` : '';
|
|
170
|
+
return `${name}: ${status}${error}`;
|
|
171
|
+
}
|
|
172
|
+
function describeItemProgress(method, params) {
|
|
173
|
+
if (!params?.item || typeof params.item !== 'object')
|
|
174
|
+
return null;
|
|
175
|
+
const itemType = typeof params.item.type === 'string' ? params.item.type : 'item';
|
|
176
|
+
const label = typeof params.item.name === 'string' ? params.item.name
|
|
177
|
+
: typeof params.item.title === 'string' ? params.item.title
|
|
178
|
+
: typeof params.item.serverName === 'string' ? params.item.serverName
|
|
179
|
+
: typeof params.item.toolName === 'string' ? params.item.toolName
|
|
180
|
+
: '';
|
|
181
|
+
const status = typeof params.item.status === 'string' ? ` (${params.item.status})`
|
|
182
|
+
: '';
|
|
183
|
+
const prefix = method === 'item/started' ? 'Started' : 'Completed';
|
|
184
|
+
return `${prefix} ${itemType}${label ? `: ${label}` : ''}${status}`;
|
|
185
|
+
}
|
|
186
|
+
function normalizeCodexSettings(settings) {
|
|
187
|
+
return {
|
|
188
|
+
reasoningEffort: String(settings?.reasoningEffort || DEFAULT_CODEX_SETTINGS.reasoningEffort),
|
|
189
|
+
reasoningSummary: String(settings?.reasoningSummary || DEFAULT_CODEX_SETTINGS.reasoningSummary),
|
|
190
|
+
personality: String(settings?.personality || DEFAULT_CODEX_SETTINGS.personality),
|
|
191
|
+
serviceTier: String(settings?.serviceTier || DEFAULT_CODEX_SETTINGS.serviceTier),
|
|
192
|
+
sandboxPolicy: String(settings?.sandboxPolicy || DEFAULT_CODEX_SETTINGS.sandboxPolicy),
|
|
193
|
+
approvalPolicy: String(settings?.approvalPolicy || DEFAULT_CODEX_SETTINGS.approvalPolicy),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function buildThreadSandbox(sandboxPolicy) {
|
|
197
|
+
switch (sandboxPolicy) {
|
|
198
|
+
case 'read-only':
|
|
199
|
+
return 'read-only';
|
|
200
|
+
case 'workspace-write':
|
|
201
|
+
return 'workspace-write';
|
|
202
|
+
default:
|
|
203
|
+
return 'danger-full-access';
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function buildTurnSandboxPolicy(sandboxPolicy) {
|
|
207
|
+
switch (sandboxPolicy) {
|
|
208
|
+
case 'read-only':
|
|
209
|
+
return { type: 'readOnly' };
|
|
210
|
+
case 'workspace-write':
|
|
211
|
+
return { type: 'workspaceWrite' };
|
|
212
|
+
default:
|
|
213
|
+
return { type: 'dangerFullAccess' };
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function buildApprovalResponse(method, params) {
|
|
217
|
+
switch (method) {
|
|
218
|
+
case 'item/commandExecution/requestApproval':
|
|
219
|
+
return { decision: 'accept' };
|
|
220
|
+
case 'item/fileChange/requestApproval':
|
|
221
|
+
return { decision: 'accept' };
|
|
222
|
+
case 'item/permissions/requestApproval':
|
|
223
|
+
return { permissions: params?.permissions || {}, scope: 'session' };
|
|
224
|
+
case 'applyPatchApproval':
|
|
225
|
+
return { decision: 'approved' };
|
|
226
|
+
case 'execCommandApproval':
|
|
227
|
+
return { decision: 'approved' };
|
|
228
|
+
case 'item/tool/requestUserInput':
|
|
229
|
+
return { answers: {} };
|
|
230
|
+
case 'mcpServer/elicitation/request':
|
|
231
|
+
return { action: 'decline' };
|
|
232
|
+
default:
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
async function defaultPrepareCliConfig() {
|
|
237
|
+
_prepareCodexCliConfigChain = _prepareCodexCliConfigChain.then(async () => {
|
|
238
|
+
await (0, sync_cli_config_1.syncMcpToCliConfig)('codex-cli');
|
|
239
|
+
});
|
|
240
|
+
return _prepareCodexCliConfigChain;
|
|
241
|
+
}
|
|
242
|
+
class CodexAppServerManager {
|
|
243
|
+
sessions = new Map();
|
|
244
|
+
now;
|
|
245
|
+
spawnProcess;
|
|
246
|
+
prepareCliConfig;
|
|
247
|
+
logInfo;
|
|
248
|
+
logWarn;
|
|
249
|
+
clientInfo;
|
|
250
|
+
constructor(deps = {}) {
|
|
251
|
+
this.now = deps.now || (() => Date.now());
|
|
252
|
+
this.spawnProcess = deps.spawnProcess || ((command, args, options) => (0, child_process_1.spawn)(command, args, options));
|
|
253
|
+
this.prepareCliConfig = deps.prepareCliConfig || defaultPrepareCliConfig;
|
|
254
|
+
this.logInfo = deps.logInfo || ((message) => console.log(message));
|
|
255
|
+
this.logWarn = deps.logWarn || ((message) => console.warn(message));
|
|
256
|
+
this.clientInfo = deps.clientInfo || { name: 'funolio-agent', version: '1.0.74' };
|
|
257
|
+
}
|
|
258
|
+
async runTurn(opts) {
|
|
259
|
+
throwIfNotLocalDesktop(opts.runtimeMode);
|
|
260
|
+
const key = sessionKey(opts.conversationId, opts.botId);
|
|
261
|
+
const requestedEnv = scrubCodexAppServerEnv(process.env, buildCodexAppServerToolEnv({
|
|
262
|
+
botId: opts.botId,
|
|
263
|
+
botName: opts.botName,
|
|
264
|
+
projectId: opts.projectId,
|
|
265
|
+
todoTaskId: opts.currentTodoTaskId,
|
|
266
|
+
}));
|
|
267
|
+
const requestedEnvFingerprint = computeEnvFingerprint(requestedEnv);
|
|
268
|
+
let session = this.sessions.get(key);
|
|
269
|
+
const expectedThreadId = opts.forceFreshSession ? null : (opts.resumeSessionId || null);
|
|
270
|
+
if (session
|
|
271
|
+
&& (opts.forceFreshSession
|
|
272
|
+
|| session.cwd !== opts.cwd
|
|
273
|
+
|| session.closed
|
|
274
|
+
|| session.envFingerprint !== requestedEnvFingerprint
|
|
275
|
+
|| (expectedThreadId && session.threadId && session.threadId !== expectedThreadId))) {
|
|
276
|
+
this.closeSession(key);
|
|
277
|
+
session = undefined;
|
|
278
|
+
}
|
|
279
|
+
if (!session) {
|
|
280
|
+
session = await this.createSession(key, opts.cwd, requestedEnv);
|
|
281
|
+
this.sessions.set(key, session);
|
|
282
|
+
}
|
|
283
|
+
const run = async () => this.runTurnInternal(session, opts);
|
|
284
|
+
const queued = session.chain.then(run, run);
|
|
285
|
+
session.chain = queued.then(() => undefined, () => undefined);
|
|
286
|
+
return queued;
|
|
287
|
+
}
|
|
288
|
+
closeSessionByConversation(conversationId, botId) {
|
|
289
|
+
this.closeSession(sessionKey(conversationId, botId));
|
|
290
|
+
}
|
|
291
|
+
closeAll() {
|
|
292
|
+
for (const key of [...this.sessions.keys()]) {
|
|
293
|
+
this.closeSession(key);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
closeSession(key) {
|
|
297
|
+
const session = this.sessions.get(key);
|
|
298
|
+
if (!session)
|
|
299
|
+
return;
|
|
300
|
+
session.closed = true;
|
|
301
|
+
try {
|
|
302
|
+
session.child.kill();
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
// best effort
|
|
306
|
+
}
|
|
307
|
+
for (const pending of session.pendingRequests.values()) {
|
|
308
|
+
pending.reject(new Error('Codex app-server session closed'));
|
|
309
|
+
}
|
|
310
|
+
session.pendingRequests.clear();
|
|
311
|
+
if (session.activeTurn) {
|
|
312
|
+
session.activeTurn.rejectCompletion(new Error('Codex app-server session closed during active turn'));
|
|
313
|
+
session.activeTurn = null;
|
|
314
|
+
}
|
|
315
|
+
this.sessions.delete(key);
|
|
316
|
+
}
|
|
317
|
+
async createSession(key, cwd, env) {
|
|
318
|
+
await this.prepareCliConfig();
|
|
319
|
+
const child = this.spawnProcess(process.platform === 'win32' ? 'codex' : 'codex', ['app-server'], {
|
|
320
|
+
stdio: 'pipe',
|
|
321
|
+
shell: process.platform === 'win32',
|
|
322
|
+
windowsHide: true,
|
|
323
|
+
cwd,
|
|
324
|
+
env,
|
|
325
|
+
});
|
|
326
|
+
const session = {
|
|
327
|
+
key,
|
|
328
|
+
cwd,
|
|
329
|
+
env,
|
|
330
|
+
envFingerprint: computeEnvFingerprint(env),
|
|
331
|
+
child,
|
|
332
|
+
stdoutCarry: '',
|
|
333
|
+
createdAtMs: this.now(),
|
|
334
|
+
lastUsedAtMs: this.now(),
|
|
335
|
+
nextRequestId: 1,
|
|
336
|
+
pendingRequests: new Map(),
|
|
337
|
+
activeTurn: null,
|
|
338
|
+
threadId: null,
|
|
339
|
+
readyPromise: Promise.resolve(),
|
|
340
|
+
readyResolved: false,
|
|
341
|
+
closed: false,
|
|
342
|
+
chain: Promise.resolve(),
|
|
343
|
+
};
|
|
344
|
+
child.stdout.on('data', (chunk) => {
|
|
345
|
+
this.handleStdout(session, chunk.toString());
|
|
346
|
+
});
|
|
347
|
+
child.stderr.on('data', (chunk) => {
|
|
348
|
+
this.handleStderr(session, chunk.toString());
|
|
349
|
+
});
|
|
350
|
+
child.on('error', (err) => {
|
|
351
|
+
this.handleProcessError(session, err);
|
|
352
|
+
});
|
|
353
|
+
child.on('close', (code, signal) => {
|
|
354
|
+
this.handleProcessClose(session, code, signal);
|
|
355
|
+
});
|
|
356
|
+
session.readyPromise = this.initializeSession(session);
|
|
357
|
+
return session;
|
|
358
|
+
}
|
|
359
|
+
async initializeSession(session) {
|
|
360
|
+
await this.sendRequest(session, 'initialize', {
|
|
361
|
+
clientInfo: this.clientInfo,
|
|
362
|
+
});
|
|
363
|
+
this.sendNotification(session, 'initialized');
|
|
364
|
+
session.readyResolved = true;
|
|
365
|
+
}
|
|
366
|
+
async runTurnInternal(session, opts) {
|
|
367
|
+
await session.readyPromise;
|
|
368
|
+
session.lastUsedAtMs = this.now();
|
|
369
|
+
const codexSettings = normalizeCodexSettings(opts.codexSettings);
|
|
370
|
+
const eventLog = createEventLog();
|
|
371
|
+
const completionPromise = new Promise((resolve, reject) => {
|
|
372
|
+
const activeTurn = {
|
|
373
|
+
threadId: session.threadId,
|
|
374
|
+
turnId: null,
|
|
375
|
+
finalContent: '',
|
|
376
|
+
thinkingText: '',
|
|
377
|
+
done: false,
|
|
378
|
+
aborted: false,
|
|
379
|
+
callbackChain: Promise.resolve(),
|
|
380
|
+
detailFingerprints: new Set(),
|
|
381
|
+
eventLog,
|
|
382
|
+
onChunk: opts.onChunk,
|
|
383
|
+
onThinkingChunk: opts.onThinkingChunk,
|
|
384
|
+
onDetail: opts.onDetail,
|
|
385
|
+
resolveCompletion: resolve,
|
|
386
|
+
rejectCompletion: reject,
|
|
387
|
+
};
|
|
388
|
+
session.activeTurn = activeTurn;
|
|
389
|
+
});
|
|
390
|
+
const activeTurn = session.activeTurn;
|
|
391
|
+
const abortHandler = async () => {
|
|
392
|
+
activeTurn.aborted = true;
|
|
393
|
+
if (activeTurn.turnId) {
|
|
394
|
+
try {
|
|
395
|
+
await this.sendRequest(session, 'turn/interrupt', { turnId: activeTurn.turnId });
|
|
396
|
+
}
|
|
397
|
+
catch {
|
|
398
|
+
// best effort; close handler will reject if needed
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
activeTurn.rejectCompletion(buildAbortError());
|
|
402
|
+
session.activeTurn = null;
|
|
403
|
+
};
|
|
404
|
+
opts.abortSignal?.addEventListener('abort', abortHandler, { once: true });
|
|
405
|
+
try {
|
|
406
|
+
const requestedThreadId = opts.forceFreshSession ? null : (opts.resumeSessionId || null);
|
|
407
|
+
if (opts.forceFreshSession) {
|
|
408
|
+
session.threadId = null;
|
|
409
|
+
}
|
|
410
|
+
if (!session.threadId) {
|
|
411
|
+
if (requestedThreadId) {
|
|
412
|
+
const response = await this.sendRequest(session, 'thread/resume', {
|
|
413
|
+
threadId: requestedThreadId,
|
|
414
|
+
cwd: opts.cwd,
|
|
415
|
+
developerInstructions: opts.systemPrompt || null,
|
|
416
|
+
approvalPolicy: codexSettings.approvalPolicy,
|
|
417
|
+
sandbox: buildThreadSandbox(codexSettings.sandboxPolicy),
|
|
418
|
+
model: opts.model || null,
|
|
419
|
+
personality: codexSettings.personality,
|
|
420
|
+
modelProvider: 'openai',
|
|
421
|
+
});
|
|
422
|
+
session.threadId = extractThreadId(response);
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
const response = await this.sendRequest(session, 'thread/start', {
|
|
426
|
+
cwd: opts.cwd,
|
|
427
|
+
developerInstructions: opts.systemPrompt || null,
|
|
428
|
+
approvalPolicy: codexSettings.approvalPolicy,
|
|
429
|
+
sandbox: buildThreadSandbox(codexSettings.sandboxPolicy),
|
|
430
|
+
model: opts.model || null,
|
|
431
|
+
personality: codexSettings.personality,
|
|
432
|
+
modelProvider: 'openai',
|
|
433
|
+
ephemeral: false,
|
|
434
|
+
});
|
|
435
|
+
session.threadId = extractThreadId(response);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
const threadId = session.threadId;
|
|
439
|
+
if (!threadId) {
|
|
440
|
+
throw new Error('Codex app-server did not return a thread id');
|
|
441
|
+
}
|
|
442
|
+
activeTurn.threadId = threadId;
|
|
443
|
+
const turnStartResponse = await this.sendRequest(session, 'turn/start', {
|
|
444
|
+
threadId,
|
|
445
|
+
cwd: opts.cwd,
|
|
446
|
+
approvalPolicy: codexSettings.approvalPolicy,
|
|
447
|
+
effort: codexSettings.reasoningEffort,
|
|
448
|
+
sandboxPolicy: buildTurnSandboxPolicy(codexSettings.sandboxPolicy),
|
|
449
|
+
personality: codexSettings.personality,
|
|
450
|
+
input: buildCodexAppServerInput(opts.messages),
|
|
451
|
+
model: opts.model || null,
|
|
452
|
+
serviceTier: codexSettings.serviceTier,
|
|
453
|
+
summary: codexSettings.reasoningSummary,
|
|
454
|
+
});
|
|
455
|
+
activeTurn.turnId = extractTurnId(turnStartResponse);
|
|
456
|
+
return await completionPromise;
|
|
457
|
+
}
|
|
458
|
+
finally {
|
|
459
|
+
opts.abortSignal?.removeEventListener('abort', abortHandler);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
sendNotification(session, method, params) {
|
|
463
|
+
const payload = params === undefined
|
|
464
|
+
? { jsonrpc: '2.0', method }
|
|
465
|
+
: { jsonrpc: '2.0', method, params };
|
|
466
|
+
appendEvent(session.activeTurn?.eventLog, 'out', 'notification', payload);
|
|
467
|
+
session.child.stdin.write(`${JSON.stringify(payload)}\n`);
|
|
468
|
+
}
|
|
469
|
+
sendResponse(session, id, result, error) {
|
|
470
|
+
const payload = error
|
|
471
|
+
? { jsonrpc: '2.0', id, error }
|
|
472
|
+
: { jsonrpc: '2.0', id, result };
|
|
473
|
+
appendEvent(session.activeTurn?.eventLog, 'out', 'response', payload);
|
|
474
|
+
session.child.stdin.write(`${JSON.stringify(payload)}\n`);
|
|
475
|
+
}
|
|
476
|
+
sendRequest(session, method, params) {
|
|
477
|
+
if (session.closed) {
|
|
478
|
+
return Promise.reject(new Error('Codex app-server session is closed'));
|
|
479
|
+
}
|
|
480
|
+
const id = session.nextRequestId++;
|
|
481
|
+
const payload = params === undefined
|
|
482
|
+
? { jsonrpc: '2.0', id, method }
|
|
483
|
+
: { jsonrpc: '2.0', id, method, params };
|
|
484
|
+
appendEvent(session.activeTurn?.eventLog, 'out', 'request', payload);
|
|
485
|
+
return new Promise((resolve, reject) => {
|
|
486
|
+
session.pendingRequests.set(id, { method, resolve, reject });
|
|
487
|
+
session.child.stdin.write(`${JSON.stringify(payload)}\n`);
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
handleStdout(session, chunk) {
|
|
491
|
+
session.stdoutCarry += chunk;
|
|
492
|
+
const lines = session.stdoutCarry.split(/\r?\n/);
|
|
493
|
+
session.stdoutCarry = lines.pop() || '';
|
|
494
|
+
for (const line of lines) {
|
|
495
|
+
const trimmed = line.trim();
|
|
496
|
+
if (!trimmed)
|
|
497
|
+
continue;
|
|
498
|
+
const parsed = safeJsonParse(trimmed);
|
|
499
|
+
if (!parsed) {
|
|
500
|
+
appendEvent(session.activeTurn?.eventLog, 'in', 'parse_error', trimmed);
|
|
501
|
+
this.logWarn(`[codex-app-server] Non-JSON stdout line: ${trimmed}`);
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
if (isResponseMessage(parsed)) {
|
|
505
|
+
appendEvent(session.activeTurn?.eventLog, 'in', 'response', parsed);
|
|
506
|
+
this.handleResponse(session, parsed);
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
if (isServerRequestMessage(parsed)) {
|
|
510
|
+
appendEvent(session.activeTurn?.eventLog, 'in', 'server_request', parsed);
|
|
511
|
+
void this.handleServerRequest(session, parsed);
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
if (isNotificationMessage(parsed)) {
|
|
515
|
+
appendEvent(session.activeTurn?.eventLog, 'in', 'notification', parsed);
|
|
516
|
+
void this.handleNotification(session, parsed);
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
appendEvent(session.activeTurn?.eventLog, 'in', 'parse_error', parsed);
|
|
520
|
+
this.logWarn('[codex-app-server] Unrecognized stdout payload');
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
handleResponse(session, message) {
|
|
524
|
+
const pending = session.pendingRequests.get(message.id);
|
|
525
|
+
if (!pending)
|
|
526
|
+
return;
|
|
527
|
+
session.pendingRequests.delete(message.id);
|
|
528
|
+
if (message.error) {
|
|
529
|
+
pending.reject(new Error(`${pending.method} failed: ${message.error.message || 'Unknown error'}`));
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
pending.resolve(message.result);
|
|
533
|
+
}
|
|
534
|
+
async handleServerRequest(session, message) {
|
|
535
|
+
const approvalResponse = buildApprovalResponse(message.method, message.params);
|
|
536
|
+
if (approvalResponse !== null) {
|
|
537
|
+
await this.emitDetail(session.activeTurn, `Auto-handled ${message.method}`);
|
|
538
|
+
this.sendResponse(session, message.id, approvalResponse);
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
this.sendResponse(session, message.id, undefined, {
|
|
542
|
+
code: -32601,
|
|
543
|
+
message: `Unsupported server request: ${message.method}`,
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
async handleNotification(session, message) {
|
|
547
|
+
const params = message.params || {};
|
|
548
|
+
if (message.method === 'thread/started') {
|
|
549
|
+
const threadId = extractThreadId(params);
|
|
550
|
+
if (threadId) {
|
|
551
|
+
session.threadId = threadId;
|
|
552
|
+
if (session.activeTurn)
|
|
553
|
+
session.activeTurn.threadId = threadId;
|
|
554
|
+
}
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
if (message.method === 'turn/started') {
|
|
558
|
+
const turnId = extractTurnId(params);
|
|
559
|
+
if (session.activeTurn && turnId) {
|
|
560
|
+
session.activeTurn.turnId = turnId;
|
|
561
|
+
}
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
if (message.method === 'thread/tokenUsage/updated' && isTurnMatch(session.activeTurn, params)) {
|
|
565
|
+
const usage = buildTurnUsage(params.tokenUsage);
|
|
566
|
+
if (usage && session.activeTurn) {
|
|
567
|
+
session.activeTurn.usage = usage;
|
|
568
|
+
}
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
if (message.method === 'item/agentMessage/delta' && isTurnMatch(session.activeTurn, params) && session.activeTurn) {
|
|
572
|
+
const delta = typeof params.delta === 'string' ? params.delta : '';
|
|
573
|
+
if (delta) {
|
|
574
|
+
session.activeTurn.finalContent += delta;
|
|
575
|
+
await this.emitChunk(session.activeTurn, delta);
|
|
576
|
+
}
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
if (message.method === 'item/reasoning/summaryTextDelta' && isTurnMatch(session.activeTurn, params) && session.activeTurn) {
|
|
580
|
+
const delta = typeof params.delta === 'string' ? params.delta : '';
|
|
581
|
+
if (delta) {
|
|
582
|
+
session.activeTurn.thinkingText += delta;
|
|
583
|
+
await this.emitThinkingChunk(session.activeTurn, delta);
|
|
584
|
+
}
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
if (message.method === 'item/commandExecution/outputDelta' && isTurnMatch(session.activeTurn, params)) {
|
|
588
|
+
const delta = typeof params.delta === 'string' ? params.delta : '';
|
|
589
|
+
if (delta) {
|
|
590
|
+
await this.emitDetail(session.activeTurn, delta);
|
|
591
|
+
}
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
if (message.method === 'item/fileChange/outputDelta' && isTurnMatch(session.activeTurn, params)) {
|
|
595
|
+
const delta = typeof params.delta === 'string' ? params.delta : '';
|
|
596
|
+
if (delta) {
|
|
597
|
+
await this.emitDetail(session.activeTurn, delta);
|
|
598
|
+
}
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
if (message.method === 'item/mcpToolCall/progress' && isTurnMatch(session.activeTurn, params)) {
|
|
602
|
+
if (typeof params.message === 'string') {
|
|
603
|
+
await this.emitDetail(session.activeTurn, params.message);
|
|
604
|
+
}
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
if (message.method === 'mcpServer/startupStatus/updated') {
|
|
608
|
+
const detail = describeStartupStatus(params);
|
|
609
|
+
if (detail)
|
|
610
|
+
await this.emitDetail(session.activeTurn, detail);
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
if (message.method === 'item/started' && isTurnMatch(session.activeTurn, params)) {
|
|
614
|
+
const detail = describeItemProgress(message.method, params);
|
|
615
|
+
if (detail)
|
|
616
|
+
await this.emitDetail(session.activeTurn, detail);
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
if (message.method === 'item/completed' && isTurnMatch(session.activeTurn, params)) {
|
|
620
|
+
const detail = describeItemProgress(message.method, params);
|
|
621
|
+
if (detail)
|
|
622
|
+
await this.emitDetail(session.activeTurn, detail);
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
if (message.method === 'turn/completed' && isTurnMatch(session.activeTurn, params) && session.activeTurn) {
|
|
626
|
+
const activeTurn = session.activeTurn;
|
|
627
|
+
activeTurn.done = true;
|
|
628
|
+
try {
|
|
629
|
+
await activeTurn.callbackChain;
|
|
630
|
+
}
|
|
631
|
+
catch {
|
|
632
|
+
// Callback failures are already logged by the emit helpers.
|
|
633
|
+
}
|
|
634
|
+
const status = extractTurnStatus(params);
|
|
635
|
+
if (status === 'failed') {
|
|
636
|
+
const messageText = extractTurnFailureMessage(params) || 'Codex app-server turn failed';
|
|
637
|
+
session.activeTurn = null;
|
|
638
|
+
activeTurn.rejectCompletion(new Error(messageText));
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
if (status === 'interrupted' && activeTurn.aborted) {
|
|
642
|
+
session.activeTurn = null;
|
|
643
|
+
activeTurn.rejectCompletion(buildAbortError());
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
const result = {
|
|
647
|
+
content: activeTurn.finalContent.trim(),
|
|
648
|
+
sessionId: session.threadId,
|
|
649
|
+
usage: activeTurn.usage,
|
|
650
|
+
rawOutput: serializeEventLog(activeTurn.eventLog),
|
|
651
|
+
eventLog: activeTurn.eventLog,
|
|
652
|
+
thinking: activeTurn.thinkingText || undefined,
|
|
653
|
+
};
|
|
654
|
+
session.activeTurn = null;
|
|
655
|
+
activeTurn.resolveCompletion(result);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
handleStderr(session, chunk) {
|
|
659
|
+
appendEvent(session.activeTurn?.eventLog, 'stderr', 'stderr', chunk);
|
|
660
|
+
void this.emitDetail(session.activeTurn, chunk);
|
|
661
|
+
}
|
|
662
|
+
handleProcessError(session, err) {
|
|
663
|
+
this.logWarn(`[codex-app-server] Process error for ${session.key}: ${err.message}`);
|
|
664
|
+
if (session.activeTurn) {
|
|
665
|
+
const activeTurn = session.activeTurn;
|
|
666
|
+
session.activeTurn = null;
|
|
667
|
+
activeTurn.rejectCompletion(err);
|
|
668
|
+
}
|
|
669
|
+
for (const pending of session.pendingRequests.values()) {
|
|
670
|
+
pending.reject(err);
|
|
671
|
+
}
|
|
672
|
+
session.pendingRequests.clear();
|
|
673
|
+
session.closed = true;
|
|
674
|
+
this.sessions.delete(session.key);
|
|
675
|
+
}
|
|
676
|
+
handleProcessClose(session, code, signal) {
|
|
677
|
+
if (session.stdoutCarry.trim()) {
|
|
678
|
+
const parsed = safeJsonParse(session.stdoutCarry.trim());
|
|
679
|
+
if (parsed) {
|
|
680
|
+
if (isResponseMessage(parsed)) {
|
|
681
|
+
appendEvent(session.activeTurn?.eventLog, 'in', 'response', parsed);
|
|
682
|
+
this.handleResponse(session, parsed);
|
|
683
|
+
}
|
|
684
|
+
else if (isServerRequestMessage(parsed)) {
|
|
685
|
+
appendEvent(session.activeTurn?.eventLog, 'in', 'server_request', parsed);
|
|
686
|
+
void this.handleServerRequest(session, parsed);
|
|
687
|
+
}
|
|
688
|
+
else if (isNotificationMessage(parsed)) {
|
|
689
|
+
appendEvent(session.activeTurn?.eventLog, 'in', 'notification', parsed);
|
|
690
|
+
void this.handleNotification(session, parsed);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
session.closed = true;
|
|
695
|
+
this.sessions.delete(session.key);
|
|
696
|
+
const closeError = new Error(`Codex app-server exited${code !== null ? ` with code ${code}` : ''}${signal ? ` (${signal})` : ''}`);
|
|
697
|
+
for (const pending of session.pendingRequests.values()) {
|
|
698
|
+
pending.reject(closeError);
|
|
699
|
+
}
|
|
700
|
+
session.pendingRequests.clear();
|
|
701
|
+
if (session.activeTurn) {
|
|
702
|
+
const activeTurn = session.activeTurn;
|
|
703
|
+
session.activeTurn = null;
|
|
704
|
+
if (!activeTurn.done) {
|
|
705
|
+
activeTurn.rejectCompletion(closeError);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
async emitChunk(activeTurn, chunk) {
|
|
710
|
+
if (!activeTurn?.onChunk || !chunk)
|
|
711
|
+
return;
|
|
712
|
+
activeTurn.callbackChain = activeTurn.callbackChain.then(async () => {
|
|
713
|
+
await activeTurn.onChunk?.(chunk);
|
|
714
|
+
}).catch((err) => {
|
|
715
|
+
this.logWarn(`[codex-app-server] onChunk callback failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
716
|
+
});
|
|
717
|
+
await activeTurn.callbackChain;
|
|
718
|
+
}
|
|
719
|
+
async emitThinkingChunk(activeTurn, chunk) {
|
|
720
|
+
if (!activeTurn?.onThinkingChunk || !chunk)
|
|
721
|
+
return;
|
|
722
|
+
activeTurn.callbackChain = activeTurn.callbackChain.then(async () => {
|
|
723
|
+
await activeTurn.onThinkingChunk?.(chunk);
|
|
724
|
+
}).catch((err) => {
|
|
725
|
+
this.logWarn(`[codex-app-server] onThinkingChunk callback failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
726
|
+
});
|
|
727
|
+
await activeTurn.callbackChain;
|
|
728
|
+
}
|
|
729
|
+
async emitDetail(activeTurn, detail) {
|
|
730
|
+
if (!activeTurn?.onDetail)
|
|
731
|
+
return;
|
|
732
|
+
const normalized = normalizeDetail(detail);
|
|
733
|
+
if (!normalized)
|
|
734
|
+
return;
|
|
735
|
+
if (activeTurn.detailFingerprints.has(normalized))
|
|
736
|
+
return;
|
|
737
|
+
activeTurn.detailFingerprints.add(normalized);
|
|
738
|
+
activeTurn.callbackChain = activeTurn.callbackChain.then(async () => {
|
|
739
|
+
await activeTurn.onDetail?.(normalized);
|
|
740
|
+
}).catch((err) => {
|
|
741
|
+
this.logWarn(`[codex-app-server] onDetail callback failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
742
|
+
});
|
|
743
|
+
await activeTurn.callbackChain;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
exports.CodexAppServerManager = CodexAppServerManager;
|
|
747
|
+
function getCodexAppServerManager() {
|
|
748
|
+
if (!_manager) {
|
|
749
|
+
_manager = new CodexAppServerManager();
|
|
750
|
+
}
|
|
751
|
+
return _manager;
|
|
752
|
+
}
|
|
753
|
+
async function runCodexAppServerTurnForTest(manager, opts) {
|
|
754
|
+
return manager.runTurn(opts);
|
|
755
|
+
}
|
|
756
|
+
exports.__codexAppServerTestUtils = {
|
|
757
|
+
LOCAL_ONLY_ERROR,
|
|
758
|
+
buildCodexAppServerInput,
|
|
759
|
+
buildCodexAppServerToolEnv,
|
|
760
|
+
scrubCodexAppServerEnv,
|
|
761
|
+
buildApprovalResponse,
|
|
762
|
+
buildTurnUsage,
|
|
763
|
+
createEventLog,
|
|
764
|
+
serializeEventLog,
|
|
765
|
+
sessionKey,
|
|
766
|
+
delay,
|
|
767
|
+
};
|
|
768
|
+
//# sourceMappingURL=codex-app-server-manager.js.map
|