@getmarrow/mcp 3.9.101 → 3.9.103

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 (127) hide show
  1. package/README.md +63 -29
  2. package/dist/cli.js +64 -1005
  3. package/dist/cli.js.map +1 -1
  4. package/dist/codex-plugin-pins.d.ts +19 -0
  5. package/dist/codex-plugin-pins.d.ts.map +1 -0
  6. package/dist/codex-plugin-pins.js +43 -0
  7. package/dist/codex-plugin-pins.js.map +1 -0
  8. package/dist/codex-plugins.d.ts +34 -0
  9. package/dist/codex-plugins.d.ts.map +1 -0
  10. package/dist/codex-plugins.js +240 -0
  11. package/dist/codex-plugins.js.map +1 -0
  12. package/dist/env.d.ts +2 -0
  13. package/dist/env.d.ts.map +1 -1
  14. package/dist/env.js +229 -39
  15. package/dist/env.js.map +1 -1
  16. package/dist/hook-contract.d.ts +41 -3
  17. package/dist/hook-contract.d.ts.map +1 -1
  18. package/dist/hook-contract.js +126 -18
  19. package/dist/hook-contract.js.map +1 -1
  20. package/dist/hook-pre-action.d.ts +48 -2
  21. package/dist/hook-pre-action.d.ts.map +1 -1
  22. package/dist/hook-pre-action.js +199 -23
  23. package/dist/hook-pre-action.js.map +1 -1
  24. package/dist/hook-tool-policy.d.ts +3 -3
  25. package/dist/hook-tool-policy.d.ts.map +1 -1
  26. package/dist/hook-tool-policy.js +6 -22
  27. package/dist/hook-tool-policy.js.map +1 -1
  28. package/dist/hook.d.ts +2 -2
  29. package/dist/hook.d.ts.map +1 -1
  30. package/dist/hook.js +13 -6
  31. package/dist/hook.js.map +1 -1
  32. package/dist/host-approval-state.d.ts +9 -0
  33. package/dist/host-approval-state.d.ts.map +1 -1
  34. package/dist/host-approval-state.js +4 -0
  35. package/dist/host-approval-state.js.map +1 -1
  36. package/dist/host-approval.d.ts +6 -0
  37. package/dist/host-approval.d.ts.map +1 -1
  38. package/dist/host-approval.js +3 -1
  39. package/dist/host-approval.js.map +1 -1
  40. package/dist/host-session.d.ts +11 -0
  41. package/dist/host-session.d.ts.map +1 -1
  42. package/dist/host-session.js +49 -0
  43. package/dist/host-session.js.map +1 -1
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +27 -19
  46. package/dist/index.js.map +1 -1
  47. package/dist/normalized-action.d.ts +29 -6
  48. package/dist/normalized-action.d.ts.map +1 -1
  49. package/dist/normalized-action.js +497 -103
  50. package/dist/normalized-action.js.map +1 -1
  51. package/dist/own-server-identity.d.ts +123 -0
  52. package/dist/own-server-identity.d.ts.map +1 -0
  53. package/dist/own-server-identity.js +1052 -0
  54. package/dist/own-server-identity.js.map +1 -0
  55. package/dist/own-tool.d.ts +87 -0
  56. package/dist/own-tool.d.ts.map +1 -0
  57. package/dist/own-tool.js +169 -0
  58. package/dist/own-tool.js.map +1 -0
  59. package/dist/redact.d.ts +23 -0
  60. package/dist/redact.d.ts.map +1 -1
  61. package/dist/redact.js +181 -8
  62. package/dist/redact.js.map +1 -1
  63. package/dist/seatbelt/classifier-pin.d.ts +15 -0
  64. package/dist/seatbelt/classifier-pin.d.ts.map +1 -0
  65. package/dist/seatbelt/classifier-pin.js +26 -0
  66. package/dist/seatbelt/classifier-pin.js.map +1 -0
  67. package/dist/seatbelt/guards.d.ts +10 -0
  68. package/dist/seatbelt/guards.d.ts.map +1 -0
  69. package/dist/seatbelt/guards.js +509 -0
  70. package/dist/seatbelt/guards.js.map +1 -0
  71. package/dist/seatbelt/hook.d.ts +72 -0
  72. package/dist/seatbelt/hook.d.ts.map +1 -0
  73. package/dist/seatbelt/hook.js +362 -0
  74. package/dist/seatbelt/hook.js.map +1 -0
  75. package/dist/seatbelt/longopts.d.ts +68 -0
  76. package/dist/seatbelt/longopts.d.ts.map +1 -0
  77. package/dist/seatbelt/longopts.js +377 -0
  78. package/dist/seatbelt/longopts.js.map +1 -0
  79. package/dist/seatbelt/mapper.d.ts +83 -0
  80. package/dist/seatbelt/mapper.d.ts.map +1 -0
  81. package/dist/seatbelt/mapper.js +10248 -0
  82. package/dist/seatbelt/mapper.js.map +1 -0
  83. package/dist/seatbelt/record.d.ts +94 -0
  84. package/dist/seatbelt/record.d.ts.map +1 -0
  85. package/dist/seatbelt/record.js +776 -0
  86. package/dist/seatbelt/record.js.map +1 -0
  87. package/dist/seatbelt/tables.d.ts +82 -0
  88. package/dist/seatbelt/tables.d.ts.map +1 -0
  89. package/dist/seatbelt/tables.js +359 -0
  90. package/dist/seatbelt/tables.js.map +1 -0
  91. package/dist/seatbelt-deploy.d.ts +100 -0
  92. package/dist/seatbelt-deploy.d.ts.map +1 -0
  93. package/dist/seatbelt-deploy.js +393 -0
  94. package/dist/seatbelt-deploy.js.map +1 -0
  95. package/dist/seatbelt-fs.d.ts +30 -0
  96. package/dist/seatbelt-fs.d.ts.map +1 -0
  97. package/dist/seatbelt-fs.js +94 -0
  98. package/dist/seatbelt-fs.js.map +1 -0
  99. package/dist/seatbelt-git.d.ts +212 -0
  100. package/dist/seatbelt-git.d.ts.map +1 -0
  101. package/dist/seatbelt-git.js +844 -0
  102. package/dist/seatbelt-git.js.map +1 -0
  103. package/dist/seatbelt-mode.d.ts +77 -0
  104. package/dist/seatbelt-mode.d.ts.map +1 -0
  105. package/dist/seatbelt-mode.js +496 -0
  106. package/dist/seatbelt-mode.js.map +1 -0
  107. package/dist/seatbelt-path.d.ts +123 -0
  108. package/dist/seatbelt-path.d.ts.map +1 -0
  109. package/dist/seatbelt-path.js +482 -0
  110. package/dist/seatbelt-path.js.map +1 -0
  111. package/dist/seatbelt-scripts.d.ts +109 -0
  112. package/dist/seatbelt-scripts.d.ts.map +1 -0
  113. package/dist/seatbelt-scripts.js +1060 -0
  114. package/dist/seatbelt-scripts.js.map +1 -0
  115. package/dist/tool-catalog.d.ts +6852 -0
  116. package/dist/tool-catalog.d.ts.map +1 -0
  117. package/dist/tool-catalog.js +999 -0
  118. package/dist/tool-catalog.js.map +1 -0
  119. package/dist/types.d.ts +2 -1
  120. package/dist/types.d.ts.map +1 -1
  121. package/package.json +2 -1
  122. package/server.json +10 -3
  123. package/vendor/classifier/MANIFEST.json +25 -0
  124. package/vendor/classifier/classifier.js +1356 -0
  125. package/vendor/classifier/policy-baseline.js +187 -0
  126. package/vendor/classifier/redact.js +59 -0
  127. package/vendor/classifier/shell-parse.js +455 -0
@@ -3,6 +3,10 @@ import { type HoldPlan } from './host-approval';
3
3
  import { MARROW_OUTAGE_WARNING } from './hook-contract';
4
4
  /** A protected action with no Marrow key anywhere this machine keeps one (MARROW_API_KEY, ~/.marrow/env.local or ~/.marrow/env, owner-only). */
5
5
  export declare const NO_KEY_TEXT = "Marrow has no API key on this machine. Owner: create one at getmarrow.ai/signup, then run the installer activate command in a terminal. Do not retry this action.";
6
+ /** Seatbelt mode `claimed_no_key` (spec §1.1): account evidence on the machine but no usable key. Same text as src/seatbelt/hook.ts. */
7
+ export declare const CLAIMED_NO_KEY_TEXT = "Marrow is linked to an account but has no working key; owner: run `npx -y @getmarrow/install@latest doctor`.";
8
+ /** `claimed_no_key` without account evidence (a mode fallback): the same holds, never an account claim. Same text as src/seatbelt/hook.ts. */
9
+ export declare const SETUP_UNCONFIRMED_TEXT = "Marrow has no working key on this machine and its keyless mode is off here (strict mode, or a home folder it cannot trust); owner: run `npx -y @getmarrow/install@latest doctor`.";
6
10
  export { MARROW_OUTAGE_WARNING };
7
11
  export declare const MAX_PRE_ACTION_INPUT_BYTES: number;
8
12
  export declare const PRE_ACTION_CONTROL_TIMEOUT_MS = 8000;
@@ -33,6 +37,8 @@ type HookHarness = 'claude-code' | 'cline' | 'codex' | 'cursor' | 'gemini' | 'gr
33
37
  export declare const HOLD_OWNER_DENIAL = "Marrow is holding this action for approval. Retry it after approval.";
34
38
  /** Marrow did not answer within the hook's time limit: an action that can be held stays held. */
35
39
  export declare const HELD_SLOW_TEXT = "Marrow did not answer in time, so this action is held. Retry it in a moment.";
40
+ /** Marrow cannot be reached (a real outage): a high-risk action fails closed; routine actions keep the outage allow. */
41
+ export declare const HELD_OFFLINE_TEXT = "Marrow cannot be reached right now, so this high-risk action is held; routine actions keep running. Retry it when Marrow is back.";
36
42
  /** The only denial @getmarrow/install's Grok guard passes through (any other output blocks with a launch failure). */
37
43
  export declare const GROK_FIXED_DENIAL = "Marrow blocked this protected action.";
38
44
  /**
@@ -63,7 +69,7 @@ type PreActionControlResult = {
63
69
  };
64
70
  export declare function isMarrowOutage(result: PreActionControlResult): boolean;
65
71
  export declare function controlFailureKind(error: unknown): PreActionControlResult['failure'];
66
- export declare function controlRejectionMessage(error: unknown, agentId?: string): string;
72
+ export declare function controlRejectionMessage(error: unknown, agentId?: string, kind?: PreActionControlResult['failure']): string;
67
73
  export type OwnerApprovalPrompt = {
68
74
  available: boolean;
69
75
  unavailableReason: string;
@@ -97,8 +103,37 @@ export declare function advisoryGateNotice(runtime: PreActionControlResult['runt
97
103
  export declare function runtimeGateVerdict(runtime: PreActionControlResult['runtime']): GateVerdict | null;
98
104
  export declare function gateDecisionMessage(verdict: GateVerdict, ask: boolean, prompt?: OwnerApprovalPrompt): string;
99
105
  export declare function localControlAllowOutput(harness: 'claude-code' | 'cline' | 'codex' | 'cursor' | 'gemini' | 'grok' | 'windsurf' | 'mcp-client'): Record<string, unknown> | null;
106
+ /** Keyless seatbelt: a host that cannot hand this action to its own approval prompt holds it (no account to approve it). */
107
+ export declare const SEATBELT_HELD_TEXT = "MARROW SEATBELT held this action: this host cannot hand it to its own approval prompt here, and no Marrow account is linked to approve it. This needs the machine owner. Do not retry it another way or edit Marrow settings. Tell the owner: run it yourself in your own terminal, or link a Marrow account with \"npx -y @getmarrow/install@latest activate\" (it needs MARROW_API_KEY set first) so these become approval prompts.";
108
+ export declare const SEATBELT_CURSOR_ASK_USER_TEXT = "Marrow seatbelt (no account linked): approve this action here only if you expected it.";
109
+ export declare const SEATBELT_CURSOR_ASK_AGENT_TEXT = "Cursor is asking the user to approve this action (Marrow seatbelt, no account linked). Wait for the answer; do not retry it another way.";
110
+ /**
111
+ * The keyless seatbelt's answer for an action it does not stop (spec §1.3 "pass": Marrow adds no
112
+ * decision and the host's own permission flow runs). `heldBefore`: the 3.9.103 keyless hook held this
113
+ * action (protected, or not read-only on Windsurf, Gemini or Grok). `null` means exit 0, no output.
114
+ * - Not held before: exactly the answer 3.9.103 gave (its read-only and unprotected passes).
115
+ * - Held before: never an explicit allow that could skip the host's own approval step.
116
+ * Claude Code and Codex `{}` (no permissionDecision), Cline `{cancel:false}`, Windsurf no output:
117
+ * the hosts' neutral answers (see PASS_THROUGH_HOSTS), so their own approval step runs.
118
+ * Gemini CLI `{"decision":"allow"}`: the installed guard passes only that or a fixed denial (no
119
+ * output is a launch failure that blocks), and Gemini applies its own policy and confirmation
120
+ * after the BeforeTool hook whatever an "allow" says, so it is Gemini's neutral answer.
121
+ * Cursor beforeShellExecution / beforeMCPExecution: "ask", so Cursor's own approval prompt decides.
122
+ * Cursor preToolUse (no neutral answer, "ask" not enforced) and Grok (its guard passes only an
123
+ * explicit allow or the fixed denial, and nothing shows an allow keeps Grok's own approval): held,
124
+ * as 3.9.103 did. Any other or missing Cursor event name is treated as preToolUse.
125
+ */
126
+ export declare function seatbeltPassOutput(harness: 'claude-code' | 'cline' | 'codex' | 'cursor' | 'gemini' | 'grok' | 'windsurf' | 'mcp-client', hookEventName: unknown, heldBefore: boolean): Record<string, unknown> | null;
100
127
  export declare function localLoopGuardDenyOutput(harness: 'claude-code' | 'cline' | 'codex' | 'cursor' | 'gemini' | 'grok' | 'windsurf' | 'mcp-client', reason: string): Record<string, unknown> | null;
101
- export declare function classifyTool(event: PreToolUseEvent): {
128
+ /**
129
+ * `ownUnproven`: the label is one of Marrow's own, but the host's server
130
+ * identity check did not prove the official server (Codex, Cursor). Such a
131
+ * call is classified as a protected MCP tool of another server, never
132
+ * read-only (it imitates Marrow's names, so a read verb proves nothing).
133
+ */
134
+ export declare function classifyTool(event: PreToolUseEvent, options?: {
135
+ ownUnproven?: boolean;
136
+ }): {
102
137
  action: string;
103
138
  target: string;
104
139
  type: string;
@@ -145,5 +180,16 @@ export declare function installPreActionHook(startDir?: string): {
145
180
  settingsPath: string;
146
181
  installed: boolean;
147
182
  };
183
+ /**
184
+ * I3: whether a real outage (the gate never answered) holds this action. Never looser than master,
185
+ * which allowed every action in an outage. A shell command is read by the seatbelt mapper: held for an
186
+ * S0-S5/S-x hit or a program on the outage list (git push, publish, deploy, remote delete), allowed
187
+ * when the mapper read all of it and found neither (words in a message, a path or a comment never hold
188
+ * it). Anything else, or a mapper that could not finish, keeps `classifiedHighRisk` (F2).
189
+ */
190
+ export declare function outageHolds(classifiedHighRisk: boolean, read: () => {
191
+ shell: boolean;
192
+ risk: 'high' | 'not_high' | 'unknown';
193
+ }): boolean;
148
194
  export declare function runPreActionHookCommand(input?: unknown): Promise<void>;
149
195
  //# sourceMappingURL=hook-pre-action.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hook-pre-action.d.ts","sourceRoot":"","sources":["../src/hook-pre-action.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hook-pre-action.d.ts","sourceRoot":"","sources":["../src/hook-pre-action.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DENIED_DECISION_CLOSE_TIMEOUT_MS = exports.GROK_FIXED_DENIAL = exports.HELD_SLOW_TEXT = exports.HOLD_OWNER_DENIAL = exports.PreActionControlTimeoutError = exports.PRE_ACTION_CONTROL_TIMEOUT_MS = exports.MAX_PRE_ACTION_INPUT_BYTES = exports.MARROW_OUTAGE_WARNING = exports.NO_KEY_TEXT = void 0;
3
+ exports.DENIED_DECISION_CLOSE_TIMEOUT_MS = exports.SEATBELT_CURSOR_ASK_AGENT_TEXT = exports.SEATBELT_CURSOR_ASK_USER_TEXT = exports.SEATBELT_HELD_TEXT = exports.GROK_FIXED_DENIAL = exports.HELD_OFFLINE_TEXT = exports.HELD_SLOW_TEXT = exports.HOLD_OWNER_DENIAL = exports.PreActionControlTimeoutError = exports.PRE_ACTION_CONTROL_TIMEOUT_MS = exports.MAX_PRE_ACTION_INPUT_BYTES = exports.MARROW_OUTAGE_WARNING = exports.SETUP_UNCONFIRMED_TEXT = exports.CLAIMED_NO_KEY_TEXT = exports.NO_KEY_TEXT = void 0;
4
4
  exports.isMarrowControlTimeout = isMarrowControlTimeout;
5
5
  exports.isMarrowControlOutage = isMarrowControlOutage;
6
6
  exports.heldActionHookOutput = heldActionHookOutput;
@@ -16,6 +16,7 @@ exports.advisoryGateNotice = advisoryGateNotice;
16
16
  exports.runtimeGateVerdict = runtimeGateVerdict;
17
17
  exports.gateDecisionMessage = gateDecisionMessage;
18
18
  exports.localControlAllowOutput = localControlAllowOutput;
19
+ exports.seatbeltPassOutput = seatbeltPassOutput;
19
20
  exports.localLoopGuardDenyOutput = localLoopGuardDenyOutput;
20
21
  exports.classifyTool = classifyTool;
21
22
  exports.cursorPreActionHookOutput = cursorPreActionHookOutput;
@@ -27,6 +28,7 @@ exports.preActionHookOutput = preActionHookOutput;
27
28
  exports.closeDeniedDecision = closeDeniedDecision;
28
29
  exports.localApprovalCategories = localApprovalCategories;
29
30
  exports.installPreActionHook = installPreActionHook;
31
+ exports.outageHolds = outageHolds;
30
32
  exports.runPreActionHookCommand = runPreActionHookCommand;
31
33
  const node_crypto_1 = require("node:crypto");
32
34
  const index_1 = require("./index");
@@ -36,6 +38,7 @@ const control_state_1 = require("./control-state");
36
38
  const runtime_contract_1 = require("./runtime-contract");
37
39
  const host_approval_1 = require("./host-approval");
38
40
  const host_session_1 = require("./host-session");
41
+ const own_server_identity_1 = require("./own-server-identity");
39
42
  const normalized_action_1 = require("./normalized-action");
40
43
  const session_loop_guard_1 = require("./session-loop-guard");
41
44
  const hook_tool_policy_1 = require("./hook-tool-policy");
@@ -43,6 +46,12 @@ const hook_contract_1 = require("./hook-contract");
43
46
  Object.defineProperty(exports, "MARROW_OUTAGE_WARNING", { enumerable: true, get: function () { return hook_contract_1.MARROW_OUTAGE_WARNING; } });
44
47
  /** A protected action with no Marrow key anywhere this machine keeps one (MARROW_API_KEY, ~/.marrow/env.local or ~/.marrow/env, owner-only). */
45
48
  exports.NO_KEY_TEXT = 'Marrow has no API key on this machine. Owner: create one at getmarrow.ai/signup, then run the installer activate command in a terminal. Do not retry this action.';
49
+ /** Seatbelt mode `claimed_no_key` (spec §1.1): account evidence on the machine but no usable key. Same text as src/seatbelt/hook.ts. */
50
+ exports.CLAIMED_NO_KEY_TEXT = 'Marrow is linked to an account but has no working key; owner: run `npx -y @getmarrow/install@latest doctor`.';
51
+ /** `claimed_no_key` without account evidence (a mode fallback): the same holds, never an account claim. Same text as src/seatbelt/hook.ts. */
52
+ exports.SETUP_UNCONFIRMED_TEXT = 'Marrow has no working key on this machine and its keyless mode is off here (strict mode, or a home folder it cannot trust); owner: run `npx -y @getmarrow/install@latest doctor`.';
53
+ /** The seatbelt path itself could not run (its modules did not load): a stop (spec §2.1.4). */
54
+ const SEATBELT_FAILED_TEXT = 'MARROW SEATBELT stopped this action: a safety check that could not finish (U5.fail_closed, hook_error). This needs the machine owner. Do not retry it another way or edit Marrow settings.';
46
55
  // Claude Code sends the whole tool input, and a Write of a long document grows
47
56
  // further once JSON-escaped, so tens of kilobytes of markdown must still parse.
48
57
  exports.MAX_PRE_ACTION_INPUT_BYTES = 4 * 1024 * 1024;
@@ -107,6 +116,8 @@ function isMarrowControlOutage(error) {
107
116
  exports.HOLD_OWNER_DENIAL = 'Marrow is holding this action for approval. Retry it after approval.';
108
117
  /** Marrow did not answer within the hook's time limit: an action that can be held stays held. */
109
118
  exports.HELD_SLOW_TEXT = 'Marrow did not answer in time, so this action is held. Retry it in a moment.';
119
+ /** Marrow cannot be reached (a real outage): a high-risk action fails closed; routine actions keep the outage allow. */
120
+ exports.HELD_OFFLINE_TEXT = 'Marrow cannot be reached right now, so this high-risk action is held; routine actions keep running. Retry it when Marrow is back.';
110
121
  /** The only denial @getmarrow/install's Grok guard passes through (any other output blocks with a launch failure). */
111
122
  exports.GROK_FIXED_DENIAL = 'Marrow blocked this protected action.';
112
123
  /**
@@ -231,7 +242,7 @@ function plainControlFix(error) {
231
242
  }
232
243
  // Names a reached control failure by HTTP status and stable failure code, with a plain
233
244
  // fix (see plainControlFix); private service text is never echoed into the agent transcript.
234
- function controlRejectionMessage(error, agentId) {
245
+ function controlRejectionMessage(error, agentId, kind = controlFailureKind(error)) {
235
246
  const detail = [];
236
247
  if (error instanceof request_reliability_1.MarrowRequestError) {
237
248
  if (typeof error.status === 'number' && Number.isInteger(error.status))
@@ -241,7 +252,6 @@ function controlRejectionMessage(error, agentId) {
241
252
  detail.push(code);
242
253
  }
243
254
  const suffix = detail.length ? ` (${detail.join(' ')})` : '';
244
- const kind = controlFailureKind(error);
245
255
  if (kind === 'credential_scope') {
246
256
  const agent = agentId && SAFE_AGENT_ID.test(agentId)
247
257
  ? `agent "${agentId}"`
@@ -410,6 +420,46 @@ function localControlAllowOutput(harness) {
410
420
  return { decision: 'allow' };
411
421
  return {};
412
422
  }
423
+ /** The Cursor events where Cursor enforces "ask" with its own approval prompt (the installer's execution hooks). */
424
+ const CURSOR_ASK_EVENTS = new Set(['beforeShellExecution', 'beforeMCPExecution']);
425
+ /** Keyless seatbelt: a host that cannot hand this action to its own approval prompt holds it (no account to approve it). */
426
+ exports.SEATBELT_HELD_TEXT = 'MARROW SEATBELT held this action: this host cannot hand it to its own approval prompt here, and no Marrow account is linked to approve it. This needs the machine owner. Do not retry it another way or edit Marrow settings. Tell the owner: run it yourself in your own terminal, or link a Marrow account with "npx -y @getmarrow/install@latest activate" (it needs MARROW_API_KEY set first) so these become approval prompts.';
427
+ exports.SEATBELT_CURSOR_ASK_USER_TEXT = 'Marrow seatbelt (no account linked): approve this action here only if you expected it.';
428
+ exports.SEATBELT_CURSOR_ASK_AGENT_TEXT = 'Cursor is asking the user to approve this action (Marrow seatbelt, no account linked). Wait for the answer; do not retry it another way.';
429
+ /**
430
+ * The keyless seatbelt's answer for an action it does not stop (spec §1.3 "pass": Marrow adds no
431
+ * decision and the host's own permission flow runs). `heldBefore`: the 3.9.103 keyless hook held this
432
+ * action (protected, or not read-only on Windsurf, Gemini or Grok). `null` means exit 0, no output.
433
+ * - Not held before: exactly the answer 3.9.103 gave (its read-only and unprotected passes).
434
+ * - Held before: never an explicit allow that could skip the host's own approval step.
435
+ * Claude Code and Codex `{}` (no permissionDecision), Cline `{cancel:false}`, Windsurf no output:
436
+ * the hosts' neutral answers (see PASS_THROUGH_HOSTS), so their own approval step runs.
437
+ * Gemini CLI `{"decision":"allow"}`: the installed guard passes only that or a fixed denial (no
438
+ * output is a launch failure that blocks), and Gemini applies its own policy and confirmation
439
+ * after the BeforeTool hook whatever an "allow" says, so it is Gemini's neutral answer.
440
+ * Cursor beforeShellExecution / beforeMCPExecution: "ask", so Cursor's own approval prompt decides.
441
+ * Cursor preToolUse (no neutral answer, "ask" not enforced) and Grok (its guard passes only an
442
+ * explicit allow or the fixed denial, and nothing shows an allow keeps Grok's own approval): held,
443
+ * as 3.9.103 did. Any other or missing Cursor event name is treated as preToolUse.
444
+ */
445
+ function seatbeltPassOutput(harness, hookEventName, heldBefore) {
446
+ if (!heldBefore)
447
+ return localControlAllowOutput(harness);
448
+ if (harness === 'windsurf')
449
+ return null;
450
+ if (harness === 'cline')
451
+ return { cancel: false };
452
+ if (harness === 'gemini')
453
+ return { decision: 'allow' };
454
+ if (harness === 'grok')
455
+ return { decision: 'deny', reason: exports.GROK_FIXED_DENIAL };
456
+ if (harness === 'cursor') {
457
+ return typeof hookEventName === 'string' && CURSOR_ASK_EVENTS.has(hookEventName)
458
+ ? { permission: 'ask', user_message: exports.SEATBELT_CURSOR_ASK_USER_TEXT, agent_message: exports.SEATBELT_CURSOR_ASK_AGENT_TEXT }
459
+ : { permission: 'deny', user_message: exports.SEATBELT_HELD_TEXT, agent_message: exports.SEATBELT_HELD_TEXT };
460
+ }
461
+ return {};
462
+ }
413
463
  function localLoopGuardDenyOutput(harness, reason) {
414
464
  const bounded = reason.replace(/[\u0000-\u001f\u007f]/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 500);
415
465
  if (harness === 'windsurf')
@@ -418,7 +468,10 @@ function localLoopGuardDenyOutput(harness, reason) {
418
468
  return { permission: 'deny', user_message: bounded, agent_message: bounded };
419
469
  if (harness === 'cline')
420
470
  return { cancel: true, errorMessage: bounded };
421
- if (harness === 'gemini' || harness === 'grok')
471
+ // The installed Grok guard passes only an allow or this fixed denial; any other text is its launch failure (audit M1).
472
+ if (harness === 'grok')
473
+ return { decision: 'deny', reason: exports.GROK_FIXED_DENIAL };
474
+ if (harness === 'gemini')
422
475
  return { decision: 'deny', reason: bounded };
423
476
  return { hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: bounded } };
424
477
  }
@@ -456,7 +509,13 @@ function localControlDisabled() {
456
509
  return false;
457
510
  }
458
511
  }
459
- function classifyTool(event) {
512
+ /**
513
+ * `ownUnproven`: the label is one of Marrow's own, but the host's server
514
+ * identity check did not prove the official server (Codex, Cursor). Such a
515
+ * call is classified as a protected MCP tool of another server, never
516
+ * read-only (it imitates Marrow's names, so a read verb proves nothing).
517
+ */
518
+ function classifyTool(event, options = {}) {
460
519
  const tool = String(event.tool_name || 'tool').slice(0, 64);
461
520
  const normalizedTool = (0, hook_tool_policy_1.normalizeHookToolName)(tool);
462
521
  // Editing and task tools are judged by their target; shell-like words in the
@@ -464,7 +523,8 @@ function classifyTool(event) {
464
523
  const shellGoverned = (0, hook_tool_policy_1.isShellGovernedTool)(event);
465
524
  const command = shellGoverned ? (0, hook_tool_policy_1.hookToolCommand)(event) : '';
466
525
  const input = (0, hook_tool_policy_1.toolClassificationText)(event).toLowerCase();
467
- const readOnly = (0, hook_tool_policy_1.isReadOnlyToolEvent)(event);
526
+ // A look-alike of Marrow's own tool is never read-only, whatever its verb.
527
+ const readOnly = options.ownUnproven !== true && (0, hook_tool_policy_1.isReadOnlyToolEvent)(event);
468
528
  const secretAccess = (0, hook_tool_policy_1.isSecretMaterialAccess)(event);
469
529
  const protectedShellCommand = shellGoverned && (0, hook_tool_policy_1.isProtectedShellMutation)(command);
470
530
  const infrastructureDeployment = /\b(?:kubectl|terraform|pulumi|helm)\b/.test(command.toLowerCase())
@@ -493,7 +553,7 @@ function classifyTool(event) {
493
553
  const protectedAction = !readOnly && (secretAccess
494
554
  || (shellGoverned && /\b(?:deploy|release|publish|git\s+push|git\s+merge|gh\s+pr\s+merge|migration|migrate|secret|credential|rotate|revoke|payment|refund|charge|invoice|production|prod)\b/.test(input))
495
555
  || protectedShellCommand
496
- || ((0, hook_tool_policy_1.isMcpHookTool)(event.tool_name) && !(0, hook_tool_policy_1.isOfficialMarrowMcpTool)(event.tool_name))
556
+ || ((0, hook_tool_policy_1.isMcpHookTool)(event.tool_name) && (options.ownUnproven === true || !(0, hook_tool_policy_1.isOfficialMarrowMcpTool)(event.tool_name)))
497
557
  || (['use_mcp_tool', 'use_tool'].includes(normalizedTool) && !(0, hook_tool_policy_1.isOfficialMarrowMcpEvent)(event)));
498
558
  const risk = readOnly ? 'low' : protectedAction ? 'high' : 'medium';
499
559
  const target = surfaces.includes('npm') ? `npm:${type}`
@@ -548,11 +608,12 @@ function clinePreActionHookOutput(result) {
548
608
  };
549
609
  }
550
610
  if (result.protectedRisk && (!result.runtime || !result.permit?.verified)) {
551
- const credentialsUnavailable = result.enforcementError === exports.NO_KEY_TEXT;
611
+ const credentialsUnavailable = result.enforcementError === exports.NO_KEY_TEXT || result.enforcementError === exports.CLAIMED_NO_KEY_TEXT
612
+ || result.enforcementError === exports.SETUP_UNCONFIRMED_TEXT;
552
613
  return {
553
614
  cancel: true,
554
615
  errorMessage: credentialsUnavailable
555
- ? exports.NO_KEY_TEXT
616
+ ? result.enforcementError
556
617
  : result.failure === 'credential_scope'
557
618
  ? 'This Marrow API key is not authorized to obtain action permits for this agent. Use the API key issued to this agent and retry.'
558
619
  : result.failure === 'unavailable'
@@ -828,11 +889,54 @@ function installPreActionHook(startDir = process.cwd()) {
828
889
  fs.writeFileSync(path, JSON.stringify(settings, null, 2) + '\n');
829
890
  return { settingsPath: path, installed: reconciled.changed };
830
891
  }
892
+ /**
893
+ * I3: whether a real outage (the gate never answered) holds this action. Never looser than master,
894
+ * which allowed every action in an outage. A shell command is read by the seatbelt mapper: held for an
895
+ * S0-S5/S-x hit or a program on the outage list (git push, publish, deploy, remote delete), allowed
896
+ * when the mapper read all of it and found neither (words in a message, a path or a comment never hold
897
+ * it). Anything else, or a mapper that could not finish, keeps `classifiedHighRisk` (F2).
898
+ */
899
+ function outageHolds(classifiedHighRisk, read) {
900
+ let reading = { shell: false, risk: 'unknown' };
901
+ try {
902
+ reading = read();
903
+ }
904
+ catch { /* unknown: the classifier's answer */ }
905
+ if (reading.risk === 'high')
906
+ return true;
907
+ if (reading.shell && reading.risk === 'not_high')
908
+ return false;
909
+ return classifiedHighRisk;
910
+ }
911
+ function seatbeltEvent(source, rawEvent) {
912
+ const raw = asRecord(rawEvent) || {};
913
+ const event = source;
914
+ return {
915
+ tool_name: source.tool_name, tool_input: source.tool_input, cwd: event.cwd ?? raw.cwd, truncated: event.input_truncated === true,
916
+ session: source.session_id || source.conversation_id || source.task_id || undefined,
917
+ };
918
+ }
831
919
  async function runPreActionHookCommand(input) {
920
+ // Account mode: the S0-S5 floor in watch runs after the answer is written, for the record only.
921
+ const watch = {};
922
+ try {
923
+ await runPreActionHook(input, watch);
924
+ }
925
+ finally {
926
+ if (watch.harness && watch.event) {
927
+ try {
928
+ require('./seatbelt/hook').accountFloor(watch.harness, watch.event, (0, host_approval_1.preToolDeadline)((0, host_approval_1.approvalHostFor)(watch.harness)));
929
+ }
930
+ catch { /* watch never changes the answer */ }
931
+ }
932
+ }
933
+ }
934
+ async function runPreActionHook(input, watch) {
832
935
  if (process.env.MARROW_AUTO_HOOK === 'false')
833
936
  return;
834
937
  const identity = (0, hook_contract_1.resolveNativeHookIdentity)(process.argv[2]);
835
938
  let event = input;
939
+ let rawEvent = input;
836
940
  let inputFailure = null;
837
941
  if (event === undefined) {
838
942
  try {
@@ -842,7 +946,8 @@ async function runPreActionHookCommand(input) {
842
946
  }
843
947
  else {
844
948
  const raw = stdin.raw.trim();
845
- event = raw ? (0, hook_contract_1.normalizeHookEventPayload)(JSON.parse(raw)) : {};
949
+ rawEvent = raw ? JSON.parse(raw) : {};
950
+ event = (0, hook_contract_1.normalizeHookEventPayload)(rawEvent);
846
951
  }
847
952
  }
848
953
  catch {
@@ -865,7 +970,7 @@ async function runPreActionHookCommand(input) {
865
970
  emitDecision({ runtime: null, permit: null, protectedRisk: true, enforcementError: inputFailure || 'Marrow could not classify this mutation-capable tool request.' }, identity.harness);
866
971
  return;
867
972
  }
868
- if ((0, hook_tool_policy_1.isOfficialMarrowMcpEvent)(source)) {
973
+ if ((0, own_server_identity_1.isOfficialMarrowMcpEventOnHost)(identity.harness, source, rawEvent)) {
869
974
  if (identity.harness === 'windsurf') {
870
975
  process.exitCode = 0;
871
976
  return;
@@ -876,7 +981,7 @@ async function runPreActionHookCommand(input) {
876
981
  : {}));
877
982
  return;
878
983
  }
879
- const classified = classifyTool(source);
984
+ const classified = classifyTool(source, { ownUnproven: (0, hook_tool_policy_1.isOfficialMarrowMcpEvent)(source) });
880
985
  let localControl;
881
986
  try {
882
987
  localControl = (0, control_state_1.readLocalControlState)();
@@ -962,6 +1067,55 @@ async function runPreActionHookCommand(input) {
962
1067
  emitLoopGuardDenial(identity.harness, loopDecision.reason || `Marrow local loop guard blocked this unchanged repeat. Receipt: ${loopDecision.receipt}.`);
963
1068
  return;
964
1069
  }
1070
+ // No usable key: the seatbelt decides first (spec §1.1), before the read-only pass so S4 reads stop.
1071
+ // `seatbelt` answers here with no network call; `claimed_no_key` stops S0-S5 here and keeps the holds below.
1072
+ // Whether a keyless hold may say "linked to an account" (only with account evidence, never for a mode fallback).
1073
+ let keylessAccountLinked = false;
1074
+ // The keyless hold text when the seatbelt does not answer: master's no-key text in seatbelt mode (a
1075
+ // tool the mapper does not recognise, audit H1), the claimed_no_key texts otherwise.
1076
+ let keylessSeatbeltMode = false;
1077
+ if (!resolved.apiKey) {
1078
+ let keyless;
1079
+ try {
1080
+ keyless = require('./seatbelt/hook').keylessSeatbelt(identity.harness, seatbeltEvent(source, rawEvent));
1081
+ }
1082
+ catch {
1083
+ keyless = { mode: 'claimed_no_key', stopText: SEATBELT_FAILED_TEXT };
1084
+ }
1085
+ keylessAccountLinked = keyless.accountLinked === true;
1086
+ keylessSeatbeltMode = keyless.mode === 'seatbelt';
1087
+ if (keyless.stopText) {
1088
+ emitLoopGuardDenial(identity.harness, keyless.stopText);
1089
+ return;
1090
+ }
1091
+ // A tool the mapper does not recognise never gets the seatbelt pass: master's answer below.
1092
+ if (keyless.mode === 'seatbelt' && keyless.recognized === true) {
1093
+ // Never an explicit allow for an action 3.9.103 held: the host's own approval step decides.
1094
+ const answer = seatbeltPassOutput(identity.harness, source.hook_event_name, !classified.readOnly && enforcementRequired);
1095
+ if (answer === null)
1096
+ process.exitCode = 0;
1097
+ else
1098
+ process.stdout.write(JSON.stringify(answer));
1099
+ return;
1100
+ }
1101
+ }
1102
+ else {
1103
+ watch.harness = identity.harness;
1104
+ watch.event = seatbeltEvent(source, rawEvent);
1105
+ let floor = null;
1106
+ try {
1107
+ floor = require('./seatbelt/hook');
1108
+ }
1109
+ catch { /* watch only: a missing module never changes the account flow */ }
1110
+ if (floor && floor.SEATBELT_ROLLOUT.account_floor === 'on') {
1111
+ watch.harness = undefined;
1112
+ const stopText = floor.accountFloor(identity.harness, watch.event, (0, host_approval_1.preToolDeadline)((0, host_approval_1.approvalHostFor)(identity.harness)));
1113
+ if (stopText) {
1114
+ emitLoopGuardDenial(identity.harness, stopText);
1115
+ return;
1116
+ }
1117
+ }
1118
+ }
965
1119
  if (classified.readOnly) {
966
1120
  const allow = localControlAllowOutput(identity.harness);
967
1121
  if (allow === null)
@@ -988,7 +1142,7 @@ async function runPreActionHookCommand(input) {
988
1142
  runtime: null,
989
1143
  permit: null,
990
1144
  protectedRisk: enforcementRequired,
991
- enforcementError: exports.NO_KEY_TEXT,
1145
+ enforcementError: keylessSeatbeltMode ? exports.NO_KEY_TEXT : keylessAccountLinked ? exports.CLAIMED_NO_KEY_TEXT : exports.SETUP_UNCONFIRMED_TEXT,
992
1146
  }, identity.harness);
993
1147
  return;
994
1148
  }
@@ -1017,7 +1171,9 @@ async function runPreActionHookCommand(input) {
1017
1171
  }
1018
1172
  // The same action retried after a hold that waited for approval: its status decides.
1019
1173
  const dialogAvailable = holdContext.host === 'claude-code' && ownerApprovalPrompt(identity.harness, source).available;
1020
- const waited = await (0, host_approval_1.resumeWaitingHold)(holdContext, { correlation, toolUseId, generationId, dialogAvailable }).catch(() => null);
1174
+ // The exact target (not only the correlation) binds a hold's approval to this call.
1175
+ const target = (0, hook_contract_1.exactHookTarget)(source);
1176
+ const waited = await (0, host_approval_1.resumeWaitingHold)(holdContext, { correlation, target, toolUseId, generationId, dialogAvailable }).catch(() => null);
1021
1177
  if (waited?.kind === 'allow') {
1022
1178
  emitHookOutput(identity.harness, approvedHoldHookOutput(identity.harness, waited.contextText));
1023
1179
  await afterAnswer(holdContext, {});
@@ -1056,6 +1212,11 @@ async function runPreActionHookCommand(input) {
1056
1212
  });
1057
1213
  // The decision and gate receipt this control path holds, so a denial can close them.
1058
1214
  const held = { decisionId: null, gateReceiptId: null };
1215
+ // Set once the runtime gate has answered. A later network or 5xx failure (decision, permit issue
1216
+ // or verify) on a high-risk action is a reached answer that no permit can be had: a denial, never
1217
+ // "offline, allowed". Routine actions keep the outage policy (failure_mode low_risk_behavior).
1218
+ const reach = { gateAnswered: false };
1219
+ const highRisk = classified.protected || classified.risk === 'high';
1059
1220
  const control = async (signal) => {
1060
1221
  const runtime = await (0, index_1.marrowAgentRuntime)(resolved.apiKey, baseUrl, {
1061
1222
  action: classified.action,
@@ -1070,6 +1231,7 @@ async function runPreActionHookCommand(input) {
1070
1231
  // non-durable fast_gate receipt can never back an action permit.
1071
1232
  ...(enforcementRequired ? { risk_level: classified.risk } : {}),
1072
1233
  }, sessionId, agentId, signal);
1234
+ reach.gateAnswered = true;
1073
1235
  const heldIds = [...new Set([runtime.decision_id, runtime.completion_contract?.decision_id, runtime.runtime_authorization?.decision_id]
1074
1236
  .filter((value) => typeof value === 'string' && SAFE_DECISION_ID.test(value)))];
1075
1237
  held.decisionId = heldIds.length === 1 ? heldIds[0] : null;
@@ -1171,7 +1333,7 @@ async function runPreActionHookCommand(input) {
1171
1333
  timedOut: true,
1172
1334
  enforcementError: hook_contract_1.MARROW_OUTAGE_WARNING,
1173
1335
  }
1174
- : isMarrowControlOutage(error)
1336
+ : isMarrowControlOutage(error) && !(reach.gateAnswered && highRisk)
1175
1337
  ? {
1176
1338
  runtime: null,
1177
1339
  permit: null,
@@ -1179,13 +1341,21 @@ async function runPreActionHookCommand(input) {
1179
1341
  outage: true,
1180
1342
  enforcementError: hook_contract_1.MARROW_OUTAGE_WARNING,
1181
1343
  }
1182
- : {
1183
- runtime: null,
1184
- permit: null,
1185
- protectedRisk: enforcementRequired,
1186
- enforcementError: controlRejectionMessage(error, agentId),
1187
- ...(controlFailureKind(error) ? { failure: controlFailureKind(error) } : {}),
1188
- })),
1344
+ : isMarrowControlOutage(error)
1345
+ ? {
1346
+ runtime: null,
1347
+ permit: null,
1348
+ protectedRisk: enforcementRequired,
1349
+ enforcementError: controlRejectionMessage(error, agentId, 'unavailable'),
1350
+ failure: 'unavailable',
1351
+ }
1352
+ : {
1353
+ runtime: null,
1354
+ permit: null,
1355
+ protectedRisk: enforcementRequired,
1356
+ enforcementError: controlRejectionMessage(error, agentId),
1357
+ ...(controlFailureKind(error) ? { failure: controlFailureKind(error) } : {}),
1358
+ })),
1189
1359
  lifecycle,
1190
1360
  ]);
1191
1361
  const claudePrompt = ownerApprovalPrompt(identity.harness, source);
@@ -1203,12 +1373,17 @@ async function runPreActionHookCommand(input) {
1203
1373
  }
1204
1374
  if (result.outage) {
1205
1375
  // Marrow cannot be reached. An action in a category the account owner
1206
- // protects stays held on every host; everything else keeps the outage policy.
1376
+ // protects stays held on every host; a high-risk action fails closed (high-risk
1377
+ // work needs a fresh gate); routine actions keep the outage policy.
1207
1378
  const protectedNow = (0, host_approval_1.protectedAmong)(holdContext, localApprovalCategories(classified));
1208
1379
  if (protectedNow.length) {
1209
1380
  emitHeldPlan(identity.harness, { kind: 'deny', agentText: host_approval_1.HELD_UNREACHABLE_TEXT, userText: host_approval_1.HELD_UNREACHABLE_TEXT, code: false });
1210
1381
  return;
1211
1382
  }
1383
+ if (outageHolds(highRisk, () => require('./seatbelt/hook').outageRisk(seatbeltEvent(source, rawEvent), holdContext.deadlineAt ?? Number.POSITIVE_INFINITY))) {
1384
+ emitHeldPlan(identity.harness, { kind: 'deny', agentText: exports.HELD_OFFLINE_TEXT, userText: exports.HELD_OFFLINE_TEXT, code: false });
1385
+ return;
1386
+ }
1212
1387
  }
1213
1388
  const verdict = result.outage ? null : runtimeGateVerdict(result.runtime);
1214
1389
  const guidance = verdict?.kind === 'review' ? (0, runtime_contract_1.ordinaryApprovalGuidance)(result.runtime) : null;
@@ -1258,6 +1433,7 @@ async function runPreActionHookCommand(input) {
1258
1433
  const hold = plan.kind === 'deny' && plan.retryFresh ? null : (0, host_approval_1.rememberHold)(holdContext, {
1259
1434
  guidance: guidance || (0, host_approval_1.arbitrationHoldGuidance)(arbitration),
1260
1435
  correlation,
1436
+ target,
1261
1437
  toolUseId,
1262
1438
  generationId,
1263
1439
  toolName: String(source.tool_name || 'tool'),