@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
@@ -1,5 +1,5 @@
1
- import { registryToolName, mcpServerInfo, parseToolGeneration } from "@humwick/sdk";
2
- import { relayHeader, relayHeaderConflict, errMessage, isRecord, resendOnRotatedToken } from "@humwick/sdk";
1
+ import { MCP_SERVER_INFO } from "@humwick/sdk";
2
+ import { relayHeader, errMessage, isRecord, resendOnRotatedToken } from "@humwick/sdk";
3
3
  import { forwardSharedMcp, type WebToolLogOptions } from "./web-tool-call-proxy";
4
4
  import { materializeArtifactPathUpload } from "./artifact-path-materialize";
5
5
  import { logger } from "./logger";
@@ -10,12 +10,12 @@ import { RELAY_MCP_ONDEMAND_SURFACE_VALUE } from "./relay-mcp";
10
10
  // member; the real global `fetch` satisfies it.
11
11
  export type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
12
12
 
13
- // #2429 — Bun accepts a non-standard `timeout` member on the fetch init (ms, or `false` to disable
13
+ // Bun accepts a non-standard `timeout` member on the fetch init (ms, or `false` to disable
14
14
  // its native HTTP idle watchdog), but bun-types@1.3.14 don't declare it, so a bare literal fails
15
- // typecheck. Widen locally (mirrors the #2428 Phase-1 pattern) instead of casting to `any`.
15
+ // typecheck. Widen locally (mirrors the Phase-1 pattern) instead of casting to `any`.
16
16
  export type RelayForwardRequestInit = RequestInit & { timeout?: number | false };
17
17
 
18
- // Stage 2 of #213/#215 — the Runner becomes the MCP endpoint the agent connects to,
18
+ // Stage 2 of — the Runner becomes the MCP endpoint the agent connects to,
19
19
  // fronting the relay. The agent's MCP client points at this localhost server instead of
20
20
  // directly at the relay, so the Runner owns the relay connection, reconnect/backoff, and a
21
21
  // durable buffer. A relay restart/crash becomes invisible to the agent.
@@ -27,7 +27,7 @@ export type RelayForwardRequestInit = RequestInit & { timeout?: number | false }
27
27
  // the live-tool-set capability the relay's static endpoint can't.
28
28
  // 2. `tools/list` — forward (the relay already scope-filters per token), then context-
29
29
  // NARROW: hide tools that don't apply to this agent's workspace mode/
30
- // state (e.g. `relay_workspace_*` for a non-worktree agent). Strictly
30
+ // state (e.g. `humwick_workspace_*` for a non-worktree agent). Strictly
31
31
  // a subset — never widens. Served from a last-known cache during a blip.
32
32
  // 3. `tools/call` — forward with the runner's LIVE token; for bufferable writes during a
33
33
  // relay outage, enqueue durably + return a synthetic "queued" ok so the
@@ -41,7 +41,7 @@ export type RelayForwardRequestInit = RequestInit & { timeout?: number | false }
41
41
  // already returned, so a proxy bug can never become an auth bypass — the relay owns the lock at
42
42
  // `tools/call`, the proxy owns the menu.
43
43
  //
44
- // #1450 (the #1570 unify) adds a SECOND narrowing dimension for Codex lean workers: the on-demand
44
+ // adds a SECOND narrowing dimension for Codex lean workers: the on-demand
45
45
  // relay set. The agent's DIRECT relay client has its on-demand tools hidden here so only the eager
46
46
  // hot-path set boots; the SAME tools are simultaneously fronted by the per-agent metaOnly callmux
47
47
  // (a second relay downstream over THIS proxy), reachable via callmux_call. callmux's requests carry
@@ -55,7 +55,7 @@ const PROXY_PATH = "/mcp";
55
55
  // Relay-side failures we treat as "relay down" (buffer/serve-cache), as opposed to a real 4xx
56
56
  // rejection that must be surfaced to the agent verbatim.
57
57
  const GATEWAY_STATUSES = new Set([502, 503, 504]);
58
- // #2089 — gateway sub-classes for the connect-retry safety boundary. A 502/503 means the reverse
58
+ // gateway sub-classes for the connect-retry safety boundary. A 502/503 means the reverse
59
59
  // proxy could not reach the relay at all → the request never executed → safe to retry any tool
60
60
  // (connect-phase). A 504 means the proxy DID forward and the relay's response was lost → the outcome
61
61
  // is ambiguous → only safe to retry an idempotent call (same rule as a mid-flight socket reset).
@@ -63,15 +63,15 @@ const CONNECT_PHASE_GATEWAY_STATUSES = new Set([502, 503]);
63
63
  const AMBIGUOUS_GATEWAY_STATUSES = new Set([504]);
64
64
  const SSE_KEEPALIVE_MS = 25_000;
65
65
 
66
- // #1790/#2089 — relay_spawn_agent is IDEMPOTENT by spawnRequestId: the server dedupes a repeat of the
66
+ // humwick_spawn_agent is IDEMPOTENT by spawnRequestId: the server dedupes a repeat of the
67
67
  // same id to the existing spawn instead of creating a second agent (see src/services/spawn-agent.ts).
68
68
  // The proxy stabilizes that id up-front (below) so a retry across a restart can't double-create. This
69
- // name is used only for that id-stabilization; the connect-retry itself (#2089) is GENERIC across every
69
+ // name is used only for that id-stabilization; the connect-retry itself is GENERIC across every
70
70
  // tool and keys "safe to resend an ambiguous failure" off the presence of an idempotency key, never a
71
71
  // tool name.
72
- const SPAWN_TOOL = "relay_spawn_agent";
72
+ const SPAWN_TOOL = "humwick_spawn_agent";
73
73
 
74
- // #2089 — bounded connect-retry defaults. A CD deploy restarts the fleet relay on every green
74
+ // bounded connect-retry defaults. A CD deploy restarts the fleet relay on every green
75
75
  // origin/main SHA (systemctl restart, no drain/quiesce); tool calls in that ~1-2 min window otherwise
76
76
  // fail hard with "-32002 relay unreachable" and require a manual retry. The window (~75s) rides out
77
77
  // the restart; backoff climbs 0.5→1→2→4s (capped) with jitter to avoid a thundering-herd reconnect.
@@ -84,12 +84,12 @@ const DEFAULT_CONNECT_RETRY_MAX_BACKOFF_MS = 4_000;
84
84
  const DEFAULT_CONNECT_RETRY_WINDOW_MS = 75_000;
85
85
  const RELAY_MCP_METADATA_FORWARD_TIMEOUT_MS = 10_000;
86
86
  // A tool call can legitimately block in Relay: workspace status alone permits a
87
- // 600-second wait, and spawn/artifact operations can also take time. relay_run
88
- // separately caps its wait at 10s (#2790): this proxy budget cannot extend a
87
+ // 600-second wait, and spawn/artifact operations can also take time. humwick_run
88
+ // separately caps its wait at 10s: this proxy budget cannot extend a
89
89
  // provider client's shorter timeout. Keep the deadline for the other tools.
90
90
  const RELAY_MCP_TOOL_CALL_FORWARD_TIMEOUT_MS = 610_000;
91
91
 
92
- // #2089 — the connect-retry safety boundary rests on distinguishing WHEN in a request's lifecycle the
92
+ // the connect-retry safety boundary rests on distinguishing WHEN in a request's lifecycle the
93
93
  // transport failed. A failure is one of three classes:
94
94
  //
95
95
  // "connect" — the request provably NEVER reached the relay app: connection refused, DNS failure,
@@ -117,8 +117,8 @@ function classifyGatewayStatus(status: number): TransportErrorClass {
117
117
  return "fatal";
118
118
  }
119
119
 
120
- // #2089 — a tool call is idempotent (safe to resend after an ambiguous failure) iff it carries a key the
121
- // server dedupes on: spawnRequestId (relay_spawn_agent) or idempotencyKey (buffered writes, task deliver).
120
+ // a tool call is idempotent (safe to resend after an ambiguous failure) iff it carries a key the
121
+ // server dedupes on: spawnRequestId (humwick_spawn_agent) or idempotencyKey (buffered writes, task deliver).
122
122
  // Generic — no tool names — so any tool gaining such a key qualifies automatically.
123
123
  function isIdempotentCall(args: Record<string, unknown>): boolean {
124
124
  const spawnRequestId = typeof args.spawnRequestId === "string" ? args.spawnRequestId.trim() : "";
@@ -130,37 +130,37 @@ function isIdempotentCall(args: Record<string, unknown>): boolean {
130
130
  // does not need synchronously — safe to queue durably and replay on reconnect. Reads, claims
131
131
  // (409 contention), spawn/shutdown (need a real ack) are deliberately NOT bufferable.
132
132
  const DEFAULT_BUFFERABLE_TOOLS = new Set<string>([
133
- "relay_send_message",
134
- "relay_reply",
135
- "relay_task_deliver",
133
+ "humwick_send_message",
134
+ "humwick_reply",
135
+ "humwick_task_deliver",
136
136
  ]);
137
137
 
138
138
  // Tools surfaced ONLY to an agent that owns a live isolated worktree. For any other agent the
139
139
  // proxy narrows them out of `tools/list` even though the token scope permits them (the relay
140
- // would return them). This is the context the coarse token scope can't express (#214/#215).
140
+ // would return them). This is the context the coarse token scope can't express.
141
141
  const WORKTREE_ONLY_TOOLS = new Set<string>([
142
- "relay_workspace_status",
143
- "relay_workspace_deps",
144
- "relay_workspace_list",
145
- "relay_workspace_claim",
146
- "relay_workspace_release",
142
+ "humwick_workspace_status",
143
+ "humwick_workspace_deps",
144
+ "humwick_workspace_list",
145
+ "humwick_workspace_claim",
146
+ "humwick_workspace_release",
147
147
  ]);
148
148
 
149
149
  interface ProxyContext {
150
150
  // The agent owns a live (non-terminal) isolated git worktree → workspace tools apply.
151
151
  isolatedWorktree: boolean;
152
- // #1502 — this worktree agent's own workspace descriptor (from AGENT_RELAY_WORKSPACE_JSON). The
152
+ // this worktree agent's own workspace descriptor (from HUMWICK_WORKSPACE_JSON). The
153
153
  // relay writes the workspace ROW lazily via the orchestrator's managed-agents report, so a worker
154
154
  // that delivers before that write (or whose registration was lost under concurrent-spawn load)
155
- // would 404. We inject this descriptor into `relay_task_deliver` so the relay can self-heal the
155
+ // would 404. We inject this descriptor into `humwick_task_deliver` so the relay can self-heal the
156
156
  // missing registration from the worker's own truth instead of stranding the delivery.
157
157
  workspaceDescriptor?: Record<string, unknown>;
158
158
  }
159
159
 
160
- // #1502 — tool calls the proxy augments with the agent's workspace descriptor for self-heal.
161
- const WORKSPACE_SELF_HEAL_TOOLS = new Set(["relay_task_deliver"]);
160
+ // tool calls the proxy augments with the agent's workspace descriptor for self-heal.
161
+ const WORKSPACE_SELF_HEAL_TOOLS = new Set(["humwick_task_deliver"]);
162
162
 
163
- // #1502 — the minimal descriptor the relay needs to (re)register a dropped workspace row, distilled
163
+ // the minimal descriptor the relay needs to (re)register a dropped workspace row, distilled
164
164
  // from the agent's WorkspaceMetadata. Undefined unless this agent owns a live isolated worktree with
165
165
  // the required id/repo/worktree present, so a shared/non-worktree agent never sends one.
166
166
  export function isolatedWorkspaceDescriptor(
@@ -200,7 +200,7 @@ export interface RelayMcpProxyOptions {
200
200
  enqueueBuffered(call: BufferedToolCall): void;
201
201
  initialContext?: ProxyContext;
202
202
  bufferableTools?: Set<string>;
203
- // #1450 — the Codex-worker on-demand relay set to hide from the agent's DIRECT relay client so
203
+ // the Codex-worker on-demand relay set to hide from the agent's DIRECT relay client so
204
204
  // only the eager hot-path set boots. Undefined for every agent that isn't a lean Codex worker with
205
205
  // callmux simultaneously fronting these tools (Claude, coordinators, reviewers, proxy-off) → no
206
206
  // hiding, full surface. callmux's own relay downstream opts out via RELAY_MCP_ONDEMAND_SURFACE_HEADER.
@@ -209,14 +209,14 @@ export interface RelayMcpProxyOptions {
209
209
  fetchImpl?: FetchLike;
210
210
  // Test seam for inspecting the deadline selected for each relay forward.
211
211
  abortSignal?: (timeoutMs: number) => AbortSignal;
212
- // #2089 — bounded connect-retry across a relay restart, GENERIC over every tool. Overridable for tests
212
+ // bounded connect-retry across a relay restart, GENERIC over every tool. Overridable for tests
213
213
  // (fast/deterministic backoff + attempt cap); defaults ride out a CD-deploy's brief unreachable window.
214
214
  // attempts — hard cap on forward attempts (safety bound; the window is the real bound in prod)
215
215
  // backoffMs — base (first-retry) delay; doubles each retry up to maxBackoffMs
216
216
  // maxBackoffMs — per-delay ceiling
217
217
  // maxWindowMs — total retry budget (sum of backoffs) before the original error is surfaced
218
218
  connectRetry?: { attempts?: number; backoffMs?: number; maxBackoffMs?: number; maxWindowMs?: number };
219
- // #1790 — deprecated alias for connectRetry (attempts/backoffMs only). `connectRetry` wins if both set.
219
+ // deprecated alias for connectRetry (attempts/backoffMs only). `connectRetry` wins if both set.
220
220
  spawnRetry?: { attempts?: number; backoffMs?: number };
221
221
  // Test seam so the retry backoff doesn't sleep real wall-clock time in unit tests.
222
222
  sleep?: (ms: number) => Promise<void>;
@@ -236,7 +236,7 @@ interface SseClient {
236
236
  keepalive: ReturnType<typeof setInterval>;
237
237
  }
238
238
 
239
- export class RelayMcpProxy {
239
+ export class HumwickMcpProxy {
240
240
  private readonly redactToolResponse?: RelayMcpProxyOptions["redactToolResponse"];
241
241
  private readonly relayMcpEndpoint: string;
242
242
  private readonly getToken: () => string | undefined;
@@ -270,8 +270,8 @@ export class RelayMcpProxy {
270
270
  this.getToken = options.getToken;
271
271
  this.authSecret = options.authSecret;
272
272
  this.enqueueBuffered = options.enqueueBuffered;
273
- this.bufferableTools = options.bufferableTools ?? DEFAULT_BUFFERABLE_TOOLS;
274
- this.onDemandTools = options.onDemandTools;
273
+ this.bufferableTools = options.bufferableTools ? new Set(options.bufferableTools) : DEFAULT_BUFFERABLE_TOOLS;
274
+ this.onDemandTools = options.onDemandTools ? new Set(options.onDemandTools) : undefined;
275
275
  this.fetchImpl = options.fetchImpl ?? fetch;
276
276
  this.abortSignal = options.abortSignal ?? AbortSignal.timeout;
277
277
  const retry = options.connectRetry ?? options.spawnRetry;
@@ -323,7 +323,7 @@ export class RelayMcpProxy {
323
323
  }
324
324
 
325
325
  // The runner calls this after a token re-mint (scope may change, e.g. command:spawn) and on every
326
- // bus re-register, since a deploy restarts the relay with new tools or schemas (#2973). Re-fetch
326
+ // bus re-register, since a deploy restarts the relay with new tools or schemas. Re-fetch
327
327
  // the tool list with the live token and emit list_changed if the visible set changed; setContext
328
328
  // covers the workspace mode/state path. Best-effort: a failed refresh keeps the last-known list.
329
329
  async refreshTools(): Promise<void> {
@@ -337,8 +337,6 @@ export class RelayMcpProxy {
337
337
  }
338
338
 
339
339
  private async handle(req: Request): Promise<Response> {
340
- const conflict = relayHeaderConflict(req.headers);
341
- if (conflict) return Response.json({ error: conflict }, { status: 400 });
342
340
  const url = new URL(req.url);
343
341
  if (this.options.webToolLog && url.pathname === `/web/${this.options.authSecret}`) return forwardSharedMcp(req, this.options.webToolLog);
344
342
  if (url.pathname !== PROXY_PATH) return new Response("not found", { status: 404 });
@@ -359,7 +357,7 @@ export class RelayMcpProxy {
359
357
  }
360
358
 
361
359
  private async handleRpc(req: Request): Promise<Response> {
362
- // #1450 — read the on-demand-front header BEFORE consuming the body. The per-agent metaOnly
360
+ // read the on-demand-front header BEFORE consuming the body. The per-agent metaOnly
363
361
  // callmux's relay downstream sets it to opt OUT of the on-demand hiding (it fronts those tools);
364
362
  // the agent's direct relay client never sets it, so it gets the eager hot-path set only.
365
363
  const frontsOnDemand = relayHeader(req.headers, "ondemand-front") === RELAY_MCP_ONDEMAND_SURFACE_VALUE;
@@ -395,7 +393,7 @@ export class RelayMcpProxy {
395
393
  // usable initialize result advertising the proxy's capabilities.
396
394
  const result: Record<string, unknown> = isRecord(relay?.body?.result) ? { ...(relay!.body!.result as Record<string, unknown>) } : {
397
395
  protocolVersion: "2024-11-05",
398
- serverInfo: { ...mcpServerInfo(parseToolGeneration(new URL(this.relayMcpEndpoint).searchParams.get("tools"))), version: "proxy" },
396
+ serverInfo: { ...MCP_SERVER_INFO, version: "proxy" },
399
397
  };
400
398
  const caps = isRecord(result.capabilities) ? { ...result.capabilities } : {};
401
399
  // The capability the relay's static endpoint doesn't advertise: live tool sets.
@@ -413,7 +411,7 @@ export class RelayMcpProxy {
413
411
  // If the relay is down and we have no cache yet, surface an empty list rather than erroring —
414
412
  // the agent can still operate (writes buffer; tools/list refreshes on the next call).
415
413
  const base = tools ?? this.lastRelayTools;
416
- // #1450 — callmux's relay downstream (frontsOnDemand) sees the full set so it can route
414
+ // callmux's relay downstream (frontsOnDemand) sees the full set so it can route
417
415
  // callmux_call to any of it; the direct client has the on-demand set hidden.
418
416
  const narrowed = this.narrow(base, frontsOnDemand);
419
417
  this.lastFingerprints[frontsOnDemand ? "front" : "direct"] = toolsFingerprint(narrowed);
@@ -421,14 +419,14 @@ export class RelayMcpProxy {
421
419
  }
422
420
 
423
421
  // Strict subset of the relay's already-scope-filtered list. Only removes — never adds. Narrowing
424
- // rules: (1) workspace tools apply only to an agent that owns a live worktree; (2) #1450 — the
422
+ // rules: (1) workspace tools apply only to an agent that owns a live worktree; (2) — the
425
423
  // on-demand relay set is hidden from the agent's DIRECT client (only when `hideOnDemand`), since
426
424
  // the per-agent metaOnly callmux fronts those tools on demand. `frontsOnDemand` (callmux's own
427
425
  // request) skips rule 2 so callmux sees the full relay set.
428
426
  private narrow(tools: Array<Record<string, unknown>>, frontsOnDemand = false): Array<Record<string, unknown>> {
429
427
  const hideOnDemand = !frontsOnDemand && this.onDemandTools !== undefined && this.onDemandTools.size > 0;
430
428
  return tools.filter((tool) => {
431
- const name = registryToolName(typeof tool.name === "string" ? tool.name : "");
429
+ const name = typeof tool.name === "string" ? tool.name : "";
432
430
  if (WORKTREE_ONLY_TOOLS.has(name) && !this.context.isolatedWorktree) return false;
433
431
  if (hideOnDemand && this.onDemandTools!.has(name)) return false;
434
432
  return true;
@@ -437,10 +435,11 @@ export class RelayMcpProxy {
437
435
 
438
436
  private async handleToolsCall(id: string | number | null, message: JsonRpcMessage): Promise<Response> {
439
437
  const params = isRecord(message.params) ? message.params : {};
440
- const toolName = registryToolName(typeof params.name === "string" ? params.name : "");
438
+ const wireName = typeof params.name === "string" ? params.name : "";
439
+ const toolName = wireName;
441
440
  let args = isRecord(params.arguments) ? params.arguments : {};
442
441
 
443
- // #1502 — inject this agent's workspace descriptor so the relay can self-heal a missing/dropped
442
+ // inject this agent's workspace descriptor so the relay can self-heal a missing/dropped
444
443
  // workspace registration during delivery. Only for worktree agents, only when the agent didn't
445
444
  // already supply one, and the augmented args are forwarded (not just used locally).
446
445
  if (
@@ -453,7 +452,7 @@ export class RelayMcpProxy {
453
452
  message = { ...message, params: { ...params, arguments: args } };
454
453
  }
455
454
 
456
- // #1790 — stabilize the spawn correlation id BEFORE the first attempt so a retry across a relay
455
+ // stabilize the spawn correlation id BEFORE the first attempt so a retry across a relay
457
456
  // restart reuses it and the server dedupes to the SAME spawn (never a second agent). The caller's
458
457
  // own id wins if supplied; otherwise mint an fs-safe `sp_<uuid>` here, because the relay would
459
458
  // otherwise mint a FRESH id on each attempt and a retry would double-create. This is id-provisioning,
@@ -466,10 +465,10 @@ export class RelayMcpProxy {
466
465
  }
467
466
  }
468
467
 
469
- // #2857 — a `path` upload names a file on THIS host, but the relay reads it from
468
+ // a `path` upload names a file on THIS host, but the relay reads it from
470
469
  // its own filesystem, so remote agents always got "path not found". Materialize the
471
470
  // file here and forward bytes; rejections mirror the relay's own messages verbatim.
472
- if (toolName === "relay_upload_artifact") {
471
+ if (toolName === "humwick_upload_artifact") {
473
472
  const materialized = await materializeArtifactPathUpload(args, this.getToken);
474
473
  if (materialized.error !== undefined) return Response.json(jsonRpcError(id, -32602, materialized.error));
475
474
  if (materialized.args !== undefined) {
@@ -478,14 +477,14 @@ export class RelayMcpProxy {
478
477
  }
479
478
  }
480
479
 
481
- // #2089 — the call is safe to resend after an AMBIGUOUS (sent-then-response-lost) failure only if it
480
+ // the call is safe to resend after an AMBIGUOUS (sent-then-response-lost) failure only if it
482
481
  // carries an idempotency key the server dedupes on. Read generically off the args (spawnRequestId or
483
482
  // idempotencyKey) — never off the tool name — so every idempotent tool qualifies and no other does.
484
483
  const idempotent = isIdempotentCall(args);
485
484
 
486
485
  const relay = await this.forwardWithConnectionRetry(message, toolName, idempotent).catch((error) => {
487
486
  // A thrown fetch = transport failure (relay unreachable / DNS / connection refused / socket closed).
488
- // The generic connect-retry (#2089) has already exhausted its bounded window by here.
487
+ // The generic connect-retry has already exhausted its bounded window by here.
489
488
  return { ok: false, status: 0, body: null, transportError: errMessage(error) } as ForwardResult;
490
489
  });
491
490
 
@@ -495,17 +494,17 @@ export class RelayMcpProxy {
495
494
  const idempotencyKey = typeof args.idempotencyKey === "string" && args.idempotencyKey
496
495
  ? args.idempotencyKey
497
496
  : `mcp-${toolName}-${crypto.randomUUID()}`;
498
- this.enqueueBuffered({ tool: toolName, arguments: { ...args, idempotencyKey }, idempotencyKey });
497
+ this.enqueueBuffered({ tool: wireName, arguments: { ...args, idempotencyKey }, idempotencyKey });
499
498
  logger.info("mcp-proxy", `relay unreachable — buffered ${toolName} (idempotencyKey=${idempotencyKey})`);
500
499
  return Response.json(jsonRpcResult(id, toolResult({
501
500
  queued: true,
502
- tool: toolName,
501
+ tool: wireName,
503
502
  idempotencyKey,
504
503
  note: "Relay was unreachable; your call was queued durably and will be delivered automatically when the relay comes back. It is not lost.",
505
504
  })));
506
505
  }
507
506
 
508
- // #1418: propagate the relay's actual status (e.g. 413 payload-too-large, 400 validation,
507
+ // propagate the relay's actual status (e.g. 413 payload-too-large, 400 validation,
509
508
  // 403 auth) instead of the default 200 `Response.json` gives an untouched status option —
510
509
  // a hard rejection silently reads as a normal 200 response to anything downstream that
511
510
  // branches on HTTP status rather than parsing the JSON-RPC `error` field, masking a large
@@ -542,7 +541,7 @@ export class RelayMcpProxy {
542
541
  const payload: Record<string, unknown> = { jsonrpc: "2.0", method: message.method };
543
542
  if (message.id !== undefined) payload.id = message.id;
544
543
  if (message.params !== undefined) payload.params = message.params;
545
- // #2429 — Bun 1.3.x's native HTTP idle watchdog aborts any fetch that sees no response bytes
544
+ // Bun 1.3.x's native HTTP idle watchdog aborts any fetch that sees no response bytes
546
545
  // for ~300s, and an AbortSignal does NOT govern it: the server awaits the WHOLE tool call
547
546
  // before its first response byte (src/mcp/index.ts `tools/call` → Response.json, no streaming),
548
547
  // so a legitimate >~300s tool call was killed client-side at ~300s despite the 610s budget.
@@ -561,7 +560,7 @@ export class RelayMcpProxy {
561
560
  return { ok: response.ok, status: response.status, body: parsed };
562
561
  }
563
562
 
564
- // #2089 — GENERIC bounded connect-retry so a relay restart (CD deploy, no drain) is invisible to the
563
+ // GENERIC bounded connect-retry so a relay restart (CD deploy, no drain) is invisible to the
565
564
  // agent for EVERY tool, not just spawn. One chokepoint, uniform across the tool suite. The retry
566
565
  // decision is driven purely by the failure CLASS (connect / ambiguous / fatal) and the call's
567
566
  // idempotency — never by tool name:
@@ -650,7 +649,7 @@ export class RelayMcpProxy {
650
649
  }
651
650
 
652
651
  private maybeEmitListChanged(): void {
653
- // Definitions, not names: a new parameter on an existing tool is a change too (#2973). A view
652
+ // Definitions, not names: a new parameter on an existing tool is a change too. A view
654
653
  // nobody has listed yet needs no notification; its first list is current.
655
654
  let changed = false;
656
655
  for (const view of ["direct", "front"] as const) {