@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.
Files changed (211) hide show
  1. package/agent-bin/humwick +2 -2
  2. package/agent-bin/humwick-browser +4 -0
  3. package/hooks/fff-search-redirect/fff-search-redirect.cjs +1 -1
  4. package/hooks/web-tools-redirect/web-tools-redirect.cjs +2 -2
  5. package/package.json +7 -10
  6. package/plugins/browser/skills/playwright-cli/SKILL.md +8 -6
  7. package/plugins/claude/.claude-plugin/plugin.json +3 -3
  8. package/plugins/claude/claude-statusline-probe.cjs +26 -16
  9. package/plugins/claude/hooks/gate-run-guard.provisioned.mjs +47 -17
  10. package/plugins/claude/hooks/gate-run-guard.sh +3 -3
  11. package/plugins/claude/hooks/gate-run-guard.ts +5 -5
  12. package/plugins/claude/hooks/kill-guard.sh +4 -4
  13. package/plugins/claude/hooks/kill-guard.ts +1 -1
  14. package/plugins/claude/hooks/permission-request.sh +1 -1
  15. package/plugins/claude/hooks/post-compact.sh +1 -1
  16. package/plugins/claude/hooks/post-tool-use.sh +3 -3
  17. package/plugins/claude/hooks/pre-compact.sh +2 -2
  18. package/plugins/claude/hooks/read-only-bash-guard.sh +4 -4
  19. package/plugins/claude/hooks/relay-status.sh +32 -32
  20. package/plugins/claude/hooks/session-end.sh +2 -2
  21. package/plugins/claude/hooks/session-start.sh +3 -3
  22. package/plugins/claude/hooks/shell-trust.ts +1 -1
  23. package/plugins/claude/hooks/stop-failure.sh +2 -2
  24. package/plugins/claude/hooks/stop.sh +6 -6
  25. package/plugins/claude/hooks/subagent-skill-policy.cjs +8 -6
  26. package/plugins/claude/hooks/subagent-stop.sh +3 -3
  27. package/plugins/claude/hooks/user-prompt-submit.sh +1 -1
  28. package/plugins/claude/hooks/wait-loop-guard.ts +3 -3
  29. package/plugins/claude/monitors/monitors.json +2 -2
  30. package/plugins/claude/monitors/relay-monitor.provisioned.mjs +96 -62
  31. package/plugins/claude/monitors/relay-monitor.ts +5 -5
  32. package/plugins/claude/skills/label/SKILL.md +3 -3
  33. package/plugins/claude/skills/message/SKILL.md +5 -5
  34. package/plugins/claude/skills/react/SKILL.md +1 -1
  35. package/plugins/claude/skills/read-message/SKILL.md +4 -4
  36. package/plugins/claude/skills/reply/SKILL.md +5 -5
  37. package/plugins/claude/skills/send-claimable/SKILL.md +5 -5
  38. package/plugins/claude/skills/slice-first/SKILL.md +5 -3
  39. package/plugins/claude/skills/slice-first/references/plan-recipe.md +30 -29
  40. package/plugins/claude/skills/status/SKILL.md +1 -1
  41. package/plugins/claude/skills/tags/SKILL.md +5 -5
  42. package/plugins/claude/skills/task/SKILL.md +10 -10
  43. package/plugins/claude-output-style/.claude-plugin/plugin.json +1 -1
  44. package/plugins/claude-output-style/output-styles/{agent-relay.md → humwick.md} +1 -1
  45. package/plugins/codex/skills/guide/SKILL.md +1 -1
  46. package/plugins/codex/skills/label/SKILL.md +3 -3
  47. package/plugins/codex/skills/message/SKILL.md +5 -5
  48. package/plugins/codex/skills/react/SKILL.md +1 -1
  49. package/plugins/codex/skills/read-message/SKILL.md +4 -4
  50. package/plugins/codex/skills/reply/SKILL.md +5 -5
  51. package/plugins/codex/skills/send-claimable/SKILL.md +5 -5
  52. package/plugins/codex/skills/slice-first/SKILL.md +5 -3
  53. package/plugins/codex/skills/slice-first/references/plan-recipe.md +30 -29
  54. package/plugins/codex/skills/status/SKILL.md +1 -1
  55. package/plugins/codex/skills/tags/SKILL.md +5 -5
  56. package/plugins/codex/skills/task/SKILL.md +11 -10
  57. package/src/adapter.ts +95 -92
  58. package/src/adapters/claude-api.ts +8 -11
  59. package/src/adapters/claude-context-command.ts +1 -1
  60. package/src/adapters/claude-credentials.ts +7 -3
  61. package/src/adapters/claude-delivery-lanes.ts +2 -2
  62. package/src/adapters/claude-delivery.ts +16 -16
  63. package/src/adapters/claude-effort.ts +2 -2
  64. package/src/adapters/claude-exit.ts +2 -2
  65. package/src/adapters/claude-headless-mirror.ts +18 -2
  66. package/src/adapters/claude-headless-refusal.ts +3 -3
  67. package/src/adapters/claude-headless.ts +11 -10
  68. package/src/adapters/claude-launch-exit.ts +1 -1
  69. package/src/adapters/claude-monitor-delivery-plan.ts +36 -16
  70. package/src/adapters/claude-permission.ts +5 -5
  71. package/src/adapters/claude-prompt-gates.ts +9 -9
  72. package/src/adapters/claude-prompt-hook.ts +2 -2
  73. package/src/adapters/claude-session-capture.ts +6 -6
  74. package/src/adapters/claude-session-probe.ts +25 -25
  75. package/src/adapters/claude-shutdown.ts +11 -11
  76. package/src/adapters/claude-status-detectors.ts +31 -31
  77. package/src/adapters/claude-subagents.ts +7 -7
  78. package/src/adapters/claude-submit-confirmation.ts +6 -6
  79. package/src/adapters/claude-tmux.ts +43 -43
  80. package/src/adapters/claude-token-usage-capture.ts +5 -5
  81. package/src/adapters/claude-tool-policy.ts +7 -7
  82. package/src/adapters/claude-transcript.ts +24 -24
  83. package/src/adapters/claude.ts +100 -100
  84. package/src/adapters/codex-agent-message-capture.ts +26 -63
  85. package/src/adapters/codex-app-server.ts +1 -1
  86. package/src/adapters/codex-client.ts +9 -9
  87. package/src/adapters/codex-effective-config.ts +1 -1
  88. package/src/adapters/codex-launch-config.ts +5 -5
  89. package/src/adapters/codex-rollout-context.ts +2 -2
  90. package/src/adapters/codex-skill-discovery.ts +1 -1
  91. package/src/adapters/codex-token-usage-capture.ts +16 -16
  92. package/src/adapters/codex-tool-output.ts +4 -4
  93. package/src/adapters/codex-version.ts +3 -3
  94. package/src/adapters/codex.ts +64 -64
  95. package/src/adapters/opencode-api.ts +1 -1
  96. package/src/adapters/opencode-context.ts +20 -22
  97. package/src/adapters/opencode-model-registry.ts +7 -7
  98. package/src/adapters/opencode-provider.ts +8 -8
  99. package/src/adapters/opencode-provisioning.ts +30 -0
  100. package/src/adapters/opencode-server-retry.ts +1 -1
  101. package/src/adapters/opencode-soft-steer.ts +5 -5
  102. package/src/adapters/opencode-spawn.ts +18 -14
  103. package/src/adapters/opencode-token-usage-capture.ts +12 -12
  104. package/src/adapters/opencode-tool-events.ts +15 -15
  105. package/src/adapters/opencode.ts +232 -232
  106. package/src/adapters/project-shell.ts +11 -11
  107. package/src/adapters/service-access.ts +6 -6
  108. package/src/adapters/tmux-offload.ts +9 -9
  109. package/src/adapters/tool-summary.ts +5 -5
  110. package/src/agent-redact.ts +1 -1
  111. package/src/agent-runner/commands/dispatch.ts +10 -10
  112. package/src/agent-runner/commands/lifecycle.ts +2 -2
  113. package/src/agent-runner/commands/prompt.ts +19 -19
  114. package/src/agent-runner/credentials.ts +5 -5
  115. package/src/agent-runner/inbox.ts +1 -1
  116. package/src/agent-runner/lifecycle-milestones.ts +3 -3
  117. package/src/agent-runner/lifecycle-phase.ts +2 -2
  118. package/src/agent-runner/liveness-exchange.ts +4 -10
  119. package/src/agent-runner/registration.ts +8 -8
  120. package/src/agent-runner/runner-context.ts +9 -14
  121. package/src/agent-runner/session-mirror.ts +12 -12
  122. package/src/agent-runner/task-claims.ts +2 -2
  123. package/src/agent-runner/token-usage-coverage.ts +1 -1
  124. package/src/agent-runner/turn-report.ts +12 -12
  125. package/src/api-budget-accounting.ts +3 -3
  126. package/src/area-memory.ts +6 -6
  127. package/src/artifact-path-materialize.ts +1 -1
  128. package/src/async-guard.ts +5 -5
  129. package/src/attachment-cache.ts +17 -22
  130. package/src/background-script-reconcile.ts +6 -6
  131. package/src/bounded-log.ts +1 -1
  132. package/src/busy-reconciler.ts +22 -22
  133. package/src/claim-tracker.ts +3 -3
  134. package/src/claude-settings.ts +8 -8
  135. package/src/claude-system-prompt.ts +1 -1
  136. package/src/claude-workspace-trust.ts +1 -1
  137. package/src/config.ts +62 -61
  138. package/src/context-boundary.ts +5 -5
  139. package/src/context-command.ts +3 -3
  140. package/src/control-server.ts +32 -32
  141. package/src/delivery-result.ts +2 -2
  142. package/src/effort-command.ts +2 -2
  143. package/src/empty-turn.ts +22 -14
  144. package/src/free-port.ts +1 -1
  145. package/src/gate-run-config.ts +3 -3
  146. package/src/host-asset-plan.ts +24 -17
  147. package/src/host-claude-config-worker.ts +2 -2
  148. package/src/host-sources.ts +2 -2
  149. package/src/index.ts +16 -16
  150. package/src/launch-assembly.ts +94 -96
  151. package/src/liveness.ts +15 -15
  152. package/src/logger.ts +3 -3
  153. package/src/mcp-outbox.ts +3 -3
  154. package/src/mcp-preflight.ts +2 -2
  155. package/src/mcp-source-selection.ts +2 -2
  156. package/src/mcp-tool-deferral.ts +1 -1
  157. package/src/mcp-vault-env.ts +2 -2
  158. package/src/message-body-config/config.ts +4 -4
  159. package/src/mid-turn-delivery.ts +2 -2
  160. package/src/native-memory-lever.ts +8 -8
  161. package/src/outbox.ts +15 -15
  162. package/src/pending-permission-store.ts +1 -1
  163. package/src/profile-home.ts +19 -19
  164. package/src/profile-projection.ts +26 -24
  165. package/src/project-instructions.ts +5 -5
  166. package/src/provider-auth-warning.ts +17 -0
  167. package/src/provider-double.ts +1 -1
  168. package/src/provider-hold.ts +3 -3
  169. package/src/provider-home-marker.ts +1 -1
  170. package/src/provider-home-tombstone.ts +1 -1
  171. package/src/provider-input-queue.ts +2 -2
  172. package/src/provider-integration.ts +15 -13
  173. package/src/provider-setup.ts +15 -3
  174. package/src/providers.ts +1 -1
  175. package/src/provisioning.ts +37 -40
  176. package/src/question-decision.ts +2 -2
  177. package/src/quota.ts +0 -6
  178. package/src/rate-limit.ts +24 -13
  179. package/src/refused-prompt-store.ts +2 -2
  180. package/src/relay-injection-events.ts +2 -2
  181. package/src/relay-instructions.ts +5 -5
  182. package/src/relay-mcp-proxy.long-turn-e2e.ts +13 -13
  183. package/src/relay-mcp-proxy.ts +57 -58
  184. package/src/relay-mcp.ts +55 -69
  185. package/src/reply-obligation-cache.ts +9 -9
  186. package/src/response-capture-report.ts +9 -9
  187. package/src/response-routing.ts +1 -1
  188. package/src/runner-core.ts +209 -222
  189. package/src/runner-exit.ts +1 -1
  190. package/src/runner-helpers.ts +10 -10
  191. package/src/runner-insights.ts +4 -2
  192. package/src/runner-mcp-proxy.ts +3 -3
  193. package/src/runner-spawn-config.ts +3 -3
  194. package/src/runtime-token-file.ts +1 -1
  195. package/src/runtime-token-remint.ts +1 -1
  196. package/src/service-access.ts +2 -2
  197. package/src/session-destroy.ts +1 -1
  198. package/src/session-insights.ts +1 -1
  199. package/src/session-scratch.ts +24 -37
  200. package/src/spawn-input.ts +6 -6
  201. package/src/startup-config-fetch.ts +4 -4
  202. package/src/subreaper.ts +3 -5
  203. package/src/test-run-dedup.ts +13 -13
  204. package/src/transcript-context.ts +2 -2
  205. package/src/transport-errors.ts +1 -1
  206. package/src/turn-opener.ts +1 -1
  207. package/src/turn-reconcile-event.ts +2 -2
  208. package/src/vault-proxy-hop.ts +2 -2
  209. package/src/web-tools.ts +2 -2
  210. package/agent-bin/agent-relay-browser +0 -10
  211. package/src/adapters/provider-quota-harvest.ts +0 -76
@@ -6,7 +6,8 @@ import type {
6
6
  AgentProfileProjectionTrust,
7
7
  SpawnProvider,
8
8
  } from "@humwick/sdk";
9
- import { isRecord, mcpServerInfo } from "@humwick/sdk";
9
+ import { isRecord, MCP_SERVER_INFO } from "@humwick/sdk";
10
+ import { SHARED_PROFILE_CAPABILITIES, STORED_NAMES } from "@humwick/sdk/stored-names";
10
11
  import { getManifest } from "@humwick/providers";
11
12
  import type { AssembledLaunch } from "./launch-assembly";
12
13
  import { projectSourceEnabled, type ResolvedSourceShadow } from "./provisioning";
@@ -23,10 +24,10 @@ interface AgentProfileProjectionInput {
23
24
  generatedAt?: number;
24
25
  }
25
26
 
26
- type MatrixSource = "host" | "project" | "agent-relay";
27
+ type MatrixSource = "host" | "project" | typeof STORED_NAMES.source;
27
28
  type MatrixCapability = "instructions" | "mcp" | "hooks" | "skills" | "plugins";
28
29
 
29
- const MATRIX_SOURCES: readonly MatrixSource[] = ["host", "project", "agent-relay"];
30
+ const MATRIX_SOURCES: readonly MatrixSource[] = ["host", "project", STORED_NAMES.source];
30
31
  const MATRIX_CAPABILITIES: readonly MatrixCapability[] = ["instructions", "mcp", "hooks", "skills", "plugins"];
31
32
 
32
33
  export function agentProfileProjectionReport(input: AgentProfileProjectionInput): AgentProfileProjectionReport {
@@ -72,11 +73,11 @@ function matrixEntries(a: AssembledLaunch): AgentProfileProjectionEntry[] {
72
73
  "project.hooks": projectHooks(a),
73
74
  "project.skills": projectSkills(a),
74
75
  "project.plugins": projectPlugins(a),
75
- "agent-relay.instructions": relayInstructions(a),
76
- "agent-relay.mcp": relayMcp(a),
77
- "agent-relay.hooks": relayHooks(a),
78
- "agent-relay.skills": relaySkills(a),
79
- "agent-relay.plugins": relayPlugins(a),
76
+ [SHARED_PROFILE_CAPABILITIES.instructions]: relayInstructions(a),
77
+ [SHARED_PROFILE_CAPABILITIES.mcp]: relayMcp(a),
78
+ [SHARED_PROFILE_CAPABILITIES.hooks]: relayHooks(a),
79
+ [SHARED_PROFILE_CAPABILITIES.skills]: relaySkills(a),
80
+ [SHARED_PROFILE_CAPABILITIES.plugins]: relayPlugins(a),
80
81
  };
81
82
  addCrossSourceShadows(cells, a);
82
83
  return MATRIX_SOURCES.flatMap((source) => MATRIX_CAPABILITIES.map((capability) => cells[`${source}.${capability}`]));
@@ -93,10 +94,11 @@ function addCrossSourceShadows(
93
94
  a: AssembledLaunch,
94
95
  ): void {
95
96
  for (const shadow of a.sourceShadows ?? []) {
96
- const entry = cells[`${shadow.source}.${shadow.capability === "hooks" ? "hooks" : shadow.capability}`];
97
- const provenance = shadow.source === "agent-relay"
97
+ const source = shadow.source;
98
+ const entry = cells[`${source}.${shadow.capability}`];
99
+ const provenance = source === STORED_NAMES.source
98
100
  ? relayProvenance(a, sourceKind(shadow.capability), shadow.name)
99
- : shadow.source;
101
+ : source;
100
102
  entry.shadowed = dedupeAssets([...(entry.shadowed ?? []), asset(shadow.name, provenance)]);
101
103
  }
102
104
  }
@@ -159,7 +161,7 @@ function hostPlugins(a: AssembledLaunch): AgentProfileProjectionEntry {
159
161
 
160
162
  function projectInstructions(a: AssembledLaunch): AgentProfileProjectionEntry {
161
163
  if (a.instructions.repo === "allow") return scalarCell("project.instructions", "allow", "applied", "allow", projectInstructionMechanism(a), "Project/repo instructions remain available.");
162
- // #2086 — the native project scope was dropped to gate ambient hooks, but P.instructions
164
+ // the native project scope was dropped to gate ambient hooks, but P.instructions
163
165
  // re-injected the repo CLAUDE.md/AGENTS.md through the controlled append-system-prompt channel.
164
166
  // Report it as applied+honored (trusted), NOT suppressed, so the launch shape is not misreported.
165
167
  if (a.instructions.repo === "reinjected") return scalarCell("project.instructions", "allow", "applied", "reinjected", "controlled append-system-prompt re-injection", "Repo CLAUDE.md/AGENTS.md are re-injected through Relay's controlled channel because the native project scope is dropped to gate ambient hooks.", "trusted");
@@ -204,40 +206,40 @@ function projectPlugins(a: AssembledLaunch): AgentProfileProjectionEntry {
204
206
 
205
207
  function relayInstructions(a: AssembledLaunch): AgentProfileProjectionEntry {
206
208
  if (a.relayContextPending || a.launchContextPending) {
207
- return scalarCell("agent-relay.instructions", "enabled", "applied", "pending first provider turn", "runner provider-delivery injection", "Relay-authored instructions are available and will be prepended to the first provider turn; no provider turn has been emitted at launch.");
209
+ return scalarCell(SHARED_PROFILE_CAPABILITIES.instructions, "enabled", "applied", "pending first provider turn", "runner provider-delivery injection", "Relay-authored instructions are available and will be prepended to the first provider turn; no provider turn has been emitted at launch.");
208
210
  }
209
211
  const active = a.relayContext || a.instructions.systemPromptAppend;
210
- return scalarCell("agent-relay.instructions", active ? "enabled" : "disabled", active ? "applied" : "disabled", active ? "injected" : "off", "runner context/prompt injection", active ? "Relay-authored instructions reached the provider turn." : "No Relay-authored instruction material was emitted.");
212
+ return scalarCell(SHARED_PROFILE_CAPABILITIES.instructions, active ? "enabled" : "disabled", active ? "applied" : "disabled", active ? "injected" : "off", "runner context/prompt injection", active ? "Relay-authored instructions reached the provider turn." : "No Relay-authored instruction material was emitted.");
211
213
  }
212
214
 
213
215
  function relayMcp(a: AssembledLaunch): AgentProfileProjectionEntry {
214
216
  const assets = [
215
- ...(a.mcp.relayEndpoint ? [asset(mcpServerInfo(a.mcp.generation ?? "relay").name, "reserved")] : []),
217
+ ...(a.mcp.relayEndpoint ? [asset(MCP_SERVER_INFO.name, "reserved")] : []),
216
218
  ...a.mcp.servers.map((name) => asset(name, relayProvenance(a, "mcp", name))),
217
219
  ];
218
- return assetCell("agent-relay.mcp", assets.length ? "enabled" : "disabled", assets.length ? "applied" : "disabled", assets, "Relay MCP endpoint / controlled MCP config", assets.length ? `Relay MCP assets reached the launch (${assets.map((item) => item.name).join(", ")}).` : "No Relay MCP asset reached this launch.", relayShadows(a, "mcp", assets), "trusted");
220
+ return assetCell(SHARED_PROFILE_CAPABILITIES.mcp, assets.length ? "enabled" : "disabled", assets.length ? "applied" : "disabled", assets, "Relay MCP endpoint / controlled MCP config", assets.length ? `Relay MCP assets reached the launch (${assets.map((item) => item.name).join(", ")}).` : "No Relay MCP asset reached this launch.", relayShadows(a, "mcp", assets), "trusted");
219
221
  }
220
222
 
221
223
  function relayHooks(a: AssembledLaunch): AgentProfileProjectionEntry {
222
- if (a.hooksSuppressed?.names.length) return assetCell("agent-relay.hooks", "enabled", "partial", [], "hook delivery withheld", `Relay hooks were resolved but suppressed: ${a.hooksSuppressed.reason}.`, [], "trusted");
224
+ if (a.hooksSuppressed?.names.length) return assetCell(SHARED_PROFILE_CAPABILITIES.hooks, "enabled", "partial", [], "hook delivery withheld", `Relay hooks were resolved but suppressed: ${a.hooksSuppressed.reason}.`, [], "trusted");
223
225
  const assets = a.hooks.map((item) => asset(item.name, relayProvenance(a, "hook", item.name)));
224
- return assetCell("agent-relay.hooks", assets.length ? "enabled" : "disabled", assets.length ? "applied" : "disabled", assets, "provider hook settings/hooks.json", assets.length ? `Relay hooks reached the launch (${assets.map((item) => item.name).join(", ")}).` : "No Relay hook reached this launch.", relayShadows(a, "hook", assets), "trusted");
226
+ return assetCell(SHARED_PROFILE_CAPABILITIES.hooks, assets.length ? "enabled" : "disabled", assets.length ? "applied" : "disabled", assets, "provider hook settings/hooks.json", assets.length ? `Relay hooks reached the launch (${assets.map((item) => item.name).join(", ")}).` : "No Relay hook reached this launch.", relayShadows(a, "hook", assets), "trusted");
225
227
  }
226
228
 
227
229
  function relaySkills(a: AssembledLaunch): AgentProfileProjectionEntry {
228
230
  const assets = [
229
- ...(a.relaySkills ? [asset("agent-relay", "bundled")] : []),
231
+ ...(a.relaySkills ? [asset("humwick", "bundled")] : []),
230
232
  ...a.skills.map((item) => asset(item.name, relayProvenance(a, "skill", item.name))),
231
233
  ];
232
- return assetCell("agent-relay.skills", assets.length ? "enabled" : "disabled", assets.length ? "applied" : "disabled", assets, "bundled skills config / Relay plugin", assets.length ? `Relay skill assets reached the launch (${assets.map((item) => item.name).join(", ")}).` : "No Relay skill asset reached this launch.", relayShadows(a, "skill", assets), "trusted");
234
+ return assetCell(SHARED_PROFILE_CAPABILITIES.skills, assets.length ? "enabled" : "disabled", assets.length ? "applied" : "disabled", assets, "bundled skills config / Relay plugin", assets.length ? `Relay skill assets reached the launch (${assets.map((item) => item.name).join(", ")}).` : "No Relay skill asset reached this launch.", relayShadows(a, "skill", assets), "trusted");
233
235
  }
234
236
 
235
237
  function relayPlugins(a: AssembledLaunch): AgentProfileProjectionEntry {
236
238
  const assets = [
237
- ...(a.relayPlugin ? [asset(getManifest(a.provider)?.provisioning?.bundledPlugin?.ref ?? "agent-relay", "bundled")] : []),
239
+ ...(a.relayPlugin ? [asset(getManifest(a.provider)?.provisioning?.bundledPlugin?.ref ?? "humwick", "bundled")] : []),
238
240
  ...a.plugins.map((item) => asset(item.name, relayProvenance(a, "plugin", item.name))),
239
241
  ];
240
- return assetCell("agent-relay.plugins", assets.length ? "enabled" : "disabled", assets.length ? "applied" : "disabled", assets, "provider plugin-dir launch args", assets.length ? `Relay plugin assets reached the launch (${assets.map((item) => item.name).join(", ")}).` : "No Relay plugin asset reached this launch.", relayShadows(a, "plugin", assets), "trusted");
242
+ return assetCell(SHARED_PROFILE_CAPABILITIES.plugins, assets.length ? "enabled" : "disabled", assets.length ? "applied" : "disabled", assets, "provider plugin-dir launch args", assets.length ? `Relay plugin assets reached the launch (${assets.map((item) => item.name).join(", ")}).` : "No Relay plugin asset reached this launch.", relayShadows(a, "plugin", assets), "trusted");
241
243
  }
242
244
 
243
245
  function relayStatusLineEntry(a: AssembledLaunch): AgentProfileProjectionEntry {
@@ -249,7 +251,7 @@ function configHomeEntry(a: AssembledLaunch): AgentProfileProjectionEntry {
249
251
  }
250
252
 
251
253
  function launchShapeEntry(a: AssembledLaunch): AgentProfileProjectionEntry {
252
- // #2367 — argv can contain the full system prompt and inlined config. The old cell copied that
254
+ // argv can contain the full system prompt and inlined config. The old cell copied that
253
255
  // string into requested, effective, and detail, turning a ~68 KiB launch into ~203 KiB of static
254
256
  // agent meta that every status frame then carried through a read/merge/stringify/write cycle.
255
257
  // The projection is diagnostic, not the launch authority: retain its useful shape/size facts and
@@ -356,7 +358,7 @@ function relaySourceKindTiers(a: AssembledLaunch): Record<"skill" | "plugin" | "
356
358
  .filter((value): value is { source: string; ref: string } => isRecord(value) && value.source === "relay" && typeof value.ref === "string" && !skippedMcpRefs.has(value.ref))
357
359
  .map((value) => value.ref);
358
360
  return {
359
- skill: { reserved: [], registry: relayRegistryRefs(profile?.skills), bundled: a.relaySkills ? ["agent-relay"] : [] },
361
+ skill: { reserved: [], registry: relayRegistryRefs(profile?.skills), bundled: a.relaySkills ? ["humwick"] : [] },
360
362
  plugin: { reserved: [], registry: relayRegistryRefs(profile?.plugins), bundled: a.relayPlugin && provisioning?.bundledPlugin ? [provisioning.bundledPlugin.ref] : [] },
361
363
  hook: { reserved: bundledHooks.filter((hook) => hook.baseline).map((hook) => hook.ref), registry: relayRegistryRefs(profile?.hooks?.refs), bundled: bundledHooks.filter((hook) => !hook.baseline).map((hook) => hook.ref) },
362
364
  mcp: { reserved: a.mcp.relayEndpoint ? [RELAY_MCP_SERVER_NAME] : [], registry: mcpRegistry, bundled: [] },
@@ -12,7 +12,7 @@ import { projectSettingSourcesOn } from "./profile-home";
12
12
  const REPO_INSTRUCTION_FILES = ["CLAUDE.md", "AGENTS.md"] as const;
13
13
 
14
14
  // Bound each re-injected file so a pathological repo doc cannot balloon the context.
15
- // The composed append travels by private file (#3830). Mirrors Codex's
15
+ // The composed append travels by private file. Mirrors Codex's
16
16
  // project_doc_max_bytes default; oversized content is truncated with a loud marker
17
17
  // rather than silently dropped.
18
18
  const REPO_INSTRUCTIONS_MAX_BYTES = 64 * 1024;
@@ -32,7 +32,7 @@ export interface ComposedSystemPromptAppend {
32
32
  }
33
33
 
34
34
  /**
35
- * Fold the caller's `systemPromptAppend` together with any #2086 repo re-injection into the single
35
+ * Fold the caller's `systemPromptAppend` together with any repo re-injection into the single
36
36
  * `--append-system-prompt` value the launch emits. The two channels are distinct in reporting
37
37
  * (relay-authored append vs. repo-doc re-injection), so `reinjected` is returned separately.
38
38
  */
@@ -45,7 +45,7 @@ export function composeSystemPromptAppend(config: Pick<RunnerSpawnConfig, "agent
45
45
  }
46
46
 
47
47
  /**
48
- * #2086 — the instructions sibling of {@link resolveProjectHooks}. When `P.hooks` is
48
+ * the instructions sibling of {@link resolveProjectHooks}. When `P.hooks` is
49
49
  * named, the runner drops the ENTIRE native project setting-source scope so ambient repo
50
50
  * hooks can never bypass the trust gate (profile-home `projectSettingSourcesOn`). That drop
51
51
  * also strips the repo CLAUDE.md/AGENTS.md cascade even when `P.instructions` is on, silently
@@ -106,14 +106,14 @@ export function projectInstructionsReinjected(config: Pick<RunnerSpawnConfig, "a
106
106
  }
107
107
 
108
108
  const REPO_INSTRUCTIONS_PREAMBLE =
109
- "[agent-relay] The repository instruction docs below are re-injected by Relay because the " +
109
+ "[humwick] The repository instruction docs below are re-injected by Relay because the " +
110
110
  "native project setting-source scope was dropped to gate ambient project hooks. Follow them " +
111
111
  "as the repository's CLAUDE.md/AGENTS.md guidance.";
112
112
 
113
113
  function renderRepoInstructionBlock(name: string, content: string): string {
114
114
  const bytes = Buffer.byteLength(content, "utf8");
115
115
  const body = bytes > REPO_INSTRUCTIONS_MAX_BYTES
116
- ? `${Buffer.from(content, "utf8").subarray(0, REPO_INSTRUCTIONS_MAX_BYTES).toString("utf8")}\n[agent-relay: ${name} truncated at ${REPO_INSTRUCTIONS_MAX_BYTES} bytes]`
116
+ ? `${Buffer.from(content, "utf8").subarray(0, REPO_INSTRUCTIONS_MAX_BYTES).toString("utf8")}\n[humwick: ${name} truncated at ${REPO_INSTRUCTIONS_MAX_BYTES} bytes]`
117
117
  : content.replace(/\s+$/, "");
118
118
  return `<repo-instructions source="${name}">\n${body}\n</repo-instructions>`;
119
119
  }
@@ -0,0 +1,17 @@
1
+ import { hostname } from "node:os";
2
+ import type { RunnerSpawnConfig } from "./adapter";
3
+ import type { RunnerTimelineEvent } from "./turn-reconcile-event";
4
+
5
+ /** Claude's status command resolves file/keychain auth without Relay inspecting credentials. */
6
+ export function localClaudeOAuthWarning(config: Pick<RunnerSpawnConfig, "provider" | "agentId" | "instanceId">): RunnerTimelineEvent {
7
+ const host = hostname();
8
+ return {
9
+ status: "provider.auth.local_oauth_fallback",
10
+ id: `local-oauth-${config.instanceId}-${crypto.randomUUID()}`,
11
+ timestamp: Date.now(),
12
+ title: "Claude is using the host's local OAuth login",
13
+ body: `Host ${host}, agent ${config.agentId}, provider ${config.provider}: no effective CLAUDE_CODE_OAUTH_TOKEN. Claude reports a local OAuth login (Claude config home or platform keychain). This agent will use the host login's account and quota. Restore the provider's injected CLAUDE_CODE_OAUTH_TOKEN and restart the agent to use the intended account.`,
14
+ icon: "ti-alert-triangle",
15
+ metadata: { eventType: "provider.auth.local_oauth_fallback", severity: "warning", host, agentId: config.agentId, provider: config.provider, authSource: "local-oauth" },
16
+ };
17
+ }
@@ -54,6 +54,6 @@ export function primaryProviderInvocation(provider: SpawnProvider, spawnArgs: Sp
54
54
  logicalCommand: spawnArgs.command,
55
55
  argv: [...spawnArgs.args],
56
56
  cwd: spawnArgs.cwd,
57
- env: writeChildEnv({}, { ...spawnArgs.env }),
57
+ env: writeChildEnv({}, { ...spawnArgs.env }, []),
58
58
  };
59
59
  }
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  import type { Message } from "@humwick/sdk";
3
3
 
4
4
  /**
5
- * #2619 — the provider hold/resume state machine: ONE owner for "the provider refused input, hold,
5
+ * the provider hold/resume state machine: ONE owner for "the provider refused input, hold,
6
6
  * resume exactly once". Pure (no I/O); runner-core drives it and does the delivery.
7
7
  *
8
8
  * State: the active hold EPISODE (id, or none) and ONE parked queue of refused input. Every
@@ -53,10 +53,10 @@ export function resumeWakeEpisode(message: Message): string | undefined {
53
53
  return typeof id === "string" && id ? id : undefined;
54
54
  }
55
55
 
56
- export const PROVIDER_HELD_REDELIVERY_NOTE = "[agent-relay] Redelivered after a provider usage-limit hold: the provider refused this message before you could act on it, so it may already appear earlier in your conversation unanswered. Handle it now, exactly once.";
56
+ export const PROVIDER_HELD_REDELIVERY_NOTE = "[humwick] Redelivered after a provider usage-limit hold: the provider refused this message before you could act on it, so it may already appear earlier in your conversation unanswered. Handle it now, exactly once.";
57
57
 
58
58
  /** A late refusal (the turn started, then the provider refused a later call) does not prove the input never ran. */
59
- export const PROVIDER_HELD_LATE_REDELIVERY_NOTE = "[agent-relay] Redelivered after a provider usage-limit hold: the provider cut off the turn for this message partway, so you may already have acted on part of it. Check what is already done and continue only the unfinished work, then answer once.";
59
+ export const PROVIDER_HELD_LATE_REDELIVERY_NOTE = "[humwick] Redelivered after a provider usage-limit hold: the provider cut off the turn for this message partway, so you may already have acted on part of it. Check what is already done and continue only the unfinished work, then answer once.";
60
60
 
61
61
  export type ResumeDecision = { action: "settle" } | { action: "continue" } | { action: "replay"; messages: Message[] };
62
62
 
@@ -1,3 +1,3 @@
1
- // Re-export shim (#2547) — the marker moved to the SDK so the runner (writer), the server
1
+ // Re-export shim — the marker moved to the SDK so the runner (writer), the server
2
2
  // reaper and the per-orchestrator-host reaper script share one implementation.
3
3
  export * from "@humwick/sdk/provider-home-marker";
@@ -1,4 +1,4 @@
1
- // Re-export shim (#2547) — tombstone naming + spawn-side reclaim moved to the SDK so the
1
+ // Re-export shim — tombstone naming + spawn-side reclaim moved to the SDK so the
2
2
  // runner, the server reaper and the per-orchestrator-host reaper script share one
3
3
  // implementation of the quarantine name-spaces.
4
4
  export * from "@humwick/sdk/provider-home-tombstone";
@@ -1,4 +1,4 @@
1
- // One agent, ONE provider input stream (#1565 F1).
1
+ // One agent, ONE provider input stream.
2
2
  //
3
3
  // Every payload the runner hands to a provider — a relay message batch, an injected prompt, a
4
4
  // self-resume continuation, a memory preamble — ends up in the same place: the provider's single
@@ -60,7 +60,7 @@ export class ProviderInputQueue {
60
60
  this.stuckAfterMs = options.stuckAfterMs ?? DEFAULT_STUCK_AFTER_MS;
61
61
  if (options.onStuck) this.onStuck = options.onStuck;
62
62
  // Capture native timers: another subsystem's synchronous timer shim must not expire
63
- // a freshly granted slot and feed an immediate delivery/re-arm loop (#2668).
63
+ // a freshly granted slot and feed an immediate delivery/re-arm loop.
64
64
  this.schedule = options.schedule ?? ((fn, ms) => {
65
65
  const timer = setTimeout(fn, ms);
66
66
  timer.unref();
@@ -1,4 +1,4 @@
1
- // #3470 — bounded provider bridge; project owned metadata, never user assets or physical paths.
1
+ // bounded provider bridge; project owned metadata, never user assets or physical paths.
2
2
  import { readFileSync } from "node:fs";
3
3
  import { basename, join, resolve } from "node:path";
4
4
  import { getManifest } from "@humwick/providers";
@@ -6,8 +6,7 @@ import { sanitizeFsName } from "@humwick/sdk/fs-name";
6
6
  import type { RunnerSpawnConfig } from "./adapter";
7
7
  import type { ResolvedAsset } from "./provisioning";
8
8
  import { providerHomeRootFromEnv } from "./config";
9
- import { runnerMcpGeneration } from "./relay-mcp";
10
- import { isRecord, mcpServerInfo, wireToolName, wireToolText, type ProvisioningAssetFile, type ToolGeneration } from "@humwick/sdk";
9
+ import { isRecord, MCP_SERVER_INFO, registryToolName, type ProvisioningAssetFile } from "@humwick/sdk";
11
10
 
12
11
  export const CLAUDE_OWNED_PLUGIN_IDS = ["agent-relay@agent-relay", "humwick@humwick"];
13
12
  const OWNED_PLUGIN_NAMES = new Set(["agent-relay", "agent-relay-runner", "humwick", "humwick-runner"]);
@@ -17,14 +16,19 @@ function ownedClaudePluginName(dir: string): string | undefined {
17
16
  catch { return undefined; }
18
17
  }
19
18
 
20
- export function selectClaudePluginDirs(dirs: string[], generation: ToolGeneration, ownedProvided: boolean): string[] {
19
+ export function selectClaudePluginDirs(dirs: string[], ownedProvided: boolean): string[] {
21
20
  const entries = [...new Set(dirs)].map(dir => ({ dir, name: ownedClaudePluginName(dir) }));
22
- const owned = ownedProvided ? undefined : entries.find(entry => entry.name === mcpServerInfo(generation).name || entry.name === `${mcpServerInfo(generation).name}-runner`) ?? entries.find(entry => entry.name);
21
+ const owned = ownedProvided ? undefined : entries.find(entry => entry.name === MCP_SERVER_INFO.name || entry.name === `${MCP_SERVER_INFO.name}-runner`) ?? entries.find(entry => entry.name);
23
22
  return entries.filter(entry => !entry.name || entry === owned).map(entry => entry.dir);
24
23
  }
25
24
 
26
- export function providerIntegrationFiles(name: string, files: ProvisioningAssetFile[], generation: ToolGeneration): ProvisioningAssetFile[] {
27
- if (generation === "relay" || !(name === "agent-relay-claude-plugin" || name.startsWith("agent-relay-codex-"))) return files;
25
+ export function canonicalIntegrationAssetName(name: string): string {
26
+ return name.replace(/^agent-relay-/, "humwick-");
27
+ }
28
+
29
+ export function providerIntegrationFiles(name: string, files: ProvisioningAssetFile[]): ProvisioningAssetFile[] {
30
+ const canonicalName = canonicalIntegrationAssetName(name);
31
+ if (!(canonicalName === "humwick-claude-plugin" || canonicalName.startsWith("humwick-codex-") || canonicalName.startsWith("humwick-opencode-"))) return files;
28
32
  return files.map(file => {
29
33
  if (file.path === ".claude-plugin/plugin.json") {
30
34
  const manifest = JSON.parse(file.content);
@@ -33,7 +37,7 @@ export function providerIntegrationFiles(name: string, files: ProvisioningAssetF
33
37
  return { ...file, content: JSON.stringify(manifest, null, 2) };
34
38
  }
35
39
  if (!file.path.endsWith(".md")) return file;
36
- return { ...file, content: wireToolText(file.content, generation)
40
+ return { ...file, content: file.content
37
41
  .replace(/\bAgent Relay\b/g, "Humwick")
38
42
  .replace(/(?<![\w/.-])agent-relay(?![\w/.-])/g, "humwick") };
39
43
  });
@@ -43,8 +47,6 @@ const bundleCache = new Map<string, ProvisioningAssetFile[]>();
43
47
 
44
48
  // The same manifest overrides as registry seeding make relocated monitors self-contained.
45
49
  export function bundledIntegrationAsset(config: RunnerSpawnConfig, source: string, name: string): ResolvedAsset {
46
- const generation = runnerMcpGeneration(config.env);
47
- if (generation === "relay") return { name, dir: source, source: { kind: "inPlace" } };
48
50
  let files = bundleCache.get(source);
49
51
  if (!files) {
50
52
  const descriptor = getManifest(config.provider)?.provisioning?.bundledPlugin;
@@ -55,7 +57,7 @@ export function bundledIntegrationAsset(config: RunnerSpawnConfig, source: strin
55
57
  bundleCache.set(source, files);
56
58
  }
57
59
  return { name, dir: join(bundledIntegrationHome(config), basename(source)),
58
- source: { kind: "files", files: providerIntegrationFiles(name, files, generation) } };
60
+ source: { kind: "files", files: providerIntegrationFiles(name, files) } };
59
61
  }
60
62
 
61
63
  export function bundledIntegrationAssets(config: RunnerSpawnConfig): ResolvedAsset[] {
@@ -82,12 +84,12 @@ export function bundledIntegrationHome(config: RunnerSpawnConfig): string {
82
84
  return join(providerHomeRootFromEnv(), config.provider, "integrations", sanitizeFsName(config.instanceId || config.runnerId));
83
85
  }
84
86
 
85
- export function projectClaudePermissionRules(settings: Record<string, unknown>, generation: ToolGeneration): Record<string, unknown> {
87
+ export function projectClaudePermissionRules(settings: Record<string, unknown>): Record<string, unknown> {
86
88
  if (!isRecord(settings.permissions)) return settings;
87
89
  const permissions = { ...settings.permissions };
88
90
  for (const kind of ["allow", "deny", "ask"]) {
89
91
  if (Array.isArray(permissions[kind])) permissions[kind] = permissions[kind].map(rule => typeof rule === "string"
90
- ? rule.replace(/^mcp__(?:agent-relay|humwick)__(.*)$/, (_match, tool) => `mcp__${mcpServerInfo(generation).name}__${wireToolName(tool, generation)}`) : rule);
92
+ ? rule.replace(/^mcp__(?:agent-relay|humwick)__(.*)$/, (_match, tool) => `mcp__${MCP_SERVER_INFO.name}__${registryToolName(tool)}`) : rule);
91
93
  }
92
94
  return { ...settings, permissions };
93
95
  }
@@ -1,19 +1,31 @@
1
1
  import { errMessage } from "@humwick/sdk";
2
2
  import { checkProviderAuth } from "@humwick/sdk/provider-probe";
3
3
  import { selectProviderConfigEnv } from "@humwick/sdk/child-env";
4
+ import { getManifest } from "@humwick/providers";
4
5
  import type { ProviderAdapter, ProviderStatusEvent } from "./adapter";
5
6
  import { logger } from "./logger";
6
7
  import { hostSourcesFor } from "./host-asset-plan";
7
8
  import { resolveLaunchCommand } from "./provider-command";
9
+ import { localClaudeOAuthWarning } from "./provider-auth-warning";
8
10
 
9
- /** All real adapters share the managed-spawn login check, including replacements (#3751). */
11
+ /** All real adapters share the managed-spawn login check, including replacements. */
10
12
  export function withProviderSetup(adapter: ProviderAdapter): ProviderAdapter {
11
13
  const spawn = adapter.spawn.bind(adapter);
12
14
  adapter.spawn = async (config) => {
15
+ const manifest = getManifest(config.provider);
16
+ const claudeOAuth = manifest?.probe?.authCheck?.command === "claude" && !manifest.apiAuth;
17
+ // probe the artifact we will actually launch, after home and credential isolation.
18
+ if (claudeOAuth) config = await adapter.hydrateSpawnConfig?.(config) ?? config;
19
+ const prepared = claudeOAuth ? adapter.buildSpawnArgs(config, config.providerConfig) : undefined;
13
20
  const env = selectProviderConfigEnv(config.providerConfig.env, hostSourcesFor(config).settings);
14
- const auth = await checkProviderAuth(config.provider, undefined, env, resolveLaunchCommand(config.providerConfig.command), config.env);
21
+ const auth = await checkProviderAuth(config.provider, undefined, env, prepared?.command ?? resolveLaunchCommand(config.providerConfig.command), config.env, prepared?.env);
15
22
  if (!auth.ok) throw new Error(auth.reason);
16
- return spawn(config);
23
+ if (prepared && !prepared.env.CLAUDE_CODE_OAUTH_TOKEN?.trim() && auth.authSource === "local-oauth") {
24
+ const warning = localClaudeOAuthWarning(config);
25
+ console.warn(`${warning.title}: ${warning.body}`);
26
+ config.onLaunchWarning?.(warning);
27
+ }
28
+ return spawn(config, prepared);
17
29
  };
18
30
  return adapter;
19
31
  }
package/src/providers.ts CHANGED
@@ -14,7 +14,7 @@ interface RunnerProviderRegistration {
14
14
 
15
15
  const RUNNER_PROVIDERS: Record<string, RunnerProviderRegistration> = {
16
16
  claude: { provider: "claude", createAdapter: (args, mode) => createClaudeTransportAdapter("claude", args, mode) },
17
- // #2513 — claude-b reuses the Claude adapter under its own id (the OpenCodeAdapter/zai
17
+ // claude-b reuses the Claude adapter under its own id (the OpenCodeAdapter/zai
18
18
  // pattern): same binary and launch wiring, its own manifest home/credential.
19
19
  "claude-b": { provider: "claude-b", createAdapter: (args, mode) => createClaudeTransportAdapter("claude-b", args, mode) },
20
20
  "claude-api": { provider: "claude-api", createAdapter: () => new ClaudeApiAdapter() },
@@ -1,12 +1,11 @@
1
1
  import { providerIntegrationFiles, projectClaudePermissionRules } from "./provider-integration";
2
- import { runnerMcpGeneration } from "./relay-mcp";
3
2
  import { writeChildEnv } from "@humwick/sdk/env";
4
3
  import { createHash, randomUUID } from "node:crypto";
5
4
  import { existsSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
6
5
  import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
7
6
  import { homedir } from "node:os";
8
7
  import { sanitizeFsName } from "@humwick/sdk/fs-name";
9
- import { RETIRED_SHARED_MCP_SERVER_NAMES, isRelayMcpServerName, mcpServerInfo, wireToolName, type ToolGeneration, coerceProjectMcpServer, fffMcpBinaryPath, PROJECT_MCP_FILE, profileProjectSourceEnabled } from "@humwick/sdk";
8
+ import { RETIRED_SHARED_MCP_SERVER_NAMES, isRelayMcpServerName, MCP_SERVER_INFO, coerceProjectMcpServer, fffMcpBinaryPath, PROJECT_MCP_FILE, profileProjectSourceEnabled } from "@humwick/sdk";
10
9
  import { getManifest } from "@humwick/providers";
11
10
  import type { ProvisioningAssetFile, ProvisioningHookSet, ProvisioningMcpServer, ResolvedProvisioning } from "@humwick/sdk";
12
11
  import type { RunnerSpawnConfig } from "./adapter";
@@ -17,11 +16,11 @@ import { profileAllowsHostGlobalInstructions, providerSourceHome } from "./profi
17
16
  import { hostSourcesFor, skillDirsUnder } from "./host-asset-plan";
18
17
 
19
18
  // Runner-side materialization of the relay-resolved provisioning bundle
20
- // (#554/#555/#556). The relay resolves a profile's refs against the DB registry and
19
+ // The relay resolves a profile's refs against the DB registry and
21
20
  // attaches the concrete definitions to profile.provisioning; here we turn those into
22
21
  // real on-disk dirs (skills/plugins). MCP is handled in relay-mcp.ts.
23
22
  //
24
- // #557 — split into PURE RESOLUTION (resolve* → where each asset lands, no writes) and
23
+ // split into PURE RESOLUTION (resolve* → where each asset lands, no writes) and
25
24
  // MATERIALIZATION (materialize* → the disk writes). The single launch assembler
26
25
  // (launch-assembly.ts) calls the resolvers to build BOTH the launch args and the
27
26
  // projection report, so what's reported can never drift from what's written; the
@@ -38,11 +37,11 @@ export function profileProvisioning(config: Pick<RunnerSpawnConfig, "agentProfil
38
37
 
39
38
  // The fff search-redirect hook is bundled per provider manifest; it only needs the binary path in env.
40
39
  function providerBundlesFffRedirect(provider: RunnerSpawnConfig["provider"]): boolean {
41
- return Boolean(getManifest(provider)?.provisioning?.bundledHooks?.some((hook) => hook.ref === "agent-relay-fff-search-redirect"));
40
+ return Boolean(getManifest(provider)?.provisioning?.bundledHooks?.some((hook) => hook.ref === "humwick-fff-search-redirect"));
42
41
  }
43
42
 
44
- // #2729 — provisioned stdio commands authored on another host are swapped for this host's
45
- // provisioned descriptor or dropped here, never emitted broken. #2809 — the fff path rewrite
43
+ // provisioned stdio commands authored on another host are swapped for this host's
44
+ // provisioned descriptor or dropped here, never emitted broken. The fff path rewrite
46
45
  // lives here too so every provider launch gets it before the preflight judges the path.
47
46
  export function launchProvisionedMcpServers(
48
47
  config: Pick<RunnerSpawnConfig, "agentProfile">,
@@ -59,8 +58,8 @@ export function launchProvisionedMcpServers(
59
58
  }
60
59
 
61
60
  export function fffMcpHookEnv(config: Pick<RunnerSpawnConfig, "agentProfile" | "provider">): Record<string, string> {
62
- return writeChildEnv({}, { AGENT_RELAY_FFF_MCP_BINARY: providerBundlesFffRedirect(config.provider)
63
- ? launchProvisionedMcpServers(config).fff?.command ?? "" : "" });
61
+ return writeChildEnv({}, { HUMWICK_FFF_MCP_BINARY: providerBundlesFffRedirect(config.provider)
62
+ ? launchProvisionedMcpServers(config).fff?.command ?? "" : "" }, []);
64
63
  }
65
64
 
66
65
  /** The resolved source matrix decides project MCP/skill projection (see profileProjectSourceEnabled). */
@@ -80,13 +79,13 @@ export function projectSettingsEnabled(config: Pick<RunnerSpawnConfig, "agentPro
80
79
  return Boolean(config.agentProfile?.projectSettings);
81
80
  }
82
81
 
83
- // #667 — project `.mcp.json` MCP projection. OPT-IN via the resolved profile's `projectMcp`
82
+ // project `.mcp.json` MCP projection. OPT-IN via the resolved profile's `projectMcp`
84
83
  // (set per-spawn or by profile, relay-side). Reads the spawn cwd's COMMITTED `.mcp.json` and
85
84
  // returns its `mcpServers` as a provider-neutral record, so the launch assembler injects them
86
85
  // through the CONTROLLED `--mcp-config` (Claude) / `-c mcp_servers.*` (Codex) path that
87
86
  // survives `--strict-mcp-config` — the project's own servers travel with the repo like they do
88
87
  // for a human who clones it, WITHOUT enabling Claude's ambient project-discovery trust modal
89
- // (which wedges headless agents, the reason #662 made every managed launch strict). The runner
88
+ // (which wedges headless agents, the reason made every managed launch strict). The runner
90
89
  // is the filesystem authority for the spawn cwd (relay may run on another host), so the read
91
90
  // lands here. Best-effort: a missing/malformed file (or one with no `mcpServers`) yields {} and
92
91
  // never aborts the launch. Returns {} when projection is OFF.
@@ -99,7 +98,7 @@ export function resolveProjectMcpServers(
99
98
  // server decides roots/pinned/any against its registry and carries the result
100
99
  // through the spawn-scoped profile. Missing/denied decisions fail closed.
101
100
  if (profile?.sources?.project && !profile.projectGates?.mcp?.allowed) return {};
102
- // #3043 — a project role's entries were read at one commit; the working tree never applies.
101
+ // a project role's entries were read at one commit; the working tree never applies.
103
102
  if (profile?.pinnedProjectMcp) return { ...profile.pinnedProjectMcp.servers };
104
103
  const file = join(config.cwd, PROJECT_MCP_FILE);
105
104
  if (!existsSync(file)) return {};
@@ -146,13 +145,13 @@ export function resolveClaudeHostSkills(
146
145
  provider: string = "claude",
147
146
  ): ResolvedAsset[] {
148
147
  if (!hostSourcesFor(config).skills) return [];
149
- // #2513 — keyed by provider id so a dedicatedHome claude-family provider (claude-b)
148
+ // keyed by provider id so a dedicatedHome claude-family provider (claude-b)
150
149
  // resolves host skills from ITS OWN source home, never ~/.claude's.
151
150
  return linkedSkillAssets(join(providerSourceHome(provider), "skills"), claudeHome, reservedNames);
152
151
  }
153
152
 
154
153
  export interface ResolvedSourceShadow {
155
- source: "host" | "project" | "agent-relay";
154
+ source: import("@humwick/sdk").ProfileSourceKind;
156
155
  capability: "mcp" | "hooks" | "skills" | "plugins";
157
156
  name: string;
158
157
  }
@@ -328,7 +327,7 @@ function materializeAsset(asset: ResolvedAsset): boolean {
328
327
  return existsSync(asset.dir);
329
328
  }
330
329
 
331
- // #554 (Claude) — each provisioned skill lands at <claudeHome>/skills/<name> so it's
330
+ // (Claude) — each provisioned skill lands at <claudeHome>/skills/<name> so it's
332
331
  // auto-discovered under the launch's `--setting-sources "user"` scope (host skills
333
332
  // never bleed). Inline files are written there; a pre-existing dir is symlinked in.
334
333
  export function resolveClaudeProvisionedSkills(claudeHome: string, config: RunnerSpawnConfig): ResolvedAsset[] {
@@ -338,7 +337,7 @@ export function resolveClaudeProvisionedSkills(claudeHome: string, config: Runne
338
337
  for (const skill of skills) {
339
338
  const dir = join(skillsRoot, assetDirName(skill.name));
340
339
  if (skill.files?.length) {
341
- resolved.push({ name: skill.name, dir, source: { kind: "files", files: providerIntegrationFiles(skill.name, skill.files, runnerMcpGeneration(config.env)) } });
340
+ resolved.push({ name: skill.name, dir, source: { kind: "files", files: providerIntegrationFiles(skill.name, skill.files) } });
342
341
  continue;
343
342
  }
344
343
  if (skill.dir) {
@@ -349,7 +348,7 @@ export function resolveClaudeProvisionedSkills(claudeHome: string, config: Runne
349
348
  return resolved;
350
349
  }
351
350
 
352
- // #556 (Claude) — each provisioned plugin resolves to a --plugin-dir. Inline plugins
351
+ // (Claude) — each provisioned plugin resolves to a --plugin-dir. Inline plugins
353
352
  // materialize under <claudeHome>/relay-provisioned-plugins/<name>; declared dirs are
354
353
  // used in place. The resolved `dir` is appended to the launch's --plugin-dir set.
355
354
  export function resolveClaudeProvisionedPlugins(claudeHome: string, config: RunnerSpawnConfig): ResolvedAsset[] {
@@ -358,7 +357,7 @@ export function resolveClaudeProvisionedPlugins(claudeHome: string, config: Runn
358
357
  const resolved: ResolvedAsset[] = [];
359
358
  for (const plugin of plugins) {
360
359
  if (plugin.files?.length) {
361
- resolved.push({ name: plugin.name, dir: join(parent, assetDirName(plugin.name)), source: { kind: "files", files: providerIntegrationFiles(plugin.name, plugin.files, runnerMcpGeneration(config.env)) } });
360
+ resolved.push({ name: plugin.name, dir: join(parent, assetDirName(plugin.name)), source: { kind: "files", files: providerIntegrationFiles(plugin.name, plugin.files) } });
362
361
  continue;
363
362
  }
364
363
  if (plugin.dir) {
@@ -369,18 +368,18 @@ export function resolveClaudeProvisionedPlugins(claudeHome: string, config: Runn
369
368
  return resolved;
370
369
  }
371
370
 
372
- // #554 (Codex) — each provisioned skill resolves to a dir for `-c skills.config`.
371
+ // (Codex) — each provisioned skill resolves to a dir for `-c skills.config`.
373
372
  // Inline skills materialize under <codexHome>/relay-provisioned-skills/<name>; declared
374
373
  // dirs are used in place. Codex requires a SKILL.md at the dir root to register it, so
375
374
  // inline sets without one and dirs missing it are dropped at resolution time.
376
- export function resolveCodexProvisionedSkills(codexHome: string, config: RunnerSpawnConfig): ResolvedAsset[] {
375
+ export function resolveProvisionedSkillPaths(home: string, config: RunnerSpawnConfig): ResolvedAsset[] {
377
376
  const { skills } = profileProvisioning(config);
378
- const parent = join(codexHome, "relay-provisioned-skills");
377
+ const parent = join(home, "relay-provisioned-skills");
379
378
  const resolved: ResolvedAsset[] = [];
380
379
  for (const skill of skills) {
381
380
  if (skill.files?.length) {
382
381
  if (!hasRootSkillManifest(skill.files)) continue;
383
- resolved.push({ name: skill.name, dir: join(parent, assetDirName(skill.name)), source: { kind: "files", files: providerIntegrationFiles(skill.name, skill.files, runnerMcpGeneration(config.env)) } });
382
+ resolved.push({ name: skill.name, dir: join(parent, assetDirName(skill.name)), source: { kind: "files", files: providerIntegrationFiles(skill.name, skill.files) } });
384
383
  continue;
385
384
  }
386
385
  if (skill.dir) {
@@ -391,6 +390,8 @@ export function resolveCodexProvisionedSkills(codexHome: string, config: RunnerS
391
390
  return resolved;
392
391
  }
393
392
 
393
+ export { resolveProvisionedSkillPaths as resolveCodexProvisionedSkills };
394
+
394
395
  export function resolveProvisionedHooks(home: string, config: RunnerSpawnConfig): ResolvedHookAsset[] {
395
396
  const { hooks } = profileProvisioning(config);
396
397
  const parent = join(home, "relay-provisioned-hooks");
@@ -464,7 +465,7 @@ export function codexHookTrustEntries(codexHome: string, hooks: ProvisioningHook
464
465
  return hookTrustEntries([...new Set([codexHome, realpathOr(codexHome)])].map((home) => join(home, "hooks.json")), hooks);
465
466
  }
466
467
 
467
- // #3025 — Codex also reads TOML hooks and their trust state from the `-c` session-flags layer,
468
+ // Codex also reads TOML hooks and their trust state from the `-c` session-flags layer,
468
469
  // keyed on this synthetic source, so a launch can carry hooks while keeping any CODEX_HOME.
469
470
  const CODEX_SESSION_FLAGS_SOURCE = "/<session-flags>/config.toml";
470
471
 
@@ -577,7 +578,7 @@ function canonicalJson(value: unknown): unknown {
577
578
  return value;
578
579
  }
579
580
 
580
- // #1096 — write relay-owned settings into an ISOLATED Claude managed home's `settings.json`
581
+ // write relay-owned settings into an ISOLATED Claude managed home's `settings.json`
581
582
  // (the DOCUMENTED hook source Claude honors under `--setting-sources "user"` or the default,
582
583
  // unlike the inline `--settings` CLI flag). The managed home is relay-owned and freshly keyed
583
584
  // per spawn, so an unconditional write is safe (profile-home seeds only `.claude.json`/`CLAUDE.md`,
@@ -601,7 +602,7 @@ export function writeClaudeSettingsJson(claudeHome: string, settings: Record<str
601
602
  }
602
603
  }
603
604
 
604
- // T3182 — merge a permissions.deny entry into a managed Claude settings payload WITHOUT
605
+ // merge a permissions.deny entry into a managed Claude settings payload WITHOUT
605
606
  // clobbering permissions the payload already carries (deny lists union, everything else passes
606
607
  // through). Pure — the caller composes and writes.
607
608
  export function mergeClaudeSettingsDeny(settings: Record<string, unknown>, deny: string[]): Record<string, unknown> {
@@ -622,22 +623,18 @@ export function mergeClaudeSettingsAllow(settings: Record<string, unknown>, allo
622
623
  }
623
624
 
624
625
  export const CLAUDE_GUARDED_RELAY_TOOLS = [
625
- "mcp__agent-relay__relay_send_message",
626
- "mcp__agent-relay__relay_reply",
627
- "mcp__agent-relay__relay_task_set_status",
628
- "mcp__agent-relay__relay_task_comment",
629
- "mcp__agent-relay__relay_task_deliver",
626
+ "mcp__humwick__humwick_send_message",
627
+ "mcp__humwick__humwick_reply",
628
+ "mcp__humwick__humwick_task_set_status",
629
+ "mcp__humwick__humwick_task_comment",
630
+ "mcp__humwick__humwick_task_deliver",
630
631
  ];
631
632
 
632
- function guardedRelayTools(generation: ToolGeneration): string[] {
633
- return CLAUDE_GUARDED_RELAY_TOOLS.map(name => `mcp__${mcpServerInfo(generation).name}__${wireToolName(name.split("__").at(-1)!, generation)}`);
634
- }
635
-
636
- export function claudeGuardedRelaySettings(approvalMode: string | undefined, generation: ToolGeneration = "relay"): Record<string, unknown> {
637
- return approvalMode === "guarded" ? { permissions: { allow: guardedRelayTools(generation) } } : {};
633
+ export function claudeGuardedRelaySettings(approvalMode: string | undefined): Record<string, unknown> {
634
+ return approvalMode === "guarded" ? { permissions: { allow: [...CLAUDE_GUARDED_RELAY_TOOLS] } } : {};
638
635
  }
639
636
 
640
- // #3388 — Claude's project scan loads CLAUDE.md, CLAUDE.local.md, .claude/CLAUDE.md and .claude/rules/**
637
+ // Claude's project scan loads CLAUDE.md, CLAUDE.local.md, .claude/CLAUDE.md and .claude/rules/**
641
638
  // from EVERY ancestor of the cwd, so host files above the repo (~/.claude/CLAUDE.md, ~/CLAUDE.md,
642
639
  // ~/projects/CLAUDE.md) reach an agent despite the isolated CLAUDE_CONFIG_DIR. Host instructions
643
640
  // `ignore` excludes them through the supported `claudeMdExcludes` setting. Rule: the repo root
@@ -687,9 +684,9 @@ export function mergeClaudeRelayPermissions(settings: Record<string, unknown>, r
687
684
  return mergeClaudeSettingsAllow(settings, allow);
688
685
  }
689
686
 
690
- export function withManagedClaudePermissions(settings: Record<string, unknown>, approvalMode: string | undefined, generation: ToolGeneration = "relay"): Record<string, unknown> {
691
- const guarded = approvalMode === "guarded" ? mergeClaudeSettingsAllow(settings, guardedRelayTools(generation)) : settings;
692
- return projectClaudePermissionRules(mergeClaudeSettingsDeny({ ...guarded, ...claudeBypassPermissionSettings(approvalMode), idleCompaction: false }, ["SendFeedback"]), generation);
687
+ export function withManagedClaudePermissions(settings: Record<string, unknown>, approvalMode: string | undefined): Record<string, unknown> {
688
+ const guarded = approvalMode === "guarded" ? mergeClaudeSettingsAllow(settings, [...CLAUDE_GUARDED_RELAY_TOOLS]) : settings;
689
+ return projectClaudePermissionRules(mergeClaudeSettingsDeny({ ...guarded, ...claudeBypassPermissionSettings(approvalMode), idleCompaction: false }, ["SendFeedback"]));
693
690
  }
694
691
 
695
692
  // Write the resolved assets to disk; returns the dirs that materialized successfully,