@parall/codex-agent 1.24.0 → 1.26.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 +8 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -1
- package/dist/dispatch.d.ts +5 -0
- package/dist/dispatch.d.ts.map +1 -1
- package/dist/dispatch.js +10 -9
- package/dist/index.js +34 -4
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +11 -22
- package/package.json +4 -4
- package/src/config.ts +13 -8
- package/src/dispatch.ts +11 -10
- package/src/index.ts +33 -3
- package/src/workspace.ts +16 -23
package/dist/config.d.ts
CHANGED
|
@@ -11,14 +11,14 @@ export type CodexAgentConfig = {
|
|
|
11
11
|
model?: string;
|
|
12
12
|
reasoningEffort?: string;
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
14
|
+
* Sandbox mode passed to codex on `thread/{start,resume}`. Defaults to
|
|
15
|
+
* `danger-full-access` — same as the hosted container — because codex's
|
|
16
|
+
* inner bubblewrap sandbox requires unprivileged user namespaces that
|
|
17
|
+
* most environments lack, and the agent needs unrestricted shell access
|
|
18
|
+
* (e.g. `parall messages send`). Users can still override via
|
|
19
|
+
* `PRLL_CODEX_SANDBOX` env var.
|
|
20
20
|
*/
|
|
21
|
-
sandbox
|
|
21
|
+
sandbox: string;
|
|
22
22
|
/**
|
|
23
23
|
* Approval policy is forced to `"never"` by default (not configurable
|
|
24
24
|
* via config.toml override): Parall's HITL contract routes approvals
|
|
@@ -51,5 +51,6 @@ export declare function normalizeApprovalPolicy(value: string): string;
|
|
|
51
51
|
export declare function resolveWsUrl(apiUrl: string, explicitWsUrl?: string, swimlaneName?: string): string;
|
|
52
52
|
export declare function buildCodexRuntimeKey(agentUserId: string): string;
|
|
53
53
|
export declare function sessionStateFilePathForRuntime(stateDir: string, runtimeKey: string): string;
|
|
54
|
+
export declare function contextFilePathForSession(stateDir: string, sessionKey: string): string;
|
|
54
55
|
export declare function stepIdFilePathForSession(stateDir: string, sessionKey: string): string;
|
|
55
56
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -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,
|
|
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,CAwC9F;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,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAMlG;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"}
|
package/dist/config.js
CHANGED
|
@@ -41,7 +41,7 @@ export function resolveCodexAgentConfig(env = process.env) {
|
|
|
41
41
|
workspaceDir,
|
|
42
42
|
model: env.PRLL_CODEX_MODEL?.trim() || undefined,
|
|
43
43
|
reasoningEffort: env.PRLL_CODEX_REASONING_EFFORT?.trim() || undefined,
|
|
44
|
-
sandbox: env.PRLL_CODEX_SANDBOX?.trim() ||
|
|
44
|
+
sandbox: env.PRLL_CODEX_SANDBOX?.trim() || "danger-full-access",
|
|
45
45
|
approvalPolicy: env.PRLL_CODEX_APPROVAL?.trim() || "never",
|
|
46
46
|
runtimeKey: env.PRLL_CODEX_RUNTIME_KEY?.trim() || undefined,
|
|
47
47
|
};
|
|
@@ -101,6 +101,10 @@ export function sessionStateFilePathForRuntime(stateDir, runtimeKey) {
|
|
|
101
101
|
const fileName = Buffer.from(runtimeKey).toString("base64url");
|
|
102
102
|
return path.join(stateDir, "threads", `${fileName}.json`);
|
|
103
103
|
}
|
|
104
|
+
export function contextFilePathForSession(stateDir, sessionKey) {
|
|
105
|
+
const fileName = Buffer.from(sessionKey).toString("base64url");
|
|
106
|
+
return path.join(stateDir, "dispatch-context", `${fileName}.json`);
|
|
107
|
+
}
|
|
104
108
|
export function stepIdFilePathForSession(stateDir, sessionKey) {
|
|
105
109
|
const fileName = Buffer.from(sessionKey).toString("base64url");
|
|
106
110
|
return path.join(stateDir, "step-ids", `${fileName}.txt`);
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { CodexSessionManager } from "./session-manager.js";
|
|
|
4
4
|
type CodexAppServerAdapterOptions = Pick<CodexAgentConfig, "approvalPolicy" | "codexBin" | "codexHome" | "model" | "reasoningEffort" | "sandbox" | "workspaceDir"> & {
|
|
5
5
|
sessionManager: CodexSessionManager;
|
|
6
6
|
log?: GatewayLogger;
|
|
7
|
+
contextFilePath?: string;
|
|
7
8
|
};
|
|
8
9
|
/**
|
|
9
10
|
* Bridge driver backed by `codex app-server --listen stdio://`.
|
|
@@ -38,6 +39,10 @@ export declare class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
38
39
|
*/
|
|
39
40
|
private setActiveTurn;
|
|
40
41
|
constructor(opts: CodexAppServerAdapterOptions);
|
|
42
|
+
updateConfig(config: {
|
|
43
|
+
model?: string | null;
|
|
44
|
+
reasoningEffort?: string | null;
|
|
45
|
+
}): void;
|
|
41
46
|
dispatch({ event, bodyForAgent, sessionKey, context }: DispatchOpts): AsyncIterable<RuntimeEvent>;
|
|
42
47
|
forkSession({ sessionKey: parentSessionKey }: ForkOpts): Promise<ForkSessionHandle | null>;
|
|
43
48
|
cleanupFork({ fork }: CleanupForkOpts): void;
|
package/dist/dispatch.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/dispatch.js
CHANGED
|
@@ -49,6 +49,12 @@ export class CodexAppServerAdapter {
|
|
|
49
49
|
constructor(opts) {
|
|
50
50
|
this.opts = opts;
|
|
51
51
|
}
|
|
52
|
+
updateConfig(config) {
|
|
53
|
+
if (config.model !== undefined)
|
|
54
|
+
this.opts.model = config.model ?? undefined;
|
|
55
|
+
if (config.reasoningEffort !== undefined)
|
|
56
|
+
this.opts.reasoningEffort = config.reasoningEffort ?? undefined;
|
|
57
|
+
}
|
|
52
58
|
async *dispatch({ event, bodyForAgent, sessionKey, context }) {
|
|
53
59
|
await this.ensureStarted(context.log);
|
|
54
60
|
// After ensureStarted resolves the subprocess could still die before we
|
|
@@ -304,6 +310,9 @@ export class CodexAppServerAdapter {
|
|
|
304
310
|
FORCE_COLOR: "0",
|
|
305
311
|
NO_COLOR: "1",
|
|
306
312
|
};
|
|
313
|
+
if (this.opts.contextFilePath) {
|
|
314
|
+
env.PRLL_CONTEXT_FILE = this.opts.contextFilePath;
|
|
315
|
+
}
|
|
307
316
|
const args = ["app-server", "--listen", "stdio://"];
|
|
308
317
|
(log ?? this.opts.log)?.info?.(`codex-agent: spawning ${this.opts.codexBin} ${args.join(" ")}`);
|
|
309
318
|
const proc = spawn(this.opts.codexBin, args, {
|
|
@@ -382,15 +391,7 @@ export class CodexAppServerAdapter {
|
|
|
382
391
|
const commonParams = {
|
|
383
392
|
approvalPolicy: normalizeApprovalPolicy(this.opts.approvalPolicy),
|
|
384
393
|
};
|
|
385
|
-
|
|
386
|
-
// Otherwise let codex read `sandbox_mode` from ~/.codex/config.toml — the
|
|
387
|
-
// user's own configuration is authoritative for a self-hosted bridge,
|
|
388
|
-
// and codex 0.120.0's workspace-write mode has a write_stdin bug that
|
|
389
|
-
// our previous `workspace-write` default tripped for users whose config
|
|
390
|
-
// had danger-full-access.
|
|
391
|
-
if (this.opts.sandbox) {
|
|
392
|
-
commonParams.sandbox = normalizeSandbox(this.opts.sandbox);
|
|
393
|
-
}
|
|
394
|
+
commonParams.sandbox = normalizeSandbox(this.opts.sandbox);
|
|
394
395
|
if (this.opts.model)
|
|
395
396
|
commonParams.model = this.opts.model;
|
|
396
397
|
if (this.opts.reasoningEffort) {
|
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, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
3
|
+
import { ParallAgentGateway, createPlatformConfigManager, isPlatformManagedProfile, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
4
4
|
import { ApiError, ParallClient, ParallWs } from "@parall/sdk";
|
|
5
|
-
import { buildCodexRuntimeKey, resolveCodexAgentConfig, resolveWsUrl, sessionStateFilePathForRuntime, stepIdFilePathForSession, } from "./config.js";
|
|
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 } from "./workspace.js";
|
|
@@ -41,6 +41,7 @@ async function main() {
|
|
|
41
41
|
description: me.agent_profile?.description ?? undefined,
|
|
42
42
|
});
|
|
43
43
|
const runtimeKey = config.runtimeKey || buildCodexRuntimeKey(agentUserId);
|
|
44
|
+
const mainContextFilePath = contextFilePathForSession(config.stateDir, runtimeKey);
|
|
44
45
|
const sessionStateFilePath = sessionStateFilePathForRuntime(config.stateDir, runtimeKey);
|
|
45
46
|
const sessionManager = new CodexSessionManager(runtimeKey, sessionStateFilePath, log);
|
|
46
47
|
const resolvedWsUrl = resolveWsUrl(config.apiUrl, config.wsUrl, config.swimlaneName);
|
|
@@ -48,16 +49,26 @@ async function main() {
|
|
|
48
49
|
getTicket: () => client.getWsTicket(),
|
|
49
50
|
wsUrl: resolvedWsUrl,
|
|
50
51
|
});
|
|
52
|
+
const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "codex", log });
|
|
53
|
+
const platformDefaults = await configMgr.fetch();
|
|
54
|
+
let platformManaged = isPlatformManagedProfile(me.agent_profile);
|
|
55
|
+
const resolvedModel = platformManaged ? (platformDefaults.model ?? config.model) : config.model;
|
|
56
|
+
const resolvedEffort = platformManaged ? (platformDefaults.thinkingEffort ?? config.reasoningEffort) : config.reasoningEffort;
|
|
57
|
+
if (platformDefaults.model)
|
|
58
|
+
log.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
|
|
59
|
+
if (platformDefaults.thinkingEffort)
|
|
60
|
+
log.info(`platform config: reasoning_effort=${platformDefaults.thinkingEffort} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
|
|
51
61
|
const adapter = new CodexAppServerAdapter({
|
|
52
62
|
codexBin: config.codexBin,
|
|
53
63
|
codexHome: config.codexHome,
|
|
54
64
|
workspaceDir: config.workspaceDir,
|
|
55
|
-
model:
|
|
56
|
-
reasoningEffort:
|
|
65
|
+
model: resolvedModel,
|
|
66
|
+
reasoningEffort: resolvedEffort,
|
|
57
67
|
sandbox: config.sandbox,
|
|
58
68
|
approvalPolicy: config.approvalPolicy,
|
|
59
69
|
sessionManager,
|
|
60
70
|
log,
|
|
71
|
+
contextFilePath: mainContextFilePath,
|
|
61
72
|
});
|
|
62
73
|
const gateway = new ParallAgentGateway({
|
|
63
74
|
accountId: agentUserId,
|
|
@@ -82,7 +93,26 @@ async function main() {
|
|
|
82
93
|
dispatchAdapter: adapter,
|
|
83
94
|
log,
|
|
84
95
|
shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
|
|
96
|
+
contextFilePathForSession: (sessionKey) => contextFilePathForSession(config.stateDir, sessionKey),
|
|
85
97
|
stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
|
|
98
|
+
onConfigUpdate: async () => {
|
|
99
|
+
const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
|
|
100
|
+
platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
|
|
101
|
+
const updated = await configMgr.fetch();
|
|
102
|
+
adapter.updateConfig({
|
|
103
|
+
model: platformManaged ? (updated.model ?? config.model) : (config.model ?? null),
|
|
104
|
+
reasoningEffort: platformManaged ? (updated.thinkingEffort ?? config.reasoningEffort) : (config.reasoningEffort ?? null),
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
onSessionReady: async () => {
|
|
108
|
+
const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
|
|
109
|
+
platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
|
|
110
|
+
const updated = await configMgr.fetch();
|
|
111
|
+
adapter.updateConfig({
|
|
112
|
+
model: platformManaged ? (updated.model ?? config.model) : (config.model ?? null),
|
|
113
|
+
reasoningEffort: platformManaged ? (updated.thinkingEffort ?? config.reasoningEffort) : (config.reasoningEffort ?? null),
|
|
114
|
+
});
|
|
115
|
+
},
|
|
86
116
|
});
|
|
87
117
|
const abortController = new AbortController();
|
|
88
118
|
const abort = () => abortController.abort();
|
package/dist/workspace.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/workspace.js
CHANGED
|
@@ -1,32 +1,21 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import { BRIDGE_WORKSPACE_INSTRUCTIONS, PRLL_BEHAVIOR, PRLL_REFERENCE_GUIDE, buildIdentity, } from "@parall/agent-core";
|
|
3
|
+
import { BRIDGE_WORKSPACE_INSTRUCTIONS, PRLL_BEHAVIOR, PRLL_REFERENCE_GUIDE, buildIdentity, buildSkillReferences, writeSkillFiles, } from "@parall/agent-core";
|
|
4
4
|
export function ensureCodexWorkspace(workspaceDir, log, agentIdentity) {
|
|
5
|
-
const
|
|
5
|
+
const systemPrompt = [
|
|
6
6
|
buildIdentity(agentIdentity),
|
|
7
7
|
BRIDGE_WORKSPACE_INSTRUCTIONS,
|
|
8
8
|
PRLL_BEHAVIOR,
|
|
9
9
|
PRLL_REFERENCE_GUIDE,
|
|
10
|
+
buildSkillReferences(workspaceDir),
|
|
10
11
|
].join("\n\n");
|
|
11
12
|
fs.mkdirSync(workspaceDir, { recursive: true });
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
const existing = fs.readFileSync(agentsMdPath, "utf8");
|
|
22
|
-
if (existing !== AGENTS_MD) {
|
|
23
|
-
log.warn(`codex-agent: overwriting divergent ${agentsMdPath} with bridge-managed template ` +
|
|
24
|
-
`(local edits to AGENTS.md are not preserved — customize other files in the workspace instead)`);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
// file missing or unreadable — first-boot case, no warning needed
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
fs.writeFileSync(agentsMdPath, AGENTS_MD, "utf8");
|
|
13
|
+
const parallDir = path.join(workspaceDir, ".parall");
|
|
14
|
+
fs.mkdirSync(parallDir, { recursive: true });
|
|
15
|
+
fs.writeFileSync(path.join(parallDir, "system-prompt.md"), systemPrompt, "utf8");
|
|
16
|
+
writeSkillFiles(path.join(parallDir, "skills"));
|
|
17
|
+
const codexConfigDir = path.join(workspaceDir, ".codex");
|
|
18
|
+
fs.mkdirSync(codexConfigDir, { recursive: true });
|
|
19
|
+
const toml = `developer_instructions = """\n${systemPrompt.replace(/\\/g, "\\\\").replace(/"""/g, '\\"""')}\n"""\n`;
|
|
20
|
+
fs.writeFileSync(path.join(codexConfigDir, "config.toml"), toml, "utf8");
|
|
32
21
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/codex-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.26.0",
|
|
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.
|
|
29
|
-
"@parall/cli": "1.
|
|
30
|
-
"@parall/sdk": "1.
|
|
28
|
+
"@parall/agent-core": "1.26.0",
|
|
29
|
+
"@parall/cli": "1.26.0",
|
|
30
|
+
"@parall/sdk": "1.26.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^22.0.0",
|
package/src/config.ts
CHANGED
|
@@ -14,14 +14,14 @@ export type CodexAgentConfig = {
|
|
|
14
14
|
model?: string;
|
|
15
15
|
reasoningEffort?: string;
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
17
|
+
* Sandbox mode passed to codex on `thread/{start,resume}`. Defaults to
|
|
18
|
+
* `danger-full-access` — same as the hosted container — because codex's
|
|
19
|
+
* inner bubblewrap sandbox requires unprivileged user namespaces that
|
|
20
|
+
* most environments lack, and the agent needs unrestricted shell access
|
|
21
|
+
* (e.g. `parall messages send`). Users can still override via
|
|
22
|
+
* `PRLL_CODEX_SANDBOX` env var.
|
|
23
23
|
*/
|
|
24
|
-
sandbox
|
|
24
|
+
sandbox: string;
|
|
25
25
|
/**
|
|
26
26
|
* Approval policy is forced to `"never"` by default (not configurable
|
|
27
27
|
* via config.toml override): Parall's HITL contract routes approvals
|
|
@@ -80,7 +80,7 @@ export function resolveCodexAgentConfig(env: NodeJS.ProcessEnv = process.env): C
|
|
|
80
80
|
workspaceDir,
|
|
81
81
|
model: env.PRLL_CODEX_MODEL?.trim() || undefined,
|
|
82
82
|
reasoningEffort: env.PRLL_CODEX_REASONING_EFFORT?.trim() || undefined,
|
|
83
|
-
sandbox: env.PRLL_CODEX_SANDBOX?.trim() ||
|
|
83
|
+
sandbox: env.PRLL_CODEX_SANDBOX?.trim() || "danger-full-access",
|
|
84
84
|
approvalPolicy: env.PRLL_CODEX_APPROVAL?.trim() || "never",
|
|
85
85
|
runtimeKey: env.PRLL_CODEX_RUNTIME_KEY?.trim() || undefined,
|
|
86
86
|
};
|
|
@@ -145,6 +145,11 @@ export function sessionStateFilePathForRuntime(stateDir: string, runtimeKey: str
|
|
|
145
145
|
return path.join(stateDir, "threads", `${fileName}.json`);
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
export function contextFilePathForSession(stateDir: string, sessionKey: string): string {
|
|
149
|
+
const fileName = Buffer.from(sessionKey).toString("base64url");
|
|
150
|
+
return path.join(stateDir, "dispatch-context", `${fileName}.json`);
|
|
151
|
+
}
|
|
152
|
+
|
|
148
153
|
export function stepIdFilePathForSession(stateDir: string, sessionKey: string): string {
|
|
149
154
|
const fileName = Buffer.from(sessionKey).toString("base64url");
|
|
150
155
|
return path.join(stateDir, "step-ids", `${fileName}.txt`);
|
package/src/dispatch.ts
CHANGED
|
@@ -35,6 +35,7 @@ type CodexAppServerAdapterOptions = Pick<
|
|
|
35
35
|
> & {
|
|
36
36
|
sessionManager: CodexSessionManager;
|
|
37
37
|
log?: GatewayLogger;
|
|
38
|
+
contextFilePath?: string;
|
|
38
39
|
};
|
|
39
40
|
|
|
40
41
|
type TurnEventEnvelope =
|
|
@@ -87,6 +88,11 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
87
88
|
|
|
88
89
|
constructor(private readonly opts: CodexAppServerAdapterOptions) {}
|
|
89
90
|
|
|
91
|
+
updateConfig(config: { model?: string | null; reasoningEffort?: string | null }): void {
|
|
92
|
+
if (config.model !== undefined) this.opts.model = config.model ?? undefined;
|
|
93
|
+
if (config.reasoningEffort !== undefined) this.opts.reasoningEffort = config.reasoningEffort ?? undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
90
96
|
async *dispatch({ event, bodyForAgent, sessionKey, context }: DispatchOpts): AsyncIterable<RuntimeEvent> {
|
|
91
97
|
await this.ensureStarted(context.log);
|
|
92
98
|
// After ensureStarted resolves the subprocess could still die before we
|
|
@@ -334,12 +340,15 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
334
340
|
// (git/ssh/npm/etc.). Earlier versions also rewrote HOME, which broke
|
|
335
341
|
// tooling for users who set a custom CODEX_HOME pointing somewhere
|
|
336
342
|
// other than their shell home.
|
|
337
|
-
const env = {
|
|
343
|
+
const env: Record<string, string | undefined> = {
|
|
338
344
|
...process.env,
|
|
339
345
|
CODEX_HOME: this.opts.codexHome,
|
|
340
346
|
FORCE_COLOR: "0",
|
|
341
347
|
NO_COLOR: "1",
|
|
342
348
|
};
|
|
349
|
+
if (this.opts.contextFilePath) {
|
|
350
|
+
env.PRLL_CONTEXT_FILE = this.opts.contextFilePath;
|
|
351
|
+
}
|
|
343
352
|
const args = ["app-server", "--listen", "stdio://"];
|
|
344
353
|
(log ?? this.opts.log)?.info?.(`codex-agent: spawning ${this.opts.codexBin} ${args.join(" ")}`);
|
|
345
354
|
const proc = spawn(this.opts.codexBin, args, {
|
|
@@ -430,15 +439,7 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
430
439
|
const commonParams: Record<string, unknown> = {
|
|
431
440
|
approvalPolicy: normalizeApprovalPolicy(this.opts.approvalPolicy),
|
|
432
441
|
};
|
|
433
|
-
|
|
434
|
-
// Otherwise let codex read `sandbox_mode` from ~/.codex/config.toml — the
|
|
435
|
-
// user's own configuration is authoritative for a self-hosted bridge,
|
|
436
|
-
// and codex 0.120.0's workspace-write mode has a write_stdin bug that
|
|
437
|
-
// our previous `workspace-write` default tripped for users whose config
|
|
438
|
-
// had danger-full-access.
|
|
439
|
-
if (this.opts.sandbox) {
|
|
440
|
-
commonParams.sandbox = normalizeSandbox(this.opts.sandbox);
|
|
441
|
-
}
|
|
442
|
+
commonParams.sandbox = normalizeSandbox(this.opts.sandbox);
|
|
442
443
|
if (this.opts.model) commonParams.model = this.opts.model;
|
|
443
444
|
if (this.opts.reasoningEffort) {
|
|
444
445
|
// The app-server JSON-RPC surface uses camelCase for overrides —
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import * as os from "node:os";
|
|
4
|
-
import { ParallAgentGateway, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
4
|
+
import { ParallAgentGateway, createPlatformConfigManager, isPlatformManagedProfile, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
5
5
|
import { ApiError, ParallClient, ParallWs } from "@parall/sdk";
|
|
6
6
|
import {
|
|
7
7
|
buildCodexRuntimeKey,
|
|
8
|
+
contextFilePathForSession,
|
|
8
9
|
resolveCodexAgentConfig,
|
|
9
10
|
resolveWsUrl,
|
|
10
11
|
sessionStateFilePathForRuntime,
|
|
@@ -52,6 +53,7 @@ async function main() {
|
|
|
52
53
|
description: me.agent_profile?.description ?? undefined,
|
|
53
54
|
});
|
|
54
55
|
const runtimeKey = config.runtimeKey || buildCodexRuntimeKey(agentUserId);
|
|
56
|
+
const mainContextFilePath = contextFilePathForSession(config.stateDir, runtimeKey);
|
|
55
57
|
const sessionStateFilePath = sessionStateFilePathForRuntime(config.stateDir, runtimeKey);
|
|
56
58
|
const sessionManager = new CodexSessionManager(
|
|
57
59
|
runtimeKey,
|
|
@@ -65,16 +67,25 @@ async function main() {
|
|
|
65
67
|
wsUrl: resolvedWsUrl,
|
|
66
68
|
});
|
|
67
69
|
|
|
70
|
+
const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "codex", log });
|
|
71
|
+
const platformDefaults = await configMgr.fetch();
|
|
72
|
+
let platformManaged = isPlatformManagedProfile(me.agent_profile);
|
|
73
|
+
const resolvedModel = platformManaged ? (platformDefaults.model ?? config.model) : config.model;
|
|
74
|
+
const resolvedEffort = platformManaged ? (platformDefaults.thinkingEffort ?? config.reasoningEffort) : config.reasoningEffort;
|
|
75
|
+
if (platformDefaults.model) log.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
|
|
76
|
+
if (platformDefaults.thinkingEffort) log.info(`platform config: reasoning_effort=${platformDefaults.thinkingEffort} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
|
|
77
|
+
|
|
68
78
|
const adapter = new CodexAppServerAdapter({
|
|
69
79
|
codexBin: config.codexBin,
|
|
70
80
|
codexHome: config.codexHome,
|
|
71
81
|
workspaceDir: config.workspaceDir,
|
|
72
|
-
model:
|
|
73
|
-
reasoningEffort:
|
|
82
|
+
model: resolvedModel,
|
|
83
|
+
reasoningEffort: resolvedEffort,
|
|
74
84
|
sandbox: config.sandbox,
|
|
75
85
|
approvalPolicy: config.approvalPolicy,
|
|
76
86
|
sessionManager,
|
|
77
87
|
log,
|
|
88
|
+
contextFilePath: mainContextFilePath,
|
|
78
89
|
});
|
|
79
90
|
|
|
80
91
|
const gateway = new ParallAgentGateway({
|
|
@@ -100,7 +111,26 @@ async function main() {
|
|
|
100
111
|
dispatchAdapter: adapter,
|
|
101
112
|
log,
|
|
102
113
|
shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
|
|
114
|
+
contextFilePathForSession: (sessionKey) => contextFilePathForSession(config.stateDir, sessionKey),
|
|
103
115
|
stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
|
|
116
|
+
onConfigUpdate: async () => {
|
|
117
|
+
const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
|
|
118
|
+
platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
|
|
119
|
+
const updated = await configMgr.fetch();
|
|
120
|
+
adapter.updateConfig({
|
|
121
|
+
model: platformManaged ? (updated.model ?? config.model) : (config.model ?? null),
|
|
122
|
+
reasoningEffort: platformManaged ? (updated.thinkingEffort ?? config.reasoningEffort) : (config.reasoningEffort ?? null),
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
onSessionReady: async () => {
|
|
126
|
+
const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
|
|
127
|
+
platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
|
|
128
|
+
const updated = await configMgr.fetch();
|
|
129
|
+
adapter.updateConfig({
|
|
130
|
+
model: platformManaged ? (updated.model ?? config.model) : (config.model ?? null),
|
|
131
|
+
reasoningEffort: platformManaged ? (updated.thinkingEffort ?? config.reasoningEffort) : (config.reasoningEffort ?? null),
|
|
132
|
+
});
|
|
133
|
+
},
|
|
104
134
|
});
|
|
105
135
|
|
|
106
136
|
const abortController = new AbortController();
|
package/src/workspace.ts
CHANGED
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
PRLL_BEHAVIOR,
|
|
6
6
|
PRLL_REFERENCE_GUIDE,
|
|
7
7
|
buildIdentity,
|
|
8
|
+
buildSkillReferences,
|
|
9
|
+
writeSkillFiles,
|
|
8
10
|
} from "@parall/agent-core";
|
|
9
11
|
import type { AgentIdentity } from "@parall/agent-core";
|
|
10
12
|
|
|
@@ -12,34 +14,25 @@ export function ensureCodexWorkspace(
|
|
|
12
14
|
workspaceDir: string,
|
|
13
15
|
log?: { warn: (msg: string) => void },
|
|
14
16
|
agentIdentity?: AgentIdentity,
|
|
15
|
-
) {
|
|
16
|
-
const
|
|
17
|
+
): void {
|
|
18
|
+
const systemPrompt = [
|
|
17
19
|
buildIdentity(agentIdentity),
|
|
18
20
|
BRIDGE_WORKSPACE_INSTRUCTIONS,
|
|
19
21
|
PRLL_BEHAVIOR,
|
|
20
22
|
PRLL_REFERENCE_GUIDE,
|
|
23
|
+
buildSkillReferences(workspaceDir),
|
|
21
24
|
].join("\n\n");
|
|
25
|
+
|
|
22
26
|
fs.mkdirSync(workspaceDir, { recursive: true });
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (existing !== AGENTS_MD) {
|
|
35
|
-
log.warn(
|
|
36
|
-
`codex-agent: overwriting divergent ${agentsMdPath} with bridge-managed template ` +
|
|
37
|
-
`(local edits to AGENTS.md are not preserved — customize other files in the workspace instead)`,
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
} catch {
|
|
41
|
-
// file missing or unreadable — first-boot case, no warning needed
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
fs.writeFileSync(agentsMdPath, AGENTS_MD, "utf8");
|
|
28
|
+
const parallDir = path.join(workspaceDir, ".parall");
|
|
29
|
+
fs.mkdirSync(parallDir, { recursive: true });
|
|
30
|
+
fs.writeFileSync(path.join(parallDir, "system-prompt.md"), systemPrompt, "utf8");
|
|
31
|
+
|
|
32
|
+
writeSkillFiles(path.join(parallDir, "skills"));
|
|
33
|
+
|
|
34
|
+
const codexConfigDir = path.join(workspaceDir, ".codex");
|
|
35
|
+
fs.mkdirSync(codexConfigDir, { recursive: true });
|
|
36
|
+
const toml = `developer_instructions = """\n${systemPrompt.replace(/\\/g, "\\\\").replace(/"""/g, '\\"""')}\n"""\n`;
|
|
37
|
+
fs.writeFileSync(path.join(codexConfigDir, "config.toml"), toml, "utf8");
|
|
45
38
|
}
|