agent-relay-runner 0.60.1 → 0.61.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-runner",
3
- "version": "0.60.1",
3
+ "version": "0.61.1",
4
4
  "description": "Unified provider lifecycle runner for Agent Relay",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,7 +20,7 @@
20
20
  "directory": "runner"
21
21
  },
22
22
  "dependencies": {
23
- "agent-relay-sdk": "0.2.36"
23
+ "agent-relay-sdk": "0.2.37"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/bun": "latest",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-relay-runner",
3
3
  "description": "Thin Agent Relay runner bridge for Claude Code",
4
- "version": "0.60.1",
4
+ "version": "0.61.1",
5
5
  "agentRelayContracts": {
6
6
  "providerPluginProtocol": 1
7
7
  }
package/src/config.ts CHANGED
@@ -2,8 +2,8 @@ import { execFileSync } from "node:child_process";
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { homedir, hostname } from "node:os";
4
4
  import { join, resolve } from "node:path";
5
- import { DEFAULT_RELAY_URL, stringValue } from "agent-relay-sdk";
6
- import type { WorkspaceMetadata } from "agent-relay-sdk";
5
+ import { DEFAULT_RELAY_URL, RELAY_TOKEN_HEADER, errMessage, stringValue } from "agent-relay-sdk";
6
+ import type { SettingEntry, WorkspaceMetadata } from "agent-relay-sdk";
7
7
  import { sanitizeFsName } from "agent-relay-sdk/fs-name";
8
8
  import type { ProviderConfig } from "./adapter";
9
9
 
@@ -36,6 +36,7 @@ export function defaultProviderConfig(provider: string): ProviderConfig {
36
36
  defaultApprovalMode: "guarded",
37
37
  defaultTags: [],
38
38
  chatCaptureMode: "final",
39
+ reasoningCapture: true,
39
40
  headless: {
40
41
  tmuxPrefix: `${provider}-relay`,
41
42
  shutdownTimeoutMs: 10_000,
@@ -67,6 +68,7 @@ export function loadProviderConfig(provider: string, home = agentRelayHome()): L
67
68
  defaultApprovalMode: stringValue(raw.defaultApprovalMode) ?? defaults.defaultApprovalMode,
68
69
  defaultTags: stringArray(raw.defaultTags) ?? defaults.defaultTags,
69
70
  chatCaptureMode: enumValue(raw.chatCaptureMode, ["final", "full"]) ?? defaults.chatCaptureMode,
71
+ reasoningCapture: booleanValue(raw.reasoningCapture) ?? defaults.reasoningCapture,
70
72
  headless: {
71
73
  tmuxPrefix: stringValue(recordValue(raw.headless).tmuxPrefix) ?? defaults.headless.tmuxPrefix,
72
74
  shutdownTimeoutMs: positiveInteger(recordValue(raw.headless).shutdownTimeoutMs) ?? defaults.headless.shutdownTimeoutMs,
@@ -74,6 +76,24 @@ export function loadProviderConfig(provider: string, home = agentRelayHome()): L
74
76
  };
75
77
  }
76
78
 
79
+ export async function loadProviderConfigFromRelay(
80
+ provider: string,
81
+ opts: { relayUrl: string; token?: string; home?: string; host?: string },
82
+ ): Promise<LoadedProviderConfig> {
83
+ const host = opts.host ?? hostname();
84
+ const home = opts.home ?? agentRelayHome();
85
+ const local = loadProviderConfig(provider, home);
86
+ const key = `${host}/${provider}`;
87
+
88
+ try {
89
+ const fetched = await fetchProviderConfig(provider, opts.relayUrl, opts.token, key);
90
+ if (fetched) return fetched;
91
+ } catch (error) {
92
+ console.warn(`[agent-relay] provider config relay read failed (${errMessage(error)}); using local fallback ${local.path}.`);
93
+ }
94
+ return local;
95
+ }
96
+
77
97
  export function writeProviderConfig(provider: string, config: ProviderConfig, home = agentRelayHome()): LoadedProviderConfig {
78
98
  const dir = providersDir(home);
79
99
  mkdirSync(dir, { recursive: true });
@@ -93,6 +113,7 @@ export function providerConfigPublic(config: LoadedProviderConfig): Record<strin
93
113
  defaultApprovalMode: config.defaultApprovalMode,
94
114
  defaultTags: config.defaultTags,
95
115
  chatCaptureMode: config.chatCaptureMode,
116
+ reasoningCapture: config.reasoningCapture,
96
117
  headless: config.headless,
97
118
  };
98
119
  }
@@ -156,6 +177,10 @@ function enumValue<T extends string>(value: unknown, allowed: T[]): T | undefine
156
177
  return typeof value === "string" && allowed.includes(value as T) ? (value as T) : undefined;
157
178
  }
158
179
 
180
+ function booleanValue(value: unknown): boolean | undefined {
181
+ return typeof value === "boolean" ? value : undefined;
182
+ }
183
+
159
184
  function stringArray(value: unknown): string[] | undefined {
160
185
  return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : undefined;
161
186
  }
@@ -178,3 +203,42 @@ function maskEnv(env: Record<string, string>): Record<string, string> {
178
203
  }
179
204
  return result;
180
205
  }
206
+
207
+ async function fetchProviderConfig(
208
+ provider: string,
209
+ relayUrl: string,
210
+ token: string | undefined,
211
+ key: string,
212
+ ): Promise<LoadedProviderConfig | null> {
213
+ const url = new URL(`/api/settings/provider-config/${encodeURIComponent(key)}`, relayUrl);
214
+ const res = await fetch(url, {
215
+ headers: token ? { [RELAY_TOKEN_HEADER]: token } : undefined,
216
+ signal: AbortSignal.timeout(5_000),
217
+ });
218
+ if (res.status === 404) return null;
219
+ const body = await res.json().catch(() => null) as (Partial<SettingEntry> & { error?: string }) | null;
220
+ if (!res.ok) throw new Error(body?.error ? `${res.status}: ${body.error}` : `HTTP ${res.status}`);
221
+ if (body?.version === 0) return null;
222
+ return providerConfigFromValue(provider, body?.value, `config:provider-config/${key}`);
223
+ }
224
+
225
+ function providerConfigFromValue(provider: string, value: unknown, path: string): LoadedProviderConfig {
226
+ const raw = recordValue(value);
227
+ const defaults = defaultProviderConfig(provider);
228
+ return {
229
+ path,
230
+ command: stringValue(raw.command) ?? defaults.command,
231
+ defaultArgs: stringArray(raw.defaultArgs) ?? defaults.defaultArgs,
232
+ env: stringRecord(raw.env) ?? defaults.env,
233
+ pluginDirs: stringArray(raw.pluginDirs) ?? defaults.pluginDirs,
234
+ defaultCapabilities: stringArray(raw.defaultCapabilities) ?? defaults.defaultCapabilities,
235
+ defaultApprovalMode: stringValue(raw.defaultApprovalMode) ?? defaults.defaultApprovalMode,
236
+ defaultTags: stringArray(raw.defaultTags) ?? defaults.defaultTags,
237
+ chatCaptureMode: enumValue(raw.chatCaptureMode, ["final", "full"]) ?? defaults.chatCaptureMode,
238
+ reasoningCapture: booleanValue(raw.reasoningCapture) ?? defaults.reasoningCapture,
239
+ headless: {
240
+ tmuxPrefix: stringValue(recordValue(raw.headless).tmuxPrefix) ?? defaults.headless.tmuxPrefix,
241
+ shutdownTimeoutMs: positiveInteger(recordValue(raw.headless).shutdownTimeoutMs) ?? defaults.headless.shutdownTimeoutMs,
242
+ },
243
+ };
244
+ }
package/src/index.ts CHANGED
@@ -2,16 +2,15 @@
2
2
  import { hostname } from "node:os";
3
3
  import { basename } from "node:path";
4
4
  import { isatty } from "node:tty";
5
- import { ClaudeAdapter } from "./adapters/claude";
6
- import { CodexAdapter } from "./adapters/codex";
7
5
  import { AgentRunner } from "./runner";
8
- import { loadGlobalConfig, loadProviderConfig, resolveCwd, runnerId } from "./config";
6
+ import { loadGlobalConfig, loadProviderConfigFromRelay, resolveCwd, runnerId } from "./config";
7
+ import { createProviderAdapter } from "./providers";
9
8
  import { VERSION } from "./version";
10
- import type { AgentProfile, WorkspaceMetadata } from "agent-relay-sdk";
9
+ import type { AgentProfile, SpawnProvider, WorkspaceMetadata } from "agent-relay-sdk";
11
10
  import { errMessage, normalizeAgentLifecycle, RELAY_TOKEN_HEADER } from "agent-relay-sdk";
12
11
 
13
12
  interface CliOptions {
14
- provider: "claude" | "codex";
13
+ provider: SpawnProvider;
15
14
  model?: string;
16
15
  effort?: string;
17
16
  headless: boolean;
@@ -38,11 +37,14 @@ export async function main(argv = process.argv): Promise<void> {
38
37
  }
39
38
  const opts = parseArgs(argv);
40
39
  const globalConfig = loadGlobalConfig();
41
- const providerConfig = loadProviderConfig(opts.provider);
40
+ const relayUrl = opts.relayUrl ?? globalConfig.relayUrl;
41
+ const providerConfig = await loadProviderConfigFromRelay(opts.provider, {
42
+ relayUrl,
43
+ token: opts.token ?? globalConfig.token,
44
+ });
42
45
  const cwd = resolveCwd(opts.cwd, globalConfig.defaultCwd);
43
46
  const id = runnerId(opts.provider, cwd, opts.label);
44
- const adapter = opts.provider === "claude" ? new ClaudeAdapter() : new CodexAdapter();
45
- const relayUrl = opts.relayUrl ?? globalConfig.relayUrl;
47
+ const adapter = createProviderAdapter(opts.provider);
46
48
  const runtimeAuth = await resolveRunnerToken({
47
49
  interactive: opts.interactive,
48
50
  relayUrl,
@@ -212,7 +214,7 @@ export function isVersionRequest(argv: string[]): boolean {
212
214
 
213
215
  function parseArgs(argv: string[]): CliOptions {
214
216
  const bin = [argv[1], process.env._].map((s) => basename(s || "")).join(" ");
215
- let provider: "claude" | "codex" = bin.includes("claude") ? "claude" : "codex";
217
+ let provider: SpawnProvider = bin.includes("claude") ? "claude" : "codex";
216
218
  const relayArgs = argv.slice(2);
217
219
  const providerSep = relayArgs.indexOf("--");
218
220
  const ownArgs = providerSep >= 0 ? relayArgs.slice(0, providerSep) : relayArgs;
@@ -0,0 +1,18 @@
1
+ import type { SpawnProvider } from "agent-relay-sdk";
2
+ import type { ProviderAdapter } from "./adapter";
3
+ import { ClaudeAdapter } from "./adapters/claude";
4
+ import { CodexAdapter } from "./adapters/codex";
5
+
6
+ interface RunnerProviderRegistration {
7
+ provider: SpawnProvider;
8
+ createAdapter: () => ProviderAdapter;
9
+ }
10
+
11
+ const RUNNER_PROVIDERS: Record<SpawnProvider, RunnerProviderRegistration> = {
12
+ claude: { provider: "claude", createAdapter: () => new ClaudeAdapter() },
13
+ codex: { provider: "codex", createAdapter: () => new CodexAdapter() },
14
+ };
15
+
16
+ export function createProviderAdapter(provider: SpawnProvider): ProviderAdapter {
17
+ return RUNNER_PROVIDERS[provider].createAdapter();
18
+ }