agent-relay-runner 0.123.0 → 0.123.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
package/src/launch-assembly.ts
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
resolveProjectMcpServers,
|
|
33
33
|
profileProvisioning,
|
|
34
34
|
writeCodexHooksJson,
|
|
35
|
+
writeClaudeSettingsJson,
|
|
35
36
|
} from "./provisioning";
|
|
36
37
|
import {
|
|
37
38
|
claudeMcpLaunchArgs,
|
|
@@ -325,6 +326,28 @@ export function stripSettingsArgs(args: string[]): string[] {
|
|
|
325
326
|
return result;
|
|
326
327
|
}
|
|
327
328
|
|
|
329
|
+
// #1096 — the settings.json relay WRITES into an isolated managed Claude home so the provisioned
|
|
330
|
+
// hooks (baseline guard included) land in the DOCUMENTED hook source Claude honors
|
|
331
|
+
// ($CLAUDE_CONFIG_DIR/settings.json — loaded under `--setting-sources "user"` and the default),
|
|
332
|
+
// rather than riding ONLY the inline `--settings` CLI flag. Claude does not document `--settings`
|
|
333
|
+
// as a hook source, and — unlike a file written straight to disk — that flag's JSON must survive
|
|
334
|
+
// shell/tmux launcher transit and passes Claude's "silently ignore settings that fail validation"
|
|
335
|
+
// gate; a written settings.json bypasses both, so the baseline guard can't be silently absent
|
|
336
|
+
// (the #1096 inert-at-runtime symptom). Returns null when:
|
|
337
|
+
// • there is NO isolated managed home (host base reuses the real ~/.claude — relay must not
|
|
338
|
+
// clobber it; that path keeps the inline --settings delivery), or
|
|
339
|
+
// • a caller --settings is present (the merge/suppression path owns hook delivery inline; see
|
|
340
|
+
// claudeSettingsArgs) — so the file and inline channels never DOUBLE-register the same hook.
|
|
341
|
+
// Both assembleClaude (to keep these hooks OUT of the inline --settings) and materializeClaudeAssets
|
|
342
|
+
// (to write the file) call this, so the launch and the disk agree.
|
|
343
|
+
export function claudeManagedHookSettings(config: RunnerSpawnConfig, configHome: string | undefined): { hooks: ProvisioningHookSet } | null {
|
|
344
|
+
if (!configHome) return null;
|
|
345
|
+
const hostDefaultArgs = profileUsesProviderHostGlobals(config) ? config.providerConfig.defaultArgs : [];
|
|
346
|
+
if (hasSettingsArg(hostDefaultArgs) || hasSettingsArg(config.providerArgs)) return null;
|
|
347
|
+
const hookSet = renderProvisionedHookSet(resolveProvisionedHooks(configHome, config));
|
|
348
|
+
return Object.keys(hookSet).length ? { hooks: hookSet } : null;
|
|
349
|
+
}
|
|
350
|
+
|
|
328
351
|
// ── Instruction disposition (the vanilla floor's effect on the host instruction cascade) ──
|
|
329
352
|
|
|
330
353
|
function repoInstructionDisposition(profile: AgentProfile): AssembledInstructionDisposition {
|
|
@@ -374,9 +397,15 @@ function assembleClaude(config: RunnerSpawnConfig, providerConfig: ProviderConfi
|
|
|
374
397
|
const hookHome = configHome ?? assetHome;
|
|
375
398
|
const provisionedHooks = hookHome ? resolveProvisionedHooks(hookHome, config) : [];
|
|
376
399
|
const provisionedHookSet = renderProvisionedHookSet(provisionedHooks);
|
|
400
|
+
// #1096 — for an isolated managed home with no caller --settings, the provisioned hooks are
|
|
401
|
+
// delivered through the managed settings.json (the documented, transit-safe hook source), so
|
|
402
|
+
// keep them OUT of the inline --settings to avoid double-registering the same guard. Host-base
|
|
403
|
+
// and caller-`--settings` launches keep inline delivery (see claudeManagedHookSettings).
|
|
404
|
+
const managedHookSettings = claudeManagedHookSettings(config, configHome);
|
|
405
|
+
const inlineHookSet = managedHookSettings ? {} : provisionedHookSet;
|
|
377
406
|
const settings = claudeSettingsArgs(
|
|
378
407
|
projectSettings.settings,
|
|
379
|
-
|
|
408
|
+
inlineHookSet,
|
|
380
409
|
provisionedHooks.map((h) => h.name),
|
|
381
410
|
statusLine,
|
|
382
411
|
hostDefaultArgs,
|
|
@@ -629,6 +658,10 @@ function materializeClaudeAssets(config: RunnerSpawnConfig): void {
|
|
|
629
658
|
materializeResolvedAssets(hostSkills);
|
|
630
659
|
materializeResolvedAssets(resolveClaudeProjectSkills(configHome, config, new Set([...skills, ...hostSkills].map((s) => s.name))));
|
|
631
660
|
materializeResolvedAssets(resolveProvisionedHooks(configHome, config));
|
|
661
|
+
// #1096 — land the provisioned hooks in the managed home's settings.json (the documented
|
|
662
|
+
// hook source), matching the assembler's decision to keep them out of the inline --settings.
|
|
663
|
+
const managedHookSettings = claudeManagedHookSettings(config, configHome);
|
|
664
|
+
if (managedHookSettings) writeClaudeSettingsJson(configHome, managedHookSettings);
|
|
632
665
|
}
|
|
633
666
|
if (assetHome) materializeResolvedAssets(resolveClaudeProvisionedPlugins(assetHome, config));
|
|
634
667
|
if (!configHome && assetHome) materializeResolvedAssets(resolveProvisionedHooks(assetHome, config));
|
package/src/provisioning.ts
CHANGED
|
@@ -357,6 +357,21 @@ export function writeCodexHooksJson(codexHome: string, hooks: ProvisioningHookSe
|
|
|
357
357
|
writeFileSync(join(codexHome, "hooks.json"), JSON.stringify({ hooks }, null, 2), { mode: 0o600 });
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
+
// #1096 — write relay-owned settings into an ISOLATED Claude managed home's `settings.json`
|
|
361
|
+
// (the DOCUMENTED hook source Claude honors under `--setting-sources "user"` or the default,
|
|
362
|
+
// unlike the inline `--settings` CLI flag). The managed home is relay-owned and freshly keyed
|
|
363
|
+
// per spawn, so an unconditional write is safe (profile-home seeds only `.claude.json`/`CLAUDE.md`,
|
|
364
|
+
// never `settings.json`). Removes the file when there is nothing relay-owned to deliver so disk
|
|
365
|
+
// stays == report. Never called for a host-base launch (which reuses the real `~/.claude`).
|
|
366
|
+
export function writeClaudeSettingsJson(claudeHome: string, settings: Record<string, unknown>): void {
|
|
367
|
+
mkdirSync(claudeHome, { recursive: true });
|
|
368
|
+
if (!Object.keys(settings).length) {
|
|
369
|
+
rmSync(join(claudeHome, "settings.json"), { force: true });
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
writeFileSync(join(claudeHome, "settings.json"), JSON.stringify(settings, null, 2), { mode: 0o600 });
|
|
373
|
+
}
|
|
374
|
+
|
|
360
375
|
// Write the resolved assets to disk; returns the dirs that materialized successfully,
|
|
361
376
|
// in resolution order. Idempotent — safe to re-run against an existing home.
|
|
362
377
|
export function materializeResolvedAssets(assets: ResolvedAsset[]): string[] {
|