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.
@@ -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,
@@ -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 { describeApprovalMode, parseApprovalMode } from "../approval.ts";
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
- outputContext("Agent Relay is managed by codex-relay for this session; the launcher attached Relay and TUI to the same Codex App Server thread.");
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
- if (threadId) {
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
- if (!threadId) {
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: new Date().toISOString(),
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
- "When calling the relay API, include AGENT_RELAY_TOKEN as the X-Agent-Relay-Token header if that environment variable is set.",
772
- `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.`,
773
- `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.`,
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
- if (typeof message.meta?.pairId === "string") {
794
- lines.push(`Pair ID: ${message.meta.pairId}`);
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
- typeof message.meta?.pairId === "string"
802
- ? `Reply routing: pairId=${JSON.stringify(message.meta.pairId)}.`
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
- if (typeof message.meta?.pairId === "string") lines.push(`Pair ID: ${message.meta.pairId}`);
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
- typeof message.meta?.pairId === "string"
829
- ? `Reply routing: pairId=${JSON.stringify(message.meta.pairId)}.`
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.type === "system") return `SYSTEM [msg:${message.id}]: ${message.body}`;
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.type !== "system") return null;
849
- const control = message.meta?.agentControl;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-codex",
3
- "version": "0.4.39",
3
+ "version": "0.6.0",
4
4
  "description": "Codex integration for Agent Relay — auto-registers sessions as agents and enables inter-agent messaging",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay",
3
- "version": "0.4.39",
3
+ "version": "0.6.0",
4
4
  "description": "Agent Relay integration for Codex sessions",
5
5
  "author": {
6
6
  "name": "Edin Mujkanovic"
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
- type?: string;
18
+ payload?: Record<string, unknown>;
18
19
  meta?: Record<string, unknown>;
19
20
  createdAt: number;
20
21
  }