agent-relay-runner 0.113.0 → 0.115.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.
|
|
3
|
+
"version": "0.115.0",
|
|
4
4
|
"description": "Unified provider lifecycle runner for Agent Relay",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"agent-relay-providers": "0.104.1",
|
|
24
|
-
"agent-relay-sdk": "0.2.
|
|
24
|
+
"agent-relay-sdk": "0.2.97",
|
|
25
25
|
"callmux": "0.23.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
package/src/launch-assembly.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import {
|
|
11
11
|
projectConfigProjectionOn,
|
|
12
12
|
hostUserScopeMcpServers,
|
|
13
|
+
profileAllowsHostAssets,
|
|
13
14
|
profileUsesProviderHostGlobals,
|
|
14
15
|
providerHomePathFor,
|
|
15
16
|
providerProvisioningHomePathFor,
|
|
@@ -19,6 +20,7 @@ import { claudeLaunchPromptGateSettings } from "./claude-prompt-gates";
|
|
|
19
20
|
import {
|
|
20
21
|
materializeResolvedAssets,
|
|
21
22
|
resolveClaudeProjectSkills,
|
|
23
|
+
resolveClaudeHostSkills,
|
|
22
24
|
resolveProjectClaudeSettings,
|
|
23
25
|
resolveClaudeProvisionedPlugins,
|
|
24
26
|
resolveClaudeProvisionedSkills,
|
|
@@ -127,10 +129,14 @@ export interface AssembledLaunch {
|
|
|
127
129
|
skills: AssembledAsset[];
|
|
128
130
|
/** Project `.claude/skills` materialized for this Claude launch (#668). */
|
|
129
131
|
projectSkills: AssembledAsset[];
|
|
132
|
+
/** Host/global Claude skills explicitly inherited without enabling host MCP. */
|
|
133
|
+
hostSkills: AssembledAsset[];
|
|
130
134
|
/** Project `.claude/settings.json` sanitized and injected for this Claude launch (#669). */
|
|
131
135
|
projectSettings: { applied: boolean; keys: string[]; ignoredKeys: string[]; file?: string };
|
|
132
136
|
/** Relay-provisioned plugins (Claude --plugin-dir). Empty for Codex (no plugin surface). */
|
|
133
137
|
plugins: AssembledAsset[];
|
|
138
|
+
/** Host/global provider plugin dirs explicitly inherited without enabling host MCP. */
|
|
139
|
+
hostPluginDirs: string[];
|
|
134
140
|
mcp: AssembledMcp;
|
|
135
141
|
instructions: AssembledInstructions;
|
|
136
142
|
/** Provider provisioning/vanilla/instruction launch-arg block, in launch order. */
|
|
@@ -236,8 +242,11 @@ function assembleClaude(config: RunnerSpawnConfig, providerConfig: ProviderConfi
|
|
|
236
242
|
const statusLine = profileAllowsRelayFeature(config, "statusLine");
|
|
237
243
|
|
|
238
244
|
const skills = configHome ? resolveClaudeProvisionedSkills(configHome, config) : [];
|
|
245
|
+
const hostSkills = configHome
|
|
246
|
+
? resolveClaudeHostSkills(configHome, config, new Set(skills.map((s) => s.name)))
|
|
247
|
+
: [];
|
|
239
248
|
const projectSkills = configHome
|
|
240
|
-
? resolveClaudeProjectSkills(configHome, config, new Set(skills.map((s) => s.name)))
|
|
249
|
+
? resolveClaudeProjectSkills(configHome, config, new Set([...skills, ...hostSkills].map((s) => s.name)))
|
|
241
250
|
: [];
|
|
242
251
|
const projectSettings = resolveProjectClaudeSettings(config);
|
|
243
252
|
const provisionedPlugins = assetHome ? resolveClaudeProvisionedPlugins(assetHome, config) : [];
|
|
@@ -249,7 +258,7 @@ function assembleClaude(config: RunnerSpawnConfig, providerConfig: ProviderConfi
|
|
|
249
258
|
// host plugin dirs → ONLY host-base profiles; isolated/vanilla must not pull them back in.
|
|
250
259
|
const relayPluginDirs = !config.agentProfile && relayPlugin ? [CLAUDE_RELAY_PLUGIN_DIR] : [];
|
|
251
260
|
const provisionedPluginDirs = provisionedPlugins.map((p) => p.dir);
|
|
252
|
-
const hostPluginDirs =
|
|
261
|
+
const hostPluginDirs = profileAllowsHostAssets(config, "plugins") ? providerConfig.pluginDirs : [];
|
|
253
262
|
const pluginDirs = [...new Set([...relayPluginDirs, ...provisionedPluginDirs, ...hostPluginDirs])];
|
|
254
263
|
|
|
255
264
|
const provisionedMcp = profileProvisioning(config).mcpServers;
|
|
@@ -333,6 +342,7 @@ function assembleClaude(config: RunnerSpawnConfig, providerConfig: ProviderConfi
|
|
|
333
342
|
relayPlugin,
|
|
334
343
|
statusLine,
|
|
335
344
|
skills: skills.map((s) => ({ name: s.name, dir: s.dir })),
|
|
345
|
+
hostSkills: hostSkills.map((s) => ({ name: s.name, dir: s.dir })),
|
|
336
346
|
projectSkills: projectSkills.map((s) => ({ name: s.name, dir: s.dir })),
|
|
337
347
|
projectSettings: {
|
|
338
348
|
applied: projectSettings.applied,
|
|
@@ -341,6 +351,7 @@ function assembleClaude(config: RunnerSpawnConfig, providerConfig: ProviderConfi
|
|
|
341
351
|
...(projectSettings.file ? { file: projectSettings.file } : {}),
|
|
342
352
|
},
|
|
343
353
|
plugins: provisionedPlugins.map((p) => ({ name: p.name, dir: p.dir })),
|
|
354
|
+
hostPluginDirs,
|
|
344
355
|
mcp: {
|
|
345
356
|
relayEndpoint, strict: strictMcp, servers: mcpServers, projectServers, hostServers: hostMcpServers,
|
|
346
357
|
...(sharedListenerUrl ? { sharedListener: { url: sharedListenerUrl } } : {}),
|
|
@@ -410,9 +421,11 @@ function assembleCodex(config: RunnerSpawnConfig, _providerConfig: ProviderConfi
|
|
|
410
421
|
relayPlugin: false,
|
|
411
422
|
statusLine: false,
|
|
412
423
|
skills: provisionedSkills.map((s) => ({ name: s.name, dir: s.dir })),
|
|
424
|
+
hostSkills: [],
|
|
413
425
|
projectSkills: [],
|
|
414
426
|
projectSettings: { applied: false, keys: [], ignoredKeys: [] },
|
|
415
427
|
plugins: [],
|
|
428
|
+
hostPluginDirs: [],
|
|
416
429
|
mcp: {
|
|
417
430
|
relayEndpoint, strict: false, servers: mcpServers, projectServers, hostServers: [],
|
|
418
431
|
...(sharedListenerUrl ? { sharedListener: { url: sharedListenerUrl } } : {}),
|
|
@@ -451,7 +464,9 @@ function materializeClaudeAssets(config: RunnerSpawnConfig): void {
|
|
|
451
464
|
if (configHome) {
|
|
452
465
|
const skills = resolveClaudeProvisionedSkills(configHome, config);
|
|
453
466
|
materializeResolvedAssets(skills);
|
|
454
|
-
|
|
467
|
+
const hostSkills = resolveClaudeHostSkills(configHome, config, new Set(skills.map((s) => s.name)));
|
|
468
|
+
materializeResolvedAssets(hostSkills);
|
|
469
|
+
materializeResolvedAssets(resolveClaudeProjectSkills(configHome, config, new Set([...skills, ...hostSkills].map((s) => s.name))));
|
|
455
470
|
}
|
|
456
471
|
if (assetHome) materializeResolvedAssets(resolveClaudeProvisionedPlugins(assetHome, config));
|
|
457
472
|
}
|
package/src/profile-home.ts
CHANGED
|
@@ -18,6 +18,17 @@ export function profileUsesHostProviderGlobals(config: { agentProfile?: RunnerSp
|
|
|
18
18
|
return !config.agentProfile || config.agentProfile.base === "host";
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
export function profileAllowsHostAssets(
|
|
22
|
+
config: { agentProfile?: RunnerSpawnConfig["agentProfile"] },
|
|
23
|
+
asset: "skills" | "plugins",
|
|
24
|
+
): boolean {
|
|
25
|
+
return profileUsesProviderHostGlobals(config) || Boolean(config.agentProfile?.hostAssets?.[asset]);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function profileAllowsHostGlobalInstructions(config: { agentProfile?: RunnerSpawnConfig["agentProfile"] }): boolean {
|
|
29
|
+
return !config.agentProfile || config.agentProfile.instructions.globalInstructions === "allow";
|
|
30
|
+
}
|
|
31
|
+
|
|
21
32
|
export function projectConfigProjectionOn(config: { agentProfile?: RunnerSpawnConfig["agentProfile"] }): boolean {
|
|
22
33
|
return Boolean(config.agentProfile?.projectSkills || config.agentProfile?.projectSettings);
|
|
23
34
|
}
|
|
@@ -90,6 +101,7 @@ function prepareProviderHome(provider: SpawnProvider, config: RunnerSpawnConfig)
|
|
|
90
101
|
const authItems = manifest?.home?.authItems ?? [];
|
|
91
102
|
const sourceHome = providerSourceHome(provider);
|
|
92
103
|
const authLinked = linkExistingAuthItems(sourceHome, target, authItems);
|
|
104
|
+
linkHostGlobalInstructionFiles(provider, sourceHome, target, config);
|
|
93
105
|
return { path: target, authLinked };
|
|
94
106
|
}
|
|
95
107
|
|
|
@@ -101,14 +113,16 @@ function bootstrapCodexFirstRun(codexHome: string, config: RunnerSpawnConfig): v
|
|
|
101
113
|
}
|
|
102
114
|
|
|
103
115
|
function bootstrapClaudeFirstRun(claudeHome: string, config: RunnerSpawnConfig): void {
|
|
104
|
-
if (profileAllowsRelayFeature(config, "context")
|
|
116
|
+
if (profileAllowsRelayFeature(config, "context") && !profileAllowsHostGlobalInstructions(config)) {
|
|
117
|
+
writeClaudeRelayManual(claudeHome);
|
|
118
|
+
}
|
|
105
119
|
seedClaudeConfigIfMissing(claudeHome, config);
|
|
106
120
|
}
|
|
107
121
|
|
|
108
122
|
BOOTSTRAP_HOOKS.set("codex", bootstrapCodexFirstRun);
|
|
109
123
|
BOOTSTRAP_HOOKS.set("claude", bootstrapClaudeFirstRun);
|
|
110
124
|
|
|
111
|
-
function providerSourceHome(provider: string): string {
|
|
125
|
+
export function providerSourceHome(provider: string): string {
|
|
112
126
|
const manifest = getManifest(provider);
|
|
113
127
|
const envVar = manifest?.home?.configDirEnvVar;
|
|
114
128
|
const configDir = manifest?.home?.configDir ?? `.${provider}`;
|
|
@@ -161,15 +175,23 @@ function sanitizePathPart(value: string): string {
|
|
|
161
175
|
function linkExistingAuthItems(sourceHome: string, targetHome: string, items: string[]): string[] {
|
|
162
176
|
const linked: string[] = [];
|
|
163
177
|
for (const item of items) {
|
|
164
|
-
|
|
165
|
-
const target = join(targetHome, item);
|
|
166
|
-
if (!existsSync(source) || existsSync(target)) continue;
|
|
167
|
-
symlinkSync(source, target);
|
|
168
|
-
linked.push(item);
|
|
178
|
+
if (linkExistingPath(join(sourceHome, item), join(targetHome, item))) linked.push(item);
|
|
169
179
|
}
|
|
170
180
|
return linked;
|
|
171
181
|
}
|
|
172
182
|
|
|
183
|
+
function linkExistingPath(source: string, target: string): boolean {
|
|
184
|
+
if (!existsSync(source) || existsSync(target)) return false;
|
|
185
|
+
symlinkSync(source, target);
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function linkHostGlobalInstructionFiles(provider: SpawnProvider, sourceHome: string, targetHome: string, config: RunnerSpawnConfig): void {
|
|
190
|
+
if (!profileAllowsHostGlobalInstructions(config)) return;
|
|
191
|
+
const files = provider === "claude" ? ["CLAUDE.md"] : provider === "codex" ? ["AGENTS.md"] : [];
|
|
192
|
+
for (const file of files) linkExistingPath(join(sourceHome, file), join(targetHome, file));
|
|
193
|
+
}
|
|
194
|
+
|
|
173
195
|
function trustWorkspaceForCodex(codexHome: string, config: RunnerSpawnConfig): void {
|
|
174
196
|
if (!config.cwd) return;
|
|
175
197
|
const path = join(codexHome, "config.toml");
|
|
@@ -33,6 +33,8 @@ export function agentProfileProjectionReport(input: AgentProfileProjectionInput)
|
|
|
33
33
|
add(hooksEntry(assembled));
|
|
34
34
|
add(provisioningSkillsEntry(assembled));
|
|
35
35
|
add(provisioningPluginsEntry(assembled));
|
|
36
|
+
add(hostSkillsEntry(assembled));
|
|
37
|
+
add(hostPluginsEntry(assembled));
|
|
36
38
|
add(provisioningMcpEntry(assembled));
|
|
37
39
|
for (const entry of provisioningSkippedEntries(assembled)) add(entry);
|
|
38
40
|
add(projectMcpEntry(assembled));
|
|
@@ -166,6 +168,29 @@ function provisioningPluginsEntry(a: AssembledLaunch): AgentProfileProjectionEnt
|
|
|
166
168
|
return applied("provisioning.plugins", `${count}`, `Relay-provisioned plugins passed to ${label} via the plugin dir mechanism${names}.`);
|
|
167
169
|
}
|
|
168
170
|
|
|
171
|
+
function hostSkillsEntry(a: AssembledLaunch): AgentProfileProjectionEntry {
|
|
172
|
+
const optedIn = Boolean(a.profile?.hostAssets?.skills);
|
|
173
|
+
if (!optedIn) return notApplicable("hostAssets.skills", "off", "Host/global skill inheritance is off for this resolved profile.");
|
|
174
|
+
const label = getManifest(a.provider)?.label ?? a.provider;
|
|
175
|
+
if (!getManifest(a.provider)?.provisioning?.bundledPlugin) {
|
|
176
|
+
return notApplicable("hostAssets.skills", "unsupported", `${label} does not expose host skill-dir projection through Agent Relay.`);
|
|
177
|
+
}
|
|
178
|
+
if (!a.hostSkills.length) return applied("hostAssets.skills", "none", "Host/global skill inheritance is on, but no valid host skill dirs were found.");
|
|
179
|
+
const names = a.hostSkills.map((s) => s.name).join(", ");
|
|
180
|
+
return applied("hostAssets.skills", `${a.hostSkills.length}`, `Host/global skills materialized into Relay's managed ${label} config home (${names}).`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function hostPluginsEntry(a: AssembledLaunch): AgentProfileProjectionEntry {
|
|
184
|
+
const optedIn = Boolean(a.profile?.hostAssets?.plugins);
|
|
185
|
+
if (!optedIn) return notApplicable("hostAssets.plugins", "off", "Host/global plugin inheritance is off for this resolved profile.");
|
|
186
|
+
const label = getManifest(a.provider)?.label ?? a.provider;
|
|
187
|
+
if (!getManifest(a.provider)?.provisioning?.bundledPlugin) {
|
|
188
|
+
return notApplicable("hostAssets.plugins", "unsupported", `${label} has no plugin-dir surface for host plugin inheritance.`);
|
|
189
|
+
}
|
|
190
|
+
if (!a.hostPluginDirs.length) return applied("hostAssets.plugins", "none", "Host/global plugin inheritance is on, but no host plugin dirs are configured.");
|
|
191
|
+
return applied("hostAssets.plugins", `${a.hostPluginDirs.length}`, `Host/global plugin dirs passed to ${label} (${a.hostPluginDirs.join(", ")}).`);
|
|
192
|
+
}
|
|
193
|
+
|
|
169
194
|
function provisioningMcpEntry(a: AssembledLaunch): AgentProfileProjectionEntry {
|
|
170
195
|
const names = a.mcp.servers;
|
|
171
196
|
if (!names.length) return notApplicable("provisioning.mcp", "none", "No relay-provisioned MCP servers materialized for this launch.");
|
package/src/provisioning.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
|
3
3
|
import { sanitizeFsName } from "agent-relay-sdk/fs-name";
|
|
4
4
|
import type { ProvisioningAssetFile, ProvisioningMcpServer, ResolvedProvisioning } from "agent-relay-sdk";
|
|
5
5
|
import type { RunnerSpawnConfig } from "./adapter";
|
|
6
|
+
import { profileAllowsHostAssets, providerSourceHome } from "./profile-home";
|
|
6
7
|
|
|
7
8
|
// Runner-side materialization of the relay-resolved provisioning bundle
|
|
8
9
|
// (#554/#555/#556). The relay resolves a profile's refs against the DB registry and
|
|
@@ -97,6 +98,29 @@ export function resolveClaudeProjectSkills(
|
|
|
97
98
|
return resolved.sort((a, b) => a.name.localeCompare(b.name));
|
|
98
99
|
}
|
|
99
100
|
|
|
101
|
+
export function resolveClaudeHostSkills(
|
|
102
|
+
claudeHome: string,
|
|
103
|
+
config: Pick<RunnerSpawnConfig, "agentProfile">,
|
|
104
|
+
reservedNames: ReadonlySet<string> = new Set(),
|
|
105
|
+
): ResolvedAsset[] {
|
|
106
|
+
if (!profileAllowsHostAssets(config, "skills")) return [];
|
|
107
|
+
const skillsRoot = join(providerSourceHome("claude"), "skills");
|
|
108
|
+
if (!existsSync(skillsRoot)) return [];
|
|
109
|
+
const targetRoot = join(claudeHome, "skills");
|
|
110
|
+
const resolved: ResolvedAsset[] = [];
|
|
111
|
+
for (const entry of readdirSync(skillsRoot, { withFileTypes: true })) {
|
|
112
|
+
if (!entry.isDirectory() || reservedNames.has(entry.name)) continue;
|
|
113
|
+
const source = join(skillsRoot, entry.name);
|
|
114
|
+
if (!existsSync(join(source, "SKILL.md"))) continue;
|
|
115
|
+
resolved.push({
|
|
116
|
+
name: entry.name,
|
|
117
|
+
dir: join(targetRoot, assetDirName(entry.name)),
|
|
118
|
+
source: { kind: "link", source },
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return resolved.sort((a, b) => a.name.localeCompare(b.name));
|
|
122
|
+
}
|
|
123
|
+
|
|
100
124
|
const UNSAFE_PROJECT_CLAUDE_SETTINGS = new Set([
|
|
101
125
|
"apiKeyHelper",
|
|
102
126
|
"allowDangerouslySkipPermissions",
|