agent-relay-runner 0.72.0 → 0.73.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-runner",
3
- "version": "0.72.0",
3
+ "version": "0.73.0",
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.47"
23
+ "agent-relay-sdk": "0.2.48"
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.72.0",
4
+ "version": "0.73.0",
5
5
  "agentRelayContracts": {
6
6
  "providerPluginProtocol": 1
7
7
  }
package/src/config.ts CHANGED
@@ -111,6 +111,10 @@ export function workspaceJsonFromEnv(): string | undefined {
111
111
  return process.env.AGENT_RELAY_WORKSPACE_JSON;
112
112
  }
113
113
 
114
+ export function relayInjectionEventsJsonFromEnv(): string | undefined {
115
+ return process.env.AGENT_RELAY_INJECTION_EVENTS_JSON;
116
+ }
117
+
114
118
  export function tmuxSessionFromEnv(): string | undefined {
115
119
  return process.env.AGENT_RELAY_TMUX_SESSION;
116
120
  }
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  loadGlobalConfig,
13
13
  loadProviderConfigFromRelay,
14
14
  policyNameFromEnv,
15
+ relayInjectionEventsJsonFromEnv,
15
16
  resolveCwd,
16
17
  runnerId,
17
18
  runtimeTokenExpiresAtFromEnv,
@@ -23,8 +24,9 @@ import {
23
24
  } from "./config";
24
25
  import { createProviderAdapter } from "./providers";
25
26
  import { VERSION } from "./version";
26
- import type { AgentProfile, SpawnProvider, WorkspaceMetadata } from "agent-relay-sdk";
27
- import { errMessage, normalizeAgentLifecycle, RELAY_TOKEN_HEADER } from "agent-relay-sdk";
27
+ import type { AgentProfile, RelayInjectionCategory, SpawnProvider, WorkspaceMetadata } from "agent-relay-sdk";
28
+ import { errMessage, isRecord, normalizeAgentLifecycle, RELAY_TOKEN_HEADER } from "agent-relay-sdk";
29
+ import type { RunnerRelayInjectionEvent } from "./relay-injection-events";
28
30
 
29
31
  interface CliOptions {
30
32
  provider: SpawnProvider;
@@ -42,6 +44,7 @@ interface CliOptions {
42
44
  agentId?: string;
43
45
  prompt?: string;
44
46
  systemPromptAppend?: string;
47
+ relayInjectionEvents?: RunnerRelayInjectionEvent[];
45
48
  tags: string[];
46
49
  caps: string[];
47
50
  providerArgs: string[];
@@ -106,6 +109,7 @@ export async function main(argv = process.argv): Promise<void> {
106
109
  workspace: parseWorkspaceEnv(workspaceJsonFromEnv()),
107
110
  prompt: opts.prompt,
108
111
  systemPromptAppend: opts.systemPromptAppend,
112
+ relayInjectionEvents: parseRelayInjectionEventsEnv(relayInjectionEventsJsonFromEnv()),
109
113
  tmuxSession: tmuxSessionFromEnv(),
110
114
  tags: opts.tags,
111
115
  capabilities: opts.caps,
@@ -306,6 +310,23 @@ function parseWorkspaceEnv(raw: string | undefined): WorkspaceMetadata | undefin
306
310
  }
307
311
  }
308
312
 
313
+ function parseRelayInjectionEventsEnv(raw: string | undefined): RunnerRelayInjectionEvent[] | undefined {
314
+ if (!raw) return undefined;
315
+ try {
316
+ const parsed = JSON.parse(raw);
317
+ if (!Array.isArray(parsed)) return undefined;
318
+ const categories = new Set(["memory", "instructions", "tools", "comms", "knowledge", "context"]);
319
+ const events = parsed.flatMap((item): RunnerRelayInjectionEvent[] => {
320
+ if (!isRecord(item) || typeof item.summary !== "string" || !categories.has(String(item.category))) return [];
321
+ const detail = isRecord(item.detail) ? item.detail : {};
322
+ return [{ category: item.category as RelayInjectionCategory, summary: item.summary, detail }];
323
+ });
324
+ return events.length ? events : undefined;
325
+ } catch {
326
+ return undefined;
327
+ }
328
+ }
329
+
309
330
  function printHelp(provider: string): void {
310
331
  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...]`);
311
332
  }
@@ -0,0 +1,97 @@
1
+ import type { MessageSessionMeta, RelayInjectionCategory, SpawnProvider } from "agent-relay-sdk";
2
+ import type { ProviderConfig, RunnerSpawnConfig } from "./adapter";
3
+ import { assembleLaunch } from "./launch-assembly";
4
+
5
+ export interface RunnerRelayInjectionEvent {
6
+ category: RelayInjectionCategory;
7
+ summary: string;
8
+ detail: Record<string, unknown>;
9
+ }
10
+
11
+ interface RunnerRelayInjectionSessionEvent {
12
+ from: string;
13
+ to: string;
14
+ body: string;
15
+ session: MessageSessionMeta;
16
+ }
17
+
18
+ interface KeyedRelayInjectionEvent {
19
+ key: string;
20
+ event: RunnerRelayInjectionEvent;
21
+ }
22
+
23
+ interface RunnerLaunchInjectionInput {
24
+ carried?: RunnerRelayInjectionEvent[];
25
+ provider: string;
26
+ config: RunnerSpawnConfig;
27
+ providerConfig: ProviderConfig;
28
+ agentId: string;
29
+ providerSessionId: string;
30
+ timestamp?: number;
31
+ }
32
+
33
+ function supportsLaunchAssembly(provider: string): provider is SpawnProvider {
34
+ return provider === "claude" || provider === "codex";
35
+ }
36
+
37
+ function launchInjectionEvents(input: RunnerLaunchInjectionInput): KeyedRelayInjectionEvent[] {
38
+ const events: KeyedRelayInjectionEvent[] = (input.carried ?? []).map((event, index) => ({ key: `spawn-${index}`, event }));
39
+ if (!supportsLaunchAssembly(input.provider)) return events;
40
+ const assembled = assembleLaunch(input.provider, input.config, input.providerConfig);
41
+ const toolCount = assembled.skills.length + assembled.plugins.length + assembled.mcp.servers.length
42
+ + (assembled.relaySkills ? 1 : 0)
43
+ + (assembled.relayPlugin ? 1 : 0)
44
+ + (assembled.mcp.relayEndpoint ? 1 : 0)
45
+ + (assembled.statusLine ? 1 : 0);
46
+ if (toolCount === 0) return events;
47
+ const parts = [
48
+ assembled.relaySkills ? "relay skills" : "",
49
+ assembled.relayPlugin ? "relay plugin" : "",
50
+ assembled.statusLine ? "status line" : "",
51
+ assembled.mcp.relayEndpoint ? "relay MCP" : "",
52
+ assembled.skills.length ? `${assembled.skills.length} skill${assembled.skills.length === 1 ? "" : "s"}` : "",
53
+ assembled.plugins.length ? `${assembled.plugins.length} plugin${assembled.plugins.length === 1 ? "" : "s"}` : "",
54
+ assembled.mcp.servers.length ? `${assembled.mcp.servers.length} MCP server${assembled.mcp.servers.length === 1 ? "" : "s"}` : "",
55
+ ].filter(Boolean);
56
+ events.push({
57
+ key: "tools",
58
+ event: {
59
+ category: "tools",
60
+ summary: `provisioned ${parts.join(", ")}`,
61
+ detail: {
62
+ source: "launch-assembly",
63
+ provider: assembled.provider,
64
+ base: assembled.base,
65
+ vanilla: assembled.vanilla,
66
+ relayContext: assembled.relayContext,
67
+ relaySkills: assembled.relaySkills,
68
+ relayPlugin: assembled.relayPlugin,
69
+ statusLine: assembled.statusLine,
70
+ skills: assembled.skills,
71
+ plugins: assembled.plugins,
72
+ mcp: assembled.mcp,
73
+ },
74
+ },
75
+ });
76
+ return events;
77
+ }
78
+
79
+ export function runnerLaunchInjectionSessionEvents(input: RunnerLaunchInjectionInput): RunnerRelayInjectionSessionEvent[] {
80
+ const timestamp = input.timestamp ?? Date.now();
81
+ return launchInjectionEvents(input).map(({ event, key }) => ({
82
+ from: input.agentId,
83
+ to: "user",
84
+ body: event.summary,
85
+ session: {
86
+ type: "relay-injection",
87
+ stepId: `relay-injection:${input.providerSessionId}:${key}`,
88
+ relayInjection: {
89
+ category: event.category,
90
+ summary: event.summary,
91
+ detail: event.detail,
92
+ agentId: input.agentId,
93
+ timestamp,
94
+ },
95
+ },
96
+ }));
97
+ }
@@ -23,6 +23,7 @@ import { deliverBufferedMcpCall as deliverBufferedMcpOutboxCall, deliverRunnerOu
23
23
  import { RunnerInsights } from "./runner-insights";
24
24
  import { BusyReconciler } from "./busy-reconciler";
25
25
  import { publishCapturedResponse } from "./response-capture-report";
26
+ import { runnerLaunchInjectionSessionEvents, type RunnerRelayInjectionEvent } from "./relay-injection-events";
26
27
  import { capsFromEnv, contextStateDirFromEnv, logLevelFromEnv, mcpProxyEnabledFromEnv, orchestratorBaseDirFromEnv, orchestratorUrlFromEnv, registrationTimeoutMsFromEnv, runnerInfoFileFromEnv, runnerLogFileFromEnv, runnerOutboxDirWithInfoFallback, sessionDebugEnabled, tagsFromEnv, workspaceModeFromEnv } from "./config";
27
28
  import {
28
29
  appliedAgentProfileMetadata,
@@ -115,6 +116,7 @@ interface RunnerOptions {
115
116
  agentProfile?: AgentProfile;
116
117
  prompt?: string;
117
118
  systemPromptAppend?: string;
119
+ relayInjectionEvents?: RunnerRelayInjectionEvent[];
118
120
  tags: string[];
119
121
  capabilities: string[];
120
122
  providerArgs: string[];
@@ -601,7 +603,12 @@ export class AgentRunner {
601
603
  },
602
604
  };
603
605
  const launchSeededPrompt = this.options.adapter.seedsInitialPromptAtLaunch ? this.options.prompt?.trim() : ""; if (launchSeededPrompt) this.recordInjectedPrompt(launchSeededPrompt);
604
- return this.options.adapter.spawn(config);
606
+ const managedProcess = await this.options.adapter.spawn(config);
607
+ const injectionEvents = runnerLaunchInjectionSessionEvents({ carried: this.options.relayInjectionEvents, provider: this.options.provider, config, providerConfig: this.options.providerConfig, agentId: this.agentId, providerSessionId: this.providerSessionId });
608
+ for (const event of injectionEvents) {
609
+ this.publishSessionEvent(event);
610
+ }
611
+ return managedProcess;
605
612
  }
606
613
 
607
614
  private async terminalAttachSpec(): Promise<TerminalAttachSpec> {