remote-codex 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/supervisor-api/dist/index.js +7515 -6185
- package/apps/supervisor-web/dist/assets/{highlighted-body-OFNGDK62-DvEvXPo5.js → highlighted-body-OFNGDK62-owvlMiML.js} +1 -1
- package/apps/supervisor-web/dist/assets/index-CbIt0KnL.css +32 -0
- package/apps/supervisor-web/dist/assets/index-CrcX157r.js +377 -0
- package/apps/supervisor-web/dist/assets/{xterm-CWQ1ih_R.js → xterm-BQ_J5An_.js} +1 -1
- package/apps/supervisor-web/dist/index.html +2 -2
- package/package.json +5 -1
- package/packages/agent-runtime/src/index.ts +2 -0
- package/packages/agent-runtime/src/registry.ts +44 -0
- package/packages/agent-runtime/src/types.ts +534 -0
- package/packages/codex/src/appServerManager.test.ts +328 -0
- package/packages/codex/src/appServerManager.ts +656 -0
- package/packages/codex/src/historyItems.ts +1256 -0
- package/packages/codex/src/hookHistory.ts +224 -0
- package/packages/codex/src/index.ts +6 -0
- package/packages/codex/src/jsonrpc.test.ts +58 -0
- package/packages/codex/src/jsonrpc.ts +198 -0
- package/packages/codex/src/requestMapper.test.ts +127 -0
- package/packages/codex/src/requestMapper.ts +511 -0
- package/packages/codex/src/runtimeAdapter.ts +743 -0
- package/packages/codex/src/types.ts +403 -0
- package/packages/db/migrations/0015_agent_provider_fields.sql +14 -0
- package/packages/db/migrations/0016_remove_codex_thread_goal_id.sql +46 -0
- package/packages/db/migrations/0017_remove_codex_thread_columns.sql +85 -0
- package/packages/db/src/client.ts +53 -0
- package/packages/db/src/index.ts +5 -0
- package/packages/db/src/migrate.test.ts +36 -0
- package/packages/db/src/migrate.ts +84 -0
- package/packages/db/src/repositories.ts +898 -0
- package/packages/db/src/schema.ts +177 -0
- package/packages/db/src/seed.ts +51 -0
- package/packages/shared/src/index.ts +880 -0
- package/apps/supervisor-web/dist/assets/index-CQu6sRq7.css +0 -32
- package/apps/supervisor-web/dist/assets/index-MELw9ga_.js +0 -377
|
@@ -0,0 +1,743 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
AgentModel,
|
|
5
|
+
AgentGoal,
|
|
6
|
+
AgentProviderCapabilities,
|
|
7
|
+
AgentProviderNotification,
|
|
8
|
+
AgentProviderRequest,
|
|
9
|
+
AgentPendingProviderRequest,
|
|
10
|
+
AgentRuntime,
|
|
11
|
+
AgentRuntimeEvent,
|
|
12
|
+
AgentRuntimeManagementSchema,
|
|
13
|
+
AgentRuntimeStatus,
|
|
14
|
+
AgentSessionDetail,
|
|
15
|
+
AgentSessionStatus,
|
|
16
|
+
AgentSessionSummary,
|
|
17
|
+
AgentTurn,
|
|
18
|
+
InterruptAgentTurnInput,
|
|
19
|
+
ResumeAgentSessionInput,
|
|
20
|
+
SendAgentInputInput,
|
|
21
|
+
SetAgentGoalInput,
|
|
22
|
+
StartAgentSessionInput,
|
|
23
|
+
StartAgentSessionResult,
|
|
24
|
+
StartAgentTurnInput,
|
|
25
|
+
} from '../../agent-runtime/src/index';
|
|
26
|
+
import {
|
|
27
|
+
buildCodexProviderRequestResponse,
|
|
28
|
+
mapCodexProviderRequest,
|
|
29
|
+
} from './requestMapper';
|
|
30
|
+
import {
|
|
31
|
+
codexHookRunToHistoryItem,
|
|
32
|
+
} from './hookHistory';
|
|
33
|
+
import {
|
|
34
|
+
codexTurnToAgentTurn,
|
|
35
|
+
liveCodexItemToHistoryItem,
|
|
36
|
+
} from './historyItems';
|
|
37
|
+
import { AgentRuntimeError } from '../../agent-runtime/src/index';
|
|
38
|
+
import {
|
|
39
|
+
AppServerStatusSnapshot,
|
|
40
|
+
CodexAppServerManager,
|
|
41
|
+
CodexThreadRecord,
|
|
42
|
+
CodexThreadStatus,
|
|
43
|
+
CodexThreadGoalRecord,
|
|
44
|
+
CodexTurnRecord,
|
|
45
|
+
CodexTurnItem,
|
|
46
|
+
CodexServerEvent,
|
|
47
|
+
ReasoningEffort,
|
|
48
|
+
SandboxPolicy,
|
|
49
|
+
SandboxMode,
|
|
50
|
+
ThreadResumeInput,
|
|
51
|
+
ThreadStartInput,
|
|
52
|
+
TurnStartInput,
|
|
53
|
+
JsonRpcClientError,
|
|
54
|
+
} from './index';
|
|
55
|
+
|
|
56
|
+
export const codexCapabilities: AgentProviderCapabilities = {
|
|
57
|
+
sessions: {
|
|
58
|
+
list: true,
|
|
59
|
+
read: true,
|
|
60
|
+
resume: true,
|
|
61
|
+
importLocal: true,
|
|
62
|
+
},
|
|
63
|
+
turns: {
|
|
64
|
+
start: true,
|
|
65
|
+
streamInput: false,
|
|
66
|
+
steer: true,
|
|
67
|
+
interrupt: true,
|
|
68
|
+
compact: true,
|
|
69
|
+
},
|
|
70
|
+
branching: {
|
|
71
|
+
fork: true,
|
|
72
|
+
hardRollback: true,
|
|
73
|
+
resumeAt: false,
|
|
74
|
+
rewindFiles: false,
|
|
75
|
+
},
|
|
76
|
+
controls: {
|
|
77
|
+
planMode: true,
|
|
78
|
+
permissionRequests: true,
|
|
79
|
+
sandboxMode: true,
|
|
80
|
+
performanceMode: true,
|
|
81
|
+
goals: true,
|
|
82
|
+
},
|
|
83
|
+
management: {
|
|
84
|
+
models: true,
|
|
85
|
+
mcpStatus: true,
|
|
86
|
+
skills: true,
|
|
87
|
+
hooks: true,
|
|
88
|
+
hookTrust: true,
|
|
89
|
+
hostConfigFiles: true,
|
|
90
|
+
providerSettings: false,
|
|
91
|
+
},
|
|
92
|
+
usage: {
|
|
93
|
+
contextWindow: true,
|
|
94
|
+
tokenUsage: true,
|
|
95
|
+
costUsd: false,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
function toIsoFromEpoch(value: number | null | undefined) {
|
|
100
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const epochMs = value < 10_000_000_000 ? value * 1000 : value;
|
|
104
|
+
return new Date(epochMs).toISOString();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function normalizeStatus(status: CodexThreadStatus): AgentSessionStatus {
|
|
108
|
+
switch (status.type) {
|
|
109
|
+
case 'active':
|
|
110
|
+
return 'running';
|
|
111
|
+
case 'idle':
|
|
112
|
+
return 'idle';
|
|
113
|
+
case 'notLoaded':
|
|
114
|
+
return 'not_loaded';
|
|
115
|
+
case 'systemError':
|
|
116
|
+
return 'system_error';
|
|
117
|
+
default:
|
|
118
|
+
return 'system_error';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function mapStatus(status: AppServerStatusSnapshot): AgentRuntimeStatus {
|
|
123
|
+
return {
|
|
124
|
+
state: status.state,
|
|
125
|
+
transport: status.transport,
|
|
126
|
+
lastStartedAt: status.lastStartedAt,
|
|
127
|
+
lastError: status.lastError,
|
|
128
|
+
restartCount: status.restartCount,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function mapModel(model: Awaited<ReturnType<CodexAppServerManager['listModels']>>[number]): AgentModel {
|
|
133
|
+
return {
|
|
134
|
+
id: model.id,
|
|
135
|
+
model: model.model,
|
|
136
|
+
displayName: model.displayName,
|
|
137
|
+
description: model.description,
|
|
138
|
+
isDefault: model.isDefault,
|
|
139
|
+
hidden: model.hidden,
|
|
140
|
+
supportedReasoningEfforts: model.supportedReasoningEfforts.map((entry) => ({
|
|
141
|
+
reasoningEffort: entry.reasoningEffort,
|
|
142
|
+
description: entry.description,
|
|
143
|
+
})),
|
|
144
|
+
defaultReasoningEffort: model.defaultReasoningEffort,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function mapTurn(turn: CodexTurnRecord): AgentTurn {
|
|
149
|
+
return codexTurnToAgentTurn(turn);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function mapGoal(goal: CodexThreadGoalRecord): AgentGoal {
|
|
153
|
+
return {
|
|
154
|
+
providerSessionId: goal.threadId,
|
|
155
|
+
objective: goal.objective,
|
|
156
|
+
status: goal.status,
|
|
157
|
+
tokenBudget: goal.tokenBudget,
|
|
158
|
+
tokensUsed: goal.tokensUsed,
|
|
159
|
+
timeUsedSeconds: goal.timeUsedSeconds,
|
|
160
|
+
createdAt: goal.createdAt,
|
|
161
|
+
updatedAt: goal.updatedAt,
|
|
162
|
+
rawGoal: goal,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function serviceTierForPerformanceMode(
|
|
167
|
+
performanceMode: StartAgentSessionInput['performanceMode'],
|
|
168
|
+
): 'fast' | null | undefined {
|
|
169
|
+
if (performanceMode === undefined) {
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
return performanceMode === 'fast' ? 'fast' : null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function buildSandboxPolicy(
|
|
176
|
+
sandboxMode: StartAgentTurnInput['sandboxMode'],
|
|
177
|
+
workspacePath: StartAgentTurnInput['workspacePath'],
|
|
178
|
+
): SandboxPolicy | null | undefined {
|
|
179
|
+
if (sandboxMode === undefined) {
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
switch (sandboxMode) {
|
|
184
|
+
case 'danger-full-access':
|
|
185
|
+
return {
|
|
186
|
+
type: 'dangerFullAccess',
|
|
187
|
+
};
|
|
188
|
+
case 'read-only':
|
|
189
|
+
return {
|
|
190
|
+
type: 'readOnly',
|
|
191
|
+
access: {
|
|
192
|
+
type: 'fullAccess',
|
|
193
|
+
},
|
|
194
|
+
networkAccess: false,
|
|
195
|
+
};
|
|
196
|
+
case 'workspace-write':
|
|
197
|
+
default:
|
|
198
|
+
return {
|
|
199
|
+
type: 'workspaceWrite',
|
|
200
|
+
writableRoots: workspacePath ? [workspacePath] : [],
|
|
201
|
+
readOnlyAccess: {
|
|
202
|
+
type: 'fullAccess',
|
|
203
|
+
},
|
|
204
|
+
networkAccess: false,
|
|
205
|
+
excludeTmpdirEnvVar: false,
|
|
206
|
+
excludeSlashTmp: false,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function mapSession(thread: CodexThreadRecord): AgentSessionDetail {
|
|
212
|
+
return {
|
|
213
|
+
provider: 'codex',
|
|
214
|
+
providerSessionId: thread.id,
|
|
215
|
+
cwd: thread.cwd,
|
|
216
|
+
title: thread.name,
|
|
217
|
+
preview: thread.preview,
|
|
218
|
+
createdAt: toIsoFromEpoch(thread.createdAt),
|
|
219
|
+
updatedAt: toIsoFromEpoch(thread.updatedAt),
|
|
220
|
+
status: normalizeStatus(thread.status),
|
|
221
|
+
turns: thread.turns.map(mapTurn),
|
|
222
|
+
rawSession: thread,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function mapCodexNotification(event: CodexServerEvent): AgentRuntimeEvent | null {
|
|
227
|
+
switch (event.method) {
|
|
228
|
+
case 'thread/status/changed': {
|
|
229
|
+
const params = event.params as { threadId: string; status: CodexThreadStatus };
|
|
230
|
+
return {
|
|
231
|
+
type: 'session.status.changed',
|
|
232
|
+
provider: 'codex',
|
|
233
|
+
providerSessionId: params.threadId,
|
|
234
|
+
status: normalizeStatus(params.status),
|
|
235
|
+
rawStatus: params.status,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
case 'thread/name/updated': {
|
|
239
|
+
const params = event.params as { threadId: string; threadName?: string };
|
|
240
|
+
return params.threadName
|
|
241
|
+
? {
|
|
242
|
+
type: 'session.title.updated',
|
|
243
|
+
provider: 'codex',
|
|
244
|
+
providerSessionId: params.threadId,
|
|
245
|
+
title: params.threadName,
|
|
246
|
+
}
|
|
247
|
+
: null;
|
|
248
|
+
}
|
|
249
|
+
case 'thread/goal/updated': {
|
|
250
|
+
const params = event.params as {
|
|
251
|
+
threadId: string;
|
|
252
|
+
turnId: string | null;
|
|
253
|
+
goal: CodexThreadGoalRecord;
|
|
254
|
+
};
|
|
255
|
+
return {
|
|
256
|
+
type: 'goal.updated',
|
|
257
|
+
provider: 'codex',
|
|
258
|
+
providerSessionId: params.threadId,
|
|
259
|
+
providerTurnId: params.turnId,
|
|
260
|
+
goal: mapGoal(params.goal),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
case 'thread/goal/cleared': {
|
|
264
|
+
const params = event.params as { threadId: string };
|
|
265
|
+
return {
|
|
266
|
+
type: 'goal.cleared',
|
|
267
|
+
provider: 'codex',
|
|
268
|
+
providerSessionId: params.threadId,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
case 'thread/tokenUsage/updated': {
|
|
272
|
+
const params = event.params as {
|
|
273
|
+
threadId: string;
|
|
274
|
+
turnId: string;
|
|
275
|
+
tokenUsage: unknown;
|
|
276
|
+
};
|
|
277
|
+
return {
|
|
278
|
+
type: 'usage.updated',
|
|
279
|
+
provider: 'codex',
|
|
280
|
+
providerSessionId: params.threadId,
|
|
281
|
+
providerTurnId: params.turnId,
|
|
282
|
+
usage: params.tokenUsage,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
case 'turn/started': {
|
|
286
|
+
const params = event.params as { threadId: string; turn: CodexTurnRecord };
|
|
287
|
+
return {
|
|
288
|
+
type: 'turn.started',
|
|
289
|
+
provider: 'codex',
|
|
290
|
+
providerSessionId: params.threadId,
|
|
291
|
+
turn: mapTurn(params.turn),
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
case 'hook/started': {
|
|
295
|
+
const params = event.params as {
|
|
296
|
+
threadId: string;
|
|
297
|
+
turnId: string | null;
|
|
298
|
+
run: unknown;
|
|
299
|
+
};
|
|
300
|
+
return {
|
|
301
|
+
type: 'hook.started',
|
|
302
|
+
provider: 'codex',
|
|
303
|
+
providerSessionId: params.threadId,
|
|
304
|
+
providerTurnId: params.turnId,
|
|
305
|
+
item: codexHookRunToHistoryItem(
|
|
306
|
+
params.run as Parameters<typeof codexHookRunToHistoryItem>[0],
|
|
307
|
+
),
|
|
308
|
+
rawHookRun: params.run,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
case 'hook/completed': {
|
|
312
|
+
const params = event.params as {
|
|
313
|
+
threadId: string;
|
|
314
|
+
turnId: string | null;
|
|
315
|
+
run: unknown;
|
|
316
|
+
};
|
|
317
|
+
return {
|
|
318
|
+
type: 'hook.completed',
|
|
319
|
+
provider: 'codex',
|
|
320
|
+
providerSessionId: params.threadId,
|
|
321
|
+
providerTurnId: params.turnId,
|
|
322
|
+
item: codexHookRunToHistoryItem(
|
|
323
|
+
params.run as Parameters<typeof codexHookRunToHistoryItem>[0],
|
|
324
|
+
),
|
|
325
|
+
rawHookRun: params.run,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
case 'item/started': {
|
|
329
|
+
const params = event.params as {
|
|
330
|
+
threadId: string;
|
|
331
|
+
turnId: string;
|
|
332
|
+
item: CodexTurnItem;
|
|
333
|
+
};
|
|
334
|
+
const item = liveCodexItemToHistoryItem(params.item, 'started');
|
|
335
|
+
return item ? {
|
|
336
|
+
type: 'item.started',
|
|
337
|
+
provider: 'codex',
|
|
338
|
+
providerSessionId: params.threadId,
|
|
339
|
+
providerTurnId: params.turnId,
|
|
340
|
+
item,
|
|
341
|
+
} : null;
|
|
342
|
+
}
|
|
343
|
+
case 'item/completed': {
|
|
344
|
+
const params = event.params as {
|
|
345
|
+
threadId: string;
|
|
346
|
+
turnId: string;
|
|
347
|
+
item: CodexTurnItem;
|
|
348
|
+
};
|
|
349
|
+
const item = liveCodexItemToHistoryItem(params.item, 'completed');
|
|
350
|
+
return item ? {
|
|
351
|
+
type: 'item.completed',
|
|
352
|
+
provider: 'codex',
|
|
353
|
+
providerSessionId: params.threadId,
|
|
354
|
+
providerTurnId: params.turnId,
|
|
355
|
+
item,
|
|
356
|
+
} : null;
|
|
357
|
+
}
|
|
358
|
+
case 'turn/plan/updated': {
|
|
359
|
+
const params = event.params as {
|
|
360
|
+
threadId: string;
|
|
361
|
+
turnId: string;
|
|
362
|
+
explanation: string | null;
|
|
363
|
+
plan: Array<{ step: string; status: string }>;
|
|
364
|
+
};
|
|
365
|
+
return {
|
|
366
|
+
type: 'plan.updated',
|
|
367
|
+
provider: 'codex',
|
|
368
|
+
providerSessionId: params.threadId,
|
|
369
|
+
providerTurnId: params.turnId,
|
|
370
|
+
explanation: params.explanation,
|
|
371
|
+
plan: params.plan,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
case 'item/agentMessage/delta': {
|
|
375
|
+
const params = event.params as {
|
|
376
|
+
threadId: string;
|
|
377
|
+
turnId: string;
|
|
378
|
+
itemId: string;
|
|
379
|
+
delta: string;
|
|
380
|
+
};
|
|
381
|
+
return {
|
|
382
|
+
type: 'output.delta',
|
|
383
|
+
provider: 'codex',
|
|
384
|
+
providerSessionId: params.threadId,
|
|
385
|
+
providerTurnId: params.turnId,
|
|
386
|
+
itemId: params.itemId,
|
|
387
|
+
delta: params.delta,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
case 'turn/completed': {
|
|
391
|
+
const params = event.params as { threadId: string; turn: CodexTurnRecord };
|
|
392
|
+
return {
|
|
393
|
+
type: 'turn.completed',
|
|
394
|
+
provider: 'codex',
|
|
395
|
+
providerSessionId: params.threadId,
|
|
396
|
+
turn: mapTurn(params.turn),
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
case 'error': {
|
|
400
|
+
const params = event.params as {
|
|
401
|
+
threadId: string;
|
|
402
|
+
turnId: string;
|
|
403
|
+
error: { message?: string };
|
|
404
|
+
willRetry: boolean;
|
|
405
|
+
};
|
|
406
|
+
return {
|
|
407
|
+
type: 'turn.failed',
|
|
408
|
+
provider: 'codex',
|
|
409
|
+
providerSessionId: params.threadId,
|
|
410
|
+
providerTurnId: params.turnId,
|
|
411
|
+
error: params.error.message ?? 'Turn failed unexpectedly.',
|
|
412
|
+
willRetry: params.willRetry,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
default:
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function mapCodexRuntimeError(error: unknown): never {
|
|
421
|
+
if (error instanceof AgentRuntimeError) {
|
|
422
|
+
throw error;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (error instanceof JsonRpcClientError) {
|
|
426
|
+
throw new AgentRuntimeError(
|
|
427
|
+
error.message,
|
|
428
|
+
'codex',
|
|
429
|
+
error.code === 'request_timeout'
|
|
430
|
+
? 'request_timeout'
|
|
431
|
+
: error.code === 'remote_error'
|
|
432
|
+
? 'remote_error'
|
|
433
|
+
: error.code === 'client_closed'
|
|
434
|
+
? 'client_closed'
|
|
435
|
+
: error.code === 'app_server_unavailable'
|
|
436
|
+
? 'provider_unavailable'
|
|
437
|
+
: 'request_failed',
|
|
438
|
+
error.details,
|
|
439
|
+
error,
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (error instanceof Error) {
|
|
444
|
+
throw new AgentRuntimeError(error.message, 'codex', 'request_failed', undefined, error);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
throw new AgentRuntimeError('Codex runtime request failed.', 'codex', 'request_failed', undefined, error);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
async function codexRuntimeCall<T>(callback: () => Promise<T>): Promise<T> {
|
|
451
|
+
try {
|
|
452
|
+
return await callback();
|
|
453
|
+
} catch (error) {
|
|
454
|
+
mapCodexRuntimeError(error);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export class CodexRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
459
|
+
readonly provider = 'codex' as const;
|
|
460
|
+
readonly displayName = 'Codex';
|
|
461
|
+
readonly description = 'Local Codex app-server runtime.';
|
|
462
|
+
readonly capabilities = codexCapabilities;
|
|
463
|
+
readonly managementSchema: AgentRuntimeManagementSchema = {
|
|
464
|
+
hostConfigFiles: [
|
|
465
|
+
{
|
|
466
|
+
name: 'config.toml',
|
|
467
|
+
label: 'config.toml',
|
|
468
|
+
description: 'Runtime configuration',
|
|
469
|
+
roles: ['runtime', 'mcp'],
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
name: 'auth.json',
|
|
473
|
+
label: 'auth.json',
|
|
474
|
+
description: 'Authentication state',
|
|
475
|
+
roles: ['auth'],
|
|
476
|
+
},
|
|
477
|
+
],
|
|
478
|
+
toolboxItems: [
|
|
479
|
+
{ action: 'fast', command: '/fast', label: 'Fast mode' },
|
|
480
|
+
{ action: 'compact', command: '/compact', label: 'Compact context' },
|
|
481
|
+
{ action: 'goal', command: '/goal', label: 'Goal' },
|
|
482
|
+
{ action: 'fork', command: '/fork', label: 'Fork', panel: 'fork' },
|
|
483
|
+
{ action: 'skills', command: '/skills', label: 'Skills', panel: 'skills' },
|
|
484
|
+
{ action: 'mcp', command: '/mcp', label: 'MCP', panel: 'mcp' },
|
|
485
|
+
{ action: 'hooks', command: '/hooks', label: 'Hooks', panel: 'hooks' },
|
|
486
|
+
],
|
|
487
|
+
hookCommandTemplates: [
|
|
488
|
+
{
|
|
489
|
+
eventName: 'preToolUse',
|
|
490
|
+
command:
|
|
491
|
+
'node -e "process.stdin.resume(); process.stdin.on(\'end\', () => console.error(\'remote-codex hook ran\'))"',
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
eventName: 'stop',
|
|
495
|
+
command:
|
|
496
|
+
'node -e \'process.stdin.resume(); process.stdin.on("end", () => console.log(JSON.stringify({ systemMessage: "remote-codex hook ran" })))\'',
|
|
497
|
+
},
|
|
498
|
+
],
|
|
499
|
+
providerConfigFormat: 'toml',
|
|
500
|
+
mcpConfigFormat: 'codex-toml',
|
|
501
|
+
configArchives: true,
|
|
502
|
+
buildRestart: true,
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
constructor(readonly manager: CodexAppServerManager) {
|
|
506
|
+
super();
|
|
507
|
+
this.manager.on('status', (status) => {
|
|
508
|
+
this.emit('status', mapStatus(status));
|
|
509
|
+
});
|
|
510
|
+
this.manager.on('notification', (event) => {
|
|
511
|
+
const runtimeEvent = mapCodexNotification(event);
|
|
512
|
+
if (runtimeEvent) {
|
|
513
|
+
this.emit('event', runtimeEvent);
|
|
514
|
+
}
|
|
515
|
+
this.emit('provider-notification', {
|
|
516
|
+
provider: 'codex',
|
|
517
|
+
method: event.method,
|
|
518
|
+
params: event.params,
|
|
519
|
+
rawNotification: event,
|
|
520
|
+
} satisfies AgentProviderNotification);
|
|
521
|
+
});
|
|
522
|
+
this.manager.on('request', (request) => {
|
|
523
|
+
this.emit('provider-request', {
|
|
524
|
+
provider: 'codex',
|
|
525
|
+
id: request.id,
|
|
526
|
+
method: request.method,
|
|
527
|
+
params: request.params,
|
|
528
|
+
rawRequest: request,
|
|
529
|
+
} satisfies AgentProviderRequest);
|
|
530
|
+
});
|
|
531
|
+
this.manager.on('stderr', (message) => {
|
|
532
|
+
this.emit('stderr', message);
|
|
533
|
+
});
|
|
534
|
+
this.manager.on('warning', (warning) => {
|
|
535
|
+
this.emit('warning', warning);
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
getStatus(): AgentRuntimeStatus {
|
|
540
|
+
return mapStatus(this.manager.getStatus());
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
start() {
|
|
544
|
+
return codexRuntimeCall(() => this.manager.start());
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
stop() {
|
|
548
|
+
return codexRuntimeCall(() => this.manager.stop());
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
async listModels(): Promise<AgentModel[]> {
|
|
552
|
+
return (await codexRuntimeCall(() => this.manager.listModels())).map(mapModel);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
async listSessions(): Promise<AgentSessionSummary[]> {
|
|
556
|
+
return (await codexRuntimeCall(() => this.manager.listThreads())).map((thread) => {
|
|
557
|
+
const session = mapSession(thread);
|
|
558
|
+
return {
|
|
559
|
+
provider: session.provider,
|
|
560
|
+
providerSessionId: session.providerSessionId,
|
|
561
|
+
cwd: session.cwd,
|
|
562
|
+
title: session.title,
|
|
563
|
+
preview: session.preview,
|
|
564
|
+
createdAt: session.createdAt,
|
|
565
|
+
updatedAt: session.updatedAt,
|
|
566
|
+
status: session.status,
|
|
567
|
+
rawSession: thread,
|
|
568
|
+
};
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
listLoadedSessions() {
|
|
573
|
+
return codexRuntimeCall(() => this.manager.listLoadedThreads());
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
async readSession(providerSessionId: string): Promise<AgentSessionDetail> {
|
|
577
|
+
return mapSession(await codexRuntimeCall(() => this.manager.readThread(providerSessionId)));
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
async startSession(input: StartAgentSessionInput): Promise<StartAgentSessionResult> {
|
|
581
|
+
const startInput: ThreadStartInput = {
|
|
582
|
+
cwd: input.cwd,
|
|
583
|
+
model: input.model,
|
|
584
|
+
approvalPolicy: input.approvalMode === 'guarded' ? 'on-request' : 'never',
|
|
585
|
+
};
|
|
586
|
+
if (input.sandboxMode !== undefined) {
|
|
587
|
+
startInput.sandbox = input.sandboxMode as SandboxMode | null;
|
|
588
|
+
}
|
|
589
|
+
const serviceTier = serviceTierForPerformanceMode(input.performanceMode);
|
|
590
|
+
if (serviceTier !== undefined) {
|
|
591
|
+
startInput.serviceTier = serviceTier;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const response = await codexRuntimeCall(() => this.manager.startThread(startInput));
|
|
595
|
+
return {
|
|
596
|
+
provider: 'codex',
|
|
597
|
+
providerSessionId: response.thread.id,
|
|
598
|
+
model: response.model,
|
|
599
|
+
reasoningEffort: response.reasoningEffort ?? null,
|
|
600
|
+
sandboxMode: response.sandbox ?? null,
|
|
601
|
+
session: mapSession(response.thread),
|
|
602
|
+
rawSession: response.thread,
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
async resumeSession(input: ResumeAgentSessionInput): Promise<StartAgentSessionResult> {
|
|
607
|
+
const resumeInput: ThreadResumeInput = {
|
|
608
|
+
threadId: input.providerSessionId,
|
|
609
|
+
};
|
|
610
|
+
if (input.model !== undefined) {
|
|
611
|
+
resumeInput.model = input.model;
|
|
612
|
+
}
|
|
613
|
+
if (input.sandboxMode !== undefined) {
|
|
614
|
+
resumeInput.sandbox = input.sandboxMode as SandboxMode | null;
|
|
615
|
+
}
|
|
616
|
+
const serviceTier = serviceTierForPerformanceMode(input.performanceMode);
|
|
617
|
+
if (serviceTier !== undefined) {
|
|
618
|
+
resumeInput.serviceTier = serviceTier;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const response = await codexRuntimeCall(() => this.manager.resumeThread(resumeInput));
|
|
622
|
+
return {
|
|
623
|
+
provider: 'codex',
|
|
624
|
+
providerSessionId: response.thread.id,
|
|
625
|
+
model: response.model,
|
|
626
|
+
reasoningEffort: response.reasoningEffort ?? null,
|
|
627
|
+
sandboxMode: response.sandbox ?? null,
|
|
628
|
+
session: mapSession(response.thread),
|
|
629
|
+
rawSession: response.thread,
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
async startTurn(input: StartAgentTurnInput): Promise<AgentTurn> {
|
|
634
|
+
const turnInput: TurnStartInput = {
|
|
635
|
+
threadId: input.providerSessionId,
|
|
636
|
+
prompt: input.prompt,
|
|
637
|
+
};
|
|
638
|
+
if (input.model !== undefined) {
|
|
639
|
+
turnInput.model = input.model;
|
|
640
|
+
}
|
|
641
|
+
if (input.reasoningEffort !== undefined) {
|
|
642
|
+
turnInput.effort = input.reasoningEffort as ReasoningEffort | null;
|
|
643
|
+
}
|
|
644
|
+
if (input.collaborationMode !== undefined) {
|
|
645
|
+
turnInput.collaborationMode = input.collaborationMode;
|
|
646
|
+
}
|
|
647
|
+
const sandboxPolicy = buildSandboxPolicy(input.sandboxMode, input.workspacePath);
|
|
648
|
+
if (sandboxPolicy !== undefined) {
|
|
649
|
+
turnInput.sandboxPolicy = sandboxPolicy;
|
|
650
|
+
}
|
|
651
|
+
const serviceTier = serviceTierForPerformanceMode(input.performanceMode);
|
|
652
|
+
if (serviceTier !== undefined) {
|
|
653
|
+
turnInput.serviceTier = serviceTier;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
return mapTurn(await codexRuntimeCall(() => this.manager.startTurn(turnInput)));
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
async sendInput(input: SendAgentInputInput): Promise<AgentTurn | null> {
|
|
660
|
+
const turn = await codexRuntimeCall(() => this.manager.steerTurn({
|
|
661
|
+
threadId: input.providerSessionId,
|
|
662
|
+
turnId: input.providerTurnId,
|
|
663
|
+
prompt: input.prompt,
|
|
664
|
+
}));
|
|
665
|
+
return turn ? mapTurn(turn) : null;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
async interruptTurn(input: InterruptAgentTurnInput): Promise<AgentTurn | null> {
|
|
669
|
+
const turn = await codexRuntimeCall(() => this.manager.interruptTurn(
|
|
670
|
+
input.providerSessionId,
|
|
671
|
+
input.providerTurnId,
|
|
672
|
+
));
|
|
673
|
+
return turn ? mapTurn(turn) : null;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
compactSession(providerSessionId: string) {
|
|
677
|
+
return codexRuntimeCall(() => this.manager.compactThread(providerSessionId));
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
async forkSession(input: { providerSessionId: string }): Promise<AgentSessionDetail> {
|
|
681
|
+
return mapSession(await codexRuntimeCall(() => this.manager.forkThread({
|
|
682
|
+
threadId: input.providerSessionId,
|
|
683
|
+
})));
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
async rollbackSession(input: { providerSessionId: string; count: number }): Promise<AgentSessionDetail> {
|
|
687
|
+
return mapSession(await codexRuntimeCall(() => this.manager.rollbackThread({
|
|
688
|
+
threadId: input.providerSessionId,
|
|
689
|
+
count: input.count,
|
|
690
|
+
})));
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
listMcpServers() {
|
|
694
|
+
return codexRuntimeCall(() => this.manager.listMcpServers());
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
listSkills(input: { cwds?: string[]; forceReload?: boolean } = {}) {
|
|
698
|
+
return codexRuntimeCall(() => this.manager.listSkills(input));
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
listHooks(input: { cwds?: string[] } = {}) {
|
|
702
|
+
return codexRuntimeCall(() => this.manager.listHooks(input));
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
setHookTrust(input: { key: string; trustedHash: string | null }) {
|
|
706
|
+
return codexRuntimeCall(() => this.manager.setHookTrust(input));
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
mapProviderRequest(request: AgentProviderRequest, options: { approvalMode: 'yolo' | 'guarded' }) {
|
|
710
|
+
return mapCodexProviderRequest(request, options.approvalMode);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
buildProviderRequestResponse(
|
|
714
|
+
pending: AgentPendingProviderRequest,
|
|
715
|
+
input: { answers: Record<string, { answers: string[] }> },
|
|
716
|
+
) {
|
|
717
|
+
return buildCodexProviderRequestResponse(pending, input);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
respondToProviderRequest(id: string | number, result: unknown) {
|
|
721
|
+
this.manager.respondToServerRequest(Number(id), result);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
async getGoal(providerSessionId: string): Promise<AgentGoal | null> {
|
|
725
|
+
const goal = await codexRuntimeCall(() => this.manager.getThreadGoal(providerSessionId));
|
|
726
|
+
return goal ? mapGoal(goal) : null;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
async setGoal(input: SetAgentGoalInput): Promise<AgentGoal> {
|
|
730
|
+
return mapGoal(
|
|
731
|
+
await codexRuntimeCall(() => this.manager.setThreadGoal({
|
|
732
|
+
threadId: input.providerSessionId,
|
|
733
|
+
...(input.objective !== undefined ? { objective: input.objective } : {}),
|
|
734
|
+
...(input.status !== undefined ? { status: input.status as CodexThreadGoalRecord['status'] | null } : {}),
|
|
735
|
+
...(input.tokenBudget !== undefined ? { tokenBudget: input.tokenBudget } : {}),
|
|
736
|
+
})),
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
clearGoal(providerSessionId: string) {
|
|
741
|
+
return codexRuntimeCall(() => this.manager.clearThreadGoal(providerSessionId));
|
|
742
|
+
}
|
|
743
|
+
}
|