agent-relay-codex 0.4.39 → 0.6.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/bin/agent-relay-codex.ts +1 -0
- package/hooks/session-start.ts +6 -64
- package/live-sidecar.ts +34 -14
- package/package.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/relay.ts +2 -1
package/bin/agent-relay-codex.ts
CHANGED
|
@@ -952,6 +952,7 @@ async function start(args: string[]): Promise<void> {
|
|
|
952
952
|
AGENT_RELAY_CODEX_PACKAGE_ROOT: activePackageRoot(),
|
|
953
953
|
AGENT_RELAY_CODEX_RUN_ID: runId,
|
|
954
954
|
AGENT_RELAY_CODEX_RUNTIME_DIR: runDir,
|
|
955
|
+
AGENT_RELAY_CONTEXT_PATH: join(runDir, "agent-context.json"),
|
|
955
956
|
CODEX_APP_SERVER_URL: listenUrl,
|
|
956
957
|
CODEX_THREAD_MODE: threadMode,
|
|
957
958
|
CODEX_THREAD_ID: threadId || undefined,
|
package/hooks/session-start.ts
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync } from "node:fs";
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import {
|
|
5
|
+
import { parseApprovalMode } from "../approval.ts";
|
|
6
6
|
import { loadAgentRelayProfile } from "../profile.ts";
|
|
7
|
-
import { buildAgentIdentity } from "../relay.ts";
|
|
8
7
|
import { pickThreadId, type HookInput } from "./session-start-lib.ts";
|
|
9
8
|
|
|
10
9
|
type HookHandshake = {
|
|
@@ -84,8 +83,7 @@ if (process.env.AGENT_RELAY_DISABLED === "1") {
|
|
|
84
83
|
}
|
|
85
84
|
|
|
86
85
|
if (process.env.AGENT_RELAY_CODEX_MANAGED === "1") {
|
|
87
|
-
|
|
88
|
-
process.exit(0);
|
|
86
|
+
outputContinue();
|
|
89
87
|
}
|
|
90
88
|
|
|
91
89
|
if (!appServerUrl || !runId) {
|
|
@@ -99,6 +97,7 @@ const sessionKey = sanitize(threadId || "auto");
|
|
|
99
97
|
const sessionDir = join(runtimeDir, sessionKey);
|
|
100
98
|
const pidPath = join(sessionDir, "sidecar.pid");
|
|
101
99
|
const statePath = join(sessionDir, "live-state.json");
|
|
100
|
+
const contextPath = join(sessionDir, "agent-context.json");
|
|
102
101
|
const logPath = join(sessionDir, "sidecar.log");
|
|
103
102
|
mkdirSync(sessionDir, { recursive: true });
|
|
104
103
|
mkdirSync(runtimeDir, { recursive: true });
|
|
@@ -114,24 +113,7 @@ if (activePid !== null) {
|
|
|
114
113
|
threadId: threadId || undefined,
|
|
115
114
|
timestamp: new Date().toISOString(),
|
|
116
115
|
});
|
|
117
|
-
|
|
118
|
-
const identity = buildAgentIdentity({
|
|
119
|
-
relayUrl,
|
|
120
|
-
cwd,
|
|
121
|
-
rig,
|
|
122
|
-
label: profile.label,
|
|
123
|
-
capabilities: profile.capabilities,
|
|
124
|
-
tags: profile.tags,
|
|
125
|
-
channels: profile.channels,
|
|
126
|
-
profileName: profile.profileName,
|
|
127
|
-
profileMeta: profile.meta,
|
|
128
|
-
threadId,
|
|
129
|
-
model: input.model,
|
|
130
|
-
appServerUrl,
|
|
131
|
-
});
|
|
132
|
-
outputContext(buildStartupContext(identity.id, relayUrl, approvalMode));
|
|
133
|
-
}
|
|
134
|
-
outputContext(`Agent Relay sidecar already running (pid ${activePid}). Relay URL: ${relayUrl}. Approval mode: ${approvalMode} (${describeApprovalMode(approvalMode)}).`);
|
|
116
|
+
outputContinue();
|
|
135
117
|
}
|
|
136
118
|
|
|
137
119
|
const spawnEnv: Record<string, string | undefined> = {
|
|
@@ -141,6 +123,7 @@ const spawnEnv: Record<string, string | undefined> = {
|
|
|
141
123
|
CODEX_THREAD_MODE: threadId ? "resume" : process.env.CODEX_THREAD_MODE || "start",
|
|
142
124
|
AGENT_RELAY_CODEX_CWD: cwd,
|
|
143
125
|
AGENT_RELAY_CODEX_STATE_PATH: statePath,
|
|
126
|
+
AGENT_RELAY_CONTEXT_PATH: process.env.AGENT_RELAY_CONTEXT_PATH || contextPath,
|
|
144
127
|
CODEX_MODEL: input.model || process.env.CODEX_MODEL || "",
|
|
145
128
|
AGENT_RELAY_APPROVAL: process.env.AGENT_RELAY_APPROVAL || profile.approval || "",
|
|
146
129
|
AGENT_RELAY_CODEX_PARENT_PID: String(process.ppid),
|
|
@@ -183,45 +166,4 @@ try {
|
|
|
183
166
|
throw error;
|
|
184
167
|
}
|
|
185
168
|
|
|
186
|
-
|
|
187
|
-
outputContext(`Agent Relay sidecar started in auto-thread mode. Relay URL: ${relayUrl}. Incoming messages will arrive as live user turns once the sidecar resolves the active thread.`);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const identity = buildAgentIdentity({
|
|
191
|
-
relayUrl,
|
|
192
|
-
cwd,
|
|
193
|
-
rig,
|
|
194
|
-
label: profile.label,
|
|
195
|
-
capabilities: profile.capabilities,
|
|
196
|
-
tags: profile.tags,
|
|
197
|
-
channels: profile.channels,
|
|
198
|
-
profileName: profile.profileName,
|
|
199
|
-
profileMeta: profile.meta,
|
|
200
|
-
threadId,
|
|
201
|
-
model: input.model,
|
|
202
|
-
appServerUrl,
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
outputContext(buildStartupContext(identity.id, relayUrl, approvalMode, profile));
|
|
206
|
-
|
|
207
|
-
function buildStartupContext(agentId: string, url: string, mode: string, resolvedProfile = profile): string {
|
|
208
|
-
return [
|
|
209
|
-
"Agent Relay active.",
|
|
210
|
-
`Agent ID: ${agentId}`,
|
|
211
|
-
`Relay URL: ${url}`,
|
|
212
|
-
resolvedProfile.profileName ? `Profile: ${resolvedProfile.profileName}` : "",
|
|
213
|
-
resolvedProfile.label ? `Label: ${resolvedProfile.label}` : "",
|
|
214
|
-
resolvedProfile.tags.length ? `Tags: ${resolvedProfile.tags.join(", ")}` : "",
|
|
215
|
-
resolvedProfile.capabilities.length ? `Capabilities: ${resolvedProfile.capabilities.join(", ")}` : "",
|
|
216
|
-
resolvedProfile.channels.length ? `Channels: ${resolvedProfile.channels.join(", ")}` : "",
|
|
217
|
-
`Approval mode: ${mode} (${describeApprovalMode(parseApprovalMode(mode))})`,
|
|
218
|
-
"Incoming messages will arrive as live user turns.",
|
|
219
|
-
`To send a message, POST JSON to ${url}/api/messages with from="${agentId}", to, subject, and body.`,
|
|
220
|
-
"Targets can be an agent id, tag:name, cap:name, label:name, or broadcast.",
|
|
221
|
-
`To pair with one available session, POST JSON to ${url}/api/pairs with from="${agentId}", target, and optional objective; pair chat uses /api/pairs/{id}/messages.`,
|
|
222
|
-
"To reply to a specific incoming message, include replyTo set to that message id.",
|
|
223
|
-
"If AGENT_RELAY_TOKEN is set, include it as the X-Agent-Relay-Token header.",
|
|
224
|
-
"Message etiquette: acknowledge incoming agent messages briefly unless they are obvious noise.",
|
|
225
|
-
"Anti-loop rule: do not auto-reply to pure acknowledgements/thanks/received messages; acknowledge once, then follow up only when there is new work, a decision, or a deliverable.",
|
|
226
|
-
].filter(Boolean).join(" ");
|
|
227
|
-
}
|
|
169
|
+
outputContinue();
|
package/live-sidecar.ts
CHANGED
|
@@ -21,6 +21,7 @@ interface Config {
|
|
|
21
21
|
profileName?: string;
|
|
22
22
|
profileMeta: Record<string, unknown>;
|
|
23
23
|
statePath: string;
|
|
24
|
+
contextPath?: string;
|
|
24
25
|
pollIntervalMs: number;
|
|
25
26
|
heartbeatIntervalMs: number;
|
|
26
27
|
threadCheckIntervalMs: number;
|
|
@@ -699,6 +700,7 @@ class CodexLiveSidecar {
|
|
|
699
700
|
|
|
700
701
|
private writeState(): void {
|
|
701
702
|
if (!this.threadId) return;
|
|
703
|
+
const updatedAt = new Date().toISOString();
|
|
702
704
|
const state: RuntimeState = {
|
|
703
705
|
agentId: this.agentId,
|
|
704
706
|
agentInstanceId: this.agentInstanceId,
|
|
@@ -712,9 +714,24 @@ class CodexLiveSidecar {
|
|
|
712
714
|
appServerUrl: this.config.appServerUrl,
|
|
713
715
|
relayUrl: this.config.relayUrl,
|
|
714
716
|
cwd: this.config.cwd,
|
|
715
|
-
updatedAt
|
|
717
|
+
updatedAt,
|
|
716
718
|
};
|
|
717
719
|
writeFileSync(this.config.statePath, JSON.stringify(state, null, 2));
|
|
720
|
+
if (this.config.contextPath) {
|
|
721
|
+
mkdirSync(dirname(this.config.contextPath), { recursive: true });
|
|
722
|
+
writeFileSync(this.config.contextPath, JSON.stringify({
|
|
723
|
+
version: 1,
|
|
724
|
+
agentId: this.agentId,
|
|
725
|
+
provider: "codex",
|
|
726
|
+
relayUrl: this.config.relayUrl,
|
|
727
|
+
cwd: this.config.cwd,
|
|
728
|
+
statePath: this.config.statePath,
|
|
729
|
+
matchEnv: [
|
|
730
|
+
{ name: "AGENT_RELAY_CONTEXT_PATH", value: this.config.contextPath },
|
|
731
|
+
],
|
|
732
|
+
updatedAt,
|
|
733
|
+
}, null, 2));
|
|
734
|
+
}
|
|
718
735
|
}
|
|
719
736
|
|
|
720
737
|
private log(message: string): void {
|
|
@@ -768,9 +785,9 @@ export function formatRelayPrompt(
|
|
|
768
785
|
"Agent Relay live-message primer:",
|
|
769
786
|
"Treat Agent Relay deliveries as live incoming messages from other agents. Respond or act on them as appropriate.",
|
|
770
787
|
...(opts.headless ? ["This Codex session is running headless; Agent Relay is the primary user and agent communication surface."] : []),
|
|
771
|
-
"
|
|
772
|
-
|
|
773
|
-
`
|
|
788
|
+
"For normal replies, prefer `agent-relay /message TARGET BODY --reply-to MSG_ID`; the CLI detects this session's Agent Relay ID.",
|
|
789
|
+
"For pair chat replies, prefer `agent-relay /pair send PAIR_ID BODY`.",
|
|
790
|
+
`If the CLI is unavailable, call ${relayUrl}/api/messages or /api/pairs/{pairId}/messages directly and include AGENT_RELAY_TOKEN as X-Agent-Relay-Token when set.`,
|
|
774
791
|
"",
|
|
775
792
|
];
|
|
776
793
|
|
|
@@ -790,16 +807,17 @@ export function formatRelayPrompt(
|
|
|
790
807
|
|
|
791
808
|
if (message.subject) lines.push(`Subject: ${message.subject}`);
|
|
792
809
|
if (message.replyTo) lines.push(`Reply To: ${message.replyTo}`);
|
|
793
|
-
|
|
794
|
-
|
|
810
|
+
const pairId = typeof message.payload?.pairId === "string" ? message.payload.pairId : undefined;
|
|
811
|
+
if (pairId) {
|
|
812
|
+
lines.push(`Pair ID: ${pairId}`);
|
|
795
813
|
}
|
|
796
814
|
lines.push(
|
|
797
815
|
"",
|
|
798
816
|
"Body:",
|
|
799
817
|
message.body,
|
|
800
818
|
"",
|
|
801
|
-
|
|
802
|
-
? `Reply routing: pairId=${JSON.stringify(
|
|
819
|
+
pairId
|
|
820
|
+
? `Reply routing: pairId=${JSON.stringify(pairId)}.`
|
|
803
821
|
: `Reply routing: to=${JSON.stringify(message.from)}, replyTo=${message.id}.`,
|
|
804
822
|
);
|
|
805
823
|
return lines.join("\n");
|
|
@@ -823,10 +841,11 @@ export function formatRelayPrompt(
|
|
|
823
841
|
);
|
|
824
842
|
if (message.subject) lines.push(`Subject: ${message.subject}`);
|
|
825
843
|
if (message.replyTo) lines.push(`Reply To: ${message.replyTo}`);
|
|
826
|
-
|
|
844
|
+
const pairId = typeof message.payload?.pairId === "string" ? message.payload.pairId : undefined;
|
|
845
|
+
if (pairId) lines.push(`Pair ID: ${pairId}`);
|
|
827
846
|
lines.push(
|
|
828
|
-
|
|
829
|
-
? `Reply routing: pairId=${JSON.stringify(
|
|
847
|
+
pairId
|
|
848
|
+
? `Reply routing: pairId=${JSON.stringify(pairId)}.`
|
|
830
849
|
: `Reply routing: to=${JSON.stringify(message.from)}, replyTo=${message.id}.`,
|
|
831
850
|
);
|
|
832
851
|
lines.push("Body:", message.body, "", "---", "");
|
|
@@ -840,13 +859,13 @@ export function formatRelayPrompt(
|
|
|
840
859
|
|
|
841
860
|
function formatMessageSummary(message: RelayMessage): string {
|
|
842
861
|
const subject = message.subject || "message";
|
|
843
|
-
if (message.
|
|
862
|
+
if (message.kind === "system" || message.kind === "control") return `SYSTEM [msg:${message.id}]: ${message.body}`;
|
|
844
863
|
return `[msg:${message.id}] ${message.from} -> ${message.to} | ${subject}: ${message.body}`;
|
|
845
864
|
}
|
|
846
865
|
|
|
847
866
|
export function agentControlActionForMessage(message: RelayMessage): AgentControlAction | null {
|
|
848
|
-
if (message.
|
|
849
|
-
const control = message.
|
|
867
|
+
if (message.kind !== "control") return null;
|
|
868
|
+
const control = message.payload?.agentControl;
|
|
850
869
|
if (!control || typeof control !== "object" || Array.isArray(control)) return null;
|
|
851
870
|
const action = (control as { action?: unknown }).action;
|
|
852
871
|
return action === "restart" || action === "shutdown" ? action : null;
|
|
@@ -929,6 +948,7 @@ export function loadConfig(env: NodeJS.ProcessEnv = process.env): Config {
|
|
|
929
948
|
profileName: profile.profileName,
|
|
930
949
|
profileMeta: profile.meta,
|
|
931
950
|
statePath: env.AGENT_RELAY_CODEX_STATE_PATH || resolve(cwd, "codex/runtime/live-state.json"),
|
|
951
|
+
contextPath: env.AGENT_RELAY_CONTEXT_PATH || undefined,
|
|
932
952
|
pollIntervalMs: envNumber(env, "AGENT_RELAY_CODEX_POLL_INTERVAL_MS", 2000),
|
|
933
953
|
heartbeatIntervalMs: envNumber(env, "AGENT_RELAY_CODEX_HEARTBEAT_INTERVAL_MS", 30000),
|
|
934
954
|
threadCheckIntervalMs: envNumber(env, "AGENT_RELAY_CODEX_THREAD_CHECK_INTERVAL_MS", 30000),
|
package/package.json
CHANGED
package/relay.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface RelayMessage {
|
|
|
6
6
|
id: number;
|
|
7
7
|
from: string;
|
|
8
8
|
to: string;
|
|
9
|
+
kind: string;
|
|
9
10
|
channel?: string;
|
|
10
11
|
subject?: string;
|
|
11
12
|
body: string;
|
|
@@ -14,7 +15,7 @@ export interface RelayMessage {
|
|
|
14
15
|
claimedBy?: string;
|
|
15
16
|
claimedAt?: number;
|
|
16
17
|
claimExpiresAt?: number;
|
|
17
|
-
|
|
18
|
+
payload?: Record<string, unknown>;
|
|
18
19
|
meta?: Record<string, unknown>;
|
|
19
20
|
createdAt: number;
|
|
20
21
|
}
|