@parall/codex-agent 1.31.0 → 1.32.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.
@@ -27,37 +27,37 @@ export class EventMapper {
27
27
  const p = (params ?? {});
28
28
  switch (method) {
29
29
  // Streaming deltas are intentionally dropped — see the class comment.
30
- case "item/agentMessage/delta":
31
- case "item/reasoning/textDelta":
32
- case "item/reasoning/summaryTextDelta":
30
+ case 'item/agentMessage/delta':
31
+ case 'item/reasoning/textDelta':
32
+ case 'item/reasoning/summaryTextDelta':
33
33
  break;
34
- case "item/started": {
34
+ case 'item/started': {
35
35
  const item = p.item;
36
- if (!item || typeof item.type !== "string")
36
+ if (!item || typeof item.type !== 'string')
37
37
  break;
38
- events.push(...this.mapItem(item, "started"));
38
+ events.push(...this.mapItem(item, 'started'));
39
39
  break;
40
40
  }
41
- case "item/completed": {
41
+ case 'item/completed': {
42
42
  const item = p.item;
43
- if (!item || typeof item.type !== "string")
43
+ if (!item || typeof item.type !== 'string')
44
44
  break;
45
- events.push(...this.mapItem(item, "completed"));
45
+ events.push(...this.mapItem(item, 'completed'));
46
46
  break;
47
47
  }
48
- case "turn/completed": {
48
+ case 'turn/completed': {
49
49
  const turn = p.turn;
50
50
  const status = turn ? asString(turn.status) : undefined;
51
51
  const error = turn?.error;
52
- if (status === "failed") {
53
- const message = asString(error?.message) ?? "Codex turn failed";
54
- events.push({ type: "error", message });
52
+ if (status === 'failed') {
53
+ const message = asString(error?.message) ?? 'Codex turn failed';
54
+ events.push({ type: 'error', message });
55
55
  }
56
56
  break;
57
57
  }
58
- case "error": {
59
- const message = asString(p.message) ?? "Codex error";
60
- events.push({ type: "error", message });
58
+ case 'error': {
59
+ const message = asString(p.message) ?? 'Codex error';
60
+ events.push({ type: 'error', message });
61
61
  break;
62
62
  }
63
63
  }
@@ -67,8 +67,8 @@ export class EventMapper {
67
67
  const type = asString(item.type);
68
68
  const id = asString(item.id);
69
69
  const now = Date.now();
70
- if (type === "agentMessage") {
71
- if (phase !== "completed")
70
+ if (type === 'agentMessage') {
71
+ if (phase !== 'completed')
72
72
  return [];
73
73
  const text = asString(item.text);
74
74
  if (!text)
@@ -76,62 +76,68 @@ export class EventMapper {
76
76
  // Layer 0 symmetric output contract: plain text is never auto-projected;
77
77
  // agents must explicitly call `@parall/cli messages send` / `dm` to reach
78
78
  // the chat. The text is still recorded as a session step for audit.
79
- return [{ type: "text", text, project: false }];
79
+ return [{ type: 'text', text, project: false }];
80
80
  }
81
- if (type === "reasoning") {
82
- if (phase !== "completed")
81
+ if (type === 'reasoning') {
82
+ if (phase !== 'completed')
83
83
  return [];
84
84
  const text = joinReasoningText(item);
85
85
  if (!text)
86
86
  return [];
87
- return [{ type: "thinking", text }];
87
+ return [{ type: 'thinking', text }];
88
88
  }
89
- if (type === "commandExecution") {
89
+ if (type === 'commandExecution') {
90
90
  const command = formatCommand(item.command);
91
91
  const callId = id ?? `shell-${now}`;
92
- if (phase === "started") {
92
+ if (phase === 'started') {
93
93
  this.toolCallStart.set(callId, now);
94
- return [{
95
- type: "tool_call",
94
+ return [
95
+ {
96
+ type: 'tool_call',
96
97
  callId,
97
- toolName: "shell",
98
+ toolName: 'shell',
98
99
  input: { command, cwd: asString(item.cwd) },
99
100
  startedAt: new Date(now).toISOString(),
100
- }];
101
+ },
102
+ ];
101
103
  }
102
104
  // Codex app-server uses camelCase: aggregatedOutput / exitCode / durationMs
103
105
  // (see openai/codex codex-rs/app-server-protocol). Earlier snake_case
104
106
  // probing dropped exit codes and split outputs that arrive as one string.
105
107
  const durationMs = this.resolveDuration(callId, now, item.durationMs);
106
108
  const output = asString(item.aggregatedOutput) ?? joinStreams(item.stdout, item.stderr);
107
- const exitCode = typeof item.exitCode === "number" ? item.exitCode : undefined;
109
+ const exitCode = typeof item.exitCode === 'number' ? item.exitCode : undefined;
108
110
  const status = asString(item.status);
109
- const failed = (exitCode !== undefined && exitCode !== 0)
110
- || status === "failed"
111
- || status === "declined";
112
- return [{
113
- type: "tool_result",
111
+ const failed = (exitCode !== undefined && exitCode !== 0) || status === 'failed' || status === 'declined';
112
+ return [
113
+ {
114
+ type: 'tool_result',
114
115
  callId,
115
- toolName: "shell",
116
+ toolName: 'shell',
116
117
  output,
117
- ...(failed ? { error: output || `shell ${status ?? "exited"} (code=${exitCode ?? "?"})` } : {}),
118
+ ...(failed
119
+ ? { error: output || `shell ${status ?? 'exited'} (code=${exitCode ?? '?'})` }
120
+ : {}),
118
121
  ...(durationMs !== undefined ? { durationMs } : {}),
119
- }];
122
+ },
123
+ ];
120
124
  }
121
- if (type === "mcpToolCall") {
125
+ if (type === 'mcpToolCall') {
122
126
  const server = asString(item.server);
123
127
  const tool = asString(item.tool);
124
- const toolName = server && tool ? `${server}:${tool}` : tool ?? server ?? "mcp";
128
+ const toolName = server && tool ? `${server}:${tool}` : (tool ?? server ?? 'mcp');
125
129
  const callId = id ?? `mcp-${now}`;
126
- if (phase === "started") {
130
+ if (phase === 'started') {
127
131
  this.toolCallStart.set(callId, now);
128
- return [{
129
- type: "tool_call",
132
+ return [
133
+ {
134
+ type: 'tool_call',
130
135
  callId,
131
136
  toolName,
132
137
  input: item.arguments ?? {},
133
138
  startedAt: new Date(now).toISOString(),
134
- }];
139
+ },
140
+ ];
135
141
  }
136
142
  const durationMs = this.resolveDuration(callId, now, item.durationMs);
137
143
  const result = item.result;
@@ -140,62 +146,70 @@ export class EventMapper {
140
146
  // success path renders an empty-quoted output and the failure path's
141
147
  // `output || "MCP tool call failed"` fallback gets eaten because the
142
148
  // literal `'""'` is truthy.
143
- const output = result == null
144
- ? ""
145
- : typeof result === "string" ? result : JSON.stringify(result);
149
+ const output = result == null ? '' : typeof result === 'string' ? result : JSON.stringify(result);
146
150
  const success = item.isSuccess !== false;
147
- return [{
148
- type: "tool_result",
151
+ return [
152
+ {
153
+ type: 'tool_result',
149
154
  callId,
150
155
  toolName,
151
156
  output,
152
- ...(success ? {} : { error: output || "MCP tool call failed" }),
157
+ ...(success ? {} : { error: output || 'MCP tool call failed' }),
153
158
  ...(durationMs !== undefined ? { durationMs } : {}),
154
- }];
159
+ },
160
+ ];
155
161
  }
156
- if (type === "fileChange") {
162
+ if (type === 'fileChange') {
157
163
  const callId = id ?? `patch-${now}`;
158
- if (phase === "started") {
164
+ if (phase === 'started') {
159
165
  this.toolCallStart.set(callId, now);
160
- return [{
161
- type: "tool_call",
166
+ return [
167
+ {
168
+ type: 'tool_call',
162
169
  callId,
163
- toolName: "patch",
170
+ toolName: 'patch',
164
171
  input: { changes: item.changes ?? [] },
165
172
  startedAt: new Date(now).toISOString(),
166
- }];
173
+ },
174
+ ];
167
175
  }
168
176
  const durationMs = this.resolveDuration(callId, now, undefined);
169
- return [{
170
- type: "tool_result",
177
+ return [
178
+ {
179
+ type: 'tool_result',
171
180
  callId,
172
- toolName: "patch",
173
- output: "",
181
+ toolName: 'patch',
182
+ output: '',
174
183
  ...(durationMs !== undefined ? { durationMs } : {}),
175
- }];
184
+ },
185
+ ];
176
186
  }
177
- if (type === "webSearch") {
187
+ if (type === 'webSearch') {
178
188
  const callId = id ?? `search-${now}`;
179
- if (phase === "started") {
189
+ if (phase === 'started') {
180
190
  this.toolCallStart.set(callId, now);
181
- return [{
182
- type: "tool_call",
191
+ return [
192
+ {
193
+ type: 'tool_call',
183
194
  callId,
184
- toolName: "web_search",
185
- input: { query: asString(item.query) ?? "" },
195
+ toolName: 'web_search',
196
+ input: { query: asString(item.query) ?? '' },
186
197
  startedAt: new Date(now).toISOString(),
187
- }];
198
+ },
199
+ ];
188
200
  }
189
201
  const durationMs = this.resolveDuration(callId, now, item.durationMs);
190
202
  const results = Array.isArray(item.results) ? item.results : [];
191
- const output = results.length > 0 ? JSON.stringify(results) : "";
192
- return [{
193
- type: "tool_result",
203
+ const output = results.length > 0 ? JSON.stringify(results) : '';
204
+ return [
205
+ {
206
+ type: 'tool_result',
194
207
  callId,
195
- toolName: "web_search",
208
+ toolName: 'web_search',
196
209
  output,
197
210
  ...(durationMs !== undefined ? { durationMs } : {}),
198
- }];
211
+ },
212
+ ];
199
213
  }
200
214
  return [];
201
215
  }
@@ -208,7 +222,7 @@ export class EventMapper {
208
222
  resolveDuration(callId, now, serverDurationMs) {
209
223
  const start = this.toolCallStart.get(callId);
210
224
  this.toolCallStart.delete(callId);
211
- if (typeof serverDurationMs === "number" && Number.isFinite(serverDurationMs)) {
225
+ if (typeof serverDurationMs === 'number' && Number.isFinite(serverDurationMs)) {
212
226
  return Math.max(0, serverDurationMs);
213
227
  }
214
228
  if (start === undefined)
@@ -217,30 +231,36 @@ export class EventMapper {
217
231
  }
218
232
  }
219
233
  function asString(value) {
220
- if (typeof value !== "string")
234
+ if (typeof value !== 'string')
221
235
  return undefined;
222
236
  const trimmed = value.trim();
223
237
  return trimmed.length > 0 ? trimmed : undefined;
224
238
  }
225
239
  function joinReasoningText(item) {
226
- const summary = Array.isArray(item.summary) ? item.summary.filter((x) => typeof x === "string") : [];
227
- const content = Array.isArray(item.content) ? item.content.filter((x) => typeof x === "string") : [];
240
+ const summary = Array.isArray(item.summary)
241
+ ? item.summary.filter((x) => typeof x === 'string')
242
+ : [];
243
+ const content = Array.isArray(item.content)
244
+ ? item.content.filter((x) => typeof x === 'string')
245
+ : [];
228
246
  const parts = [...summary, ...content, asString(item.text)].filter(Boolean);
229
- return parts.join("\n").trim();
247
+ return parts.join('\n').trim();
230
248
  }
231
249
  function joinStreams(stdout, stderr) {
232
250
  const out = asString(stdout);
233
251
  const err = asString(stderr);
234
252
  if (out && err)
235
253
  return `${out}\n${err}`;
236
- return out ?? err ?? "";
254
+ return out ?? err ?? '';
237
255
  }
238
256
  /** Codex sends `command` either as a plain string or as an argv array (e.g. ["sh", "-c", "ls"]). */
239
257
  function formatCommand(command) {
240
- if (typeof command === "string")
258
+ if (typeof command === 'string')
241
259
  return command;
242
260
  if (Array.isArray(command)) {
243
- return command.map((part) => (typeof part === "string" ? part : JSON.stringify(part))).join(" ");
261
+ return command
262
+ .map((part) => (typeof part === 'string' ? part : JSON.stringify(part)))
263
+ .join(' ');
244
264
  }
245
- return "";
265
+ return '';
246
266
  }
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import * as os from "node:os";
3
- import { ParallAgentGateway, createPlatformConfigManager, createLogger, childLogger, isPlatformManagedProfile, parseShutdownDeadlineMs, parseProviderConfig, clearAllProviderCreds, llmSource } from "@parall/agent-core";
4
- import { ApiError, ParallClient, ParallWs } from "@parall/sdk";
5
- import { buildCodexRuntimeKey, contextFilePathForSession, resolveCodexAgentConfig, resolveWsUrl, sessionStateFilePathForRuntime, stepIdFilePathForSession, } from "./config.js";
6
- import { CodexAppServerAdapter } from "./dispatch.js";
7
- import { CodexSessionManager } from "./session-manager.js";
8
- import { ensureCodexWorkspace, ensureParallProvider, ensureWorkspaceTrusted, isParallProxyMode } from "./workspace.js";
9
- const log = createLogger("codex-agent");
2
+ import * as os from 'node:os';
3
+ import { ParallAgentGateway, createPlatformConfigManager, createLogger, createOtelLogger, childLogger, isPlatformManagedProfile, isPlatformModelOverride, resolveRuntimeModel, parseShutdownDeadlineMs, parseForkDeadlineMs, parseDispatchDeadlineMs, parseProviderConfig, clearAllProviderCreds, llmSource, initAgentTelemetry, } from '@parall/agent-core';
4
+ import { ApiError, ParallClient, ParallWs } from '@parall/sdk';
5
+ import { buildCodexRuntimeKey, contextFilePathForSession, resolveCodexAgentConfig, resolveWsUrl, sessionStateFilePathForRuntime, stepIdFilePathForSession, } from './config.js';
6
+ import { CodexAppServerAdapter } from './dispatch.js';
7
+ import { CodexSessionManager } from './session-manager.js';
8
+ import { ensureCodexWorkspace, ensureParallProvider, ensureWorkspaceTrusted, isParallProxyMode, } from './workspace.js';
9
+ const log = createLogger('codex-agent');
10
10
  let activeLog = log;
11
11
  async function getAgentMeWithLegacyFallback(client, orgId) {
12
12
  try {
@@ -26,11 +26,11 @@ function resolveProviderEnv() {
26
26
  return;
27
27
  clearAllProviderCreds(process.env);
28
28
  const source = llmSource(pc);
29
- if (source === "parall") {
29
+ if (source === 'parall') {
30
30
  process.env.OPENAI_API_KEY = process.env.PRLL_API_KEY;
31
31
  process.env.OPENAI_BASE_URL = `${process.env.PRLL_API_URL}/api/llm/v1`;
32
32
  }
33
- else if (source === "custom") {
33
+ else if (source === 'custom') {
34
34
  if (pc.openai_api_key)
35
35
  process.env.OPENAI_API_KEY = pc.openai_api_key;
36
36
  if (pc.openai_base_url)
@@ -38,117 +38,153 @@ function resolveProviderEnv() {
38
38
  }
39
39
  }
40
40
  async function main() {
41
- resolveProviderEnv();
42
- const config = resolveCodexAgentConfig(process.env);
43
- const client = new ParallClient({
44
- baseUrl: config.apiUrl,
45
- token: config.apiKey,
46
- swimlaneName: config.swimlaneName,
47
- });
48
- const me = await getAgentMeWithLegacyFallback(client, config.orgId);
49
- const agentUserId = me.id;
50
- const agentLog = childLogger(log, agentUserId);
51
- activeLog = agentLog;
52
- ensureWorkspaceTrusted(config.codexHome, config.workspaceDir, agentLog);
53
- const useParallProvider = isParallProxyMode();
54
- if (useParallProvider) {
55
- ensureParallProvider(config.codexHome, config.apiUrl, agentLog);
56
- agentLog.info("parall custom provider configured (Responses API HTTP/SSE mode)");
57
- }
58
- ensureCodexWorkspace(config.workspaceDir, agentLog, {
59
- userId: agentUserId,
60
- displayName: me.display_name,
61
- description: me.agent_profile?.description ?? undefined,
62
- });
63
- const runtimeKey = config.runtimeKey || buildCodexRuntimeKey(agentUserId);
64
- const mainContextFilePath = contextFilePathForSession(config.stateDir, runtimeKey);
65
- const sessionStateFilePath = sessionStateFilePathForRuntime(config.stateDir, runtimeKey);
66
- const sessionManager = new CodexSessionManager(runtimeKey, sessionStateFilePath, agentLog);
67
- const resolvedWsUrl = resolveWsUrl(config.apiUrl, config.wsUrl, config.swimlaneName);
68
- const ws = new ParallWs({
69
- getTicket: () => client.getWsTicket(),
70
- wsUrl: resolvedWsUrl,
71
- });
72
- const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "codex", log: agentLog });
73
- const platformDefaults = await configMgr.fetch();
74
- let platformManaged = isPlatformManagedProfile(me.agent_profile);
75
- const resolvedModel = platformManaged ? (platformDefaults.model ?? config.model) : config.model;
76
- const resolvedEffort = platformManaged ? (platformDefaults.thinkingEffort ?? config.reasoningEffort) : config.reasoningEffort;
77
- if (platformDefaults.model)
78
- agentLog.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
79
- if (platformDefaults.thinkingEffort)
80
- agentLog.info(`platform config: reasoning_effort=${platformDefaults.thinkingEffort} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
81
- const adapter = new CodexAppServerAdapter({
82
- codexBin: config.codexBin,
83
- codexHome: config.codexHome,
84
- workspaceDir: config.workspaceDir,
85
- model: resolvedModel,
86
- reasoningEffort: resolvedEffort,
87
- sandbox: config.sandbox,
88
- approvalPolicy: config.approvalPolicy,
89
- sessionManager,
90
- log: agentLog,
91
- contextFilePath: mainContextFilePath,
92
- useParallProvider,
93
- });
94
- const gateway = new ParallAgentGateway({
95
- accountId: agentUserId,
96
- client,
97
- ws,
98
- connectionLabel: resolvedWsUrl,
99
- config: {
100
- parall_url: config.apiUrl,
101
- api_key: config.apiKey,
102
- org_id: config.orgId,
103
- },
104
- agentUserId,
105
- runtimeType: "codex",
106
- runtimeKey,
107
- runtimeRef: {
108
- hostname: os.hostname(),
109
- pid: process.pid,
110
- workspace_dir: config.workspaceDir,
111
- codex_home: config.codexHome,
112
- driver: "app-server",
113
- },
114
- dispatchAdapter: adapter,
115
- log: agentLog,
116
- shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
117
- contextFilePathForSession: (sessionKey) => contextFilePathForSession(config.stateDir, sessionKey),
118
- stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
119
- onConfigUpdate: async () => {
120
- const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
121
- platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
122
- const updated = await configMgr.fetch();
123
- adapter.updateConfig({
124
- model: platformManaged ? (updated.model ?? config.model) : (config.model ?? null),
125
- reasoningEffort: platformManaged ? (updated.thinkingEffort ?? config.reasoningEffort) : (config.reasoningEffort ?? null),
126
- });
127
- },
128
- onSessionReady: async () => {
129
- const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
130
- platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
131
- const updated = await configMgr.fetch();
132
- adapter.updateConfig({
133
- model: platformManaged ? (updated.model ?? config.model) : (config.model ?? null),
134
- reasoningEffort: platformManaged ? (updated.thinkingEffort ?? config.reasoningEffort) : (config.reasoningEffort ?? null),
135
- });
136
- },
137
- onNewSession: () => { sessionManager.clearMainThread(); },
138
- onSessionStale: () => { sessionManager.clearMainThread(); },
139
- });
140
- const abortController = new AbortController();
141
- const abort = () => abortController.abort();
142
- process.on("SIGINT", abort);
143
- process.on("SIGTERM", abort);
41
+ const telemetry = await initAgentTelemetry('parall-codex-agent', 'codex');
42
+ activeLog = createOtelLogger('agent', 'codex-agent');
144
43
  try {
145
- agentLog.info(`starting self-hosted Codex runtime (app-server driver) for ${me.display_name} (${agentUserId})`);
146
- await gateway.run(abortController.signal);
44
+ resolveProviderEnv();
45
+ const config = resolveCodexAgentConfig(process.env);
46
+ const client = new ParallClient({
47
+ baseUrl: config.apiUrl,
48
+ token: config.apiKey,
49
+ swimlaneName: config.swimlaneName,
50
+ });
51
+ const me = await getAgentMeWithLegacyFallback(client, config.orgId);
52
+ const agentUserId = me.id;
53
+ const agentLog = childLogger(activeLog, agentUserId);
54
+ activeLog = agentLog;
55
+ ensureWorkspaceTrusted(config.codexHome, config.workspaceDir, agentLog);
56
+ const useParallProvider = isParallProxyMode();
57
+ if (useParallProvider) {
58
+ ensureParallProvider(config.codexHome, config.apiUrl, agentLog);
59
+ agentLog.info('parall custom provider configured (Responses API HTTP/SSE mode)');
60
+ }
61
+ ensureCodexWorkspace(config.workspaceDir, agentLog, {
62
+ userId: agentUserId,
63
+ displayName: me.display_name,
64
+ description: me.agent_profile?.description ?? undefined,
65
+ });
66
+ const runtimeKey = config.runtimeKey || buildCodexRuntimeKey(agentUserId);
67
+ const mainContextFilePath = contextFilePathForSession(config.stateDir, runtimeKey);
68
+ const sessionStateFilePath = sessionStateFilePathForRuntime(config.stateDir, runtimeKey);
69
+ const sessionManager = new CodexSessionManager(runtimeKey, sessionStateFilePath, agentLog);
70
+ const resolvedWsUrl = resolveWsUrl(config.apiUrl, config.wsUrl, config.swimlaneName);
71
+ const ws = new ParallWs({
72
+ getTicket: () => client.getWsTicket(),
73
+ wsUrl: resolvedWsUrl,
74
+ });
75
+ const configMgr = createPlatformConfigManager({
76
+ client,
77
+ stateDir: config.stateDir,
78
+ runtimeType: 'codex',
79
+ log: agentLog,
80
+ });
81
+ const platformDefaults = await configMgr.fetch();
82
+ let platformManaged = isPlatformManagedProfile(me.agent_profile);
83
+ // Model precedence: operator override (platform-managed) > env > server floor.
84
+ // The server delivers a model floor for Parall-proxy routes even when
85
+ // model_management=self, so the CLI never falls back to a built-in default
86
+ // the catalog might reject — but that floor must lose to an explicit env
87
+ // override. A profile is an operator-override only when it is platform-managed
88
+ // AND not explicitly self-managed: this treats legacy hosted agents
89
+ // (model_management=null, resolved to platform server-side) as overrides,
90
+ // while excluding local self agents whose delivered model is a floor.
91
+ // reasoning_effort stays an operator override (platform).
92
+ let platformModelOverride = isPlatformModelOverride(me.agent_profile);
93
+ const resolvedModel = resolveRuntimeModel(platformModelOverride, platformDefaults.model, config.model);
94
+ const resolvedEffort = platformManaged
95
+ ? (platformDefaults.thinkingEffort ?? config.reasoningEffort)
96
+ : config.reasoningEffort;
97
+ if (platformDefaults.model)
98
+ agentLog.info(`platform config: model=${platformDefaults.model} → resolved=${resolvedModel ?? 'cli-default'} (${platformModelOverride ? 'platform-managed' : 'floor/self'})`);
99
+ if (platformDefaults.thinkingEffort)
100
+ agentLog.info(`platform config: reasoning_effort=${platformDefaults.thinkingEffort} (${platformModelOverride ? 'applied' : 'ignored, model_management=self'})`);
101
+ const adapter = new CodexAppServerAdapter({
102
+ codexBin: config.codexBin,
103
+ codexHome: config.codexHome,
104
+ workspaceDir: config.workspaceDir,
105
+ model: resolvedModel,
106
+ reasoningEffort: resolvedEffort,
107
+ sandbox: config.sandbox,
108
+ approvalPolicy: config.approvalPolicy,
109
+ sessionManager,
110
+ log: agentLog,
111
+ contextFilePath: mainContextFilePath,
112
+ useParallProvider,
113
+ });
114
+ const gateway = new ParallAgentGateway({
115
+ accountId: agentUserId,
116
+ client,
117
+ ws,
118
+ connectionLabel: resolvedWsUrl,
119
+ config: {
120
+ parall_url: config.apiUrl,
121
+ api_key: config.apiKey,
122
+ org_id: config.orgId,
123
+ },
124
+ agentUserId,
125
+ runtimeType: 'codex',
126
+ runtimeKey,
127
+ runtimeRef: {
128
+ hostname: os.hostname(),
129
+ pid: process.pid,
130
+ workspace_dir: config.workspaceDir,
131
+ codex_home: config.codexHome,
132
+ driver: 'app-server',
133
+ },
134
+ dispatchAdapter: adapter,
135
+ log: agentLog,
136
+ shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
137
+ forkDeadlineMs: parseForkDeadlineMs(process.env.PRLL_FORK_DEADLINE_MS),
138
+ dispatchDeadlineMs: parseDispatchDeadlineMs(process.env.PRLL_DISPATCH_DEADLINE_MS),
139
+ contextFilePathForSession: (sessionKey) => contextFilePathForSession(config.stateDir, sessionKey),
140
+ stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
141
+ onConfigUpdate: async () => {
142
+ const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
143
+ platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
144
+ platformModelOverride = isPlatformModelOverride(refreshed.agent_profile);
145
+ const updated = await configMgr.fetch();
146
+ adapter.updateConfig({
147
+ model: resolveRuntimeModel(platformModelOverride, updated.model, config.model) ?? null,
148
+ reasoningEffort: platformManaged
149
+ ? (updated.thinkingEffort ?? config.reasoningEffort)
150
+ : (config.reasoningEffort ?? null),
151
+ });
152
+ },
153
+ onSessionReady: async () => {
154
+ const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
155
+ platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
156
+ platformModelOverride = isPlatformModelOverride(refreshed.agent_profile);
157
+ const updated = await configMgr.fetch();
158
+ adapter.updateConfig({
159
+ model: resolveRuntimeModel(platformModelOverride, updated.model, config.model) ?? null,
160
+ reasoningEffort: platformManaged
161
+ ? (updated.thinkingEffort ?? config.reasoningEffort)
162
+ : (config.reasoningEffort ?? null),
163
+ });
164
+ },
165
+ onNewSession: () => {
166
+ sessionManager.clearMainThread();
167
+ },
168
+ onSessionStale: () => {
169
+ sessionManager.clearMainThread();
170
+ },
171
+ });
172
+ const abortController = new AbortController();
173
+ const abort = () => abortController.abort();
174
+ process.on('SIGINT', abort);
175
+ process.on('SIGTERM', abort);
176
+ try {
177
+ agentLog.info(`starting self-hosted Codex runtime (app-server driver) for ${me.display_name} (${agentUserId})`);
178
+ await gateway.run(abortController.signal);
179
+ }
180
+ finally {
181
+ await adapter.stop();
182
+ process.off('SIGINT', abort);
183
+ process.off('SIGTERM', abort);
184
+ }
147
185
  }
148
186
  finally {
149
- await adapter.stop();
150
- process.off("SIGINT", abort);
151
- process.off("SIGTERM", abort);
187
+ await telemetry.shutdown();
152
188
  }
153
189
  }
154
190
  main().catch((err) => {
@@ -1,18 +1,18 @@
1
- import type { ChildProcessWithoutNullStreams } from "node:child_process";
1
+ import type { ChildProcessWithoutNullStreams } from 'node:child_process';
2
2
  type JsonRpcId = number | string;
3
3
  export type JsonRpcRequest = {
4
- jsonrpc: "2.0";
4
+ jsonrpc: '2.0';
5
5
  id: JsonRpcId;
6
6
  method: string;
7
7
  params?: unknown;
8
8
  };
9
9
  export type JsonRpcNotification = {
10
- jsonrpc: "2.0";
10
+ jsonrpc: '2.0';
11
11
  method: string;
12
12
  params?: unknown;
13
13
  };
14
14
  export type JsonRpcResponse = {
15
- jsonrpc: "2.0";
15
+ jsonrpc: '2.0';
16
16
  id: JsonRpcId;
17
17
  result?: unknown;
18
18
  error?: {
@@ -1 +1 @@
1
- {"version":3,"file":"jsonrpc-client.d.ts","sourceRoot":"","sources":["../src/jsonrpc-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAEzE,KAAK,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAEjC,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,SAAS,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,SAAS,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC3D,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;AAW5E;;;;;;;GAOG;AACH,qBAAa,kBAAkB;IAQ3B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAT/B,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IACzD,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,QAAQ,CAAS;gBAGN,IAAI,EAAE,8BAA8B,EACpC,gBAAgB,GAAE,MAAmC,EACrD,WAAW,CAAC,GAAE,CAAC,IAAI,EAAE,8BAA8B,KAAK,IAAI,aAAA;IAS/E,sBAAsB,CAAC,OAAO,EAAE,mBAAmB;IAInD,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAoB/D,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO;IAMjD,UAAU,IAAI,OAAO;IAIrB,OAAO,CAAC,GAAG,EAAE,KAAK;IAUlB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,UAAU;CAyBnB"}
1
+ {"version":3,"file":"jsonrpc-client.d.ts","sourceRoot":"","sources":["../src/jsonrpc-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAEzE,KAAK,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAEjC,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,SAAS,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,SAAS,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC3D,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;AAW5E;;;;;;;GAOG;AACH,qBAAa,kBAAkB;IAQ3B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAT/B,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IACzD,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,QAAQ,CAAS;gBAGN,IAAI,EAAE,8BAA8B,EACpC,gBAAgB,GAAE,MAAmC,EACrD,WAAW,CAAC,GAAE,CAAC,IAAI,EAAE,8BAA8B,KAAK,IAAI,aAAA;IAS/E,sBAAsB,CAAC,OAAO,EAAE,mBAAmB;IAInD,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAsB/D,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO;IAMjD,UAAU,IAAI,OAAO;IAIrB,OAAO,CAAC,GAAG,EAAE,KAAK;IAUlB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,UAAU;CAyBnB"}