agent-relay-server 0.122.0 → 0.123.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.
Files changed (142) hide show
  1. package/docs/openapi.json +1 -1
  2. package/package.json +7 -8
  3. package/public/assets/{MessageBubble-DRdUD-kW.js → MessageBubble-BTeFExjj.js} +2 -2
  4. package/public/assets/{MessageBubble-DRdUD-kW.js.map → MessageBubble-BTeFExjj.js.map} +1 -1
  5. package/public/assets/{PlanGraphPanel-siKMPCln.js → PlanGraphPanel-jU1DXg7w.js} +2 -2
  6. package/public/assets/{PlanGraphPanel-siKMPCln.js.map → PlanGraphPanel-jU1DXg7w.js.map} +1 -1
  7. package/public/assets/{activity-D3MNcSaV.js → activity-DUig2iYW.js} +2 -2
  8. package/public/assets/{activity-D3MNcSaV.js.map → activity-DUig2iYW.js.map} +1 -1
  9. package/public/assets/{agent-profiles-CKzttq6G.js → agent-profiles-D-oFmb6S.js} +2 -2
  10. package/public/assets/{agent-profiles-CKzttq6G.js.map → agent-profiles-D-oFmb6S.js.map} +1 -1
  11. package/public/assets/{agent-type-icon-0ZJRP1Vy.js → agent-type-icon-Dan2l3Tm.js} +2 -2
  12. package/public/assets/{agent-type-icon-0ZJRP1Vy.js.map → agent-type-icon-Dan2l3Tm.js.map} +1 -1
  13. package/public/assets/{agents-CuVj_rfG.js → agents-BkC6mcTh.js} +2 -2
  14. package/public/assets/{agents-CuVj_rfG.js.map → agents-BkC6mcTh.js.map} +1 -1
  15. package/public/assets/{analytics-iob6C27d.js → analytics-C3Dlmqe9.js} +2 -2
  16. package/public/assets/{analytics-iob6C27d.js.map → analytics-C3Dlmqe9.js.map} +1 -1
  17. package/public/assets/{automation-B2ixxs4q.js → automation-AeMZ70WQ.js} +2 -2
  18. package/public/assets/{automation-B2ixxs4q.js.map → automation-AeMZ70WQ.js.map} +1 -1
  19. package/public/assets/{branch-state-badge-CafBJWKo.js → branch-state-badge-CNW10MTC.js} +2 -2
  20. package/public/assets/{branch-state-badge-CafBJWKo.js.map → branch-state-badge-CNW10MTC.js.map} +1 -1
  21. package/public/assets/{channels-C-WsbL2H.js → channels-Bc_mEd56.js} +2 -2
  22. package/public/assets/{channels-C-WsbL2H.js.map → channels-Bc_mEd56.js.map} +1 -1
  23. package/public/assets/{chat-DeoVTVob.js → chat-BCNCuTgw.js} +2 -2
  24. package/public/assets/{chat-DeoVTVob.js.map → chat-BCNCuTgw.js.map} +1 -1
  25. package/public/assets/{connectors-C2Ac03LJ.js → connectors-h0d5Mc6e.js} +2 -2
  26. package/public/assets/{connectors-C2Ac03LJ.js.map → connectors-h0d5Mc6e.js.map} +1 -1
  27. package/public/assets/{display-CJzSoTMq.js → display-GFbGUG0O.js} +2 -2
  28. package/public/assets/{display-CJzSoTMq.js.map → display-GFbGUG0O.js.map} +1 -1
  29. package/public/assets/{formatted-body-35XBOY5L.js → formatted-body-CVr3Iev9.js} +3 -3
  30. package/public/assets/{formatted-body-35XBOY5L.js.map → formatted-body-CVr3Iev9.js.map} +1 -1
  31. package/public/assets/{formatted-body-impl-RmDxI5Ux.js → formatted-body-impl-Cq5SRGpm.js} +2 -2
  32. package/public/assets/{formatted-body-impl-RmDxI5Ux.js.map → formatted-body-impl-Cq5SRGpm.js.map} +1 -1
  33. package/public/assets/{index-D0xMQQb_.js → index--9UMzCFf.js} +6 -6
  34. package/public/assets/{index-D0xMQQb_.js.map → index--9UMzCFf.js.map} +1 -1
  35. package/public/assets/{insights-B643SFvm.js → insights-C8lIBXg3.js} +2 -2
  36. package/public/assets/{insights-B643SFvm.js.map → insights-C8lIBXg3.js.map} +1 -1
  37. package/public/assets/{integrations-hFIHeCF4.js → integrations-7OFsJtY7.js} +2 -2
  38. package/public/assets/{integrations-hFIHeCF4.js.map → integrations-7OFsJtY7.js.map} +1 -1
  39. package/public/assets/{maintenance-CURvdp_Z.js → maintenance-HDpg20OY.js} +2 -2
  40. package/public/assets/{maintenance-CURvdp_Z.js.map → maintenance-HDpg20OY.js.map} +1 -1
  41. package/public/assets/{managed-agents-DrZtdlSn.js → managed-agents-muCIMxng.js} +2 -2
  42. package/public/assets/{managed-agents-DrZtdlSn.js.map → managed-agents-muCIMxng.js.map} +1 -1
  43. package/public/assets/{markdown-preview-impl-CVDO--ek.js → markdown-preview-impl-Cx7pCfA6.js} +2 -2
  44. package/public/assets/{markdown-preview-impl-CVDO--ek.js.map → markdown-preview-impl-Cx7pCfA6.js.map} +1 -1
  45. package/public/assets/{memory-Bm7dt_Qf.js → memory-CTO1FfJ-.js} +2 -2
  46. package/public/assets/{memory-Bm7dt_Qf.js.map → memory-CTO1FfJ-.js.map} +1 -1
  47. package/public/assets/{messages-DTq4i2KZ.js → messages-RWSXG2UM.js} +2 -2
  48. package/public/assets/{messages-DTq4i2KZ.js.map → messages-RWSXG2UM.js.map} +1 -1
  49. package/public/assets/{orchestrators-Dra318Ap.js → orchestrators-5aiYw3yx.js} +2 -2
  50. package/public/assets/{orchestrators-Dra318Ap.js.map → orchestrators-5aiYw3yx.js.map} +1 -1
  51. package/public/assets/{overview-yr1Vmk64.js → overview-Du6XLt5l.js} +2 -2
  52. package/public/assets/{overview-yr1Vmk64.js.map → overview-Du6XLt5l.js.map} +1 -1
  53. package/public/assets/{pairs-mnlKDBGp.js → pairs-BDRnlEta.js} +2 -2
  54. package/public/assets/{pairs-mnlKDBGp.js.map → pairs-BDRnlEta.js.map} +1 -1
  55. package/public/assets/{projects-Dzdf-gkK.js → projects-DwsWE9es.js} +2 -2
  56. package/public/assets/{projects-Dzdf-gkK.js.map → projects-DwsWE9es.js.map} +1 -1
  57. package/public/assets/{prompt-settings-ByL8SNCi.js → prompt-settings-CaJX_LbA.js} +2 -2
  58. package/public/assets/{prompt-settings-ByL8SNCi.js.map → prompt-settings-CaJX_LbA.js.map} +1 -1
  59. package/public/assets/{registry-CXWyOtpr.js → registry-C-uIL0Nl.js} +2 -2
  60. package/public/assets/{registry-CXWyOtpr.js.map → registry-C-uIL0Nl.js.map} +1 -1
  61. package/public/assets/{security-0SBsc_4W.js → security-BpVrdYhJ.js} +2 -2
  62. package/public/assets/{security-0SBsc_4W.js.map → security-BpVrdYhJ.js.map} +1 -1
  63. package/public/assets/{select-Bw5z2KJ_.js → select-DallbU93.js} +2 -2
  64. package/public/assets/{select-Bw5z2KJ_.js.map → select-DallbU93.js.map} +1 -1
  65. package/public/assets/{settings-78Cyq_tA.js → settings-BUQirdyI.js} +2 -2
  66. package/public/assets/{settings-78Cyq_tA.js.map → settings-BUQirdyI.js.map} +1 -1
  67. package/public/assets/{store-BPrVuKiv.js → store-DMZSTozC.js} +2 -2
  68. package/public/assets/{store-BPrVuKiv.js.map → store-DMZSTozC.js.map} +1 -1
  69. package/public/assets/{tasks-Cg2MMeri.js → tasks-k8mQDfKy.js} +2 -2
  70. package/public/assets/{tasks-Cg2MMeri.js.map → tasks-k8mQDfKy.js.map} +1 -1
  71. package/public/assets/{teams-DWv6UwFV.js → teams-DI-Lhhbl.js} +2 -2
  72. package/public/assets/{teams-DWv6UwFV.js.map → teams-DI-Lhhbl.js.map} +1 -1
  73. package/public/assets/{terminal-viewer-impl-DBLDjbIg.js → terminal-viewer-impl-BL853Fpi.js} +2 -2
  74. package/public/assets/{terminal-viewer-impl-DBLDjbIg.js.map → terminal-viewer-impl-BL853Fpi.js.map} +1 -1
  75. package/public/assets/types-CRptbSYh.js +2 -0
  76. package/public/assets/types-CRptbSYh.js.map +1 -0
  77. package/public/assets/{user-avatar-BsOLyVJj.js → user-avatar-D9xyGi3u.js} +2 -2
  78. package/public/assets/{user-avatar-BsOLyVJj.js.map → user-avatar-D9xyGi3u.js.map} +1 -1
  79. package/public/assets/{work-queue-DmwvnF6F.js → work-queue-DrLIdvxB.js} +2 -2
  80. package/public/assets/{work-queue-DmwvnF6F.js.map → work-queue-DrLIdvxB.js.map} +1 -1
  81. package/public/assets/{workspaces-dIBivJN8.js → workspaces-CnNL6T8Q.js} +2 -2
  82. package/public/assets/{workspaces-dIBivJN8.js.map → workspaces-CnNL6T8Q.js.map} +1 -1
  83. package/public/index.html +6 -6
  84. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  85. package/runner/src/adapters/claude-delivery.ts +127 -0
  86. package/runner/src/adapters/claude-quota-harvest.ts +92 -0
  87. package/runner/src/adapters/claude-session-probe.ts +225 -0
  88. package/runner/src/adapters/claude-status-detectors.ts +147 -0
  89. package/runner/src/adapters/claude-transcript-tail.ts +128 -0
  90. package/runner/src/adapters/claude-transcript.ts +414 -0
  91. package/runner/src/adapters/claude.ts +675 -0
  92. package/runner/src/adapters/codex-agent-message-capture.ts +139 -0
  93. package/runner/src/adapters/codex-client.ts +279 -0
  94. package/runner/src/adapters/codex.ts +1345 -0
  95. package/runner/src/attachment-cache.ts +189 -0
  96. package/runner/src/busy-reconciler.ts +102 -0
  97. package/runner/src/claim-tracker.ts +140 -0
  98. package/runner/src/claude-prompt-gates.ts +268 -0
  99. package/runner/src/codex-version.ts +63 -0
  100. package/runner/src/config.ts +7 -0
  101. package/runner/src/context-probe-state.ts +6 -0
  102. package/runner/src/continuation-archive.ts +179 -0
  103. package/runner/src/control-server.ts +639 -0
  104. package/runner/src/index.ts +396 -0
  105. package/runner/src/launch-assembly.ts +674 -0
  106. package/runner/src/liveness.ts +50 -0
  107. package/runner/src/logger.ts +99 -0
  108. package/runner/src/mcp-outbox.ts +120 -0
  109. package/runner/src/message-body-config/config.ts +3 -0
  110. package/runner/src/message-body-config.ts +1 -0
  111. package/runner/src/native-self-resume.ts +202 -0
  112. package/runner/src/outbox.ts +342 -0
  113. package/runner/src/process-meta.ts +9 -0
  114. package/runner/src/profile-home.ts +260 -0
  115. package/runner/src/profile-projection.ts +324 -0
  116. package/runner/src/providers.ts +20 -0
  117. package/runner/src/provisioning.ts +368 -0
  118. package/runner/src/quota.ts +40 -0
  119. package/runner/src/rate-limit.ts +189 -0
  120. package/runner/src/relay-injection-events.ts +105 -0
  121. package/runner/src/relay-instructions.ts +60 -0
  122. package/runner/src/relay-mcp-proxy.ts +383 -0
  123. package/runner/src/relay-mcp.ts +156 -0
  124. package/runner/src/reply-obligation-cache.ts +136 -0
  125. package/runner/src/response-capture-report.ts +63 -0
  126. package/runner/src/runner-core.ts +2409 -0
  127. package/runner/src/runner-helpers.ts +435 -0
  128. package/runner/src/runner-insights.ts +105 -0
  129. package/runner/src/runner.ts +14 -0
  130. package/runner/src/session-destroy.ts +22 -0
  131. package/runner/src/session-insights.ts +118 -0
  132. package/runner/src/session-scratch.ts +271 -0
  133. package/runner/src/template-resolver.ts +29 -0
  134. package/runner/src/version.ts +44 -0
  135. package/src/config-store.ts +2 -0
  136. package/src/db/provisioning-registry.ts +40 -9
  137. package/src/db/provisioning-resolve.ts +66 -5
  138. package/src/gate-resolver.ts +7 -12
  139. package/src/routes/provisioning-registry.ts +1 -1
  140. package/src/services/spawn-agent.ts +1 -0
  141. package/public/assets/types-BeJUSfDj.js +0 -2
  142. package/public/assets/types-BeJUSfDj.js.map +0 -1
@@ -0,0 +1,396 @@
1
+ #!/usr/bin/env bun
2
+ import { hostname } from "node:os";
3
+ import { basename } from "node:path";
4
+ import { isatty } from "node:tty";
5
+ import { AgentRunner } from "./runner";
6
+ import {
7
+ agentProfileJsonFromEnv,
8
+ agentProfileNameFromEnv,
9
+ automationIdFromEnv,
10
+ automationRunIdFromEnv,
11
+ lifecycleFromEnv,
12
+ loadGlobalConfig,
13
+ loadProviderConfigFromRelay,
14
+ policyNameFromEnv,
15
+ promptFromEnv,
16
+ relayInjectionEventsJsonFromEnv,
17
+ resolveCwd,
18
+ runnerId,
19
+ runtimeTokenExpiresAtFromEnv,
20
+ runtimeTokenJtiFromEnv,
21
+ runtimeTokenProfileFromEnv,
22
+ spawnRequestIdFromEnv,
23
+ systemPromptAppendFromEnv,
24
+ taskIdFromEnv,
25
+ tmuxSessionFromEnv,
26
+ workspaceJsonFromEnv,
27
+ } from "./config";
28
+ import { createProviderAdapter } from "./providers";
29
+ import { VERSION } from "./version";
30
+ import type { AgentProfile, RelayInjectionCategory, SpawnProvider, WorkspaceMetadata } from "agent-relay-sdk";
31
+ import { errMessage, isRecord, normalizeAgentLifecycle, RELAY_TOKEN_HEADER } from "agent-relay-sdk";
32
+ import { getAllManifests } from "agent-relay-providers";
33
+ import type { RunnerRelayInjectionEvent } from "./relay-injection-events";
34
+
35
+ interface CliOptions {
36
+ provider: SpawnProvider;
37
+ model?: string;
38
+ effort?: string;
39
+ headless: boolean;
40
+ interactive: boolean;
41
+ cwd?: string;
42
+ rig?: string;
43
+ profile?: string;
44
+ relayUrl?: string;
45
+ token?: string;
46
+ approvalMode?: string;
47
+ label?: string;
48
+ agentId?: string;
49
+ prompt?: string;
50
+ systemPromptAppend?: string;
51
+ relayInjectionEvents?: RunnerRelayInjectionEvent[];
52
+ tags: string[];
53
+ caps: string[];
54
+ providerArgs: string[];
55
+ }
56
+
57
+ export async function main(argv = process.argv): Promise<void> {
58
+ if (isVersionRequest(argv)) {
59
+ console.log(VERSION);
60
+ return;
61
+ }
62
+ const opts = parseArgs(argv);
63
+ const globalConfig = loadGlobalConfig();
64
+ const relayUrl = opts.relayUrl ?? globalConfig.relayUrl;
65
+ const providerConfig = await loadProviderConfigFromRelay(opts.provider, {
66
+ relayUrl,
67
+ token: opts.token ?? globalConfig.token,
68
+ });
69
+ const cwd = resolveCwd(opts.cwd, globalConfig.defaultCwd);
70
+ const id = runnerId(opts.provider, cwd, opts.label);
71
+ const adapter = createProviderAdapter(opts.provider);
72
+ const runtimeAuth = await resolveRunnerToken({
73
+ interactive: opts.interactive,
74
+ relayUrl,
75
+ token: opts.token ?? globalConfig.token,
76
+ runtimeTokenProfile: runtimeTokenProfileFromEnv(),
77
+ runtimeTokenJti: runtimeTokenJtiFromEnv(),
78
+ runtimeTokenExpiresAt: parseTokenExpiresAt(runtimeTokenExpiresAtFromEnv()),
79
+ provider: opts.provider,
80
+ cwd,
81
+ runnerId: id,
82
+ agentId: opts.agentId ?? id,
83
+ label: opts.label,
84
+ });
85
+
86
+ let exitResolve: (code: number) => void;
87
+ const exitPromise = new Promise<number>((resolve) => { exitResolve = resolve; });
88
+
89
+ const approvalMode = opts.approvalMode
90
+ ?? detectApprovalModeFromArgs(opts.provider, providerConfig.defaultArgs, opts.providerArgs)
91
+ ?? providerConfig.defaultApprovalMode;
92
+
93
+ const runner = new AgentRunner({
94
+ provider: opts.provider,
95
+ model: opts.model,
96
+ effort: opts.effort,
97
+ runnerId: id,
98
+ instanceId: crypto.randomUUID(),
99
+ agentId: opts.agentId,
100
+ relayUrl,
101
+ token: runtimeAuth.token,
102
+ tokenJti: runtimeAuth.jti,
103
+ tokenProfileId: runtimeAuth.profileId,
104
+ tokenExpiresAt: runtimeAuth.expiresAt,
105
+ rootTokenFallback: runtimeAuth.rootFallback,
106
+ cwd,
107
+ headless: opts.headless,
108
+ approvalMode,
109
+ label: opts.label,
110
+ rig: opts.rig,
111
+ profile: opts.profile ?? agentProfileNameFromEnv(),
112
+ agentProfile: parseAgentProfileEnv(agentProfileJsonFromEnv()),
113
+ workspace: parseWorkspaceEnv(workspaceJsonFromEnv()),
114
+ prompt: opts.prompt ?? promptFromEnv(),
115
+ systemPromptAppend: opts.systemPromptAppend ?? systemPromptAppendFromEnv(),
116
+ relayInjectionEvents: parseRelayInjectionEventsEnv(relayInjectionEventsJsonFromEnv()),
117
+ tmuxSession: tmuxSessionFromEnv(),
118
+ tags: opts.tags,
119
+ capabilities: opts.caps,
120
+ providerArgs: opts.providerArgs,
121
+ policyName: policyNameFromEnv(),
122
+ spawnRequestId: spawnRequestIdFromEnv(),
123
+ taskId: taskIdFromEnv(),
124
+ automationId: automationIdFromEnv(),
125
+ automationRunId: automationRunIdFromEnv(),
126
+ lifecycle: normalizeAgentLifecycle(lifecycleFromEnv()) ?? "persistent",
127
+ startedAt: Date.now(),
128
+ providerConfig,
129
+ adapter,
130
+ onProviderExit: (code) => exitResolve(code ?? 1),
131
+ });
132
+ await runner.run();
133
+
134
+ const code = await Promise.race([exitPromise, signalPromise()]);
135
+ await runner.stop();
136
+ process.exit(code);
137
+ }
138
+
139
+ export async function resolveRunnerToken(input: {
140
+ interactive: boolean;
141
+ relayUrl: string;
142
+ token?: string;
143
+ runtimeTokenProfile?: string;
144
+ runtimeTokenJti?: string;
145
+ runtimeTokenExpiresAt?: number;
146
+ provider: string;
147
+ cwd: string;
148
+ runnerId: string;
149
+ agentId: string;
150
+ label?: string;
151
+ }): Promise<{ token?: string; jti?: string; profileId?: string; expiresAt?: number; rootFallback?: boolean }> {
152
+ if (!input.token) return {};
153
+ const claims = decodeComponentTokenClaims(input.token);
154
+ if (input.runtimeTokenProfile) {
155
+ return {
156
+ token: input.token,
157
+ jti: input.runtimeTokenJti ?? claims?.jti,
158
+ profileId: input.runtimeTokenProfile,
159
+ expiresAt: input.runtimeTokenExpiresAt ?? claims?.exp,
160
+ };
161
+ }
162
+ const inferredRuntime = inferRuntimeTokenMetadata(claims);
163
+ if (!input.interactive && inferredRuntime) return { token: input.token, ...inferredRuntime };
164
+ if (!input.interactive) return { token: input.token };
165
+
166
+ try {
167
+ const url = new URL("/api/runtime-tokens/interactive-runner", input.relayUrl);
168
+ const res = await fetch(url, {
169
+ method: "POST",
170
+ headers: {
171
+ "Content-Type": "application/json",
172
+ [RELAY_TOKEN_HEADER]: input.token,
173
+ },
174
+ body: JSON.stringify({
175
+ provider: input.provider,
176
+ cwd: input.cwd,
177
+ runnerId: input.runnerId,
178
+ agentId: input.agentId,
179
+ label: input.label,
180
+ host: hostname(),
181
+ }),
182
+ signal: AbortSignal.timeout(5_000),
183
+ });
184
+ const body = await res.json().catch(() => null) as { token?: string; record?: { jti?: string; profileId?: string; expiresAt?: number }; error?: string } | null;
185
+ if (res.ok && body?.token) return { token: body.token, jti: body.record?.jti, profileId: body.record?.profileId ?? "provider-interactive", expiresAt: body.record?.expiresAt };
186
+ const detail = body?.error ? `: ${body.error}` : "";
187
+ console.warn(`[agent-relay] interactive scoped-token exchange failed (${res.status}${detail}); continuing with existing token. Root-token runtime fallback is deprecated.`);
188
+ } catch (error) {
189
+ console.warn(`[agent-relay] interactive scoped-token exchange failed (${errMessage(error)}); continuing with existing token. Root-token runtime fallback is deprecated.`);
190
+ }
191
+ return { token: input.token, rootFallback: true };
192
+ }
193
+
194
+ function decodeComponentTokenClaims(token: string | undefined): { sub?: string; role?: string; exp?: number; jti?: string } | undefined {
195
+ const payload = token?.split(".")[1];
196
+ if (!payload) return undefined;
197
+ try {
198
+ const normalized = payload.replace(/-/g, "+").replace(/_/g, "/");
199
+ const decoded = JSON.parse(Buffer.from(normalized, "base64").toString("utf8")) as Record<string, unknown>;
200
+ const exp = typeof decoded.exp === "number" && Number.isSafeInteger(decoded.exp) && decoded.exp > 0 ? decoded.exp : undefined;
201
+ return {
202
+ ...(typeof decoded.sub === "string" ? { sub: decoded.sub } : {}),
203
+ ...(typeof decoded.role === "string" ? { role: decoded.role } : {}),
204
+ ...(typeof decoded.jti === "string" ? { jti: decoded.jti } : {}),
205
+ ...(exp ? { exp } : {}),
206
+ };
207
+ } catch {
208
+ return undefined;
209
+ }
210
+ }
211
+
212
+ function inferRuntimeTokenMetadata(claims: { sub?: string; role?: string; exp?: number; jti?: string } | undefined): { jti?: string; profileId?: string; expiresAt?: number } | undefined {
213
+ if (claims?.role !== "provider" || !claims.exp) return undefined;
214
+ const sub = claims.sub ?? "";
215
+ const profileId = sub.startsWith("runner:interactive:")
216
+ ? "provider-interactive"
217
+ : sub.startsWith("runner:spawn:") || sub.startsWith("runner:policy:") || /^runner:(codex|claude|provider):/.test(sub)
218
+ ? "provider-agent"
219
+ : undefined;
220
+ if (!profileId) return undefined;
221
+ return {
222
+ ...(claims.jti ? { jti: claims.jti } : {}),
223
+ profileId,
224
+ expiresAt: claims.exp,
225
+ };
226
+ }
227
+
228
+ function parseTokenExpiresAt(value: string | undefined): number | undefined {
229
+ if (!value) return undefined;
230
+ const parsed = Number(value);
231
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined;
232
+ }
233
+
234
+ export function isVersionRequest(argv: string[]): boolean {
235
+ const relayArgs = argv.slice(2);
236
+ const providerSep = relayArgs.indexOf("--");
237
+ const ownArgs = providerSep >= 0 ? relayArgs.slice(0, providerSep) : relayArgs;
238
+ return ownArgs.some((arg) => arg === "--version" || arg === "-v");
239
+ }
240
+
241
+ function detectProviderFromBin(argv: string[]): SpawnProvider {
242
+ const bin = [argv[1], process.env._].map((s) => basename(s || "")).join(" ");
243
+ for (const manifest of getAllManifests()) {
244
+ if (bin.includes(manifest.id)) return manifest.id as SpawnProvider;
245
+ }
246
+ return getAllManifests()[0]?.id as SpawnProvider ?? "claude";
247
+ }
248
+
249
+ function isRegisteredProvider(value: string): value is SpawnProvider {
250
+ return getAllManifests().some((m) => m.id === value);
251
+ }
252
+
253
+ function parseArgs(argv: string[]): CliOptions {
254
+ let provider = detectProviderFromBin(argv);
255
+ const relayArgs = argv.slice(2);
256
+ const providerSep = relayArgs.indexOf("--");
257
+ const ownArgs = providerSep >= 0 ? relayArgs.slice(0, providerSep) : relayArgs;
258
+ const providerArgs = providerSep >= 0 ? relayArgs.slice(providerSep + 1) : [];
259
+ let headless = false;
260
+ let model: string | undefined;
261
+ let effort: string | undefined;
262
+ let cwd: string | undefined;
263
+ let rig: string | undefined;
264
+ let profile: string | undefined;
265
+ let relayUrl: string | undefined;
266
+ let token: string | undefined;
267
+ let approvalMode: string | undefined;
268
+ let label: string | undefined;
269
+ let agentId: string | undefined;
270
+ let prompt: string | undefined;
271
+ let systemPromptAppend: string | undefined;
272
+ let tags: string[] = [];
273
+ let caps: string[] = [];
274
+
275
+ for (let i = 0; i < ownArgs.length; i++) {
276
+ const arg = ownArgs[i];
277
+ if (arg && isRegisteredProvider(arg)) provider = arg;
278
+ else if (arg === "--headless") headless = true;
279
+ else if (arg === "--model" && ownArgs[i + 1]) model = ownArgs[++i];
280
+ else if (arg === "--effort" && ownArgs[i + 1]) effort = ownArgs[++i];
281
+ else if (arg === "--cwd" && ownArgs[i + 1]) cwd = ownArgs[++i];
282
+ else if (arg === "--rig" && ownArgs[i + 1]) rig = ownArgs[++i];
283
+ else if (arg === "--profile" && ownArgs[i + 1]) profile = ownArgs[++i];
284
+ else if (arg === "--relay-url" && ownArgs[i + 1]) relayUrl = ownArgs[++i];
285
+ else if (arg === "--token" && ownArgs[i + 1]) token = ownArgs[++i];
286
+ else if ((arg === "--approval" || arg === "--approval-mode") && ownArgs[i + 1]) approvalMode = ownArgs[++i];
287
+ else if (arg === "--label" && ownArgs[i + 1]) label = ownArgs[++i];
288
+ else if (arg === "--agent-id" && ownArgs[i + 1]) agentId = ownArgs[++i];
289
+ else if (arg === "--prompt" && ownArgs[i + 1]) prompt = ownArgs[++i];
290
+ else if ((arg === "--system-prompt-append" || arg === "--append-system-prompt") && ownArgs[i + 1]) systemPromptAppend = ownArgs[++i];
291
+ else if (arg === "--tags" && ownArgs[i + 1]) tags = csvSplit(ownArgs[++i]!);
292
+ else if (arg === "--caps" && ownArgs[i + 1]) caps = csvSplit(ownArgs[++i]!);
293
+ else if (arg === "--help" || arg === "-h") {
294
+ printHelp(provider);
295
+ process.exit(0);
296
+ } else {
297
+ providerArgs.push(arg!);
298
+ }
299
+ }
300
+
301
+ const interactive = !headless && isatty(0);
302
+ return { provider, model, effort, headless, interactive, cwd, rig, profile, relayUrl, token, approvalMode, label, agentId, prompt, systemPromptAppend, tags, caps, providerArgs };
303
+ }
304
+
305
+ function csvSplit(value: string): string[] {
306
+ return value.split(",").map((s) => s.trim()).filter(Boolean);
307
+ }
308
+
309
+ function parseAgentProfileEnv(raw: string | undefined): AgentProfile | undefined {
310
+ if (!raw) return undefined;
311
+ try {
312
+ const parsed = JSON.parse(raw) as Partial<AgentProfile>;
313
+ return parsed && typeof parsed === "object" && typeof parsed.name === "string" ? parsed as AgentProfile : undefined;
314
+ } catch {
315
+ return undefined;
316
+ }
317
+ }
318
+
319
+ function parseWorkspaceEnv(raw: string | undefined): WorkspaceMetadata | undefined {
320
+ if (!raw) return undefined;
321
+ try {
322
+ const parsed = JSON.parse(raw) as Partial<WorkspaceMetadata>;
323
+ return parsed && typeof parsed === "object" && typeof parsed.mode === "string" ? parsed as WorkspaceMetadata : undefined;
324
+ } catch {
325
+ return undefined;
326
+ }
327
+ }
328
+
329
+ function parseRelayInjectionEventsEnv(raw: string | undefined): RunnerRelayInjectionEvent[] | undefined {
330
+ if (!raw) return undefined;
331
+ try {
332
+ const parsed = JSON.parse(raw);
333
+ if (!Array.isArray(parsed)) return undefined;
334
+ const categories = new Set(["memory", "instructions", "tools", "comms", "knowledge", "context"]);
335
+ const events = parsed.flatMap((item): RunnerRelayInjectionEvent[] => {
336
+ if (!isRecord(item) || typeof item.summary !== "string" || !categories.has(String(item.category))) return [];
337
+ const detail = isRecord(item.detail) ? item.detail : {};
338
+ return [{ category: item.category as RelayInjectionCategory, summary: item.summary, detail }];
339
+ });
340
+ return events.length ? events : undefined;
341
+ } catch {
342
+ return undefined;
343
+ }
344
+ }
345
+
346
+ function printHelp(provider: string): void {
347
+ console.log(`${provider}-relay [--headless] [--model ALIAS] [--effort LEVEL] [--rig NAME] [--profile NAME] [--cwd PATH] [--relay-url URL] [--approval MODE] [--label NAME] [--agent-id ID] [--prompt TEXT] [--system-prompt-append TEXT] [--tags a,b] [--caps x,y] [-- provider-args...]`);
348
+ }
349
+
350
+ function signalPromise(): Promise<number> {
351
+ return new Promise<number>((resolve) => {
352
+ process.on("SIGINT", () => resolve(130));
353
+ process.on("SIGTERM", () => resolve(143));
354
+ });
355
+ }
356
+
357
+ type ApprovalModeDetector = (allArgs: string[]) => string | undefined;
358
+
359
+ const APPROVAL_MODE_DETECTORS: Record<string, ApprovalModeDetector> = {
360
+ claude(allArgs) {
361
+ if (allArgs.includes("--dangerously-skip-permissions")) return "open";
362
+ const modeIdx = allArgs.lastIndexOf("--permission-mode");
363
+ if (modeIdx >= 0) {
364
+ const mode = allArgs[modeIdx + 1];
365
+ if (mode === "dontAsk") return "read-only";
366
+ if (mode === "default") return "guarded";
367
+ }
368
+ return undefined;
369
+ },
370
+ codex(allArgs) {
371
+ if (allArgs.includes("--yolo")) return "open";
372
+ for (let i = 0; i < allArgs.length; i++) {
373
+ if (allArgs[i] === "-c" || allArgs[i] === "--config") {
374
+ const value = allArgs[i + 1] ?? "";
375
+ if (/sandbox_mode\s*=\s*"?danger-full-access"?/.test(value)) return "open";
376
+ if (/sandbox_mode\s*=\s*"?read-only"?/.test(value)) return "read-only";
377
+ }
378
+ }
379
+ return undefined;
380
+ },
381
+ };
382
+
383
+ export function detectApprovalModeFromArgs(provider: string, defaultArgs: string[], providerArgs: string[]): string | undefined {
384
+ const detector = APPROVAL_MODE_DETECTORS[provider];
385
+ return detector ? detector([...defaultArgs, ...providerArgs]) : undefined;
386
+ }
387
+
388
+ if (import.meta.main) {
389
+ main().catch((error) => {
390
+ // A fatal startup failure (e.g. the runner could not register with the relay within the
391
+ // deadline) must exit non-zero, not hang or swallow — the orchestrator's exit detection
392
+ // then reports it and the spawn-parent is notified instead of waiting on a silent zombie.
393
+ console.error(`[agent-relay] runner fatal: ${errMessage(error)}`);
394
+ process.exit(1);
395
+ });
396
+ }