@parall/codex-agent 1.26.0 → 1.26.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.
@@ -44,6 +44,7 @@ export declare class CodexAppServerAdapter implements DispatchAdapter {
44
44
  reasoningEffort?: string | null;
45
45
  }): void;
46
46
  dispatch({ event, bodyForAgent, sessionKey, context }: DispatchOpts): AsyncIterable<RuntimeEvent>;
47
+ getBranchPoint(_sessionKey: string): string | undefined;
47
48
  forkSession({ sessionKey: parentSessionKey }: ForkOpts): Promise<ForkSessionHandle | null>;
48
49
  cleanupFork({ fork }: CleanupForkOpts): void;
49
50
  private logForkFailure;
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAUA,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;CAC1B,CAAC;AAOF;;;;;;;;;;;;;GAaG;AACH,qBAAa,qBAAsB,YAAW,eAAe;IA6B/C,OAAO,CAAC,QAAQ,CAAC,IAAI;IA5BjC,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,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;IAK/E,QAAQ,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;IAgLlG,WAAW,CAAC,EAAE,UAAU,EAAE,gBAAgB,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAuBhG,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe;IAIrC,OAAO,CAAC,cAAc;IAKhB,IAAI;YAaI,aAAa;YAcb,OAAO;IA8DrB,OAAO,CAAC,qBAAqB;YAiCf,UAAU;IAwCxB,OAAO,CAAC,iBAAiB;CA2B1B"}
1
+ {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAUA,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;CAC1B,CAAC;AAOF;;;;;;;;;;;;;GAaG;AACH,qBAAa,qBAAsB,YAAW,eAAe;IA6B/C,OAAO,CAAC,QAAQ,CAAC,IAAI;IA5BjC,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,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;IAK/E,QAAQ,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;IAgLxG,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;IAuBhG,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe;IAIrC,OAAO,CAAC,cAAc;IAKhB,IAAI;YAaI,aAAa;YAcb,OAAO;IA8DrB,OAAO,CAAC,qBAAqB;YAiCf,UAAU;IAwCxB,OAAO,CAAC,iBAAiB;CA2B1B"}
package/dist/dispatch.js CHANGED
@@ -232,6 +232,11 @@ export class CodexAppServerAdapter {
232
232
  }
233
233
  }
234
234
  }
235
+ getBranchPoint(_sessionKey) {
236
+ // Codex app-server does not expose a branch-point API; fork scope prefix
237
+ // provides the behavioral fallback for this runtime.
238
+ return undefined;
239
+ }
235
240
  async forkSession({ sessionKey: parentSessionKey }) {
236
241
  const client = this.client;
237
242
  if (!client)
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ 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
- import { ensureCodexWorkspace } from "./workspace.js";
8
+ import { ensureCodexWorkspace, ensureWorkspaceTrusted } from "./workspace.js";
9
9
  function createLogger(prefix) {
10
10
  return {
11
11
  info: (msg) => console.log(`[${prefix}] ${msg}`),
@@ -35,6 +35,7 @@ async function main() {
35
35
  });
36
36
  const me = await getAgentMeWithLegacyFallback(client, config.orgId);
37
37
  const agentUserId = me.id;
38
+ ensureWorkspaceTrusted(config.codexHome, config.workspaceDir, log);
38
39
  ensureCodexWorkspace(config.workspaceDir, log, {
39
40
  userId: agentUserId,
40
41
  displayName: me.display_name,
@@ -1,4 +1,17 @@
1
1
  import type { AgentIdentity } from "@parall/agent-core";
2
+ /**
3
+ * Ensure the workspace directory is marked as trusted in the global Codex
4
+ * config so that project-level `developer_instructions` are loaded at
5
+ * app-server startup.
6
+ *
7
+ * Uses smol-toml for structured reads (avoids substring false-positives),
8
+ * but writes via raw text manipulation (preserves comments and formatting).
9
+ * Failures are swallowed (warn-only) so a trust write issue never blocks
10
+ * the bridge from starting.
11
+ */
12
+ export declare function ensureWorkspaceTrusted(codexHome: string, workspaceDir: string, log?: {
13
+ warn: (msg: string) => void;
14
+ }): void;
2
15
  export declare function ensureCodexWorkspace(workspaceDir: string, log?: {
3
16
  warn: (msg: string) => void;
4
17
  }, agentIdentity?: AgentIdentity): void;
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,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;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
@@ -1,6 +1,102 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
+ import { parse as parseToml } from "smol-toml";
3
4
  import { BRIDGE_WORKSPACE_INSTRUCTIONS, PRLL_BEHAVIOR, PRLL_REFERENCE_GUIDE, buildIdentity, buildSkillReferences, writeSkillFiles, } from "@parall/agent-core";
5
+ /**
6
+ * Ensure the workspace directory is marked as trusted in the global Codex
7
+ * config so that project-level `developer_instructions` are loaded at
8
+ * app-server startup.
9
+ *
10
+ * Uses smol-toml for structured reads (avoids substring false-positives),
11
+ * but writes via raw text manipulation (preserves comments and formatting).
12
+ * Failures are swallowed (warn-only) so a trust write issue never blocks
13
+ * the bridge from starting.
14
+ */
15
+ export function ensureWorkspaceTrusted(codexHome, workspaceDir, log) {
16
+ const configPath = path.join(codexHome, "config.toml");
17
+ const normalizedPath = path.resolve(workspaceDir);
18
+ try {
19
+ let content = "";
20
+ try {
21
+ content = fs.readFileSync(configPath, "utf8");
22
+ }
23
+ catch (err) {
24
+ if (err.code !== "ENOENT")
25
+ throw err;
26
+ }
27
+ // Structured read — know exactly what state we're in.
28
+ let parsed;
29
+ if (content) {
30
+ try {
31
+ parsed = parseToml(content);
32
+ }
33
+ catch {
34
+ // File is already broken TOML — don't make it worse.
35
+ log?.warn(`Codex config.toml is not valid TOML; skipping trust write for ${normalizedPath}`);
36
+ return;
37
+ }
38
+ }
39
+ const projects = parsed?.projects;
40
+ if (projects?.[normalizedPath]?.trust_level === "trusted")
41
+ return;
42
+ // TOML basic-string keys require backslash and double-quote escaping.
43
+ const escapedPath = normalizedPath.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
44
+ const sectionHeader = `[projects."${escapedPath}"]`;
45
+ const trustLine = 'trust_level = "trusted"';
46
+ const headerIdx = content.indexOf(sectionHeader);
47
+ if (headerIdx !== -1) {
48
+ // Section exists but trust_level isn't "trusted". Scan all lines
49
+ // within the section (up to the next `[` header or EOF) for an
50
+ // existing trust_level key — it may not be the first line after
51
+ // the header if the user added comments or other keys.
52
+ const headerLineEnd = content.indexOf("\n", headerIdx);
53
+ if (headerLineEnd === -1) {
54
+ content = `${content}\n${trustLine}\n`;
55
+ }
56
+ else {
57
+ const afterHeader = headerLineEnd + 1;
58
+ const sectionEnd = findSectionEnd(content, afterHeader);
59
+ const sectionBody = content.substring(afterHeader, sectionEnd);
60
+ const trustMatch = sectionBody.match(/^[ \t]*trust_level[ \t]*=.*$/m);
61
+ if (trustMatch) {
62
+ const matchStart = afterHeader + trustMatch.index;
63
+ const matchEnd = matchStart + trustMatch[0].length;
64
+ content = content.substring(0, matchStart) + trustLine + content.substring(matchEnd);
65
+ }
66
+ else {
67
+ content = content.substring(0, afterHeader) + trustLine + "\n" + content.substring(afterHeader);
68
+ }
69
+ }
70
+ fs.writeFileSync(configPath, content, "utf8");
71
+ }
72
+ else if (projects?.[normalizedPath] !== undefined) {
73
+ // smol-toml found the section but indexOf missed it — the header
74
+ // uses non-canonical TOML formatting. Appending would create a
75
+ // duplicate table. Skip rather than corrupt the file.
76
+ log?.warn(`Codex config.toml has non-canonical header for ${normalizedPath}; skipping trust write`);
77
+ }
78
+ else if (projects && !content.includes("[projects.")) {
79
+ // `projects` exists in parsed output but no `[projects.` table
80
+ // headers in the raw text — it's an inline table. Appending a
81
+ // standard table header would produce invalid TOML.
82
+ log?.warn(`Codex config.toml uses inline table for projects; skipping trust write for ${normalizedPath}`);
83
+ }
84
+ else {
85
+ // Section doesn't exist — append.
86
+ fs.mkdirSync(codexHome, { recursive: true });
87
+ fs.appendFileSync(configPath, `\n${sectionHeader}\n${trustLine}\n`, "utf8");
88
+ }
89
+ }
90
+ catch (err) {
91
+ log?.warn(`failed to write Codex project trust for ${normalizedPath}: ${String(err)}`);
92
+ }
93
+ }
94
+ function findSectionEnd(content, fromIndex) {
95
+ // A TOML section ends at the next `[` that starts a new table header.
96
+ // Match `[` at the beginning of a line (not inside a value).
97
+ const nextHeader = content.indexOf("\n[", fromIndex);
98
+ return nextHeader === -1 ? content.length : nextHeader;
99
+ }
4
100
  export function ensureCodexWorkspace(workspaceDir, log, agentIdentity) {
5
101
  const systemPrompt = [
6
102
  buildIdentity(agentIdentity),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/codex-agent",
3
- "version": "1.26.0",
3
+ "version": "1.26.1",
4
4
  "description": "Codex CLI bridge runtime for self-hosted Parall agents",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -25,9 +25,10 @@
25
25
  "src"
26
26
  ],
27
27
  "dependencies": {
28
- "@parall/agent-core": "1.26.0",
29
- "@parall/cli": "1.26.0",
30
- "@parall/sdk": "1.26.0"
28
+ "smol-toml": "^1.6.1",
29
+ "@parall/agent-core": "1.26.1",
30
+ "@parall/cli": "1.26.1",
31
+ "@parall/sdk": "1.26.1"
31
32
  },
32
33
  "devDependencies": {
33
34
  "@types/node": "^22.0.0",
package/src/dispatch.ts CHANGED
@@ -269,6 +269,12 @@ export class CodexAppServerAdapter implements DispatchAdapter {
269
269
  }
270
270
  }
271
271
 
272
+ getBranchPoint(_sessionKey: string): string | undefined {
273
+ // Codex app-server does not expose a branch-point API; fork scope prefix
274
+ // provides the behavioral fallback for this runtime.
275
+ return undefined;
276
+ }
277
+
272
278
  async forkSession({ sessionKey: parentSessionKey }: ForkOpts): Promise<ForkSessionHandle | null> {
273
279
  const client = this.client;
274
280
  if (!client) return null;
package/src/index.ts CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  } from "./config.js";
14
14
  import { CodexAppServerAdapter } from "./dispatch.js";
15
15
  import { CodexSessionManager } from "./session-manager.js";
16
- import { ensureCodexWorkspace } from "./workspace.js";
16
+ import { ensureCodexWorkspace, ensureWorkspaceTrusted } from "./workspace.js";
17
17
 
18
18
  function createLogger(prefix: string) {
19
19
  return {
@@ -47,6 +47,7 @@ async function main() {
47
47
 
48
48
  const me = await getAgentMeWithLegacyFallback(client, config.orgId);
49
49
  const agentUserId = me.id;
50
+ ensureWorkspaceTrusted(config.codexHome, config.workspaceDir, log);
50
51
  ensureCodexWorkspace(config.workspaceDir, log, {
51
52
  userId: agentUserId,
52
53
  displayName: me.display_name,
package/src/workspace.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
+ import { parse as parseToml } from "smol-toml";
3
4
  import {
4
5
  BRIDGE_WORKSPACE_INSTRUCTIONS,
5
6
  PRLL_BEHAVIOR,
@@ -10,6 +11,102 @@ import {
10
11
  } from "@parall/agent-core";
11
12
  import type { AgentIdentity } from "@parall/agent-core";
12
13
 
14
+ /**
15
+ * Ensure the workspace directory is marked as trusted in the global Codex
16
+ * config so that project-level `developer_instructions` are loaded at
17
+ * app-server startup.
18
+ *
19
+ * Uses smol-toml for structured reads (avoids substring false-positives),
20
+ * but writes via raw text manipulation (preserves comments and formatting).
21
+ * Failures are swallowed (warn-only) so a trust write issue never blocks
22
+ * the bridge from starting.
23
+ */
24
+ export function ensureWorkspaceTrusted(
25
+ codexHome: string,
26
+ workspaceDir: string,
27
+ log?: { warn: (msg: string) => void },
28
+ ): void {
29
+ const configPath = path.join(codexHome, "config.toml");
30
+ const normalizedPath = path.resolve(workspaceDir);
31
+
32
+ try {
33
+ let content = "";
34
+ try {
35
+ content = fs.readFileSync(configPath, "utf8");
36
+ } catch (err) {
37
+ if ((err as NodeJS.ErrnoException).code !== "ENOENT") throw err;
38
+ }
39
+
40
+ // Structured read — know exactly what state we're in.
41
+ let parsed: Record<string, unknown> | undefined;
42
+ if (content) {
43
+ try {
44
+ parsed = parseToml(content) as Record<string, unknown>;
45
+ } catch {
46
+ // File is already broken TOML — don't make it worse.
47
+ log?.warn(`Codex config.toml is not valid TOML; skipping trust write for ${normalizedPath}`);
48
+ return;
49
+ }
50
+ }
51
+
52
+ const projects = parsed?.projects as Record<string, Record<string, unknown>> | undefined;
53
+ if (projects?.[normalizedPath]?.trust_level === "trusted") return;
54
+
55
+ // TOML basic-string keys require backslash and double-quote escaping.
56
+ const escapedPath = normalizedPath.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
57
+ const sectionHeader = `[projects."${escapedPath}"]`;
58
+ const trustLine = 'trust_level = "trusted"';
59
+
60
+ const headerIdx = content.indexOf(sectionHeader);
61
+ if (headerIdx !== -1) {
62
+ // Section exists but trust_level isn't "trusted". Scan all lines
63
+ // within the section (up to the next `[` header or EOF) for an
64
+ // existing trust_level key — it may not be the first line after
65
+ // the header if the user added comments or other keys.
66
+ const headerLineEnd = content.indexOf("\n", headerIdx);
67
+ if (headerLineEnd === -1) {
68
+ content = `${content}\n${trustLine}\n`;
69
+ } else {
70
+ const afterHeader = headerLineEnd + 1;
71
+ const sectionEnd = findSectionEnd(content, afterHeader);
72
+ const sectionBody = content.substring(afterHeader, sectionEnd);
73
+ const trustMatch = sectionBody.match(/^[ \t]*trust_level[ \t]*=.*$/m);
74
+ if (trustMatch) {
75
+ const matchStart = afterHeader + trustMatch.index!;
76
+ const matchEnd = matchStart + trustMatch[0].length;
77
+ content = content.substring(0, matchStart) + trustLine + content.substring(matchEnd);
78
+ } else {
79
+ content = content.substring(0, afterHeader) + trustLine + "\n" + content.substring(afterHeader);
80
+ }
81
+ }
82
+ fs.writeFileSync(configPath, content, "utf8");
83
+ } else if (projects?.[normalizedPath] !== undefined) {
84
+ // smol-toml found the section but indexOf missed it — the header
85
+ // uses non-canonical TOML formatting. Appending would create a
86
+ // duplicate table. Skip rather than corrupt the file.
87
+ log?.warn(`Codex config.toml has non-canonical header for ${normalizedPath}; skipping trust write`);
88
+ } else if (projects && !content.includes("[projects.")) {
89
+ // `projects` exists in parsed output but no `[projects.` table
90
+ // headers in the raw text — it's an inline table. Appending a
91
+ // standard table header would produce invalid TOML.
92
+ log?.warn(`Codex config.toml uses inline table for projects; skipping trust write for ${normalizedPath}`);
93
+ } else {
94
+ // Section doesn't exist — append.
95
+ fs.mkdirSync(codexHome, { recursive: true });
96
+ fs.appendFileSync(configPath, `\n${sectionHeader}\n${trustLine}\n`, "utf8");
97
+ }
98
+ } catch (err) {
99
+ log?.warn(`failed to write Codex project trust for ${normalizedPath}: ${String(err)}`);
100
+ }
101
+ }
102
+
103
+ function findSectionEnd(content: string, fromIndex: number): number {
104
+ // A TOML section ends at the next `[` that starts a new table header.
105
+ // Match `[` at the beginning of a line (not inside a value).
106
+ const nextHeader = content.indexOf("\n[", fromIndex);
107
+ return nextHeader === -1 ? content.length : nextHeader;
108
+ }
109
+
13
110
  export function ensureCodexWorkspace(
14
111
  workspaceDir: string,
15
112
  log?: { warn: (msg: string) => void },