@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 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?.(`codex-agent: thread ${threadId} already had an active turn; failing the previous dispatch`);
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?.(`codex-agent: turn/steer failed: ${errToString(err)}`);
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?.(`codex-agent[${context.accountId}]: ${pending} steer injection(s) already sent; skipping turn/start`);
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?.(`codex-agent[${context.accountId}]: pending steer invalidated (subprocess died); falling through to normal dispatch`);
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?.(`codex-agent: thread/resume failed (${errToString(err)}); attempting one-shot fresh-thread start`);
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?.(`codex-agent[${context.accountId}]: failed to prepare local attachments: ${errToString(err)}`);
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?.(`codex-agent: turn/start on thread ${threadId} failed (${message}); attempting one-shot fresh-thread retry`);
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?.("codex-agent: thread/fork returned no thread id");
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?.(`codex-agent: thread/fork failed: ${errToString(err)}`);
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?.(`codex-agent: spawning ${this.opts.codexBin} ${args.join(" ")}`);
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?.(`codex-agent[stderr]: ${chunk.trim()}`);
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?.(`codex-agent: app-server subprocess ${reason} during graceful stop`);
488
+ logger?.info?.(`app-server subprocess ${reason} during graceful stop`);
489
489
  }
490
490
  else {
491
- logger?.warn?.(`codex-agent: app-server subprocess ${reason}; resetting adapter`);
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
- ensureWorkspaceTrusted(config.codexHome, config.workspaceDir, log);
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, log);
35
- log.info("parall custom provider configured (Responses API HTTP/SSE mode)");
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, log, {
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, log);
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
- log.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
60
+ agentLog.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
58
61
  if (platformDefaults.thinkingEffort)
59
- log.info(`platform config: reasoning_effort=${platformDefaults.thinkingEffort} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
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
- log.info(`starting self-hosted Codex runtime (app-server driver) for ${me.display_name} (${agentUserId})`);
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
- log.error(`fatal: ${String(err)}`);
136
+ activeLog.error(`fatal: ${String(err)}`);
133
137
  process.exitCode = 1;
134
138
  });
@@ -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,CA+CN;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"}
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
- const sectionEnd = findSectionEnd(content, headerIdx + sectionHeader.length + 1);
149
- // Only consume a contiguous [model_providers.parall.auth] subtable —
150
- // one that sits within the provider's own section, not after unrelated
151
- // tables that happen to appear between provider and auth.
152
- const authIdx = content.indexOf(authHeader, headerIdx);
153
- const blockEnd = authIdx !== -1 && authIdx < sectionEnd
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.29.3",
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.29.3",
30
- "@parall/cli": "1.29.3",
31
- "@parall/sdk": "1.29.3"
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
- `codex-agent: thread ${threadId} already had an active turn; failing the previous dispatch`,
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?.(`codex-agent: turn/steer failed: ${errToString(err)}`);
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
- `codex-agent[${context.accountId}]: ${pending} steer injection(s) already sent; skipping turn/start`,
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
- `codex-agent[${context.accountId}]: pending steer invalidated (subprocess died); falling through to normal dispatch`,
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?.(`codex-agent: thread/resume failed (${errToString(err)}); attempting one-shot fresh-thread start`);
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?.(`codex-agent[${context.accountId}]: failed to prepare local attachments: ${errToString(err)}`);
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?.(`codex-agent: turn/start on thread ${threadId} failed (${message}); attempting one-shot fresh-thread retry`);
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?.("codex-agent: thread/fork returned no thread id");
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?.(`codex-agent: thread/fork failed: ${errToString(err)}`);
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?.(`codex-agent: spawning ${this.opts.codexBin} ${args.join(" ")}`);
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?.(`codex-agent[stderr]: ${chunk.trim()}`);
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?.(`codex-agent: app-server subprocess ${reason} during graceful stop`);
538
+ logger?.info?.(`app-server subprocess ${reason} during graceful stop`);
539
539
  } else {
540
- logger?.warn?.(`codex-agent: app-server subprocess ${reason}; resetting adapter`);
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
- ensureWorkspaceTrusted(config.codexHome, config.workspaceDir, log);
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, log);
47
- log.info("parall custom provider configured (Responses API HTTP/SSE mode)");
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, log, {
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
- log,
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) log.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
75
- if (platformDefaults.thinkingEffort) log.info(`platform config: reasoning_effort=${platformDefaults.thinkingEffort} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
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
- log.info(`starting self-hosted Codex runtime (app-server driver) for ${me.display_name} (${agentUserId})`);
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
- log.error(`fatal: ${String(err)}`);
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
- const sectionEnd = findSectionEnd(content, headerIdx + sectionHeader.length + 1);
163
- // Only consume a contiguous [model_providers.parall.auth] subtable —
164
- // one that sits within the provider's own section, not after unrelated
165
- // tables that happen to appear between provider and auth.
166
- const authIdx = content.indexOf(authHeader, headerIdx);
167
- const blockEnd = authIdx !== -1 && authIdx < sectionEnd
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";