@parall/codex-agent 1.36.1 → 1.38.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/config.d.ts CHANGED
@@ -52,5 +52,13 @@ export declare function resolveWsUrl(apiUrl: string, explicitWsUrl?: string, swi
52
52
  export declare function buildCodexRuntimeKey(agentUserId: string): string;
53
53
  export declare function sessionStateFilePathForRuntime(stateDir: string, runtimeKey: string): string;
54
54
  export declare function contextFilePathForSession(stateDir: string, sessionKey: string): string;
55
+ /**
56
+ * PRLL_CONTEXT_DIR root: per-lane dispatch context files, keyed by the
57
+ * canonical lane-key (see @parall/agent-core lane-key.ts). Anchored in the
58
+ * per-agent stateDir — never the workspace. Stable for the app-server's
59
+ * lifetime, which fixes the fork-reads-main-context mismatch of the single
60
+ * PRLL_CONTEXT_FILE under codex's one-process-many-threads model.
61
+ */
62
+ export declare function dispatchContextDirPath(stateDir: string): string;
55
63
  export declare function stepIdFilePathForSession(stateDir: string, sessionKey: string): string;
56
64
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;OAOG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAcF,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,gBAAgB,CAyC9F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUtD;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAW7D;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,MAAM,EACtB,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAMR;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAG3F;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGtF;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGrF"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;OAOG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAcF,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,gBAAgB,CAyC9F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUtD;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAW7D;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,MAAM,EACtB,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAMR;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAG3F;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGtF;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGrF"}
package/dist/config.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { dispatchLaneContextDir } from '@parall/agent-core';
1
2
  import * as os from 'node:os';
2
3
  import * as path from 'node:path';
3
4
  function requireEnv(env, name) {
@@ -104,6 +105,16 @@ export function contextFilePathForSession(stateDir, sessionKey) {
104
105
  const fileName = Buffer.from(sessionKey).toString('base64url');
105
106
  return path.join(stateDir, 'dispatch-context', `${fileName}.json`);
106
107
  }
108
+ /**
109
+ * PRLL_CONTEXT_DIR root: per-lane dispatch context files, keyed by the
110
+ * canonical lane-key (see @parall/agent-core lane-key.ts). Anchored in the
111
+ * per-agent stateDir — never the workspace. Stable for the app-server's
112
+ * lifetime, which fixes the fork-reads-main-context mismatch of the single
113
+ * PRLL_CONTEXT_FILE under codex's one-process-many-threads model.
114
+ */
115
+ export function dispatchContextDirPath(stateDir) {
116
+ return dispatchLaneContextDir(stateDir);
117
+ }
107
118
  export function stepIdFilePathForSession(stateDir, sessionKey) {
108
119
  const fileName = Buffer.from(sessionKey).toString('base64url');
109
120
  return path.join(stateDir, 'step-ids', `${fileName}.txt`);
@@ -5,6 +5,7 @@ type CodexAppServerAdapterOptions = Pick<CodexAgentConfig, 'approvalPolicy' | 'c
5
5
  sessionManager: CodexSessionManager;
6
6
  log?: GatewayLogger;
7
7
  contextFilePath?: string;
8
+ contextDirPath?: string;
8
9
  useParallProvider?: boolean;
9
10
  };
10
11
  export declare class CodexAppServerAdapter implements DispatchAdapter {
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,iBAAiB,EACjB,aAAa,EACb,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,KAAK,4BAA4B,GAAG,IAAI,CACtC,gBAAgB,EACd,gBAAgB,GAChB,UAAU,GACV,WAAW,GACX,OAAO,GACP,iBAAiB,GACjB,SAAS,GACT,cAAc,CACjB,GAAG;IACF,cAAc,EAAE,mBAAmB,CAAC;IACpC,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAqCF,qBAAa,qBAAsB,YAAW,eAAe;IA+B/C,OAAO,CAAC,QAAQ,CAAC,IAAI;IA9BjC,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,IAAI,CAA+C;IAC3D,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA6B;IAC/D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IACtD,OAAO,CAAC,QAAQ,CAAS;IAEzB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;gBAYQ,IAAI,EAAE,4BAA4B;IAE/D,YAAY,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI;IAMhF,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAqB/E,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAUvC,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAI1C,QAAQ,CAAC,EACd,KAAK,EACL,YAAY,EACZ,UAAU,EACV,OAAO,GACR,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;IA0O7C,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAMjD,WAAW,CAAC,EAAE,UAAU,EAAE,gBAAgB,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IA+BhG,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe;IAIrC,OAAO,CAAC,cAAc;IAKhB,IAAI;YAiBI,aAAa;YA2Bb,OAAO;IAkFrB,OAAO,CAAC,qBAAqB;YAmCf,UAAU;IA2CxB,OAAO,CAAC,iBAAiB;CA2B1B"}
1
+ {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,iBAAiB,EACjB,aAAa,EACb,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,KAAK,4BAA4B,GAAG,IAAI,CACtC,gBAAgB,EACd,gBAAgB,GAChB,UAAU,GACV,WAAW,GACX,OAAO,GACP,iBAAiB,GACjB,SAAS,GACT,cAAc,CACjB,GAAG;IACF,cAAc,EAAE,mBAAmB,CAAC;IACpC,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAqCF,qBAAa,qBAAsB,YAAW,eAAe;IA+B/C,OAAO,CAAC,QAAQ,CAAC,IAAI;IA9BjC,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,IAAI,CAA+C;IAC3D,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA6B;IAC/D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IACtD,OAAO,CAAC,QAAQ,CAAS;IAEzB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;gBAYQ,IAAI,EAAE,4BAA4B;IAE/D,YAAY,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI;IAMhF,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAqB/E,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAUvC,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAI1C,QAAQ,CAAC,EACd,KAAK,EACL,YAAY,EACZ,UAAU,EACV,OAAO,GACR,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;IA0O7C,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAMjD,WAAW,CAAC,EAAE,UAAU,EAAE,gBAAgB,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IA+BhG,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe;IAIrC,OAAO,CAAC,cAAc;IAKhB,IAAI;YAiBI,aAAa;YA2Bb,OAAO;IAuFrB,OAAO,CAAC,qBAAqB;YAmCf,UAAU;IA2CxB,OAAO,CAAC,iBAAiB;CA2B1B"}
package/dist/dispatch.js CHANGED
@@ -448,6 +448,11 @@ export class CodexAppServerAdapter {
448
448
  if (this.opts.contextFilePath) {
449
449
  env.PRLL_CONTEXT_FILE = this.opts.contextFilePath;
450
450
  }
451
+ if (this.opts.contextDirPath) {
452
+ // Per-lane dispatch context directory — per-target lookup, so fork
453
+ // threads never read main's context (the PRLL_CONTEXT_FILE pitfall).
454
+ env.PRLL_CONTEXT_DIR = this.opts.contextDirPath;
455
+ }
451
456
  // Pin the wiki workspace to the agent workspace (same convention as the
452
457
  // OpenClaw runtime). Without this, `parall wiki` resolves mounts relative
453
458
  // to the subprocess cwd — a `cd` into any subdirectory would silently
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
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';
3
+ import { ParallAgentGateway, createPlatformConfigManager, createLogger, createOtelLogger, childLogger, deriveModelIsPin, resolveRuntimeModel, parseShutdownDeadlineMs, parseForkDeadlineMs, parseDispatchDeadlineMs, parseProviderConfig, clearAllProviderCreds, llmSource, initAgentTelemetry, } from '@parall/agent-core';
4
4
  import { ApiError, ParallClient, ParallWs } from '@parall/sdk';
5
- import { buildCodexRuntimeKey, contextFilePathForSession, resolveCodexAgentConfig, resolveWsUrl, sessionStateFilePathForRuntime, stepIdFilePathForSession, } from './config.js';
5
+ import { buildCodexRuntimeKey, contextFilePathForSession, dispatchContextDirPath, 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';
@@ -79,25 +79,18 @@ async function main() {
79
79
  log: agentLog,
80
80
  });
81
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);
82
+ // Model precedence: operator PIN (override) > env > server FLOOR. The server
83
+ // now says which it is via model_is_pin (deriveModelIsPin presence-gates the
84
+ // dual-read: old servers omit it fall back to model_management). A PIN beats
85
+ // an explicit env override; a FLOOR loses to env. reasoning_effort is delivered
86
+ // by the server only when pinned, so we no longer gate it on a local flag.
87
+ const platformModelOverride = deriveModelIsPin(platformDefaults, me.agent_profile);
93
88
  const resolvedModel = resolveRuntimeModel(platformModelOverride, platformDefaults.model, config.model);
94
- const resolvedEffort = platformManaged
95
- ? (platformDefaults.thinkingEffort ?? config.reasoningEffort)
96
- : config.reasoningEffort;
89
+ const resolvedEffort = platformDefaults.thinkingEffort ?? config.reasoningEffort;
97
90
  if (platformDefaults.model)
98
- agentLog.info(`platform config: model=${platformDefaults.model} → resolved=${resolvedModel ?? 'cli-default'} (${platformModelOverride ? 'platform-managed' : 'floor/self'})`);
91
+ agentLog.info(`platform config: model=${platformDefaults.model} → resolved=${resolvedModel ?? 'cli-default'} (${platformModelOverride ? 'pin/override' : 'floor'})`);
99
92
  if (platformDefaults.thinkingEffort)
100
- agentLog.info(`platform config: reasoning_effort=${platformDefaults.thinkingEffort} (${platformModelOverride ? 'applied' : 'ignored, model_management=self'})`);
93
+ agentLog.info(`platform config: reasoning_effort=${platformDefaults.thinkingEffort}`);
101
94
  const adapter = new CodexAppServerAdapter({
102
95
  codexBin: config.codexBin,
103
96
  codexHome: config.codexHome,
@@ -109,8 +102,34 @@ async function main() {
109
102
  sessionManager,
110
103
  log: agentLog,
111
104
  contextFilePath: mainContextFilePath,
105
+ contextDirPath: dispatchContextDirPath(config.stateDir),
112
106
  useParallProvider,
113
107
  });
108
+ // Shared by onConfigUpdate + onSessionReady. /agents/me is only consumed as
109
+ // deriveModelIsPin's legacy fallback when the server omits model_is_pin
110
+ // (old server) — skip that round trip otherwise.
111
+ const refreshPlatformConfig = async () => {
112
+ const updated = await configMgr.fetch();
113
+ let refreshedProfile;
114
+ if (updated.modelIsPin === undefined) {
115
+ try {
116
+ refreshedProfile = (await getAgentMeWithLegacyFallback(client, config.orgId))
117
+ .agent_profile;
118
+ }
119
+ catch (err) {
120
+ // configMgr.fetch() degrades to cache on failure; mirror that for the
121
+ // legacy fallback so a transient /agents/me error doesn't reject this
122
+ // config-refresh callback. No profile → deriveModelIsPin treats the
123
+ // delivered model as a floor (safe default).
124
+ agentLog.warn(`platform config refresh: legacy /agents/me fallback failed: ${String(err)}`);
125
+ }
126
+ }
127
+ const isPin = deriveModelIsPin(updated, refreshedProfile);
128
+ adapter.updateConfig({
129
+ model: resolveRuntimeModel(isPin, updated.model, config.model) ?? null,
130
+ reasoningEffort: updated.thinkingEffort ?? config.reasoningEffort ?? null,
131
+ });
132
+ };
114
133
  const gateway = new ParallAgentGateway({
115
134
  accountId: agentUserId,
116
135
  client,
@@ -138,30 +157,9 @@ async function main() {
138
157
  dispatchDeadlineMs: parseDispatchDeadlineMs(process.env.PRLL_DISPATCH_DEADLINE_MS),
139
158
  contextFilePathForSession: (sessionKey) => contextFilePathForSession(config.stateDir, sessionKey),
140
159
  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
- },
160
+ dispatchContextDir: dispatchContextDirPath(config.stateDir),
161
+ onConfigUpdate: refreshPlatformConfig,
162
+ onSessionReady: refreshPlatformConfig,
165
163
  onNewSession: () => {
166
164
  sessionManager.clearMainThread();
167
165
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/codex-agent",
3
- "version": "1.36.1",
3
+ "version": "1.38.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.36.1",
30
- "@parall/sdk": "1.36.1",
31
- "@parall/cli": "1.36.1"
29
+ "@parall/agent-core": "1.38.0",
30
+ "@parall/cli": "1.38.0",
31
+ "@parall/sdk": "1.38.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "^22.0.0",
package/src/config.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { dispatchLaneContextDir } from '@parall/agent-core';
1
2
  import * as os from 'node:os';
2
3
  import * as path from 'node:path';
3
4
 
@@ -155,6 +156,17 @@ export function contextFilePathForSession(stateDir: string, sessionKey: string):
155
156
  return path.join(stateDir, 'dispatch-context', `${fileName}.json`);
156
157
  }
157
158
 
159
+ /**
160
+ * PRLL_CONTEXT_DIR root: per-lane dispatch context files, keyed by the
161
+ * canonical lane-key (see @parall/agent-core lane-key.ts). Anchored in the
162
+ * per-agent stateDir — never the workspace. Stable for the app-server's
163
+ * lifetime, which fixes the fork-reads-main-context mismatch of the single
164
+ * PRLL_CONTEXT_FILE under codex's one-process-many-threads model.
165
+ */
166
+ export function dispatchContextDirPath(stateDir: string): string {
167
+ return dispatchLaneContextDir(stateDir);
168
+ }
169
+
158
170
  export function stepIdFilePathForSession(stateDir: string, sessionKey: string): string {
159
171
  const fileName = Buffer.from(sessionKey).toString('base64url');
160
172
  return path.join(stateDir, 'step-ids', `${fileName}.txt`);
package/src/dispatch.ts CHANGED
@@ -35,6 +35,7 @@ type CodexAppServerAdapterOptions = Pick<
35
35
  sessionManager: CodexSessionManager;
36
36
  log?: GatewayLogger;
37
37
  contextFilePath?: string;
38
+ contextDirPath?: string;
38
39
  useParallProvider?: boolean;
39
40
  };
40
41
 
@@ -497,6 +498,11 @@ export class CodexAppServerAdapter implements DispatchAdapter {
497
498
  if (this.opts.contextFilePath) {
498
499
  env.PRLL_CONTEXT_FILE = this.opts.contextFilePath;
499
500
  }
501
+ if (this.opts.contextDirPath) {
502
+ // Per-lane dispatch context directory — per-target lookup, so fork
503
+ // threads never read main's context (the PRLL_CONTEXT_FILE pitfall).
504
+ env.PRLL_CONTEXT_DIR = this.opts.contextDirPath;
505
+ }
500
506
  // Pin the wiki workspace to the agent workspace (same convention as the
501
507
  // OpenClaw runtime). Without this, `parall wiki` resolves mounts relative
502
508
  // to the subprocess cwd — a `cd` into any subdirectory would silently
package/src/index.ts CHANGED
@@ -7,8 +7,7 @@ import {
7
7
  createLogger,
8
8
  createOtelLogger,
9
9
  childLogger,
10
- isPlatformManagedProfile,
11
- isPlatformModelOverride,
10
+ deriveModelIsPin,
12
11
  resolveRuntimeModel,
13
12
  parseShutdownDeadlineMs,
14
13
  parseForkDeadlineMs,
@@ -22,6 +21,7 @@ import { ApiError, ParallClient, ParallWs } from '@parall/sdk';
22
21
  import {
23
22
  buildCodexRuntimeKey,
24
23
  contextFilePathForSession,
24
+ dispatchContextDirPath,
25
25
  resolveCodexAgentConfig,
26
26
  resolveWsUrl,
27
27
  sessionStateFilePathForRuntime,
@@ -111,33 +111,24 @@ async function main() {
111
111
  log: agentLog,
112
112
  });
113
113
  const platformDefaults = await configMgr.fetch();
114
- let platformManaged = isPlatformManagedProfile(me.agent_profile);
115
- // Model precedence: operator override (platform-managed) > env > server floor.
116
- // The server delivers a model floor for Parall-proxy routes even when
117
- // model_management=self, so the CLI never falls back to a built-in default
118
- // the catalog might reject but that floor must lose to an explicit env
119
- // override. A profile is an operator-override only when it is platform-managed
120
- // AND not explicitly self-managed: this treats legacy hosted agents
121
- // (model_management=null, resolved to platform server-side) as overrides,
122
- // while excluding local self agents whose delivered model is a floor.
123
- // reasoning_effort stays an operator override (platform).
124
- let platformModelOverride = isPlatformModelOverride(me.agent_profile);
114
+ // Model precedence: operator PIN (override) > env > server FLOOR. The server
115
+ // now says which it is via model_is_pin (deriveModelIsPin presence-gates the
116
+ // dual-read: old servers omit it fall back to model_management). A PIN beats
117
+ // an explicit env override; a FLOOR loses to env. reasoning_effort is delivered
118
+ // by the server only when pinned, so we no longer gate it on a local flag.
119
+ const platformModelOverride = deriveModelIsPin(platformDefaults, me.agent_profile);
125
120
  const resolvedModel = resolveRuntimeModel(
126
121
  platformModelOverride,
127
122
  platformDefaults.model,
128
123
  config.model,
129
124
  );
130
- const resolvedEffort = platformManaged
131
- ? (platformDefaults.thinkingEffort ?? config.reasoningEffort)
132
- : config.reasoningEffort;
125
+ const resolvedEffort = platformDefaults.thinkingEffort ?? config.reasoningEffort;
133
126
  if (platformDefaults.model)
134
127
  agentLog.info(
135
- `platform config: model=${platformDefaults.model} → resolved=${resolvedModel ?? 'cli-default'} (${platformModelOverride ? 'platform-managed' : 'floor/self'})`,
128
+ `platform config: model=${platformDefaults.model} → resolved=${resolvedModel ?? 'cli-default'} (${platformModelOverride ? 'pin/override' : 'floor'})`,
136
129
  );
137
130
  if (platformDefaults.thinkingEffort)
138
- agentLog.info(
139
- `platform config: reasoning_effort=${platformDefaults.thinkingEffort} (${platformModelOverride ? 'applied' : 'ignored, model_management=self'})`,
140
- );
131
+ agentLog.info(`platform config: reasoning_effort=${platformDefaults.thinkingEffort}`);
141
132
 
142
133
  const adapter = new CodexAppServerAdapter({
143
134
  codexBin: config.codexBin,
@@ -150,9 +141,39 @@ async function main() {
150
141
  sessionManager,
151
142
  log: agentLog,
152
143
  contextFilePath: mainContextFilePath,
144
+ contextDirPath: dispatchContextDirPath(config.stateDir),
153
145
  useParallProvider,
154
146
  });
155
147
 
148
+ // Shared by onConfigUpdate + onSessionReady. /agents/me is only consumed as
149
+ // deriveModelIsPin's legacy fallback when the server omits model_is_pin
150
+ // (old server) — skip that round trip otherwise.
151
+ const refreshPlatformConfig = async () => {
152
+ const updated = await configMgr.fetch();
153
+ let refreshedProfile:
154
+ | Awaited<ReturnType<typeof getAgentMeWithLegacyFallback>>['agent_profile']
155
+ | undefined;
156
+ if (updated.modelIsPin === undefined) {
157
+ try {
158
+ refreshedProfile = (await getAgentMeWithLegacyFallback(client, config.orgId))
159
+ .agent_profile;
160
+ } catch (err) {
161
+ // configMgr.fetch() degrades to cache on failure; mirror that for the
162
+ // legacy fallback so a transient /agents/me error doesn't reject this
163
+ // config-refresh callback. No profile → deriveModelIsPin treats the
164
+ // delivered model as a floor (safe default).
165
+ agentLog.warn(
166
+ `platform config refresh: legacy /agents/me fallback failed: ${String(err)}`,
167
+ );
168
+ }
169
+ }
170
+ const isPin = deriveModelIsPin(updated, refreshedProfile);
171
+ adapter.updateConfig({
172
+ model: resolveRuntimeModel(isPin, updated.model, config.model) ?? null,
173
+ reasoningEffort: updated.thinkingEffort ?? config.reasoningEffort ?? null,
174
+ });
175
+ };
176
+
156
177
  const gateway = new ParallAgentGateway({
157
178
  accountId: agentUserId,
158
179
  client,
@@ -182,30 +203,9 @@ async function main() {
182
203
  contextFilePathForSession(config.stateDir, sessionKey),
183
204
  stepIdFilePathForSession: (sessionKey) =>
184
205
  stepIdFilePathForSession(config.stateDir, sessionKey),
185
- onConfigUpdate: async () => {
186
- const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
187
- platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
188
- platformModelOverride = isPlatformModelOverride(refreshed.agent_profile);
189
- const updated = await configMgr.fetch();
190
- adapter.updateConfig({
191
- model: resolveRuntimeModel(platformModelOverride, updated.model, config.model) ?? null,
192
- reasoningEffort: platformManaged
193
- ? (updated.thinkingEffort ?? config.reasoningEffort)
194
- : (config.reasoningEffort ?? null),
195
- });
196
- },
197
- onSessionReady: async () => {
198
- const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
199
- platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
200
- platformModelOverride = isPlatformModelOverride(refreshed.agent_profile);
201
- const updated = await configMgr.fetch();
202
- adapter.updateConfig({
203
- model: resolveRuntimeModel(platformModelOverride, updated.model, config.model) ?? null,
204
- reasoningEffort: platformManaged
205
- ? (updated.thinkingEffort ?? config.reasoningEffort)
206
- : (config.reasoningEffort ?? null),
207
- });
208
- },
206
+ dispatchContextDir: dispatchContextDirPath(config.stateDir),
207
+ onConfigUpdate: refreshPlatformConfig,
208
+ onSessionReady: refreshPlatformConfig,
209
209
  onNewSession: () => {
210
210
  sessionManager.clearMainThread();
211
211
  },