@humwick/runner 0.130.5 → 0.130.6
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/agent-bin/humwick +2 -2
- package/agent-bin/humwick-browser +4 -0
- package/hooks/fff-search-redirect/fff-search-redirect.cjs +1 -1
- package/hooks/web-tools-redirect/web-tools-redirect.cjs +2 -2
- package/package.json +7 -10
- package/plugins/browser/skills/playwright-cli/SKILL.md +8 -6
- package/plugins/claude/.claude-plugin/plugin.json +3 -3
- package/plugins/claude/claude-statusline-probe.cjs +26 -16
- package/plugins/claude/hooks/gate-run-guard.provisioned.mjs +47 -17
- package/plugins/claude/hooks/gate-run-guard.sh +3 -3
- package/plugins/claude/hooks/gate-run-guard.ts +5 -5
- package/plugins/claude/hooks/kill-guard.sh +4 -4
- package/plugins/claude/hooks/kill-guard.ts +1 -1
- package/plugins/claude/hooks/permission-request.sh +1 -1
- package/plugins/claude/hooks/post-compact.sh +1 -1
- package/plugins/claude/hooks/post-tool-use.sh +3 -3
- package/plugins/claude/hooks/pre-compact.sh +2 -2
- package/plugins/claude/hooks/read-only-bash-guard.sh +4 -4
- package/plugins/claude/hooks/relay-status.sh +32 -32
- package/plugins/claude/hooks/session-end.sh +2 -2
- package/plugins/claude/hooks/session-start.sh +3 -3
- package/plugins/claude/hooks/shell-trust.ts +1 -1
- package/plugins/claude/hooks/stop-failure.sh +2 -2
- package/plugins/claude/hooks/stop.sh +6 -6
- package/plugins/claude/hooks/subagent-skill-policy.cjs +8 -6
- package/plugins/claude/hooks/subagent-stop.sh +3 -3
- package/plugins/claude/hooks/user-prompt-submit.sh +1 -1
- package/plugins/claude/hooks/wait-loop-guard.ts +3 -3
- package/plugins/claude/monitors/monitors.json +2 -2
- package/plugins/claude/monitors/relay-monitor.provisioned.mjs +96 -62
- package/plugins/claude/monitors/relay-monitor.ts +5 -5
- package/plugins/claude/skills/label/SKILL.md +3 -3
- package/plugins/claude/skills/message/SKILL.md +5 -5
- package/plugins/claude/skills/react/SKILL.md +1 -1
- package/plugins/claude/skills/read-message/SKILL.md +4 -4
- package/plugins/claude/skills/reply/SKILL.md +5 -5
- package/plugins/claude/skills/send-claimable/SKILL.md +5 -5
- package/plugins/claude/skills/slice-first/SKILL.md +5 -3
- package/plugins/claude/skills/slice-first/references/plan-recipe.md +30 -29
- package/plugins/claude/skills/status/SKILL.md +1 -1
- package/plugins/claude/skills/tags/SKILL.md +5 -5
- package/plugins/claude/skills/task/SKILL.md +10 -10
- package/plugins/claude-output-style/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-output-style/output-styles/{agent-relay.md → humwick.md} +1 -1
- package/plugins/codex/skills/guide/SKILL.md +1 -1
- package/plugins/codex/skills/label/SKILL.md +3 -3
- package/plugins/codex/skills/message/SKILL.md +5 -5
- package/plugins/codex/skills/react/SKILL.md +1 -1
- package/plugins/codex/skills/read-message/SKILL.md +4 -4
- package/plugins/codex/skills/reply/SKILL.md +5 -5
- package/plugins/codex/skills/send-claimable/SKILL.md +5 -5
- package/plugins/codex/skills/slice-first/SKILL.md +5 -3
- package/plugins/codex/skills/slice-first/references/plan-recipe.md +30 -29
- package/plugins/codex/skills/status/SKILL.md +1 -1
- package/plugins/codex/skills/tags/SKILL.md +5 -5
- package/plugins/codex/skills/task/SKILL.md +11 -10
- package/src/adapter.ts +95 -92
- package/src/adapters/claude-api.ts +8 -11
- package/src/adapters/claude-context-command.ts +1 -1
- package/src/adapters/claude-credentials.ts +7 -3
- package/src/adapters/claude-delivery-lanes.ts +2 -2
- package/src/adapters/claude-delivery.ts +16 -16
- package/src/adapters/claude-effort.ts +2 -2
- package/src/adapters/claude-exit.ts +2 -2
- package/src/adapters/claude-headless-mirror.ts +18 -2
- package/src/adapters/claude-headless-refusal.ts +3 -3
- package/src/adapters/claude-headless.ts +11 -10
- package/src/adapters/claude-launch-exit.ts +1 -1
- package/src/adapters/claude-monitor-delivery-plan.ts +36 -16
- package/src/adapters/claude-permission.ts +5 -5
- package/src/adapters/claude-prompt-gates.ts +9 -9
- package/src/adapters/claude-prompt-hook.ts +2 -2
- package/src/adapters/claude-session-capture.ts +6 -6
- package/src/adapters/claude-session-probe.ts +25 -25
- package/src/adapters/claude-shutdown.ts +11 -11
- package/src/adapters/claude-status-detectors.ts +31 -31
- package/src/adapters/claude-subagents.ts +7 -7
- package/src/adapters/claude-submit-confirmation.ts +6 -6
- package/src/adapters/claude-tmux.ts +43 -43
- package/src/adapters/claude-token-usage-capture.ts +5 -5
- package/src/adapters/claude-tool-policy.ts +7 -7
- package/src/adapters/claude-transcript.ts +24 -24
- package/src/adapters/claude.ts +100 -100
- package/src/adapters/codex-agent-message-capture.ts +26 -63
- package/src/adapters/codex-app-server.ts +1 -1
- package/src/adapters/codex-client.ts +9 -9
- package/src/adapters/codex-effective-config.ts +1 -1
- package/src/adapters/codex-launch-config.ts +5 -5
- package/src/adapters/codex-rollout-context.ts +2 -2
- package/src/adapters/codex-skill-discovery.ts +1 -1
- package/src/adapters/codex-token-usage-capture.ts +16 -16
- package/src/adapters/codex-tool-output.ts +4 -4
- package/src/adapters/codex-version.ts +3 -3
- package/src/adapters/codex.ts +64 -64
- package/src/adapters/opencode-api.ts +1 -1
- package/src/adapters/opencode-context.ts +20 -22
- package/src/adapters/opencode-model-registry.ts +7 -7
- package/src/adapters/opencode-provider.ts +8 -8
- package/src/adapters/opencode-provisioning.ts +30 -0
- package/src/adapters/opencode-server-retry.ts +1 -1
- package/src/adapters/opencode-soft-steer.ts +5 -5
- package/src/adapters/opencode-spawn.ts +18 -14
- package/src/adapters/opencode-token-usage-capture.ts +12 -12
- package/src/adapters/opencode-tool-events.ts +15 -15
- package/src/adapters/opencode.ts +232 -232
- package/src/adapters/project-shell.ts +11 -11
- package/src/adapters/service-access.ts +6 -6
- package/src/adapters/tmux-offload.ts +9 -9
- package/src/adapters/tool-summary.ts +5 -5
- package/src/agent-redact.ts +1 -1
- package/src/agent-runner/commands/dispatch.ts +10 -10
- package/src/agent-runner/commands/lifecycle.ts +2 -2
- package/src/agent-runner/commands/prompt.ts +19 -19
- package/src/agent-runner/credentials.ts +5 -5
- package/src/agent-runner/inbox.ts +1 -1
- package/src/agent-runner/lifecycle-milestones.ts +3 -3
- package/src/agent-runner/lifecycle-phase.ts +2 -2
- package/src/agent-runner/liveness-exchange.ts +4 -10
- package/src/agent-runner/registration.ts +8 -8
- package/src/agent-runner/runner-context.ts +9 -14
- package/src/agent-runner/session-mirror.ts +12 -12
- package/src/agent-runner/task-claims.ts +2 -2
- package/src/agent-runner/token-usage-coverage.ts +1 -1
- package/src/agent-runner/turn-report.ts +12 -12
- package/src/api-budget-accounting.ts +3 -3
- package/src/area-memory.ts +6 -6
- package/src/artifact-path-materialize.ts +1 -1
- package/src/async-guard.ts +5 -5
- package/src/attachment-cache.ts +17 -22
- package/src/background-script-reconcile.ts +6 -6
- package/src/bounded-log.ts +1 -1
- package/src/busy-reconciler.ts +22 -22
- package/src/claim-tracker.ts +3 -3
- package/src/claude-settings.ts +8 -8
- package/src/claude-system-prompt.ts +1 -1
- package/src/claude-workspace-trust.ts +1 -1
- package/src/config.ts +62 -61
- package/src/context-boundary.ts +5 -5
- package/src/context-command.ts +3 -3
- package/src/control-server.ts +32 -32
- package/src/delivery-result.ts +2 -2
- package/src/effort-command.ts +2 -2
- package/src/empty-turn.ts +22 -14
- package/src/free-port.ts +1 -1
- package/src/gate-run-config.ts +3 -3
- package/src/host-asset-plan.ts +24 -17
- package/src/host-claude-config-worker.ts +2 -2
- package/src/host-sources.ts +2 -2
- package/src/index.ts +16 -16
- package/src/launch-assembly.ts +94 -96
- package/src/liveness.ts +15 -15
- package/src/logger.ts +3 -3
- package/src/mcp-outbox.ts +3 -3
- package/src/mcp-preflight.ts +2 -2
- package/src/mcp-source-selection.ts +2 -2
- package/src/mcp-tool-deferral.ts +1 -1
- package/src/mcp-vault-env.ts +2 -2
- package/src/message-body-config/config.ts +4 -4
- package/src/mid-turn-delivery.ts +2 -2
- package/src/native-memory-lever.ts +8 -8
- package/src/outbox.ts +15 -15
- package/src/pending-permission-store.ts +1 -1
- package/src/profile-home.ts +19 -19
- package/src/profile-projection.ts +26 -24
- package/src/project-instructions.ts +5 -5
- package/src/provider-auth-warning.ts +17 -0
- package/src/provider-double.ts +1 -1
- package/src/provider-hold.ts +3 -3
- package/src/provider-home-marker.ts +1 -1
- package/src/provider-home-tombstone.ts +1 -1
- package/src/provider-input-queue.ts +2 -2
- package/src/provider-integration.ts +15 -13
- package/src/provider-setup.ts +15 -3
- package/src/providers.ts +1 -1
- package/src/provisioning.ts +37 -40
- package/src/question-decision.ts +2 -2
- package/src/quota.ts +0 -6
- package/src/rate-limit.ts +24 -13
- package/src/refused-prompt-store.ts +2 -2
- package/src/relay-injection-events.ts +2 -2
- package/src/relay-instructions.ts +5 -5
- package/src/relay-mcp-proxy.long-turn-e2e.ts +13 -13
- package/src/relay-mcp-proxy.ts +57 -58
- package/src/relay-mcp.ts +55 -69
- package/src/reply-obligation-cache.ts +9 -9
- package/src/response-capture-report.ts +9 -9
- package/src/response-routing.ts +1 -1
- package/src/runner-core.ts +209 -222
- package/src/runner-exit.ts +1 -1
- package/src/runner-helpers.ts +10 -10
- package/src/runner-insights.ts +4 -2
- package/src/runner-mcp-proxy.ts +3 -3
- package/src/runner-spawn-config.ts +3 -3
- package/src/runtime-token-file.ts +1 -1
- package/src/runtime-token-remint.ts +1 -1
- package/src/service-access.ts +2 -2
- package/src/session-destroy.ts +1 -1
- package/src/session-insights.ts +1 -1
- package/src/session-scratch.ts +24 -37
- package/src/spawn-input.ts +6 -6
- package/src/startup-config-fetch.ts +4 -4
- package/src/subreaper.ts +3 -5
- package/src/test-run-dedup.ts +13 -13
- package/src/transcript-context.ts +2 -2
- package/src/transport-errors.ts +1 -1
- package/src/turn-opener.ts +1 -1
- package/src/turn-reconcile-event.ts +2 -2
- package/src/vault-proxy-hop.ts +2 -2
- package/src/web-tools.ts +2 -2
- package/agent-bin/agent-relay-browser +0 -10
- package/src/adapters/provider-quota-harvest.ts +0 -76
package/src/launch-assembly.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { bundledIntegrationAssets, bundledIntegrationHome, suppressOwnedClaudePlugins, selectClaudePluginDirs } from "./provider-integration";
|
|
2
|
+
import { resolveOpenCodeAssets } from "./adapters/opencode-provisioning";
|
|
2
3
|
import { relayEnv, writeChildEnv } from "@humwick/sdk/env";
|
|
3
4
|
import { codexRetiredMcpConfigArgs } from "./adapters/codex-effective-config";
|
|
4
5
|
import { activeSharedMcpServers } from "@humwick/sdk";
|
|
@@ -53,7 +54,7 @@ import {
|
|
|
53
54
|
codexProvisionedMcpArgs,
|
|
54
55
|
codexProvisionedMcpTimeoutArgs,
|
|
55
56
|
openCodeMcpConfigPayload,
|
|
56
|
-
relayMcpCodexConfigArgs,
|
|
57
|
+
relayMcpCodexConfigArgs,
|
|
57
58
|
sharedMcpMetaOnlyServer,
|
|
58
59
|
writeSharedMcpMetaOnlyConfig,
|
|
59
60
|
SHARED_MCP_METAONLY_CONFIG_FILENAME,
|
|
@@ -73,7 +74,7 @@ import {
|
|
|
73
74
|
type NativeMemoryLever,
|
|
74
75
|
} from "./native-memory-lever";
|
|
75
76
|
|
|
76
|
-
//
|
|
77
|
+
// P5.7 — relay's inline `--settings` actually reaches this launch. Same gate
|
|
77
78
|
// `claudeSettingsArgs` applies below, passed to the lever so its applied/suppressed report cannot
|
|
78
79
|
// claim a settings key the merge then drops.
|
|
79
80
|
const callerSettingsMergeable = (argLists: string[][], cwd?: string): boolean => {
|
|
@@ -81,7 +82,7 @@ const callerSettingsMergeable = (argLists: string[][], cwd?: string): boolean =>
|
|
|
81
82
|
return !caller || Boolean(caller.settings);
|
|
82
83
|
};
|
|
83
84
|
|
|
84
|
-
//
|
|
85
|
+
// THE single launch-assembly contract. Given a resolved AgentProfile (+ its
|
|
85
86
|
// spawn-time ResolvedProvisioning), `assembleLaunch` produces ONE provider-neutral
|
|
86
87
|
// description of the launch: the relay-provisioned skills/plugins/MCP, the bundled relay
|
|
87
88
|
// surface, the instruction-source disposition, and `base:"vanilla"` as the zero-host
|
|
@@ -106,7 +107,7 @@ export interface AssembledInstructions {
|
|
|
106
107
|
/** Repo CLAUDE.md/AGENTS.md cascade: "allow" left available, "suppressed" by the
|
|
107
108
|
* vanilla launch mechanism, "reinjected" when the native project scope is dropped (to gate
|
|
108
109
|
* ambient hooks) but P.instructions re-injects the repo docs through the controlled
|
|
109
|
-
* append-system-prompt channel
|
|
110
|
+
* append-system-prompt channel, "unsupported" requested-but-not-enforced. */
|
|
110
111
|
repo: AssembledInstructionDisposition;
|
|
111
112
|
/** Host/global provider instructions: "allow", "isolated" (dropped by the isolated home), or "unsupported". */
|
|
112
113
|
global: AssembledInstructionDisposition;
|
|
@@ -114,47 +115,46 @@ export interface AssembledInstructions {
|
|
|
114
115
|
* context) is configured for this launch. `launchContextPending` distinguishes providers that
|
|
115
116
|
* can only transport it with their first turn from providers that emit it at process launch. */
|
|
116
117
|
systemPromptAppend: boolean;
|
|
117
|
-
// ──
|
|
118
|
-
// Scoped {profile|user} memory does not exist yet (
|
|
118
|
+
// ── SEAM ──────────────────────────────────────────────────────────────────
|
|
119
|
+
// Scoped {profile|user} memory does not exist yet (generalizes Projects' anchor
|
|
119
120
|
// to project|profile|user). When it lands, the assembled instruction world becomes
|
|
120
121
|
// "instructions + provisioned assets + scoped knowledge": the relay-side composer
|
|
121
122
|
// (composeSpawnInstructions) gains a profile/user-scoped preamble beside the existing
|
|
122
123
|
// project preamble, and THIS field reports which scopes contributed. Until then it is
|
|
123
|
-
// always "none" — a thin add
|
|
124
|
+
// always "none" — a thin add, not a refactor.
|
|
124
125
|
scopedKnowledge: "none";
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
export interface AssembledMcp {
|
|
128
|
-
generation?: import("@humwick/sdk").ToolGeneration;
|
|
129
129
|
/** The identity-bearing relay HTTP MCP endpoint is injected. */
|
|
130
130
|
relayEndpoint: boolean;
|
|
131
|
-
/** Only the assembled servers may load (Claude --strict-mcp-config)
|
|
131
|
+
/** Only the assembled servers may load (Claude --strict-mcp-config).: true for ALL
|
|
132
132
|
* managed Claude launches so the project `.mcp.json` enable-modal can't wedge a headless
|
|
133
133
|
* agent; relay's endpoint + provisioned servers (via --mcp-config) are preserved. Always
|
|
134
134
|
* false for Codex (no equivalent discovery modal). */
|
|
135
135
|
strict: boolean;
|
|
136
136
|
/** Relay-provisioned server names (excludes the relay endpoint itself). */
|
|
137
137
|
servers: string[];
|
|
138
|
-
/**
|
|
138
|
+
/** project `.mcp.json` server names projected from the spawn cwd's committed
|
|
139
139
|
* `.mcp.json` (via `--mcp-config`, so they survive strict). Sourced from the project,
|
|
140
140
|
* not ambient host MCP. Empty unless the profile/spawn opted into `projectMcp`, and a
|
|
141
141
|
* name already declared by relay-provisioned `servers` wins (not double-listed here). */
|
|
142
142
|
projectServers: string[];
|
|
143
|
-
/**
|
|
143
|
+
/** host USER-scope MCP server names (`~/.claude.json` `mcpServers`) re-injected
|
|
144
144
|
* into `--mcp-config` for an `mcp.mode:"host"` profile, so `--strict-mcp-config` keeps
|
|
145
145
|
* the ambient host MCP (callmux/qmd/gh) instead of dropping it. Isolated homes receive the
|
|
146
146
|
* explicit host cell too; non-host modes stay empty. Codex reports its host config.toml
|
|
147
147
|
* servers through the same field. */
|
|
148
148
|
hostServers: string[];
|
|
149
|
-
/**
|
|
149
|
+
/** when the profile/spawn opted into `sharedMcp`, the agent's NON-IDENTITY MCP
|
|
150
150
|
* (tokenlean, github) is routed through one orchestrator-owned shared `callmux` listener via
|
|
151
151
|
* a per-agent `callmux bridge --cwd "$WORKTREE"` (session-cwd isolation), injected through
|
|
152
152
|
* the same `--mcp-config` / `-c` channel so it survives strict. `url` is the listener the
|
|
153
153
|
* bridge connects to. Undefined when projection is OFF (the default). The bridge is opaque
|
|
154
154
|
* at assembly time — its server SET is owned by the listener config, not enumerated here.
|
|
155
|
-
* The identity-bearing relay endpoint is never on the shared listener
|
|
155
|
+
* The identity-bearing relay endpoint is never on the shared listener. —
|
|
156
156
|
* `relayFronted` marks a metaOnly callmux that ALSO fronts the relay endpoint on-demand as a
|
|
157
|
-
* second downstream (via the runner proxy, so identity
|
|
157
|
+
* second downstream (via the runner proxy, so identity hold); the relay stays per-agent. */
|
|
158
158
|
sharedListener?: { url: string; metaOnly?: boolean; relayFronted?: boolean };
|
|
159
159
|
}
|
|
160
160
|
|
|
@@ -168,10 +168,10 @@ export interface AssembledLaunch {
|
|
|
168
168
|
vanilla: boolean;
|
|
169
169
|
/** Isolated provider config home (CLAUDE_CONFIG_DIR / CODEX_HOME), or undefined for host base. */
|
|
170
170
|
configHome?: string;
|
|
171
|
-
/**
|
|
171
|
+
/** N3 — the effective Claude `--setting-sources` this launch emits, or undefined when the
|
|
172
172
|
* flag is ABSENT. Absent means the provider default loads EVERY scope: empirically
|
|
173
173
|
* `user,project,local` on Claude 2.1.x (measured with a controlled probe — the CLI documents the
|
|
174
|
-
* valid sources but not the absent-flag default;
|
|
174
|
+
* valid sources but not the absent-flag default; R1.0). An explicit array names the ONLY
|
|
175
175
|
* scopes that load, so the ambient project (`.claude/settings.json`, repo CLAUDE.md/AGENTS.md) and
|
|
176
176
|
* local (`.claude/settings.local.json`) scopes are DROPPED. Always undefined for Codex, which has
|
|
177
177
|
* no `--setting-sources` surface (it suppresses the project doc via `project_doc_max_bytes=0`). */
|
|
@@ -185,14 +185,14 @@ export interface AssembledLaunch {
|
|
|
185
185
|
statusLine: boolean;
|
|
186
186
|
/** Relay-provisioned skills materialized for this launch. */
|
|
187
187
|
skills: AssembledAsset[];
|
|
188
|
-
/** Project `.claude/skills` materialized for this Claude launch
|
|
188
|
+
/** Project `.claude/skills` materialized for this Claude launch. */
|
|
189
189
|
projectSkills: AssembledAsset[];
|
|
190
190
|
/** Host/global Claude skills explicitly inherited without enabling host MCP. */
|
|
191
191
|
hostSkills: AssembledAsset[];
|
|
192
|
-
/** Project `.claude/settings.json` stays on Claude's native project-settings path
|
|
192
|
+
/** Project `.claude/settings.json` stays on Claude's native project-settings path. */
|
|
193
193
|
projectSettings: { applied: boolean; keys: string[]; ignoredKeys: string[]; file?: string };
|
|
194
194
|
/** Relay-provisioned hooks materialized for this launch. Empty when hooks were resolved
|
|
195
|
-
* but could not be delivered — see `hooksSuppressed
|
|
195
|
+
* but could not be delivered — see `hooksSuppressed`. */
|
|
196
196
|
hooks: AssembledAsset[];
|
|
197
197
|
/** Host hooks explicitly re-injected into an isolated Claude settings.json. */
|
|
198
198
|
hostHooks: boolean;
|
|
@@ -202,19 +202,19 @@ export interface AssembledLaunch {
|
|
|
202
202
|
projectHooks: AssembledAsset[];
|
|
203
203
|
/** Project hooks requested by the source matrix but denied by the server trust gate. */
|
|
204
204
|
projectHooksSuppressed?: { reason: string };
|
|
205
|
-
/**
|
|
205
|
+
/** review — set when this launch RESOLVED provisioned hooks (incl. a baseline safety
|
|
206
206
|
* guard) that could NOT be delivered: caller `--settings` was unmergeable (Claude), a
|
|
207
207
|
* host-base Codex agent has no managed CODEX_HOME, or the installed Codex is too old to
|
|
208
208
|
* consume hooks.json. Drives a LOUD projection warning so a baseline guard is never
|
|
209
209
|
* SILENTLY absent while the report says "applied". */
|
|
210
210
|
hooksSuppressed?: { reason: string; names: string[] };
|
|
211
|
-
/**
|
|
211
|
+
/** P5.7 — the provider-NATIVE-memory lever this launch carries. `policy:"default"` means
|
|
212
212
|
* the profile declared nothing: no env/arg/settings key was emitted and the launch is
|
|
213
213
|
* byte-identical to a pre-P5.7 one. Any other policy renders EXPLICITLY in both directions.
|
|
214
214
|
* `suppressed` names a lever that was resolved but could not be delivered on this base — the
|
|
215
|
-
* `hooksSuppressed` anti-lie precedent
|
|
215
|
+
* `hooksSuppressed` anti-lie precedent, so a lever is never silently absent. */
|
|
216
216
|
nativeMemory: NativeMemoryLever;
|
|
217
|
-
/**
|
|
217
|
+
/** review — Claude only: true when the caller's own `--settings` was folded into the
|
|
218
218
|
* relay-emitted `--settings` (relay hooks/statusLine merged in). The adapter
|
|
219
219
|
* must then STRIP the caller's `--settings` from the provider args so exactly one reaches the
|
|
220
220
|
* launch. False when relay emitted its own settings standalone or added none. */
|
|
@@ -223,11 +223,11 @@ export interface AssembledLaunch {
|
|
|
223
223
|
plugins: AssembledAsset[];
|
|
224
224
|
/** Host/global provider plugin dirs explicitly inherited without enabling host MCP. */
|
|
225
225
|
hostPluginDirs: string[];
|
|
226
|
-
/**
|
|
226
|
+
/** native host plugins (Claude installed-plugin registry ids) this launch's home carries. */
|
|
227
227
|
hostPlugins: string[];
|
|
228
|
-
/**
|
|
228
|
+
/** per host asset class: applied, selected-empty, disabled or unsupported. */
|
|
229
229
|
hostSources: HostSourceReport;
|
|
230
|
-
/**
|
|
230
|
+
/** the central provider defaultArgs this launch carries (Settings/defaults class). */
|
|
231
231
|
hostDefaultArgs: string[];
|
|
232
232
|
mcp: AssembledMcp;
|
|
233
233
|
/** Lower-precedence source assets intentionally omitted from the emitted launch. */
|
|
@@ -237,12 +237,12 @@ export interface AssembledLaunch {
|
|
|
237
237
|
args: string[];
|
|
238
238
|
/** Provisioning-derived env additions (the isolated config-home var). */
|
|
239
239
|
env: Record<string, string>;
|
|
240
|
-
/**
|
|
240
|
+
/** whether agent-redact's post-tool hooks reached this launch, and how. */
|
|
241
241
|
redaction: AgentRedactState;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
// ── Bundled relay assets (relocated from the adapters so the assembler — not each
|
|
245
|
-
// adapter — owns launch-arg construction
|
|
245
|
+
// adapter — owns launch-arg construction). Adapters re-export for test parity. ──
|
|
246
246
|
|
|
247
247
|
const CODEX_BUNDLED_SKILLS_DIR = resolve(import.meta.dir, "../plugins/codex/skills");
|
|
248
248
|
|
|
@@ -259,7 +259,7 @@ function codexSkillSources(config: RunnerSpawnConfig) {
|
|
|
259
259
|
const home = providerHomePathFor("codex", config);
|
|
260
260
|
const provisioned = home ? resolveCodexProvisionedSkills(home, config) : [];
|
|
261
261
|
const bundled = !config.agentProfile && profileAllowsRelayFeature(config, "skills") ? bundledIntegrationAssets(config).map(asset => asset.dir) : [];
|
|
262
|
-
//
|
|
262
|
+
// the managed CODEX_HOME hides the host's own skills; Host Skills on names them as roots,
|
|
263
263
|
// off disables the ambient ones Codex would still discover from HOME.
|
|
264
264
|
const host = home ? codexHostPlan(config, providerSourceHome("codex")) : undefined;
|
|
265
265
|
const hostRoots = host?.skillRoots ?? [];
|
|
@@ -271,7 +271,7 @@ export function codexSkillRoots(config: RunnerSpawnConfig): string[] {
|
|
|
271
271
|
return codexSkillSources(config).roots;
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
-
//
|
|
274
|
+
// vanilla base (Codex): suppress the AGENTS.md project-doc cascade. project_doc_max_bytes
|
|
275
275
|
// (default 32768) caps bytes Codex reads from AGENTS.md walking root→cwd; 0 stops before adding
|
|
276
276
|
// any, so no repo (or fresh-CODEX_HOME global) project doc loads. The isolated CODEX_HOME already
|
|
277
277
|
// drops host config.toml MCP/hooks; this closes the cwd-resident repo instruction channel.
|
|
@@ -279,14 +279,14 @@ export function codexProjectDocSuppressionArgs(config: Pick<RunnerSpawnConfig, "
|
|
|
279
279
|
return profileIsVanillaBase(config) ? ["-c", "project_doc_max_bytes=0"] : [];
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
-
//
|
|
282
|
+
// the settings.json relay WRITES into an isolated managed Claude home so the provisioned
|
|
283
283
|
// hooks (baseline guard included) land in the DOCUMENTED hook source Claude honors
|
|
284
284
|
// ($CLAUDE_CONFIG_DIR/settings.json — loaded under `--setting-sources "user"` and the default),
|
|
285
285
|
// rather than riding ONLY the inline `--settings` CLI flag. Claude does not document `--settings`
|
|
286
286
|
// as a hook source, and — unlike a file written straight to disk — that flag's JSON must survive
|
|
287
287
|
// shell/tmux launcher transit and passes Claude's "silently ignore settings that fail validation"
|
|
288
288
|
// gate; a written settings.json bypasses both, so the baseline guard can't be silently absent
|
|
289
|
-
//
|
|
289
|
+
// Returns null when:
|
|
290
290
|
// • there is NO isolated managed home (host base reuses the real ~/.claude — relay must not
|
|
291
291
|
// clobber it; that path keeps the inline --settings delivery), or
|
|
292
292
|
// • a caller --settings is present (the merge/suppression path owns hook delivery inline; see
|
|
@@ -305,7 +305,7 @@ export function claudeManagedHookSettings(config: RunnerSpawnConfig, configHome:
|
|
|
305
305
|
|
|
306
306
|
// ── Instruction disposition (the vanilla floor's effect on the host instruction cascade) ──
|
|
307
307
|
|
|
308
|
-
//
|
|
308
|
+
// N1/N3 — the disposition follows the launch FACT, not the profile's declared policy alone.
|
|
309
309
|
// `projectScopeDropped` is true whenever the project setting-source is not loaded (Claude
|
|
310
310
|
// `--setting-sources user`, emitted for a vanilla base OR a projectConfigProjection launch; Codex
|
|
311
311
|
// `project_doc_max_bytes=0` for a vanilla base). When it is dropped, the repo CLAUDE.md/AGENTS.md
|
|
@@ -316,7 +316,7 @@ export function claudeManagedHookSettings(config: RunnerSpawnConfig, configHome:
|
|
|
316
316
|
// minimal/isolated base OR a base:"host"+projectConfigProjection agent reports `isolated`, not the
|
|
317
317
|
// old `allow` that lied for the isolated prod reviewer.
|
|
318
318
|
function repoInstructionDisposition(profile: AgentProfile, projectScopeDropped: boolean, reinjected: boolean): AssembledInstructionDisposition {
|
|
319
|
-
//
|
|
319
|
+
// the drop is not silent when P.instructions re-injects the repo docs through the
|
|
320
320
|
// controlled channel: the cascade is honored, just via append-system-prompt instead of the
|
|
321
321
|
// native project scope. Report that FACT so the anti-lie projection never says "suppressed"
|
|
322
322
|
// for a launch that actually carries the repo instructions.
|
|
@@ -328,7 +328,7 @@ function repoInstructionDisposition(profile: AgentProfile, projectScopeDropped:
|
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
function globalInstructionDisposition(profile: AgentProfile, isolatedHome: boolean): AssembledInstructionDisposition {
|
|
331
|
-
//
|
|
331
|
+
// N1 review (P1): key on whether host globals are ACTUALLY linked, not on is-isolated-home.
|
|
332
332
|
// `linkHostGlobalInstructionFiles` (profile-home.ts) symlinks the host global CLAUDE.md into the
|
|
333
333
|
// managed home whenever `globalInstructions:"allow"` — even for an isolated base — so "allow" means
|
|
334
334
|
// globals ARE present and reporting "isolated" there would be a false report. Only an `ignore`
|
|
@@ -392,7 +392,6 @@ export function openCodeMcpConfigForLaunch(config: RunnerSpawnConfig): Record<st
|
|
|
392
392
|
const { declaredMcp, relayEndpoint } = resolveOpenCodeDeclaredMcp(config);
|
|
393
393
|
return openCodeMcpConfigPayload({
|
|
394
394
|
relayUrl: config.relayUrl,
|
|
395
|
-
generation: runnerMcpGeneration(config.env),
|
|
396
395
|
...(config.relayMcpEndpoint ? { endpoint: config.relayMcpEndpoint } : {}),
|
|
397
396
|
includeRelay: relayEndpoint,
|
|
398
397
|
servers: declaredMcp,
|
|
@@ -400,16 +399,16 @@ export function openCodeMcpConfigForLaunch(config: RunnerSpawnConfig): Record<st
|
|
|
400
399
|
}
|
|
401
400
|
|
|
402
401
|
interface CodexMcpResolution extends McpSourceSelection {
|
|
403
|
-
/**
|
|
402
|
+
/** set when the shared-listener route is served through a per-agent metaOnly callmux
|
|
404
403
|
* (Codex worker) rather than the plain flat bridge. Carries the exact config path/url/cwd so
|
|
405
|
-
* materializeCodexAssets writes the SAME file assembleCodex's descriptor points at.
|
|
404
|
+
* materializeCodexAssets writes the SAME file assembleCodex's descriptor points at. —
|
|
406
405
|
* `relay` adds the identity-bearing relay endpoint as a SECOND downstream (via the runner proxy)
|
|
407
406
|
* when the proxy is active, so the on-demand relay set loads through callmux_call. */
|
|
408
407
|
metaOnlyCallmux?: { configPath: string; url: string; cwd: string; relay?: MetaOnlyRelayDownstream };
|
|
409
408
|
}
|
|
410
409
|
|
|
411
|
-
//
|
|
412
|
-
// the ~40 downstream schemas load on demand. Gated exactly like phase C's relay allowlist
|
|
410
|
+
// a Codex worker swaps the flat shared-listener bridge for a per-agent metaOnly callmux so
|
|
411
|
+
// the ~40 downstream schemas load on demand. Gated exactly like phase C's relay allowlist:
|
|
413
412
|
// an explicitly worker-classified profile AND a provider whose manifest eager-loads every tool
|
|
414
413
|
// schema (no client-native deferral). Data-driven from the manifest, never a provider-identity
|
|
415
414
|
// branch — Claude (client-defers) never takes this path, coordinators/reviewers keep the flat
|
|
@@ -417,7 +416,7 @@ interface CodexMcpResolution extends McpSourceSelection {
|
|
|
417
416
|
// the host ~/.codex); worker profiles are isolated, so this holds.
|
|
418
417
|
function codexMetaOnlyCallmuxConfigPath(config: RunnerSpawnConfig): string | undefined {
|
|
419
418
|
if (!isCodexLeanWorkerProfile(config.agentProfile?.name)) return undefined;
|
|
420
|
-
//
|
|
419
|
+
// callmux_call reaches every listener tool and callmux has no per-downstream denylist, so a
|
|
421
420
|
// profile with a web tool off keeps the flat bridge, where Codex's disabled_tools hides it.
|
|
422
421
|
if (disabledSharedWebTools(config).length) return undefined;
|
|
423
422
|
if (!runnerManagedToolDeferralRoute("codex")) return undefined;
|
|
@@ -432,14 +431,14 @@ function resolveCodexDeclaredMcp(config: RunnerSpawnConfig): CodexMcpResolution
|
|
|
432
431
|
const selected = resolveMcpSources(config, hostMcpInjected ? hostCodexMcpServers() : {});
|
|
433
432
|
const { sharedListenerUrl, relayEndpoint } = selected;
|
|
434
433
|
const metaOnlyConfigPath = sharedListenerUrl ? codexMetaOnlyCallmuxConfigPath(config) : undefined;
|
|
435
|
-
//
|
|
434
|
+
// front the relay endpoint on-demand through the SAME metaOnly callmux, but ONLY when the
|
|
436
435
|
// runner proxy is active (config.relayMcpEndpoint = the proxy URL, config.mcpProxySecret = its
|
|
437
436
|
// secret) AND the profile has the relay endpoint on. Routing through the proxy keeps the live
|
|
438
|
-
// token off disk
|
|
437
|
+
// token off disk. This gate matches runner-core's proxy on-demand-hiding gate exactly, so
|
|
439
438
|
// the direct client's on-demand tools are hidden ONLY when this front simultaneously exists.
|
|
440
439
|
const metaOnlyRelay: MetaOnlyRelayDownstream | undefined =
|
|
441
440
|
metaOnlyConfigPath && relayEndpoint && config.relayMcpEndpoint && config.mcpProxySecret
|
|
442
|
-
? { endpoint: config.relayMcpEndpoint, authSecret: config.mcpProxySecret
|
|
441
|
+
? { endpoint: config.relayMcpEndpoint, authSecret: config.mcpProxySecret }
|
|
443
442
|
: undefined;
|
|
444
443
|
const metaOnlyCallmux = sharedListenerUrl && metaOnlyConfigPath
|
|
445
444
|
? { configPath: metaOnlyConfigPath, url: sharedListenerUrl, cwd: config.cwd, ...(metaOnlyRelay ? { relay: metaOnlyRelay } : {}) }
|
|
@@ -457,7 +456,7 @@ function resolveCodexDeclaredMcp(config: RunnerSpawnConfig): CodexMcpResolution
|
|
|
457
456
|
|
|
458
457
|
// ── Per-provider arg assembly ──────────────────────────────────────────────────────
|
|
459
458
|
|
|
460
|
-
//
|
|
459
|
+
// `provider` parameterizes the claude family: claude-b reuses this assembler whole,
|
|
461
460
|
// and every home/env derivation (providerHomePathFor, configHomeEnv) keys off the id so its
|
|
462
461
|
// launch resolves the claude-b manifest's configDir instead of claude's.
|
|
463
462
|
function assembleClaude(config: RunnerSpawnConfig, providerConfig: ProviderConfig, hostDefaultArgs: string[], provider: string = "claude"): AssembledLaunch {
|
|
@@ -467,7 +466,7 @@ function assembleClaude(config: RunnerSpawnConfig, providerConfig: ProviderConfi
|
|
|
467
466
|
const vanilla = profileIsVanillaBase(config);
|
|
468
467
|
const relayPlugin = profileAllowsRelayFeature(config, "plugins");
|
|
469
468
|
const relaySkills = profileAllowsRelayFeature(config, "skills");
|
|
470
|
-
//
|
|
469
|
+
// the status line is how Relay tracks a Claude agent's context, so every profile gets it.
|
|
471
470
|
const statusLine = true;
|
|
472
471
|
|
|
473
472
|
const skillSources = configHome ? resolveClaudeSkillSources(configHome, config, provider) : { relay: [], project: [], host: [], shadows: [] };
|
|
@@ -484,7 +483,7 @@ function assembleClaude(config: RunnerSpawnConfig, providerConfig: ProviderConfi
|
|
|
484
483
|
? { reason: config.agentProfile.projectGates?.hooks?.detail ?? "partial — not acknowledged" }
|
|
485
484
|
: undefined;
|
|
486
485
|
const provisionedHookSet = renderProvisionedHookSet([...projectHooks, ...provisionedHooks]);
|
|
487
|
-
//
|
|
486
|
+
// for an isolated managed home with no caller --settings, the provisioned hooks are
|
|
488
487
|
// delivered through the managed settings.json (the documented, transit-safe hook source), so
|
|
489
488
|
// keep them OUT of the inline --settings to avoid double-registering the same guard. Host-base
|
|
490
489
|
// and caller-`--settings` launches keep inline delivery (see claudeManagedHookSettings).
|
|
@@ -493,21 +492,21 @@ function assembleClaude(config: RunnerSpawnConfig, providerConfig: ProviderConfi
|
|
|
493
492
|
const redact = resolveAgentRedact();
|
|
494
493
|
const inlineHookSet = managedHookSettings ? {} : mergeHookSets(mergeHookSets(provisionedHookSet, isAgentRedactInstall(redact) ? redact.claudeHooks : {}), claudeSubagentHooks(config));
|
|
495
494
|
const redaction = isAgentRedactInstall(redact) ? agentRedactOn(managedHookSettings ? "settings.json PostToolUse hooks" : "inline --settings PostToolUse hooks") : redact;
|
|
496
|
-
//
|
|
495
|
+
// P5.7 — the native-memory lever. Resolved BEFORE the settings args so its keys can ride
|
|
497
496
|
// relay's inline `--settings` (the only settings channel a host-base launch has); the env var
|
|
498
497
|
// below is the one that must always be present, and outranks both settings channels.
|
|
499
498
|
const nativeMemory = resolveClaudeNativeMemoryLever(config, configHome, hostDefaultArgs, (lists) => callerSettingsMergeable(lists, config.cwd), provider);
|
|
500
499
|
const settings = claudeSettingsArgs(
|
|
501
|
-
{ ...projectSettings.settings, ...(relayPlugin ? { enabledPlugins: suppressOwnedClaudePlugins(undefined) } : {}), ...claudeGuardedRelaySettings(config.approvalMode
|
|
500
|
+
{ ...projectSettings.settings, ...(relayPlugin ? { enabledPlugins: suppressOwnedClaudePlugins(undefined) } : {}), ...claudeGuardedRelaySettings(config.approvalMode), ...claudeHostInstructionExcludeSettings(config), ...claudeBypassPermissionSettings(config.approvalMode), ...claudeHarnessTrim(config).settings, ...claudeSubagentSettings(config) },
|
|
502
501
|
inlineHookSet,
|
|
503
502
|
[...projectHooks, ...provisionedHooks].map((h) => h.name),
|
|
504
503
|
statusLine,
|
|
505
504
|
nativeMemory.settings,
|
|
506
|
-
{ cwd: config.cwd, model: config.model
|
|
505
|
+
{ cwd: config.cwd, model: config.model },
|
|
507
506
|
hostDefaultArgs,
|
|
508
507
|
config.providerArgs,
|
|
509
508
|
);
|
|
510
|
-
// The anti-lie invariant
|
|
509
|
+
// The anti-lie invariant: only report hooks as materialized when they actually reach
|
|
511
510
|
// the launch. If the caller's --settings suppressed them, empty the reported set and surface a
|
|
512
511
|
// loud suppression reason instead of an "applied" report.
|
|
513
512
|
const hooksSuppressed = settings.suppressedHookNames.length
|
|
@@ -517,21 +516,21 @@ function assembleClaude(config: RunnerSpawnConfig, providerConfig: ProviderConfi
|
|
|
517
516
|
|
|
518
517
|
// Plugin dirs (--plugin-dir loads explicitly, independent of --setting-sources):
|
|
519
518
|
// relay-bundled plugin → only when relay.plugins (carries the relay monitor +
|
|
520
|
-
// turn-lifecycle hooks
|
|
521
|
-
// relay-provisioned plugins
|
|
519
|
+
// turn-lifecycle hooks, not reproducible via settings.json).
|
|
520
|
+
// relay-provisioned plugins → materialized into the isolated home.
|
|
522
521
|
// host plugin dirs → ONLY host-base profiles; isolated/vanilla must not pull them back in.
|
|
523
522
|
const relayPluginDirs = !config.agentProfile && relayPlugin ? bundledIntegrationAssets(config).map(asset => asset.dir) : [];
|
|
524
523
|
// `force-for-plugin: true` in this tiny plugin takes precedence over a caller or
|
|
525
524
|
// ambient `outputStyle` setting, so a host-base launch is covered too (there is no
|
|
526
525
|
// Relay-owned CLAUDE_CONFIG_DIR in which to write a custom style file).
|
|
527
526
|
const provisionedPluginDirs = provisionedPlugins.map((p) => p.dir);
|
|
528
|
-
const hostPluginDirs = selectClaudePluginDirs(hostPlan.pluginDirs,
|
|
527
|
+
const hostPluginDirs = selectClaudePluginDirs(hostPlan.pluginDirs, relayPlugin);
|
|
529
528
|
const pluginDirs = [...new Set([resolve(import.meta.dir, "../plugins/claude-output-style"), ...relayPluginDirs, ...provisionedPluginDirs, ...hostPluginDirs])];
|
|
530
529
|
|
|
531
530
|
const { declaredMcp, hostMcp, relayEndpoint, strictMcp, mcpServers, projectServers, hostMcpServers, sharedListenerUrl, sourceShadows: mcpSourceShadows } =
|
|
532
531
|
resolveClaudeDeclaredMcp(config);
|
|
533
532
|
|
|
534
|
-
//
|
|
533
|
+
// a provisioned/host server's env/headers can carry a literal secret; inline JSON
|
|
535
534
|
// in --mcp-config lands verbatim in argv (world-readable via ps/proc). Whenever there's a
|
|
536
535
|
// relay-owned scratch dir to write into (any launch with an agentProfile — see
|
|
537
536
|
// providerProvisioningHomePathFor, which gives even host-base profiles a relay-owned dir
|
|
@@ -539,12 +538,12 @@ function assembleClaude(config: RunnerSpawnConfig, providerConfig: ProviderConfi
|
|
|
539
538
|
// writes the file this path points at, from the SAME resolveClaudeDeclaredMcp(config) inputs.
|
|
540
539
|
const mcpConfigFilePath = assetHome ? join(assetHome, "mcp-config.json") : undefined;
|
|
541
540
|
|
|
542
|
-
//
|
|
541
|
+
// derive Claude's native setting-source cascade from the Project cells.
|
|
543
542
|
// P.mcp and P.skills have their own controlled channels and are intentionally not
|
|
544
543
|
// triggers. The same list drives the instruction disposition report.
|
|
545
544
|
const settingSources = projectSettingSources(config);
|
|
546
545
|
const projectScopeDropped = Boolean(settingSources && !settingSources.includes("project"));
|
|
547
|
-
//
|
|
546
|
+
// when P.hooks forces the project scope off but P.instructions is on, re-inject the repo
|
|
548
547
|
// CLAUDE.md/AGENTS.md through the controlled append-system-prompt channel so the docs are honored
|
|
549
548
|
// rather than silently dropped (see composeSystemPromptAppend / resolveProjectInstructions).
|
|
550
549
|
const promptFile = resolveClaudeSystemPrompt(config, hostDefaultArgs, assetHome);
|
|
@@ -562,7 +561,6 @@ function assembleClaude(config: RunnerSpawnConfig, providerConfig: ProviderConfi
|
|
|
562
561
|
...pluginDirs.flatMap((dir) => ["--plugin-dir", dir]),
|
|
563
562
|
...claudeMcpLaunchArgs({
|
|
564
563
|
relayUrl: config.relayUrl,
|
|
565
|
-
generation: runnerMcpGeneration(config.env),
|
|
566
564
|
...(config.relayMcpEndpoint ? { endpoint: config.relayMcpEndpoint } : {}),
|
|
567
565
|
includeRelay: relayEndpoint,
|
|
568
566
|
servers: declaredMcp,
|
|
@@ -611,14 +609,14 @@ function assembleClaude(config: RunnerSpawnConfig, providerConfig: ProviderConfi
|
|
|
611
609
|
hostSources: claudeHostReport(hostPlan, { mcp: hostMcpServers.length > 0, hooks: hostHooks || (!configHome && hostPlan.selection.hooks), skills: hostSkills.length > 0, defaultArgs: hostDefaultArgs.length > 0, env: Object.keys(providerConfig.env ?? {}).length > 0 && hostPlan.selection.settings }),
|
|
612
610
|
hostDefaultArgs,
|
|
613
611
|
mcp: {
|
|
614
|
-
|
|
612
|
+
|
|
615
613
|
relayEndpoint, strict: strictMcp, servers: mcpServers, projectServers, hostServers: hostMcpServers,
|
|
616
614
|
...(sharedListenerUrl ? { sharedListener: { url: sharedListenerUrl } } : {}),
|
|
617
615
|
},
|
|
618
616
|
sourceShadows: [...skillSourceShadows, ...mcpSourceShadows],
|
|
619
617
|
instructions,
|
|
620
618
|
args,
|
|
621
|
-
env: writeChildEnv({}, { ...configHomeEnv(provider, configHome, config.env), ...nativeMemory.env, ...fffMcpHookEnv(config), ...webToolsEnv(config), ...agentRedactEnv(redaction) }),
|
|
619
|
+
env: writeChildEnv({}, { ...configHomeEnv(provider, configHome, config.env), ...nativeMemory.env, ...fffMcpHookEnv(config), ...webToolsEnv(config), ...agentRedactEnv(redaction) }, []),
|
|
622
620
|
redaction,
|
|
623
621
|
};
|
|
624
622
|
}
|
|
@@ -631,7 +629,7 @@ function assembleCodex(config: RunnerSpawnConfig, providerConfig: ProviderConfig
|
|
|
631
629
|
const { provisioned: provisionedSkills, bundled: bundledSkillDirs, hostRoots, disabled: disabledSkillDirs, roots: skillDirs } = codexSkillSources(config);
|
|
632
630
|
const hostPlan = configHome ? codexHostPlan(config, providerSourceHome("codex")) : undefined;
|
|
633
631
|
const hostSelection = hostSourcesFor(config);
|
|
634
|
-
//
|
|
632
|
+
// review, findings 3 & 4 — Codex delivers hooks via CODEX_HOME/hooks.json, so a launch
|
|
635
633
|
// can only carry them when it has an isolated CODEX_HOME (host-base profiles reuse the real
|
|
636
634
|
// ~/.codex, which relay must not clobber) AND the installed Codex is new enough to consume the
|
|
637
635
|
// file. Resolve the profile's hook definitions to decide whether there was anything to deliver;
|
|
@@ -643,7 +641,7 @@ function assembleCodex(config: RunnerSpawnConfig, providerConfig: ProviderConfig
|
|
|
643
641
|
? {
|
|
644
642
|
reason: !configHome
|
|
645
643
|
? "Codex host-base profile has no managed CODEX_HOME; relay will not overwrite the host ~/.codex/hooks.json, so the baseline guard cannot be delivered"
|
|
646
|
-
: "installed Codex is older than 0.124 and ignores CODEX_HOME/hooks.json (set
|
|
644
|
+
: "installed Codex is older than 0.124 and ignores CODEX_HOME/hooks.json (set HUMWICK_CODEX_VERSION to override the probe)",
|
|
647
645
|
names: resolvedHookDefs.map((h) => h.name),
|
|
648
646
|
}
|
|
649
647
|
: undefined;
|
|
@@ -653,7 +651,7 @@ function assembleCodex(config: RunnerSpawnConfig, providerConfig: ProviderConfig
|
|
|
653
651
|
// unenforceable rather than claiming an artifact the provider ignores.
|
|
654
652
|
const hostHookSet = configHome && hooksSupported && hostSelection.hooks ? hostCodexHooks() : {};
|
|
655
653
|
const hostHooks = Object.keys(hostHookSet).length > 0;
|
|
656
|
-
//
|
|
654
|
+
// redaction rides the `-c` session-flags layer, so host-base launches keep ~/.codex.
|
|
657
655
|
const redact = resolveAgentRedact();
|
|
658
656
|
const redaction: AgentRedactState = !isAgentRedactInstall(redact) ? redact
|
|
659
657
|
: !hooksSupported ? { state: "unavailable", reason: "installed Codex is older than 0.124 and ignores hooks" }
|
|
@@ -662,14 +660,14 @@ function assembleCodex(config: RunnerSpawnConfig, providerConfig: ProviderConfig
|
|
|
662
660
|
// Codex has no `--setting-sources`: only a vanilla base drops the ambient project doc
|
|
663
661
|
// (`project_doc_max_bytes=0`); an isolated CODEX_HOME drops host/global config.toml. There is
|
|
664
662
|
// no P.hooks→scope-drop interference here (only a vanilla base suppresses the project doc), so
|
|
665
|
-
// Codex never re-injects repo instructions (
|
|
663
|
+
// Codex never re-injects repo instructions (is Claude's `--setting-sources` surface).
|
|
666
664
|
const instructions = assembleInstructions(config, {
|
|
667
665
|
projectScopeDropped: vanilla,
|
|
668
666
|
isolatedHome: Boolean(configHome),
|
|
669
667
|
repoInstructionsReinjected: false,
|
|
670
668
|
});
|
|
671
669
|
|
|
672
|
-
//
|
|
670
|
+
// a provisioned/project server's env can carry a literal secret; `-c
|
|
673
671
|
// mcp_servers.<name>.env.<K>=<V>` lands verbatim in argv (world-readable via ps/proc).
|
|
674
672
|
// An ISOLATED CODEX_HOME is a relay-owned per-instance dir Codex actually reads as its
|
|
675
673
|
// config home, so route command/args/url/env through CODEX_HOME/config.toml instead
|
|
@@ -681,14 +679,14 @@ function assembleCodex(config: RunnerSpawnConfig, providerConfig: ProviderConfig
|
|
|
681
679
|
|
|
682
680
|
// The skills/MCP/project-doc block; core launch config (model/approval/listen) stays
|
|
683
681
|
// in the adapter. -c overrides are order-independent, so the assembled block is contiguous.
|
|
684
|
-
//
|
|
682
|
+
// P5.7 — Codex's native-memory lever rides the same `-c` argv precedent as the
|
|
685
683
|
// project-doc suppressor above it (`-c project_doc_max_bytes=0`). Argv outranks config.toml, so
|
|
686
684
|
// this is the portable/authoritative half; the isolated CODEX_HOME config.toml is the belt.
|
|
687
685
|
const nativeMemory = resolveNativeMemoryLever({ provider: "codex", config, isolatedHome: Boolean(configHome) });
|
|
688
686
|
|
|
689
687
|
const args = [
|
|
690
688
|
...bundledSkillConfigArgs(skillDirs, disabledSkillDirs),
|
|
691
|
-
...(relayEndpoint ? relayMcpCodexConfigArgs(config.relayUrl, config.relayMcpEndpoint
|
|
689
|
+
...(relayEndpoint ? relayMcpCodexConfigArgs(config.relayUrl, config.relayMcpEndpoint) : []),
|
|
692
690
|
...mcpArgs,
|
|
693
691
|
...codexRetiredMcpConfigArgs(config),
|
|
694
692
|
...codexProjectDocSuppressionArgs(config),
|
|
@@ -732,14 +730,14 @@ function assembleCodex(config: RunnerSpawnConfig, providerConfig: ProviderConfig
|
|
|
732
730
|
: nativeHostReport(),
|
|
733
731
|
hostDefaultArgs,
|
|
734
732
|
mcp: {
|
|
735
|
-
|
|
733
|
+
|
|
736
734
|
relayEndpoint, strict: false, servers: mcpServers, projectServers, hostServers: hostMcpServers,
|
|
737
735
|
...(sharedListenerUrl ? { sharedListener: { url: sharedListenerUrl, ...(metaOnlyCallmux ? { metaOnly: true } : {}), ...(metaOnlyCallmux?.relay ? { relayFronted: true } : {}) } } : {}),
|
|
738
736
|
},
|
|
739
737
|
sourceShadows: mcpSourceShadows,
|
|
740
738
|
instructions,
|
|
741
739
|
args,
|
|
742
|
-
env: writeChildEnv({}, { ...configHomeEnv("codex", configHome), ...webToolsEnv(config), ...agentRedactEnv(redaction) }),
|
|
740
|
+
env: writeChildEnv({}, { ...configHomeEnv("codex", configHome), ...webToolsEnv(config), ...agentRedactEnv(redaction) }, []),
|
|
743
741
|
redaction,
|
|
744
742
|
};
|
|
745
743
|
}
|
|
@@ -748,6 +746,8 @@ function assembleCodex(config: RunnerSpawnConfig, providerConfig: ProviderConfig
|
|
|
748
746
|
// provider-home asset formats. It still needs a complete projection object: RunnerCore assembles
|
|
749
747
|
// every normal provider before registration and launch-injection reporting.
|
|
750
748
|
function assembleOpenCode(config: RunnerSpawnConfig, provider: string): AssembledLaunch {
|
|
749
|
+
const assets = resolveOpenCodeAssets(config);
|
|
750
|
+
const hooks = profileProvisioning(config).hooks ?? [];
|
|
751
751
|
const { relayEndpoint, mcpServers, projectServers, hostMcpServers, sharedListenerUrl, sourceShadows } = resolveOpenCodeDeclaredMcp(config);
|
|
752
752
|
const instructions = assembleInstructions(config, {
|
|
753
753
|
projectScopeDropped: false,
|
|
@@ -761,18 +761,17 @@ function assembleOpenCode(config: RunnerSpawnConfig, provider: string): Assemble
|
|
|
761
761
|
...(config.agentProfile ? { profile: config.agentProfile } : {}),
|
|
762
762
|
base: config.agentProfile?.base ?? "host",
|
|
763
763
|
vanilla: profileIsVanillaBase(config),
|
|
764
|
-
//
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
relayContext: false, relayContextPending: resolveRelayContext({ slug: "relay.context.opencode", config }).available, launchContextPending: Boolean(config.systemPromptAppend?.trim()),
|
|
768
|
-
relaySkills: false,
|
|
764
|
+
// Native instructions are system-level context, persistent across turns and session clears.
|
|
765
|
+
relayContext: assets.context.available,
|
|
766
|
+
relaySkills: assets.bundled.length > 0,
|
|
769
767
|
relayPlugin: false,
|
|
770
768
|
statusLine: false,
|
|
771
|
-
skills:
|
|
769
|
+
skills: assets.provisioned.map(({ name, dir }) => ({ name, dir })),
|
|
772
770
|
hostSkills: [],
|
|
773
771
|
projectSkills: [],
|
|
774
772
|
projectSettings: { applied: false, keys: [], ignoredKeys: [] },
|
|
775
773
|
hooks: [],
|
|
774
|
+
...(hooks.length ? { hooksSuppressed: { reason: "OpenCode uses native plugins, not Claude/Codex hook command manifests; these hooks were not applied.", names: hooks.map(hook => hook.name) } } : {}),
|
|
776
775
|
hostHooks: false,
|
|
777
776
|
hostHooksEnforceable: false,
|
|
778
777
|
projectHooks: [],
|
|
@@ -785,14 +784,14 @@ function assembleOpenCode(config: RunnerSpawnConfig, provider: string): Assemble
|
|
|
785
784
|
hostSources: { instructions: "unsupported", mcp: "unsupported", hooks: "unsupported", skills: "unsupported", plugins: "unsupported", settings: "unsupported" },
|
|
786
785
|
hostDefaultArgs: [],
|
|
787
786
|
mcp: {
|
|
788
|
-
|
|
787
|
+
|
|
789
788
|
relayEndpoint, strict: false, servers: mcpServers, projectServers, hostServers: hostMcpServers,
|
|
790
789
|
...(sharedListenerUrl ? { sharedListener: { url: sharedListenerUrl } } : {}),
|
|
791
790
|
},
|
|
792
791
|
sourceShadows,
|
|
793
792
|
instructions,
|
|
794
793
|
args: [],
|
|
795
|
-
env: writeChildEnv({}, { ...webToolsEnv(config), ...agentRedactEnv(redaction) }),
|
|
794
|
+
env: writeChildEnv({}, { ...webToolsEnv(config), ...agentRedactEnv(redaction) }, []),
|
|
796
795
|
redaction,
|
|
797
796
|
};
|
|
798
797
|
}
|
|
@@ -804,13 +803,13 @@ function configHomeEnv(provider: string, configHome: string | undefined, launchE
|
|
|
804
803
|
const envVar = manifest?.home?.configDirEnvVar;
|
|
805
804
|
if (!envVar) return {};
|
|
806
805
|
if (configHome) return { [envVar]: configHome };
|
|
807
|
-
//
|
|
806
|
+
// review HIGH-1 — a dedicatedHome provider (claude-b) pins the env var at its OWN
|
|
808
807
|
// effective host home even on host-base/profile-less launches, so the provider binary
|
|
809
808
|
// never falls back to the primary family home (~/.claude). The primary providers
|
|
810
809
|
// (claude, codex) keep their historical no-export host-base behavior.
|
|
811
810
|
const hostHome = providerHostConfigHome(provider);
|
|
812
811
|
if (hostHome) return { [envVar]: hostHome };
|
|
813
|
-
//
|
|
812
|
+
// a caller passing launchEnv (claude) opts in to pinning the effective dir (launch
|
|
814
813
|
// env, else the runner's) so the session-status probe, which falls back to the runner's
|
|
815
814
|
// value, reads the dir the provider process uses. Unset everywhere stays unpinned.
|
|
816
815
|
if (!launchEnv) return {};
|
|
@@ -838,7 +837,7 @@ function materializeClaudeAssets(config: RunnerSpawnConfig, provider: string = "
|
|
|
838
837
|
if (!configHome) return;
|
|
839
838
|
const hostPlan = claudeHostPlan(config, providerSourceHome(provider));
|
|
840
839
|
const skillSources = resolveClaudeSkillSources(configHome, config, provider);
|
|
841
|
-
//
|
|
840
|
+
// host classes first: drop what an opted-out class left in this reused home, then link
|
|
842
841
|
// the selected ones, before Relay/Project skills claim their names.
|
|
843
842
|
materializeHostAssets(configHome, hostPlan.links, hostPlan.files, skillSources.host.map((skill) => relative(configHome, skill.dir)));
|
|
844
843
|
materializeResolvedAssets(skillSources.relay);
|
|
@@ -846,33 +845,32 @@ function materializeClaudeAssets(config: RunnerSpawnConfig, provider: string = "
|
|
|
846
845
|
materializeResolvedAssets(skillSources.host);
|
|
847
846
|
materializeResolvedAssets(resolveProvisionedHooks(configHome, config));
|
|
848
847
|
materializeResolvedAssets(resolveProjectHooks(config));
|
|
849
|
-
//
|
|
848
|
+
// land the provisioned hooks in the managed home's settings.json (the documented
|
|
850
849
|
// hook source), matching the assembler's decision to keep them out of the inline --settings.
|
|
851
|
-
//
|
|
852
|
-
// env lever).
|
|
850
|
+
// P5.7 — the native-memory keys ride the SAME file (the isolated-home belt behind the
|
|
851
|
+
// env lever). — so do the selected host settings and native plugin enablement: this is
|
|
853
852
|
// the one writer of the managed settings.json, so nothing later can wipe plugin enablement.
|
|
854
853
|
const managedHookSettings = claudeManagedHookSettings(config, configHome, provider);
|
|
855
854
|
const nativeMemorySettings = claudeManagedNativeMemorySettings(config, configHome, (lists) => callerSettingsMergeable(lists, config.cwd), provider);
|
|
856
|
-
//
|
|
855
|
+
// the SendFeedback deny rides EVERY managed settings.json this launch writes (the
|
|
857
856
|
// file-channel belt behind the CLAUDE_CODE_SEND_FEEDBACK=0 env lever, so a stripped env
|
|
858
857
|
// still removes the tool): merged into the full payload AND the belt-retry payload so the
|
|
859
858
|
// retry can never drop it.
|
|
860
|
-
const withManagedPermissions = (settings: Record<string, unknown>) => withManagedClaudePermissions(settings, config.approvalMode
|
|
859
|
+
const withManagedPermissions = (settings: Record<string, unknown>) => withManagedClaudePermissions(settings, config.approvalMode);
|
|
861
860
|
const baseSettings = { ...hostPlan.settings, ...(managedHookSettings ?? {}) };
|
|
862
861
|
// The hook and selected-host halves of this file are NOT best-effort (a missing baseline guard
|
|
863
|
-
// is a real regression
|
|
862
|
+
// is a real regression; a selected host class must never silently vanish), so
|
|
864
863
|
// only the native-memory contribution is guarded: on a write failure, retry without it.
|
|
865
864
|
if (applyNativeMemoryBelt("the Claude managed settings.json", () => writeClaudeSettingsJson(configHome, withManagedPermissions({ ...baseSettings, ...nativeMemorySettings }))) && Object.keys(baseSettings).length) {
|
|
866
865
|
writeClaudeSettingsJson(configHome, withManagedPermissions(baseSettings));
|
|
867
866
|
}
|
|
868
867
|
materializeResolvedAssets(resolveClaudeProvisionedPlugins(configHome, config));
|
|
869
|
-
//
|
|
868
|
+
// write the file assembleClaude's --mcp-config <path> points at, from the SAME
|
|
870
869
|
// resolveClaudeDeclaredMcp(config) inputs (see the purity/consistency note there). Keeps
|
|
871
870
|
// provisioned/host-server env/headers secrets out of argv.
|
|
872
871
|
const { declaredMcp, hostMcp, relayEndpoint, strictMcp } = resolveClaudeDeclaredMcp(config);
|
|
873
872
|
const mcpServers = claudeMcpConfigPayload({
|
|
874
873
|
relayUrl: config.relayUrl,
|
|
875
|
-
generation: runnerMcpGeneration(config.env),
|
|
876
874
|
...(config.relayMcpEndpoint ? { endpoint: config.relayMcpEndpoint } : {}),
|
|
877
875
|
includeRelay: relayEndpoint,
|
|
878
876
|
servers: declaredMcp,
|
|
@@ -893,7 +891,7 @@ function materializeCodexAssets(config: RunnerSpawnConfig): void {
|
|
|
893
891
|
materializeHostAssets(configHome, hostPlan.links, []);
|
|
894
892
|
writeCodexHostSettings(configHome, hostPlan.settings, tomlInlineValue);
|
|
895
893
|
materializeResolvedAssets(resolveCodexProvisionedSkills(configHome, config));
|
|
896
|
-
//
|
|
894
|
+
// review, finding 4 — don't write CODEX_HOME/hooks.json for a Codex that would ignore it;
|
|
897
895
|
// keep disk == report (the assembler reports these hooks suppressed for such a launch).
|
|
898
896
|
if (codexHooksSupported(config.providerConfig.command)) {
|
|
899
897
|
const relayHooks = resolveProvisionedHooks(configHome, config);
|
|
@@ -902,20 +900,20 @@ function materializeCodexAssets(config: RunnerSpawnConfig): void {
|
|
|
902
900
|
const hostHookSet = hostPlan.selection.hooks ? hostCodexHooks() : {};
|
|
903
901
|
writeCodexHooksJson(configHome, mergeHookSets(hostHookSet, relayHookSet));
|
|
904
902
|
}
|
|
905
|
-
//
|
|
903
|
+
// write the CODEX_HOME/config.toml sections assembleCodex's non-secret -c
|
|
906
904
|
// timeout-only overrides expect to find command/args/url/env in (see the mcpArgs branch
|
|
907
905
|
// there). Same resolveCodexDeclaredMcp(config) inputs, so disk == launch.
|
|
908
906
|
const { declaredMcp, metaOnlyCallmux } = resolveCodexDeclaredMcp(config);
|
|
909
907
|
writeCodexMcpServersToml(configHome, declaredMcp);
|
|
910
|
-
//
|
|
908
|
+
// P5.7 — the isolated-home belt behind the `-c features.memories=…` argv lever. Same
|
|
911
909
|
// resolveNativeMemoryLever inputs assembleCodex used, so disk == launch. No-op for an
|
|
912
910
|
// undeclared profile (`configToml` is empty, so nothing is written). Guarded: never fails the spawn.
|
|
913
911
|
applyNativeMemoryBelt("the Codex CODEX_HOME/config.toml [features] table", () => {
|
|
914
912
|
writeCodexFeaturesToml(configHome, resolveNativeMemoryLever({ provider: "codex", config, isolatedHome: true }).configToml);
|
|
915
913
|
});
|
|
916
|
-
//
|
|
914
|
+
// write the per-agent metaOnly config the shared-listener descriptor points at
|
|
917
915
|
// (`callmux --config <path>`), from the SAME resolveCodexDeclaredMcp inputs so disk == launch.
|
|
918
|
-
// The optional relay downstream
|
|
916
|
+
// The optional relay downstream rides here too, so the on-demand relay front and the
|
|
919
917
|
// agent's stdio callmux descriptor come from one resolution.
|
|
920
918
|
if (metaOnlyCallmux) {
|
|
921
919
|
writeSharedMcpMetaOnlyConfig(metaOnlyCallmux.configPath, metaOnlyCallmux.url, metaOnlyCallmux.cwd, metaOnlyCallmux.relay);
|
|
@@ -934,7 +932,7 @@ export function assembleLaunch(provider: SpawnProvider, config: RunnerSpawnConfi
|
|
|
934
932
|
const hostDefaultArgs = hostProviderDefaultArgs(config, providerConfig);
|
|
935
933
|
const serviceEnv = serviceAccessLaunchEnv(config);
|
|
936
934
|
const assembled = assembler(config, providerConfig, hostDefaultArgs);
|
|
937
|
-
return { ...assembled, env: writeChildEnv({}, { ...assembled.env, ...serviceEnv }) };
|
|
935
|
+
return { ...assembled, env: writeChildEnv({}, { ...assembled.env, ...serviceEnv }, []) };
|
|
938
936
|
}
|
|
939
937
|
|
|
940
938
|
/**
|
|
@@ -944,9 +942,9 @@ export function assembleLaunch(provider: SpawnProvider, config: RunnerSpawnConfi
|
|
|
944
942
|
* system context also uses a private file for host-base and profile-less launches.
|
|
945
943
|
*/
|
|
946
944
|
export function materializeLaunchAssembly(provider: SpawnProvider, config: RunnerSpawnConfig, assembled?: AssembledLaunch): void {
|
|
947
|
-
//
|
|
945
|
+
// every Claude path, including host-base/profile-less, uses a private file.
|
|
948
946
|
if (getManifest(provider)?.shim?.kind === "claude-relay") materializeClaudeSystemPrompt((assembled ?? assembleLaunch(provider, config, config.providerConfig)).systemPromptFile);
|
|
949
|
-
if (!config.agentProfile
|
|
947
|
+
if (!config.agentProfile) {
|
|
950
948
|
const feature = getManifest(provider)?.provisioning?.bundledSkills ? "skills" : "plugins";
|
|
951
949
|
if (profileAllowsRelayFeature(config, feature)) {
|
|
952
950
|
materializeProviderHome(bundledIntegrationHome(config), { provider, profile: "integrations", instanceId: config.instanceId || config.runnerId, agentId: config.agentId });
|