@parall/codex-agent 1.29.3 → 1.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dispatch.js +13 -13
- package/dist/index.js +17 -13
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +6 -8
- package/package.json +4 -4
- package/src/dispatch.ts +13 -13
- package/src/index.ts +17 -13
- package/src/workspace.ts +6 -8
package/dist/dispatch.js
CHANGED
|
@@ -56,7 +56,7 @@ export class CodexAppServerAdapter {
|
|
|
56
56
|
setActiveTurn(threadId, sink, log) {
|
|
57
57
|
const existing = this.activeTurns.get(threadId);
|
|
58
58
|
if (existing) {
|
|
59
|
-
(log ?? this.opts.log)?.warn?.(`
|
|
59
|
+
(log ?? this.opts.log)?.warn?.(`thread ${threadId} already had an active turn; failing the previous dispatch`);
|
|
60
60
|
existing.push({ kind: "error", message: `thread ${threadId} replaced by concurrent turn` });
|
|
61
61
|
existing.close();
|
|
62
62
|
}
|
|
@@ -91,7 +91,7 @@ export class CodexAppServerAdapter {
|
|
|
91
91
|
return true;
|
|
92
92
|
}
|
|
93
93
|
catch (err) {
|
|
94
|
-
this.opts.log?.warn?.(`
|
|
94
|
+
this.opts.log?.warn?.(`turn/steer failed: ${errToString(err)}`);
|
|
95
95
|
return false;
|
|
96
96
|
}
|
|
97
97
|
}
|
|
@@ -104,7 +104,7 @@ export class CodexAppServerAdapter {
|
|
|
104
104
|
this.pendingInjections.delete(sessionKey);
|
|
105
105
|
const threadId = this.opts.sessionManager.getThreadId(sessionKey);
|
|
106
106
|
if (threadId && this.client && !this.client.isDisposed()) {
|
|
107
|
-
(this.opts.log ?? context.log)?.info?.(
|
|
107
|
+
(this.opts.log ?? context.log)?.info?.(`${pending} steer injection(s) already sent; skipping turn/start`);
|
|
108
108
|
yield {
|
|
109
109
|
type: "runtime_session",
|
|
110
110
|
runtimeSessionId: threadId,
|
|
@@ -112,7 +112,7 @@ export class CodexAppServerAdapter {
|
|
|
112
112
|
};
|
|
113
113
|
return;
|
|
114
114
|
}
|
|
115
|
-
(this.opts.log ?? context.log)?.warn?.(`
|
|
115
|
+
(this.opts.log ?? context.log)?.warn?.(`pending steer invalidated (subprocess died); falling through to normal dispatch`);
|
|
116
116
|
}
|
|
117
117
|
await this.ensureStarted(context.log);
|
|
118
118
|
// After ensureStarted resolves the subprocess could still die before we
|
|
@@ -156,7 +156,7 @@ export class CodexAppServerAdapter {
|
|
|
156
156
|
this.resumedThreadIds.add(threadId);
|
|
157
157
|
}
|
|
158
158
|
catch (err) {
|
|
159
|
-
log?.warn?.(`
|
|
159
|
+
log?.warn?.(`thread/resume failed (${errToString(err)}); attempting one-shot fresh-thread start`);
|
|
160
160
|
let freshThreadId;
|
|
161
161
|
try {
|
|
162
162
|
freshThreadId = await this.openThread(client, { resumeId: undefined });
|
|
@@ -192,7 +192,7 @@ export class CodexAppServerAdapter {
|
|
|
192
192
|
releasePreparedAttachments = pinLocalAttachmentPaths(preparedImages);
|
|
193
193
|
}
|
|
194
194
|
catch (err) {
|
|
195
|
-
log?.warn?.(`
|
|
195
|
+
log?.warn?.(`failed to prepare local attachments: ${errToString(err)}`);
|
|
196
196
|
}
|
|
197
197
|
// `localImage` is a first-class variant of `UserInput` in the Codex
|
|
198
198
|
// app-server protocol (codex-rs/app-server-protocol/src/protocol/v2.rs
|
|
@@ -229,7 +229,7 @@ export class CodexAppServerAdapter {
|
|
|
229
229
|
yield { type: "error", message: `Codex turn/start failed: ${message}` };
|
|
230
230
|
return;
|
|
231
231
|
}
|
|
232
|
-
log?.warn?.(`
|
|
232
|
+
log?.warn?.(`turn/start on thread ${threadId} failed (${message}); attempting one-shot fresh-thread retry`);
|
|
233
233
|
this.activeTurns.delete(threadId);
|
|
234
234
|
let freshThreadId;
|
|
235
235
|
try {
|
|
@@ -345,7 +345,7 @@ export class CodexAppServerAdapter {
|
|
|
345
345
|
const result = await client.sendRequest("thread/fork", forkParams);
|
|
346
346
|
const forkedThreadId = extractThreadId(result);
|
|
347
347
|
if (!forkedThreadId) {
|
|
348
|
-
this.opts.log?.warn?.("
|
|
348
|
+
this.opts.log?.warn?.("thread/fork returned no thread id");
|
|
349
349
|
return null;
|
|
350
350
|
}
|
|
351
351
|
this.opts.sessionManager.recordThreadId(handle.sessionKey, forkedThreadId);
|
|
@@ -359,7 +359,7 @@ export class CodexAppServerAdapter {
|
|
|
359
359
|
this.opts.sessionManager.cleanupFork(fork.sessionKey);
|
|
360
360
|
}
|
|
361
361
|
logForkFailure(err) {
|
|
362
|
-
this.opts.log?.warn?.(`
|
|
362
|
+
this.opts.log?.warn?.(`thread/fork failed: ${errToString(err)}`);
|
|
363
363
|
return null;
|
|
364
364
|
}
|
|
365
365
|
async stop() {
|
|
@@ -429,7 +429,7 @@ export class CodexAppServerAdapter {
|
|
|
429
429
|
env.PRLL_CONTEXT_FILE = this.opts.contextFilePath;
|
|
430
430
|
}
|
|
431
431
|
const args = ["app-server", "--listen", "stdio://"];
|
|
432
|
-
(log ?? this.opts.log)?.info?.(`
|
|
432
|
+
(log ?? this.opts.log)?.info?.(`spawning ${this.opts.codexBin} ${args.join(" ")}`);
|
|
433
433
|
const proc = spawn(IS_WIN32 ? quoteWin32Arg(this.opts.codexBin) : this.opts.codexBin, IS_WIN32 ? args.map(quoteWin32Arg) : args, {
|
|
434
434
|
cwd: this.opts.workspaceDir,
|
|
435
435
|
env,
|
|
@@ -438,7 +438,7 @@ export class CodexAppServerAdapter {
|
|
|
438
438
|
});
|
|
439
439
|
proc.stderr.setEncoding("utf8");
|
|
440
440
|
proc.stderr.on("data", (chunk) => {
|
|
441
|
-
(log ?? this.opts.log)?.warn?.(`
|
|
441
|
+
(log ?? this.opts.log)?.warn?.(`[stderr] ${chunk.trim()}`);
|
|
442
442
|
});
|
|
443
443
|
// Don't publish proc/client until the initialize handshake succeeds. If
|
|
444
444
|
// initialize rejects (timeout, malformed handshake, app-server crash on
|
|
@@ -485,10 +485,10 @@ export class CodexAppServerAdapter {
|
|
|
485
485
|
: `exited (code=${code ?? "null"}${signal ? `, signal=${signal}` : ""})`;
|
|
486
486
|
const logger = log ?? this.opts.log;
|
|
487
487
|
if (this.stopping) {
|
|
488
|
-
logger?.info?.(`
|
|
488
|
+
logger?.info?.(`app-server subprocess ${reason} during graceful stop`);
|
|
489
489
|
}
|
|
490
490
|
else {
|
|
491
|
-
logger?.warn?.(`
|
|
491
|
+
logger?.warn?.(`app-server subprocess ${reason}; resetting adapter`);
|
|
492
492
|
}
|
|
493
493
|
this.client?.dispose(err ?? new Error(`app-server ${reason}`));
|
|
494
494
|
for (const activeSink of this.activeTurns.values()) {
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as os from "node:os";
|
|
3
|
-
import { ParallAgentGateway, createPlatformConfigManager, createLogger, isPlatformManagedProfile, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
3
|
+
import { ParallAgentGateway, createPlatformConfigManager, createLogger, childLogger, isPlatformManagedProfile, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
4
4
|
import { ApiError, ParallClient, ParallWs } from "@parall/sdk";
|
|
5
5
|
import { buildCodexRuntimeKey, contextFilePathForSession, resolveCodexAgentConfig, resolveWsUrl, sessionStateFilePathForRuntime, stepIdFilePathForSession, } from "./config.js";
|
|
6
6
|
import { CodexAppServerAdapter } from "./dispatch.js";
|
|
7
7
|
import { CodexSessionManager } from "./session-manager.js";
|
|
8
8
|
import { ensureCodexWorkspace, ensureParallProvider, ensureWorkspaceTrusted, isParallProxyMode } from "./workspace.js";
|
|
9
9
|
const log = createLogger("codex-agent");
|
|
10
|
+
let activeLog = log;
|
|
10
11
|
async function getAgentMeWithLegacyFallback(client, orgId) {
|
|
11
12
|
try {
|
|
12
13
|
return await client.getAgentMe(orgId);
|
|
@@ -28,13 +29,15 @@ async function main() {
|
|
|
28
29
|
});
|
|
29
30
|
const me = await getAgentMeWithLegacyFallback(client, config.orgId);
|
|
30
31
|
const agentUserId = me.id;
|
|
31
|
-
|
|
32
|
+
const agentLog = childLogger(log, agentUserId);
|
|
33
|
+
activeLog = agentLog;
|
|
34
|
+
ensureWorkspaceTrusted(config.codexHome, config.workspaceDir, agentLog);
|
|
32
35
|
const useParallProvider = isParallProxyMode();
|
|
33
36
|
if (useParallProvider) {
|
|
34
|
-
ensureParallProvider(config.codexHome, config.apiUrl,
|
|
35
|
-
|
|
37
|
+
ensureParallProvider(config.codexHome, config.apiUrl, agentLog);
|
|
38
|
+
agentLog.info("parall custom provider configured (Responses API HTTP/SSE mode)");
|
|
36
39
|
}
|
|
37
|
-
ensureCodexWorkspace(config.workspaceDir,
|
|
40
|
+
ensureCodexWorkspace(config.workspaceDir, agentLog, {
|
|
38
41
|
userId: agentUserId,
|
|
39
42
|
displayName: me.display_name,
|
|
40
43
|
description: me.agent_profile?.description ?? undefined,
|
|
@@ -42,21 +45,21 @@ async function main() {
|
|
|
42
45
|
const runtimeKey = config.runtimeKey || buildCodexRuntimeKey(agentUserId);
|
|
43
46
|
const mainContextFilePath = contextFilePathForSession(config.stateDir, runtimeKey);
|
|
44
47
|
const sessionStateFilePath = sessionStateFilePathForRuntime(config.stateDir, runtimeKey);
|
|
45
|
-
const sessionManager = new CodexSessionManager(runtimeKey, sessionStateFilePath,
|
|
48
|
+
const sessionManager = new CodexSessionManager(runtimeKey, sessionStateFilePath, agentLog);
|
|
46
49
|
const resolvedWsUrl = resolveWsUrl(config.apiUrl, config.wsUrl, config.swimlaneName);
|
|
47
50
|
const ws = new ParallWs({
|
|
48
51
|
getTicket: () => client.getWsTicket(),
|
|
49
52
|
wsUrl: resolvedWsUrl,
|
|
50
53
|
});
|
|
51
|
-
const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "codex", log });
|
|
54
|
+
const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "codex", log: agentLog });
|
|
52
55
|
const platformDefaults = await configMgr.fetch();
|
|
53
56
|
let platformManaged = isPlatformManagedProfile(me.agent_profile);
|
|
54
57
|
const resolvedModel = platformManaged ? (platformDefaults.model ?? config.model) : config.model;
|
|
55
58
|
const resolvedEffort = platformManaged ? (platformDefaults.thinkingEffort ?? config.reasoningEffort) : config.reasoningEffort;
|
|
56
59
|
if (platformDefaults.model)
|
|
57
|
-
|
|
60
|
+
agentLog.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
|
|
58
61
|
if (platformDefaults.thinkingEffort)
|
|
59
|
-
|
|
62
|
+
agentLog.info(`platform config: reasoning_effort=${platformDefaults.thinkingEffort} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
|
|
60
63
|
const adapter = new CodexAppServerAdapter({
|
|
61
64
|
codexBin: config.codexBin,
|
|
62
65
|
codexHome: config.codexHome,
|
|
@@ -66,7 +69,7 @@ async function main() {
|
|
|
66
69
|
sandbox: config.sandbox,
|
|
67
70
|
approvalPolicy: config.approvalPolicy,
|
|
68
71
|
sessionManager,
|
|
69
|
-
log,
|
|
72
|
+
log: agentLog,
|
|
70
73
|
contextFilePath: mainContextFilePath,
|
|
71
74
|
useParallProvider,
|
|
72
75
|
});
|
|
@@ -91,7 +94,7 @@ async function main() {
|
|
|
91
94
|
driver: "app-server",
|
|
92
95
|
},
|
|
93
96
|
dispatchAdapter: adapter,
|
|
94
|
-
log,
|
|
97
|
+
log: agentLog,
|
|
95
98
|
shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
|
|
96
99
|
contextFilePathForSession: (sessionKey) => contextFilePathForSession(config.stateDir, sessionKey),
|
|
97
100
|
stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
|
|
@@ -113,13 +116,14 @@ async function main() {
|
|
|
113
116
|
reasoningEffort: platformManaged ? (updated.thinkingEffort ?? config.reasoningEffort) : (config.reasoningEffort ?? null),
|
|
114
117
|
});
|
|
115
118
|
},
|
|
119
|
+
onNewSession: () => { sessionManager.clearMainThread(); },
|
|
116
120
|
});
|
|
117
121
|
const abortController = new AbortController();
|
|
118
122
|
const abort = () => abortController.abort();
|
|
119
123
|
process.on("SIGINT", abort);
|
|
120
124
|
process.on("SIGTERM", abort);
|
|
121
125
|
try {
|
|
122
|
-
|
|
126
|
+
agentLog.info(`starting self-hosted Codex runtime (app-server driver) for ${me.display_name} (${agentUserId})`);
|
|
123
127
|
await gateway.run(abortController.signal);
|
|
124
128
|
}
|
|
125
129
|
finally {
|
|
@@ -129,6 +133,6 @@ async function main() {
|
|
|
129
133
|
}
|
|
130
134
|
}
|
|
131
135
|
main().catch((err) => {
|
|
132
|
-
|
|
136
|
+
activeLog.error(`fatal: ${String(err)}`);
|
|
133
137
|
process.exitCode = 1;
|
|
134
138
|
});
|
package/dist/workspace.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,GAAG,CAAC,EAAE;IAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GACpC,IAAI,CAyEN;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAK/E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,GAAG,CAAC,EAAE;IAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GACpC,IAAI,
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,GAAG,CAAC,EAAE;IAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GACpC,IAAI,CAyEN;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAK/E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,GAAG,CAAC,EAAE;IAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GACpC,IAAI,CA6CN;AASD,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,MAAM,EACpB,GAAG,CAAC,EAAE;IAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,EACrC,aAAa,CAAC,EAAE,aAAa,GAC5B,IAAI,CAqBN"}
|
package/dist/workspace.js
CHANGED
|
@@ -145,14 +145,12 @@ export function ensureParallProvider(codexHome, apiUrl, log) {
|
|
|
145
145
|
].join("\n");
|
|
146
146
|
const headerIdx = content.indexOf(sectionHeader);
|
|
147
147
|
if (headerIdx !== -1) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
? findSectionEnd(content, authIdx + authHeader.length + 1)
|
|
155
|
-
: sectionEnd;
|
|
148
|
+
let blockEnd = findSectionEnd(content, headerIdx + sectionHeader.length + 1);
|
|
149
|
+
let authIdx = content.indexOf(authHeader, blockEnd);
|
|
150
|
+
while (authIdx !== -1 && content.substring(blockEnd, authIdx).trim() === "") {
|
|
151
|
+
blockEnd = findSectionEnd(content, authIdx + authHeader.length + 1);
|
|
152
|
+
authIdx = content.indexOf(authHeader, blockEnd);
|
|
153
|
+
}
|
|
156
154
|
content = content.substring(0, headerIdx) + providerBlock + content.substring(blockEnd);
|
|
157
155
|
}
|
|
158
156
|
else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/codex-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.0",
|
|
4
4
|
"description": "Codex CLI bridge runtime for self-hosted Parall agents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"smol-toml": "^1.6.1",
|
|
29
|
-
"@parall/agent-core": "1.
|
|
30
|
-
"@parall/cli": "1.
|
|
31
|
-
"@parall/sdk": "1.
|
|
29
|
+
"@parall/agent-core": "1.30.0",
|
|
30
|
+
"@parall/cli": "1.30.0",
|
|
31
|
+
"@parall/sdk": "1.30.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/node": "^22.0.0",
|
package/src/dispatch.ts
CHANGED
|
@@ -94,7 +94,7 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
94
94
|
const existing = this.activeTurns.get(threadId);
|
|
95
95
|
if (existing) {
|
|
96
96
|
(log ?? this.opts.log)?.warn?.(
|
|
97
|
-
`
|
|
97
|
+
`thread ${threadId} already had an active turn; failing the previous dispatch`,
|
|
98
98
|
);
|
|
99
99
|
existing.push({ kind: "error", message: `thread ${threadId} replaced by concurrent turn` });
|
|
100
100
|
existing.close();
|
|
@@ -125,7 +125,7 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
125
125
|
this.pendingInjections.set(sessionKey, (this.pendingInjections.get(sessionKey) ?? 0) + 1);
|
|
126
126
|
return true;
|
|
127
127
|
} catch (err) {
|
|
128
|
-
this.opts.log?.warn?.(`
|
|
128
|
+
this.opts.log?.warn?.(`turn/steer failed: ${errToString(err)}`);
|
|
129
129
|
return false;
|
|
130
130
|
}
|
|
131
131
|
}
|
|
@@ -141,7 +141,7 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
141
141
|
const threadId = this.opts.sessionManager.getThreadId(sessionKey);
|
|
142
142
|
if (threadId && this.client && !this.client.isDisposed()) {
|
|
143
143
|
(this.opts.log ?? context.log)?.info?.(
|
|
144
|
-
|
|
144
|
+
`${pending} steer injection(s) already sent; skipping turn/start`,
|
|
145
145
|
);
|
|
146
146
|
yield {
|
|
147
147
|
type: "runtime_session",
|
|
@@ -151,7 +151,7 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
151
151
|
return;
|
|
152
152
|
}
|
|
153
153
|
(this.opts.log ?? context.log)?.warn?.(
|
|
154
|
-
`
|
|
154
|
+
`pending steer invalidated (subprocess died); falling through to normal dispatch`,
|
|
155
155
|
);
|
|
156
156
|
}
|
|
157
157
|
|
|
@@ -195,7 +195,7 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
195
195
|
this.opts.sessionManager.recordThreadId(sessionKey, threadId);
|
|
196
196
|
this.resumedThreadIds.add(threadId);
|
|
197
197
|
} catch (err) {
|
|
198
|
-
log?.warn?.(`
|
|
198
|
+
log?.warn?.(`thread/resume failed (${errToString(err)}); attempting one-shot fresh-thread start`);
|
|
199
199
|
let freshThreadId: string;
|
|
200
200
|
try {
|
|
201
201
|
freshThreadId = await this.openThread(client, { resumeId: undefined });
|
|
@@ -231,7 +231,7 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
231
231
|
preparedImages = prepared.attachments.images;
|
|
232
232
|
releasePreparedAttachments = pinLocalAttachmentPaths(preparedImages);
|
|
233
233
|
} catch (err) {
|
|
234
|
-
log?.warn?.(`
|
|
234
|
+
log?.warn?.(`failed to prepare local attachments: ${errToString(err)}`);
|
|
235
235
|
}
|
|
236
236
|
// `localImage` is a first-class variant of `UserInput` in the Codex
|
|
237
237
|
// app-server protocol (codex-rs/app-server-protocol/src/protocol/v2.rs
|
|
@@ -269,7 +269,7 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
269
269
|
yield { type: "error", message: `Codex turn/start failed: ${message}` };
|
|
270
270
|
return;
|
|
271
271
|
}
|
|
272
|
-
log?.warn?.(`
|
|
272
|
+
log?.warn?.(`turn/start on thread ${threadId} failed (${message}); attempting one-shot fresh-thread retry`);
|
|
273
273
|
this.activeTurns.delete(threadId);
|
|
274
274
|
let freshThreadId: string;
|
|
275
275
|
try {
|
|
@@ -382,7 +382,7 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
382
382
|
const result = await client.sendRequest("thread/fork", forkParams);
|
|
383
383
|
const forkedThreadId = extractThreadId(result);
|
|
384
384
|
if (!forkedThreadId) {
|
|
385
|
-
this.opts.log?.warn?.("
|
|
385
|
+
this.opts.log?.warn?.("thread/fork returned no thread id");
|
|
386
386
|
return null;
|
|
387
387
|
}
|
|
388
388
|
this.opts.sessionManager.recordThreadId(handle.sessionKey, forkedThreadId);
|
|
@@ -397,7 +397,7 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
397
397
|
}
|
|
398
398
|
|
|
399
399
|
private logForkFailure(err: unknown): null {
|
|
400
|
-
this.opts.log?.warn?.(`
|
|
400
|
+
this.opts.log?.warn?.(`thread/fork failed: ${errToString(err)}`);
|
|
401
401
|
return null;
|
|
402
402
|
}
|
|
403
403
|
|
|
@@ -467,7 +467,7 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
467
467
|
env.PRLL_CONTEXT_FILE = this.opts.contextFilePath;
|
|
468
468
|
}
|
|
469
469
|
const args = ["app-server", "--listen", "stdio://"];
|
|
470
|
-
(log ?? this.opts.log)?.info?.(`
|
|
470
|
+
(log ?? this.opts.log)?.info?.(`spawning ${this.opts.codexBin} ${args.join(" ")}`);
|
|
471
471
|
const proc = spawn(
|
|
472
472
|
IS_WIN32 ? quoteWin32Arg(this.opts.codexBin) : this.opts.codexBin,
|
|
473
473
|
IS_WIN32 ? args.map(quoteWin32Arg) : args, {
|
|
@@ -479,7 +479,7 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
479
479
|
|
|
480
480
|
proc.stderr.setEncoding("utf8");
|
|
481
481
|
proc.stderr.on("data", (chunk: string) => {
|
|
482
|
-
(log ?? this.opts.log)?.warn?.(`
|
|
482
|
+
(log ?? this.opts.log)?.warn?.(`[stderr] ${chunk.trim()}`);
|
|
483
483
|
});
|
|
484
484
|
|
|
485
485
|
// Don't publish proc/client until the initialize handshake succeeds. If
|
|
@@ -535,9 +535,9 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
535
535
|
: `exited (code=${code ?? "null"}${signal ? `, signal=${signal}` : ""})`;
|
|
536
536
|
const logger = log ?? this.opts.log;
|
|
537
537
|
if (this.stopping) {
|
|
538
|
-
logger?.info?.(`
|
|
538
|
+
logger?.info?.(`app-server subprocess ${reason} during graceful stop`);
|
|
539
539
|
} else {
|
|
540
|
-
logger?.warn?.(`
|
|
540
|
+
logger?.warn?.(`app-server subprocess ${reason}; resetting adapter`);
|
|
541
541
|
}
|
|
542
542
|
this.client?.dispose(err ?? new Error(`app-server ${reason}`));
|
|
543
543
|
for (const activeSink of this.activeTurns.values()) {
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import * as os from "node:os";
|
|
4
|
-
import { ParallAgentGateway, createPlatformConfigManager, createLogger, isPlatformManagedProfile, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
4
|
+
import { ParallAgentGateway, createPlatformConfigManager, createLogger, childLogger, isPlatformManagedProfile, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
5
5
|
import { ApiError, ParallClient, ParallWs } from "@parall/sdk";
|
|
6
6
|
import {
|
|
7
7
|
buildCodexRuntimeKey,
|
|
@@ -16,6 +16,7 @@ import { CodexSessionManager } from "./session-manager.js";
|
|
|
16
16
|
import { ensureCodexWorkspace, ensureParallProvider, ensureWorkspaceTrusted, isParallProxyMode } from "./workspace.js";
|
|
17
17
|
|
|
18
18
|
const log = createLogger("codex-agent");
|
|
19
|
+
let activeLog = log;
|
|
19
20
|
|
|
20
21
|
async function getAgentMeWithLegacyFallback(client: ParallClient, orgId: string) {
|
|
21
22
|
try {
|
|
@@ -40,13 +41,15 @@ async function main() {
|
|
|
40
41
|
|
|
41
42
|
const me = await getAgentMeWithLegacyFallback(client, config.orgId);
|
|
42
43
|
const agentUserId = me.id;
|
|
43
|
-
|
|
44
|
+
const agentLog = childLogger(log, agentUserId);
|
|
45
|
+
activeLog = agentLog;
|
|
46
|
+
ensureWorkspaceTrusted(config.codexHome, config.workspaceDir, agentLog);
|
|
44
47
|
const useParallProvider = isParallProxyMode();
|
|
45
48
|
if (useParallProvider) {
|
|
46
|
-
ensureParallProvider(config.codexHome, config.apiUrl,
|
|
47
|
-
|
|
49
|
+
ensureParallProvider(config.codexHome, config.apiUrl, agentLog);
|
|
50
|
+
agentLog.info("parall custom provider configured (Responses API HTTP/SSE mode)");
|
|
48
51
|
}
|
|
49
|
-
ensureCodexWorkspace(config.workspaceDir,
|
|
52
|
+
ensureCodexWorkspace(config.workspaceDir, agentLog, {
|
|
50
53
|
userId: agentUserId,
|
|
51
54
|
displayName: me.display_name,
|
|
52
55
|
description: me.agent_profile?.description ?? undefined,
|
|
@@ -57,7 +60,7 @@ async function main() {
|
|
|
57
60
|
const sessionManager = new CodexSessionManager(
|
|
58
61
|
runtimeKey,
|
|
59
62
|
sessionStateFilePath,
|
|
60
|
-
|
|
63
|
+
agentLog,
|
|
61
64
|
);
|
|
62
65
|
|
|
63
66
|
const resolvedWsUrl = resolveWsUrl(config.apiUrl, config.wsUrl, config.swimlaneName);
|
|
@@ -66,13 +69,13 @@ async function main() {
|
|
|
66
69
|
wsUrl: resolvedWsUrl,
|
|
67
70
|
});
|
|
68
71
|
|
|
69
|
-
const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "codex", log });
|
|
72
|
+
const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "codex", log: agentLog });
|
|
70
73
|
const platformDefaults = await configMgr.fetch();
|
|
71
74
|
let platformManaged = isPlatformManagedProfile(me.agent_profile);
|
|
72
75
|
const resolvedModel = platformManaged ? (platformDefaults.model ?? config.model) : config.model;
|
|
73
76
|
const resolvedEffort = platformManaged ? (platformDefaults.thinkingEffort ?? config.reasoningEffort) : config.reasoningEffort;
|
|
74
|
-
if (platformDefaults.model)
|
|
75
|
-
if (platformDefaults.thinkingEffort)
|
|
77
|
+
if (platformDefaults.model) agentLog.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
|
|
78
|
+
if (platformDefaults.thinkingEffort) agentLog.info(`platform config: reasoning_effort=${platformDefaults.thinkingEffort} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
|
|
76
79
|
|
|
77
80
|
const adapter = new CodexAppServerAdapter({
|
|
78
81
|
codexBin: config.codexBin,
|
|
@@ -83,7 +86,7 @@ async function main() {
|
|
|
83
86
|
sandbox: config.sandbox,
|
|
84
87
|
approvalPolicy: config.approvalPolicy,
|
|
85
88
|
sessionManager,
|
|
86
|
-
log,
|
|
89
|
+
log: agentLog,
|
|
87
90
|
contextFilePath: mainContextFilePath,
|
|
88
91
|
useParallProvider,
|
|
89
92
|
});
|
|
@@ -109,7 +112,7 @@ async function main() {
|
|
|
109
112
|
driver: "app-server",
|
|
110
113
|
},
|
|
111
114
|
dispatchAdapter: adapter,
|
|
112
|
-
log,
|
|
115
|
+
log: agentLog,
|
|
113
116
|
shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
|
|
114
117
|
contextFilePathForSession: (sessionKey) => contextFilePathForSession(config.stateDir, sessionKey),
|
|
115
118
|
stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
|
|
@@ -131,6 +134,7 @@ async function main() {
|
|
|
131
134
|
reasoningEffort: platformManaged ? (updated.thinkingEffort ?? config.reasoningEffort) : (config.reasoningEffort ?? null),
|
|
132
135
|
});
|
|
133
136
|
},
|
|
137
|
+
onNewSession: () => { sessionManager.clearMainThread(); },
|
|
134
138
|
});
|
|
135
139
|
|
|
136
140
|
const abortController = new AbortController();
|
|
@@ -139,7 +143,7 @@ async function main() {
|
|
|
139
143
|
process.on("SIGTERM", abort);
|
|
140
144
|
|
|
141
145
|
try {
|
|
142
|
-
|
|
146
|
+
agentLog.info(`starting self-hosted Codex runtime (app-server driver) for ${me.display_name} (${agentUserId})`);
|
|
143
147
|
await gateway.run(abortController.signal);
|
|
144
148
|
} finally {
|
|
145
149
|
await adapter.stop();
|
|
@@ -149,6 +153,6 @@ async function main() {
|
|
|
149
153
|
}
|
|
150
154
|
|
|
151
155
|
main().catch((err) => {
|
|
152
|
-
|
|
156
|
+
activeLog.error(`fatal: ${String(err)}`);
|
|
153
157
|
process.exitCode = 1;
|
|
154
158
|
});
|
package/src/workspace.ts
CHANGED
|
@@ -159,14 +159,12 @@ export function ensureParallProvider(
|
|
|
159
159
|
|
|
160
160
|
const headerIdx = content.indexOf(sectionHeader);
|
|
161
161
|
if (headerIdx !== -1) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
? findSectionEnd(content, authIdx + authHeader.length + 1)
|
|
169
|
-
: sectionEnd;
|
|
162
|
+
let blockEnd = findSectionEnd(content, headerIdx + sectionHeader.length + 1);
|
|
163
|
+
let authIdx = content.indexOf(authHeader, blockEnd);
|
|
164
|
+
while (authIdx !== -1 && content.substring(blockEnd, authIdx).trim() === "") {
|
|
165
|
+
blockEnd = findSectionEnd(content, authIdx + authHeader.length + 1);
|
|
166
|
+
authIdx = content.indexOf(authHeader, blockEnd);
|
|
167
|
+
}
|
|
170
168
|
content = content.substring(0, headerIdx) + providerBlock + content.substring(blockEnd);
|
|
171
169
|
} else {
|
|
172
170
|
content = content.trimEnd() + "\n\n" + providerBlock + "\n";
|