agent-relay-codex 0.4.28 → 0.4.33
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/README.md +1 -0
- package/bin/agent-relay-codex.ts +72 -1
- package/hooks/session-start.ts +9 -0
- package/live-sidecar.ts +29 -9
- package/package.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
package/README.md
CHANGED
|
@@ -80,6 +80,7 @@ Run with `AGENT_RELAY_PROFILE=frontend-developer agent-relay-codex start`.
|
|
|
80
80
|
| `CODEX_THREAD_ID` | — | Pin to a specific thread |
|
|
81
81
|
| `CODEX_APP_SERVER_URL` | `ws://127.0.0.1:4501` | App-server WebSocket URL |
|
|
82
82
|
| `AGENT_RELAY_CODEX_HEADLESS` | — | Set to `1` to run without opening a TUI |
|
|
83
|
+
| `AGENT_RELAY_DISABLED` | — | Set to `1` to bypass Agent Relay hooks and launcher shims |
|
|
83
84
|
|
|
84
85
|
### Advanced tuning
|
|
85
86
|
|
package/bin/agent-relay-codex.ts
CHANGED
|
@@ -259,6 +259,75 @@ function findCodexBinary(): string {
|
|
|
259
259
|
return codex;
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
+
function isRelayDisabled(env: Record<string, string | undefined> = process.env): boolean {
|
|
263
|
+
return env.AGENT_RELAY_DISABLED === "1";
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const relayBypassSubcommands = new Set([
|
|
267
|
+
"a",
|
|
268
|
+
"app-server",
|
|
269
|
+
"apply",
|
|
270
|
+
"cloud",
|
|
271
|
+
"completion",
|
|
272
|
+
"debug",
|
|
273
|
+
"e",
|
|
274
|
+
"exec",
|
|
275
|
+
"exec-server",
|
|
276
|
+
"features",
|
|
277
|
+
"login",
|
|
278
|
+
"logout",
|
|
279
|
+
"mcp",
|
|
280
|
+
"mcp-server",
|
|
281
|
+
"plugin",
|
|
282
|
+
"remote-control",
|
|
283
|
+
"review",
|
|
284
|
+
"sandbox",
|
|
285
|
+
"update",
|
|
286
|
+
]);
|
|
287
|
+
|
|
288
|
+
function codexSubcommand(args: string[]): string {
|
|
289
|
+
const optionsWithValues = new Set([
|
|
290
|
+
"-c",
|
|
291
|
+
"--config",
|
|
292
|
+
"-m",
|
|
293
|
+
"--model",
|
|
294
|
+
"-p",
|
|
295
|
+
"--profile",
|
|
296
|
+
"-s",
|
|
297
|
+
"--sandbox",
|
|
298
|
+
"-C",
|
|
299
|
+
"--cd",
|
|
300
|
+
"--add-dir",
|
|
301
|
+
]);
|
|
302
|
+
|
|
303
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
304
|
+
const arg = args[index]!;
|
|
305
|
+
if (arg === "--") return "";
|
|
306
|
+
if (optionsWithValues.has(arg)) {
|
|
307
|
+
index += 1;
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
if (arg.startsWith("-")) continue;
|
|
311
|
+
return arg;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return "";
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export function shouldBypassRelay(args: string[], env: Record<string, string | undefined> = process.env): boolean {
|
|
318
|
+
return isRelayDisabled(env) || relayBypassSubcommands.has(codexSubcommand(args));
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async function runCodexPassthrough(args: string[]): Promise<void> {
|
|
322
|
+
const codex = Bun.spawn([findCodexBinary(), ...args], {
|
|
323
|
+
env: { ...process.env, AGENT_RELAY_DISABLED: "1" },
|
|
324
|
+
stdin: "inherit",
|
|
325
|
+
stdout: "inherit",
|
|
326
|
+
stderr: "inherit",
|
|
327
|
+
});
|
|
328
|
+
process.exit(await codex.exited);
|
|
329
|
+
}
|
|
330
|
+
|
|
262
331
|
function installMarketplace(quiet = false): void {
|
|
263
332
|
syncInstalledPackage();
|
|
264
333
|
|
|
@@ -1061,6 +1130,7 @@ function uninstall(args: string[] = []): void {
|
|
|
1061
1130
|
|
|
1062
1131
|
async function main(): Promise<void> {
|
|
1063
1132
|
const [command, ...args] = process.argv.slice(2);
|
|
1133
|
+
const codexArgs = command ? [command, ...args] : [];
|
|
1064
1134
|
if (command === "help" || command === "--help" || command === "-h") usage(0);
|
|
1065
1135
|
if (command === "install") return install(args);
|
|
1066
1136
|
if (command === "uninstall") return uninstall(args);
|
|
@@ -1072,7 +1142,8 @@ async function main(): Promise<void> {
|
|
|
1072
1142
|
if (command === "doctor") return doctor();
|
|
1073
1143
|
if (command === "upgrade") return upgrade(args);
|
|
1074
1144
|
if (command === "start") return start(args);
|
|
1075
|
-
|
|
1145
|
+
if (shouldBypassRelay(codexArgs)) return runCodexPassthrough(codexArgs);
|
|
1146
|
+
return start(codexArgs);
|
|
1076
1147
|
}
|
|
1077
1148
|
|
|
1078
1149
|
if (import.meta.main) {
|
package/hooks/session-start.ts
CHANGED
|
@@ -46,6 +46,11 @@ function outputContext(context: string): never {
|
|
|
46
46
|
process.exit(0);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
function outputContinue(): never {
|
|
50
|
+
console.log(JSON.stringify({ continue: true }));
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
|
|
49
54
|
function handshakePath(runtimeDir: string): string {
|
|
50
55
|
return join(runtimeDir, "session-start-handshake.json");
|
|
51
56
|
}
|
|
@@ -74,6 +79,10 @@ const project = cwd.split("/").filter(Boolean).at(-1) || "unknown";
|
|
|
74
79
|
const profile = loadAgentRelayProfile(process.env, { provider: "codex", rig, project });
|
|
75
80
|
const approvalMode = profile.approval ?? parseApprovalMode(process.env.AGENT_RELAY_APPROVAL);
|
|
76
81
|
|
|
82
|
+
if (process.env.AGENT_RELAY_DISABLED === "1") {
|
|
83
|
+
outputContinue();
|
|
84
|
+
}
|
|
85
|
+
|
|
77
86
|
if (process.env.AGENT_RELAY_CODEX_MANAGED === "1") {
|
|
78
87
|
outputContext("Agent Relay is managed by codex-relay for this session; the launcher attached Relay and TUI to the same Codex App Server thread.");
|
|
79
88
|
process.exit(0);
|
package/live-sidecar.ts
CHANGED
|
@@ -85,6 +85,7 @@ class CodexLiveSidecar {
|
|
|
85
85
|
private draining = false;
|
|
86
86
|
private drainDueAt = 0;
|
|
87
87
|
private reconnecting: Promise<void> | null = null;
|
|
88
|
+
private relayPrimerDelivered = false;
|
|
88
89
|
private readonly pendingMessages = new Map<number, RelayMessage>();
|
|
89
90
|
private readonly activeClaimedMessageIds = new Set<number>();
|
|
90
91
|
|
|
@@ -529,7 +530,7 @@ class CodexLiveSidecar {
|
|
|
529
530
|
}
|
|
530
531
|
|
|
531
532
|
const delivery = this.pickDeliveryMode(batch.messages);
|
|
532
|
-
const prompt = formatRelayPrompt(batch.messages);
|
|
533
|
+
const prompt = formatRelayPrompt(batch.messages, { includePrimer: !this.relayPrimerDelivered });
|
|
533
534
|
const ids = batch.messages.map((message) => message.id).join(", ");
|
|
534
535
|
this.log(`delivering message ${ids} via ${delivery}`);
|
|
535
536
|
|
|
@@ -548,6 +549,7 @@ class CodexLiveSidecar {
|
|
|
548
549
|
try {
|
|
549
550
|
await this.app.turnSteer(this.threadId, this.activeTurnId, prompt);
|
|
550
551
|
for (const id of claimedMessageIds) this.activeClaimedMessageIds.add(id);
|
|
552
|
+
this.relayPrimerDelivered = true;
|
|
551
553
|
await this.markBatchRead(batch.messages);
|
|
552
554
|
this.advanceCursor(batch.messages);
|
|
553
555
|
this.writeState();
|
|
@@ -559,6 +561,7 @@ class CodexLiveSidecar {
|
|
|
559
561
|
|
|
560
562
|
await this.app.turnStart(this.threadId, prompt);
|
|
561
563
|
for (const id of claimedMessageIds) this.activeClaimedMessageIds.add(id);
|
|
564
|
+
this.relayPrimerDelivered = true;
|
|
562
565
|
await this.markBatchRead(batch.messages);
|
|
563
566
|
this.advanceCursor(batch.messages);
|
|
564
567
|
this.writeState();
|
|
@@ -654,10 +657,25 @@ function shouldIsolateMessage(message: RelayMessage): boolean {
|
|
|
654
657
|
return delivery === "interrupt" || delivery === "start" || delivery === "steer" || priority === "urgent";
|
|
655
658
|
}
|
|
656
659
|
|
|
657
|
-
export function formatRelayPrompt(
|
|
660
|
+
export function formatRelayPrompt(
|
|
661
|
+
messages: RelayMessage[],
|
|
662
|
+
opts: { includePrimer?: boolean } = {},
|
|
663
|
+
): string {
|
|
664
|
+
const includePrimer = opts.includePrimer === true;
|
|
665
|
+
const relayUrl = process.env.AGENT_RELAY_URL || "http://127.0.0.1:4850";
|
|
666
|
+
const primer = [
|
|
667
|
+
"Agent Relay live-message primer:",
|
|
668
|
+
"Treat Agent Relay deliveries as live incoming messages from other agents. Respond or act on them as appropriate.",
|
|
669
|
+
"When calling the relay API, include AGENT_RELAY_TOKEN as the X-Agent-Relay-Token header if that environment variable is set.",
|
|
670
|
+
`For normal replies, POST JSON to ${relayUrl}/api/messages with from set to your Agent Relay ID, to set to the sender Agent Relay ID, body set to your response, and replyTo set to the incoming message id.`,
|
|
671
|
+
`For pair chat replies, POST JSON to ${relayUrl}/api/pairs/{pairId}/messages with from set to your Agent Relay ID and body set to your response.`,
|
|
672
|
+
"",
|
|
673
|
+
];
|
|
674
|
+
|
|
658
675
|
if (messages.length === 1) {
|
|
659
676
|
const message = messages[0]!;
|
|
660
677
|
const lines = [
|
|
678
|
+
...(includePrimer ? primer : []),
|
|
661
679
|
"Agent Relay message received.",
|
|
662
680
|
"",
|
|
663
681
|
formatMessageSummary(message),
|
|
@@ -678,16 +696,15 @@ export function formatRelayPrompt(messages: RelayMessage[]): string {
|
|
|
678
696
|
"Body:",
|
|
679
697
|
message.body,
|
|
680
698
|
"",
|
|
681
|
-
"Treat this as a live incoming message from another agent. Respond or act on it as appropriate.",
|
|
682
|
-
"If AGENT_RELAY_TOKEN is set, include it as the X-Agent-Relay-Token header when calling the relay API.",
|
|
683
699
|
typeof message.meta?.pairId === "string"
|
|
684
|
-
? `
|
|
685
|
-
: `
|
|
700
|
+
? `Reply routing: pairId=${JSON.stringify(message.meta.pairId)}.`
|
|
701
|
+
: `Reply routing: to=${JSON.stringify(message.from)}, replyTo=${message.id}.`,
|
|
686
702
|
);
|
|
687
703
|
return lines.join("\n");
|
|
688
704
|
}
|
|
689
705
|
|
|
690
706
|
const lines = [
|
|
707
|
+
...(includePrimer ? primer : []),
|
|
691
708
|
"Agent Relay message batch received.",
|
|
692
709
|
"",
|
|
693
710
|
`Count: ${messages.length}`,
|
|
@@ -705,13 +722,16 @@ export function formatRelayPrompt(messages: RelayMessage[]): string {
|
|
|
705
722
|
if (message.subject) lines.push(`Subject: ${message.subject}`);
|
|
706
723
|
if (message.replyTo) lines.push(`Reply To: ${message.replyTo}`);
|
|
707
724
|
if (typeof message.meta?.pairId === "string") lines.push(`Pair ID: ${message.meta.pairId}`);
|
|
725
|
+
lines.push(
|
|
726
|
+
typeof message.meta?.pairId === "string"
|
|
727
|
+
? `Reply routing: pairId=${JSON.stringify(message.meta.pairId)}.`
|
|
728
|
+
: `Reply routing: to=${JSON.stringify(message.from)}, replyTo=${message.id}.`,
|
|
729
|
+
);
|
|
708
730
|
lines.push("Body:", message.body, "", "---", "");
|
|
709
731
|
}
|
|
710
732
|
|
|
711
733
|
lines.push(
|
|
712
|
-
"
|
|
713
|
-
"If AGENT_RELAY_TOKEN is set, include it as the X-Agent-Relay-Token header when calling the relay API.",
|
|
714
|
-
`To reply, POST JSON to ${process.env.AGENT_RELAY_URL || "http://127.0.0.1:4850"}/api/messages with from set to your Agent Relay ID and replyTo set to the message you are answering.`,
|
|
734
|
+
"Synthesize these messages into one coherent response or action. Use each message's Reply routing line when replying.",
|
|
715
735
|
);
|
|
716
736
|
return lines.join("\n");
|
|
717
737
|
}
|
package/package.json
CHANGED