@parall/codex-agent 1.49.0 → 1.50.1

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.
Files changed (3) hide show
  1. package/dist/index.js +37 -25
  2. package/package.json +4 -4
  3. package/src/index.ts +42 -27
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import * as os from 'node:os';
3
- import { ParallAgentGateway, capabilityBinDir, configureHttpKeepAlive, createPlatformConfigManager, createLogger, createOtelLogger, childLogger, deriveModelIsPin, materializeChannelCapabilities, resolveRuntimeModel, parseShutdownDeadlineMs, parseForkDeadlineMs, parseDispatchDeadlineMs, parseProviderConfig, clearAllProviderCreds, llmSource, initAgentTelemetry, } from '@parall/agent-core';
3
+ import { ParallAgentGateway, capabilityBinDir, configureHttpKeepAlive, createPlatformConfigManager, createLogger, createOtelLogger, childLogger, deriveModelIsPin, materializeChannelCapabilities, resolveRuntimeModel, parseShutdownDeadlineMs, parseForkDeadlineMs, parseDispatchDeadlineMs, parseProviderConfig, clearAllProviderCreds, llmSource, identityFromMe, initAgentTelemetry, } from '@parall/agent-core';
4
4
  import { ApiError, ParallClient, ParallWs } from '@parall/sdk';
5
5
  import { buildCodexRuntimeKey, contextFilePathForSession, dispatchContextDirPath, resolveCodexAgentConfig, resolveWsUrl, sessionStateFilePathForRuntime, stepIdFilePathForSession, } from './config.js';
6
6
  import { CodexAppServerAdapter } from './dispatch.js';
@@ -17,7 +17,7 @@ async function getAgentMeWithLegacyFallback(client, orgId) {
17
17
  throw err;
18
18
  }
19
19
  const user = await client.getMe();
20
- return { ...user, agent_profile: null };
20
+ return { ...user, agent_profile: null, public_profile: null };
21
21
  }
22
22
  }
23
23
  function resolveProviderEnv() {
@@ -59,11 +59,14 @@ async function main() {
59
59
  ensureParallProvider(config.codexHome, config.apiUrl, agentLog);
60
60
  agentLog.info('parall custom provider configured (Responses API HTTP/SSE mode)');
61
61
  }
62
- const agentIdentity = {
63
- userId: agentUserId,
64
- displayName: me.display_name,
65
- description: me.agent_profile?.description ?? undefined,
66
- };
62
+ // Mutable on purpose: refreshPlatformConfig refetches it (see
63
+ // identityFromMe) so profile edits reach the next activation.
64
+ let agentIdentity = identityFromMe(me);
65
+ let lastIdentitySnapshot = JSON.stringify(agentIdentity);
66
+ // Last successful /agents/me profile — deriveModelIsPin's legacy fallback
67
+ // (old servers omit model_is_pin) must not flip a pinned model to floor
68
+ // just because one refresh round-trip failed.
69
+ let lastKnownAgentProfile = me.agent_profile;
67
70
  const runtimeKey = config.runtimeKey || buildCodexRuntimeKey(agentUserId);
68
71
  const mainContextFilePath = contextFilePathForSession(config.stateDir, runtimeKey);
69
72
  const sessionStateFilePath = sessionStateFilePathForRuntime(config.stateDir, runtimeKey);
@@ -126,26 +129,28 @@ async function main() {
126
129
  capabilityBinDir: capabilityBinDir(config.stateDir),
127
130
  developerInstructions,
128
131
  });
129
- // Shared by onConfigUpdate + onSessionReady. /agents/me is only consumed as
130
- // deriveModelIsPin's legacy fallback when the server omits model_is_pin
131
- // (old server) — skip that round trip otherwise.
132
+ // Shared by onConfigUpdate + onSessionReady.
132
133
  const refreshPlatformConfig = async () => {
133
134
  const updated = await configMgr.fetch();
134
- let refreshedProfile;
135
- if (updated.modelIsPin === undefined) {
136
- try {
137
- refreshedProfile = (await getAgentMeWithLegacyFallback(client, config.orgId))
138
- .agent_profile;
139
- }
140
- catch (err) {
141
- // configMgr.fetch() degrades to cache on failure; mirror that for the
142
- // legacy fallback so a transient /agents/me error doesn't reject this
143
- // config-refresh callback. No profile → deriveModelIsPin treats the
144
- // delivered model as a floor (safe default).
145
- agentLog.warn(`platform config refresh: legacy /agents/me fallback failed: ${String(err)}`);
146
- }
135
+ // Unconditional /agents/me refetch: identity (title / public
136
+ // description / private instructions) rides the same
137
+ // agent_config.update nudge as model config. Failure degrades to the
138
+ // last-known-good identity, mirroring configMgr.fetch()'s cache
139
+ // fallback, so a transient error never rejects this callback. No
140
+ // profile → deriveModelIsPin treats the delivered model as a floor
141
+ // (safe default).
142
+ let refreshedMe;
143
+ try {
144
+ refreshedMe = await getAgentMeWithLegacyFallback(client, config.orgId);
145
+ }
146
+ catch (err) {
147
+ agentLog.warn(`platform config refresh: /agents/me refetch failed (keeping last-known identity): ${String(err)}`);
148
+ }
149
+ if (refreshedMe) {
150
+ agentIdentity = identityFromMe(refreshedMe);
151
+ lastKnownAgentProfile = refreshedMe.agent_profile;
147
152
  }
148
- const isPin = deriveModelIsPin(updated, refreshedProfile);
153
+ const isPin = deriveModelIsPin(updated, lastKnownAgentProfile);
149
154
  adapter.updateConfig({
150
155
  model: resolveRuntimeModel(isPin, updated.model, config.model) ?? null,
151
156
  reasoningEffort: updated.thinkingEffort ?? config.reasoningEffort ?? null,
@@ -175,8 +180,15 @@ async function main() {
175
180
  promptWritten = false;
176
181
  agentLog.warn(`system prompt refresh write failed (retrying next refresh): ${String(err)}`);
177
182
  }
178
- if (promptWritten && joinedFragments !== lastCapabilityFragments) {
183
+ // Identity changes ride the same lazy-restart mechanism as capability
184
+ // fragments: the next fresh-process thread/resume applies the rebuilt
185
+ // developerInstructions, and the adapter compacts so the model-visible
186
+ // context converges (two-plane semantics: src/instructions-refresh.ts).
187
+ const identitySnapshot = JSON.stringify(agentIdentity);
188
+ if (promptWritten &&
189
+ (joinedFragments !== lastCapabilityFragments || identitySnapshot !== lastIdentitySnapshot)) {
179
190
  lastCapabilityFragments = joinedFragments;
191
+ lastIdentitySnapshot = identitySnapshot;
180
192
  adapter.requestProcessRestart();
181
193
  }
182
194
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/codex-agent",
3
- "version": "1.49.0",
3
+ "version": "1.50.1",
4
4
  "description": "Codex CLI bridge runtime for self-hosted Parall agents",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -25,9 +25,9 @@
25
25
  "src"
26
26
  ],
27
27
  "dependencies": {
28
- "@parall/agent-core": "1.49.0",
29
- "@parall/cli": "1.49.0",
30
- "@parall/sdk": "1.49.0"
28
+ "@parall/cli": "1.50.1",
29
+ "@parall/sdk": "1.50.1",
30
+ "@parall/agent-core": "1.50.1"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^22.0.0",
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  parseProviderConfig,
19
19
  clearAllProviderCreds,
20
20
  llmSource,
21
+ identityFromMe,
21
22
  initAgentTelemetry,
22
23
  } from '@parall/agent-core';
23
24
  import { ApiError, ParallClient, ParallWs } from '@parall/sdk';
@@ -50,10 +51,12 @@ async function getAgentMeWithLegacyFallback(client: ParallClient, orgId: string)
50
51
  throw err;
51
52
  }
52
53
  const user = await client.getMe();
53
- return { ...user, agent_profile: null };
54
+ return { ...user, agent_profile: null, public_profile: null };
54
55
  }
55
56
  }
56
57
 
58
+ type AgentMe = Awaited<ReturnType<typeof getAgentMeWithLegacyFallback>>;
59
+
57
60
  function resolveProviderEnv(): void {
58
61
  const pc = parseProviderConfig(process.env);
59
62
  if (!pc) return;
@@ -92,11 +95,14 @@ async function main() {
92
95
  ensureParallProvider(config.codexHome, config.apiUrl, agentLog);
93
96
  agentLog.info('parall custom provider configured (Responses API HTTP/SSE mode)');
94
97
  }
95
- const agentIdentity = {
96
- userId: agentUserId,
97
- displayName: me.display_name,
98
- description: me.agent_profile?.description ?? undefined,
99
- };
98
+ // Mutable on purpose: refreshPlatformConfig refetches it (see
99
+ // identityFromMe) so profile edits reach the next activation.
100
+ let agentIdentity = identityFromMe(me);
101
+ let lastIdentitySnapshot = JSON.stringify(agentIdentity);
102
+ // Last successful /agents/me profile — deriveModelIsPin's legacy fallback
103
+ // (old servers omit model_is_pin) must not flip a pinned model to floor
104
+ // just because one refresh round-trip failed.
105
+ let lastKnownAgentProfile = me.agent_profile;
100
106
  const runtimeKey = config.runtimeKey || buildCodexRuntimeKey(agentUserId);
101
107
  const mainContextFilePath = contextFilePathForSession(config.stateDir, runtimeKey);
102
108
  const sessionStateFilePath = sessionStateFilePathForRuntime(config.stateDir, runtimeKey);
@@ -174,29 +180,29 @@ async function main() {
174
180
  developerInstructions,
175
181
  });
176
182
 
177
- // Shared by onConfigUpdate + onSessionReady. /agents/me is only consumed as
178
- // deriveModelIsPin's legacy fallback when the server omits model_is_pin
179
- // (old server) — skip that round trip otherwise.
183
+ // Shared by onConfigUpdate + onSessionReady.
180
184
  const refreshPlatformConfig = async () => {
181
185
  const updated = await configMgr.fetch();
182
- let refreshedProfile:
183
- | Awaited<ReturnType<typeof getAgentMeWithLegacyFallback>>['agent_profile']
184
- | undefined;
185
- if (updated.modelIsPin === undefined) {
186
- try {
187
- refreshedProfile = (await getAgentMeWithLegacyFallback(client, config.orgId))
188
- .agent_profile;
189
- } catch (err) {
190
- // configMgr.fetch() degrades to cache on failure; mirror that for the
191
- // legacy fallback so a transient /agents/me error doesn't reject this
192
- // config-refresh callback. No profile → deriveModelIsPin treats the
193
- // delivered model as a floor (safe default).
194
- agentLog.warn(
195
- `platform config refresh: legacy /agents/me fallback failed: ${String(err)}`,
196
- );
197
- }
186
+ // Unconditional /agents/me refetch: identity (title / public
187
+ // description / private instructions) rides the same
188
+ // agent_config.update nudge as model config. Failure degrades to the
189
+ // last-known-good identity, mirroring configMgr.fetch()'s cache
190
+ // fallback, so a transient error never rejects this callback. No
191
+ // profile deriveModelIsPin treats the delivered model as a floor
192
+ // (safe default).
193
+ let refreshedMe: AgentMe | undefined;
194
+ try {
195
+ refreshedMe = await getAgentMeWithLegacyFallback(client, config.orgId);
196
+ } catch (err) {
197
+ agentLog.warn(
198
+ `platform config refresh: /agents/me refetch failed (keeping last-known identity): ${String(err)}`,
199
+ );
200
+ }
201
+ if (refreshedMe) {
202
+ agentIdentity = identityFromMe(refreshedMe);
203
+ lastKnownAgentProfile = refreshedMe.agent_profile;
198
204
  }
199
- const isPin = deriveModelIsPin(updated, refreshedProfile);
205
+ const isPin = deriveModelIsPin(updated, lastKnownAgentProfile);
200
206
  adapter.updateConfig({
201
207
  model: resolveRuntimeModel(isPin, updated.model, config.model) ?? null,
202
208
  reasoningEffort: updated.thinkingEffort ?? config.reasoningEffort ?? null,
@@ -229,8 +235,17 @@ async function main() {
229
235
  promptWritten = false;
230
236
  agentLog.warn(`system prompt refresh write failed (retrying next refresh): ${String(err)}`);
231
237
  }
232
- if (promptWritten && joinedFragments !== lastCapabilityFragments) {
238
+ // Identity changes ride the same lazy-restart mechanism as capability
239
+ // fragments: the next fresh-process thread/resume applies the rebuilt
240
+ // developerInstructions, and the adapter compacts so the model-visible
241
+ // context converges (two-plane semantics: src/instructions-refresh.ts).
242
+ const identitySnapshot = JSON.stringify(agentIdentity);
243
+ if (
244
+ promptWritten &&
245
+ (joinedFragments !== lastCapabilityFragments || identitySnapshot !== lastIdentitySnapshot)
246
+ ) {
233
247
  lastCapabilityFragments = joinedFragments;
248
+ lastIdentitySnapshot = identitySnapshot;
234
249
  adapter.requestProcessRestart();
235
250
  }
236
251
  };