@getmarrow/mcp 3.9.61 → 3.9.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -104,7 +104,18 @@ npx -y --package=@getmarrow/mcp@latest marrow-mcp ping
104
104
 
105
105
  Detection and notification are automatic. After explicit installer activation, the local controller may restore only Marrow-managed hooks/configuration. Package upgrades, owner policy, credentials, and unrelated configuration remain explicit and subject to the operator's normal change policy.
106
106
 
107
- ## What's New in v3.9.61
107
+ ## What's New in v3.9.62
108
+
109
+ v3.9.62 integrates four model-neutral reliability and capability contracts:
110
+
111
+ - standalone `marrow_status` uses the bounded compact API contract and can return a fresh, owner-only last-known status projection without treating it as a live gate or authorization;
112
+ - ordinary runtime responses expose typed `runtime_authorization` backed by the authoritative gate receipt and omit `decision_id` unless the server actually created a decision;
113
+ - `spool-status` and `drain-spool` report the active credential namespace separately from isolated legacy debt, and a clear active namespace exits successfully without replaying, merging, editing, or deleting old-key files;
114
+ - initialize, prompt, setup, and tool responses qualify coverage by `host_capability`: MCP tools are on demand, while native hooks, an owned SDK process, a governed runner, or a custom event adapter cover only the scope proved by observed Marrow receipts.
115
+
116
+ The default surface remains six tools and the prompt remains named `marrow-always-on`. Host and model labels are display-only and never change auth, tenant, plan, policy, proof, schema, or API behavior.
117
+
118
+ ## Previous: v3.9.61
108
119
 
109
120
  v3.9.61 keeps an authoritative proof-pack rejection distinct from a control-path outage:
110
121
 
@@ -125,7 +136,7 @@ v3.9.60 restores the complete control-and-proof loop for ordinary MCP clients:
125
136
  - a plan-gated handoff is reported as unavailable for the current plan, not as an API or authentication outage;
126
137
  - the release canary runs with the customer's default client deadlines instead of silently overriding them.
127
138
 
128
- MCP-only hosts, including Grok today, receive Marrow's control instructions but cannot expose native always-on lifecycle hooks unless the host implements them. Marrow reports that capability boundary explicitly instead of claiming hook coverage it cannot observe.
139
+ MCP-only hosts receive the same model-neutral control instructions and six-tool default surface, but MCP transport alone is on demand. A host or model label never changes that coverage contract. Marrow reports passive coverage only from observed lifecycle receipts, not from configuration detection.
129
140
 
130
141
  ## Previous: v3.9.59
131
142
 
@@ -138,7 +149,7 @@ v3.9.59 makes the six-tool control path reliable and honest across ordinary edge
138
149
  - a first-session outage returns a clearly labeled local safety brief, and infrastructure failures are never mislabeled as policy denials;
139
150
  - timeout errors return a concrete retry delay instead of an unresolved placeholder;
140
151
  - Cloudflare edge denials are separated from key-scope or Marrow policy rejections;
141
- - MCP initialization carries the always-on control/proof instructions even when a client does not request the optional prompt template;
152
+ - MCP initialization carries the capability-qualified control/proof instructions even when a client does not request the optional prompt template;
142
153
  - update, launch, setup, spool, and ping commands use the unambiguous `npx --package ... marrow-mcp` form;
143
154
  - explicit spool drains tolerate slow edge delivery without extending passive hook latency.
144
155
 
@@ -260,6 +271,8 @@ With installed native hooks, protected tool calls follow this automatically. For
260
271
  4. Perform the action only when its gate allows it. For a hard external choke point, run the command through `npx @getmarrow/install run`; it verifies the signed action permit immediately before execution.
261
272
  5. Call `marrow_commit` with that `decision_id`, the outcome, gate receipt, and required proof.
262
273
 
274
+ `marrow_agent_runtime` returns `runtime_authorization` with the authoritative gate receipt. Ordinary runtime checks do not create a decision, so they omit `decision_id`; call `marrow_think` (or use `marrow_auto`) when a decision must be created and closed. An arbitration runtime that actually creates a decision returns that server-created `decision_id`. This contract is identical across MCP-compatible hosts and SDK-owned runtimes.
275
+
263
276
  Example pre-action request:
264
277
 
265
278
  ```json
@@ -354,9 +367,21 @@ it does not run models, retain prompts, or infer a winner from labels.
354
367
 
355
368
  ## Passive Use
356
369
 
357
- `npx -y --package=@getmarrow/mcp@latest marrow-mcp setup` installs supported prompt, tool-result, and session-stop hooks so the agent can receive before-action context, record meaningful tool outcomes, and keep unfinished closure visible without the owner repeatedly prompting it to use Marrow.
370
+ `npx -y --package=@getmarrow/mcp@latest marrow-mcp setup` configures supported prompt, pre-action, tool-result, and session-stop hooks. Configuration is not proof that the host invoked them. Marrow reports native passive coverage only after it observes the required prompt, pre-action, action-result, and session-end receipts.
371
+
372
+ ### Capability and coverage contract
373
+
374
+ | Integration mode | Coverage Marrow can claim |
375
+ | --- | --- |
376
+ | MCP tools-only | On demand; covers only explicit MCP tool calls |
377
+ | Verified native hooks | Passive only for the lifecycle stages backed by observed hook receipts |
378
+ | `createPassiveRuntime().install()` | Only the owned Node process, and only while that runtime is installed and running |
379
+ | Governed runner | Only the command launched through the wrapper |
380
+ | Custom host | Requires a bounded event adapter; covers only the lifecycle events whose receipts Marrow observes |
381
+
382
+ This contract is model-neutral. A model name, host header, config file, installed hook entry, or successful MCP handshake does not certify passive coverage. Only observed Marrow receipts do. An unknown MCP host therefore gets the generic `mcp-client` identity and the same on-demand tools, schemas, and API semantics as a named host.
358
383
 
359
- The hooks send compact classifications and lifecycle receipts. They do not need raw prompts, completions, command output, tool output, or credentials. A completed tool or session does not automatically become a successful business outcome; explicit success/failure closure is required.
384
+ When invoked by a supported host, the configured hooks send compact classifications and lifecycle receipts. They do not need raw prompts, completions, command output, tool output, or credentials. A completed tool or session does not automatically become a successful business outcome; explicit success/failure closure is required.
360
385
 
361
386
  Transient lifecycle receipts use a bounded owner-only spool and are retried with stable event IDs. Operators can inspect and drain it without exposing event content:
362
387
 
@@ -365,7 +390,7 @@ npx -y --package=@getmarrow/mcp@latest marrow-mcp spool-status
365
390
  npx -y --package=@getmarrow/mcp@latest marrow-mcp drain-spool
366
391
  ```
367
392
 
368
- The output contains only state, exact pending/failed counts, oldest receipt timestamps, capacity, and an exact fix. Terminal validation/authentication failures and exhausted retries remain explicit durable failures rather than cycling indefinitely.
393
+ The output contains only state, bounded pending/failed counts, oldest receipt timestamps, capacity, and an exact fix. A drain applies only to the active credential-and-agent namespace: isolated legacy namespace debt is reported separately and never changes a successful active-namespace drain into a failure. Legacy files are never replayed, merged, deleted, or attributed to the active identity. Restore the exact original identity to drain one, or preserve the selected file unchanged in a separate owner-only quarantine directory when that identity is unavailable. Terminal validation/authentication failures and exhausted retries in the active namespace remain explicit durable failures rather than cycling indefinitely.
369
394
 
370
395
  Check the installed runtime:
371
396
 
package/dist/cli.js CHANGED
@@ -17,6 +17,7 @@ const hook_session_1 = require("./hook-session");
17
17
  const hook_pre_action_1 = require("./hook-pre-action");
18
18
  const env_1 = require("./env");
19
19
  const lifecycle_spool_1 = require("./lifecycle-spool");
20
+ const spool_command_1 = require("./spool-command");
20
21
  const guidance_cache_1 = require("./guidance-cache");
21
22
  const request_reliability_1 = require("./request-reliability");
22
23
  const hook_contract_1 = require("./hook-contract");
@@ -24,6 +25,8 @@ const ping_state_1 = require("./ping-state");
24
25
  const control_path_state_1 = require("./control-path-state");
25
26
  const redact_1 = require("./redact");
26
27
  const runtime_contract_1 = require("./runtime-contract");
28
+ const status_cache_1 = require("./status-cache");
29
+ const host_capability_1 = require("./host-capability");
27
30
  // Parse CLI args
28
31
  function parseArgs() {
29
32
  const args = process.argv.slice(2);
@@ -127,17 +130,10 @@ async function runSpoolCommand(drain) {
127
130
  const status = drain
128
131
  ? await (0, lifecycle_spool_1.drainLifecycleSpool)({ apiKey, baseUrl, agentId: resolved.agentId || undefined })
129
132
  : (0, lifecycle_spool_1.lifecycleSpoolStatus)({ apiKey, agentId: resolved.agentId || undefined });
130
- const namespaceAttention = status.other_namespaces.state === 'attention_required';
131
- process.stdout.write(`${JSON.stringify({
132
- ok: drain
133
- ? status.state === 'clear' && !namespaceAttention
134
- : status.state !== 'attention_required' && !namespaceAttention,
135
- lifecycle_spool: status,
136
- }, null, 2)}\n`);
137
- if (status.state === 'attention_required' || namespaceAttention)
138
- process.exitCode = 2;
139
- else if (drain && status.state !== 'clear')
140
- process.exitCode = 1;
133
+ const outcome = (0, spool_command_1.lifecycleSpoolCommandOutcome)(status, drain);
134
+ process.stdout.write(`${JSON.stringify(outcome.output, null, 2)}\n`);
135
+ if (outcome.exitCode !== 0)
136
+ process.exitCode = outcome.exitCode;
141
137
  }
142
138
  // ─── Setup command: inject Marrow instructions into CLAUDE.md ───
143
139
  function runSetup() {
@@ -148,10 +144,10 @@ function runSetup() {
148
144
  const marrowInstructions = `${MARROW_BLOCK_START}
149
145
  ## Marrow — Agent Control and Proof (auto-injected by @getmarrow/mcp setup)
150
146
 
151
- You have Marrow MCP connected. Use it on EVERY session automatically:
147
+ Use Marrow according to the capability evidence returned by the MCP server:
152
148
 
153
- 1. **Passive by default:** Marrow hooks record compact lifecycle receipts and inject relevant policy, warnings, lessons, and risky-work decision briefs into your prompt context.
154
- 2. **Before risky actions:** Respect the returned \`allow\`, \`warn\`, \`review_required\`, or \`block\` decision and its proof contract. Call \`marrow_agent_runtime\` explicitly when the passive hook cannot cover the action.
149
+ 1. **MCP baseline is on demand:** MCP transport exposes tools; it does not provide passive hooks. Call Marrow tools explicitly unless observed receipts verify another capability mode.
150
+ 2. **Before risky actions:** Respect the returned \`allow\`, \`warn\`, \`review_required\`, or \`block\` decision and its proof contract. Call \`marrow_agent_runtime\` explicitly when verified passive coverage cannot cover the action.
155
151
  3. **After meaningful work:** Record the real success or failure with \`marrow_commit\` or \`marrow_auto\`. A tool exit or session end is not proof that the business outcome succeeded.
156
152
  4. **Unfinished work:** Leave pending outcomes visible. Do not invent success to clear a closure item.
157
153
  5. **To explain an intervention:** Use \`marrow_decision_trace\`, then relay its \`intervention_receipt\` in one factual sentence when Marrow blocked, warned, or required review. Stay quiet for routine low-risk work.
@@ -160,6 +156,8 @@ You have Marrow MCP connected. Use it on EVERY session automatically:
160
156
 
161
157
  Do not skip the gate or outcome. Marrow's value comes from controlling the action before execution and preserving evidence afterward.
162
158
 
159
+ Capability boundaries: verified native hooks cover only their observed lifecycle; \`createPassiveRuntime().install()\` covers only its owned Node process while installed; a governed runner covers only its wrapped command; and a custom host needs a bounded event adapter. A model name, host label, or installed configuration is not proof of coverage. Only observed Marrow receipts certify it.
160
+
163
161
  For one-shot logging: \`marrow_auto({ action: "did X", outcome: "result Y", success: true })\` — one call, done.
164
162
  ${MARROW_BLOCK_END}`;
165
163
  let dir = process.cwd();
@@ -203,37 +201,37 @@ ${MARROW_BLOCK_END}`;
203
201
  }
204
202
  const hookInstall = (0, hook_1.installPostToolUseHook)(process.cwd());
205
203
  if (hookInstall.installed) {
206
- process.stdout.write('Installed PostToolUse hook — your agent\'s tool calls now auto-log to Marrow.\n');
204
+ process.stdout.write('Configured PostToolUse hook. Coverage remains unverified until Marrow observes action-result receipts.\n');
207
205
  }
208
206
  else {
209
- process.stdout.write('PostToolUse hook already installed — agent tool calls auto-log to Marrow.\n');
207
+ process.stdout.write('PostToolUse hook configuration is present. Coverage remains unverified until Marrow observes action-result receipts.\n');
210
208
  }
211
209
  const contextHookInstall = (0, hook_context_1.installUserPromptSubmitHook)(process.cwd());
212
210
  if (contextHookInstall.installed) {
213
- process.stdout.write('Installed UserPromptSubmit hook — Marrow will inject relevant context and passive decision briefs into your prompts automatically.\n');
211
+ process.stdout.write('Configured UserPromptSubmit hook. Passive prompt coverage remains unverified until Marrow observes prompt receipts.\n');
214
212
  }
215
213
  else {
216
- process.stdout.write('UserPromptSubmit hook already installed — Marrow context and passive decision briefs are injected on matching prompts.\n');
214
+ process.stdout.write('UserPromptSubmit hook configuration is present. Passive prompt coverage remains unverified until Marrow observes prompt receipts.\n');
217
215
  }
218
216
  const preActionHookInstall = (0, hook_pre_action_1.installPreActionHook)(process.cwd());
219
217
  if (preActionHookInstall.installed) {
220
- process.stdout.write('Installed PreToolUse hook — Marrow now checks each matched action before execution.\n');
218
+ process.stdout.write('Configured PreToolUse hook. Pre-action coverage remains unverified until Marrow observes pre-action receipts.\n');
221
219
  }
222
220
  else {
223
- process.stdout.write('PreToolUse hook already installed — matched actions are checked before execution.\n');
221
+ process.stdout.write('PreToolUse hook configuration is present. Pre-action coverage remains unverified until Marrow observes pre-action receipts.\n');
224
222
  }
225
223
  const sessionHookInstall = (0, hook_session_1.installSessionEndHook)(process.cwd());
226
224
  if (sessionHookInstall.installed) {
227
- process.stdout.write('Installed Stop hook — unfinished outcomes remain visible for reconciliation.\n');
225
+ process.stdout.write('Configured Stop hook. Session-end coverage remains unverified until Marrow observes session-end receipts.\n');
228
226
  }
229
227
  else {
230
- process.stdout.write('Stop hook already installed — unfinished outcomes remain visible for reconciliation.\n');
228
+ process.stdout.write('Stop hook configuration is present. Session-end coverage remains unverified until Marrow observes session-end receipts.\n');
231
229
  }
232
230
  process.stdout.write(`Hook settings: ${hookInstall.settingsPath}\n`);
233
231
  process.stdout.write('Set MARROW_AUTO_HOOK=false to disable passive hooks.\n');
234
232
  process.stdout.write('Set MARROW_PASSIVE_BRIEF=false to disable automatic decision briefs, or MARROW_PASSIVE_BRIEF=always to brief every prompt.\n');
235
233
  process.stdout.write('Set MARROW_HOOK_DEBUG=true for write-side hook diagnostics, or MARROW_CONTEXT_HOOK_DEBUG=true for prompt-context diagnostics.\n');
236
- process.stdout.write('Your agent will now use Marrow automatically — both writing decisions AND reading past intelligence — in every session.\n');
234
+ process.stdout.write('Setup completed. MCP tools remain on demand; passive coverage is reported only after Marrow observes the required hook receipts.\n');
237
235
  process.exit(0);
238
236
  }
239
237
  const cliArgs = parseArgs();
@@ -489,22 +487,9 @@ if (process.argv[2] !== 'keys') {
489
487
  return result;
490
488
  }
491
489
  function mcpHostCapability() {
492
- const raw = String(process.env.MARROW_CLIENT || process.env.MARROW_HARNESS || '').trim().toLowerCase();
493
- const host = ['grok', 'claude-code', 'codex', 'cursor', 'windsurf', 'gemini', 'kimi', 'qwen', 'deepseek']
494
- .includes(raw) ? raw : 'mcp-client';
495
- return {
496
- transport: 'mcp_stdio',
497
- host,
498
- tools_available: true,
499
- tool_invocation: 'on_demand',
500
- passive_hooks: {
501
- provided_by_mcp_transport: false,
502
- external_host_hook_state: 'unverified',
503
- observed_by_this_process: false,
504
- },
505
- always_on_state: 'host_dependent',
506
- exact_next_action: 'Use the Marrow MCP tools on demand. Claim passive coverage only when the host separately configures trusted hooks and Marrow receives their lifecycle receipts.',
507
- };
490
+ return (0, host_capability_1.resolveHostCapability)({
491
+ hostLabel: process.env.MARROW_CLIENT || process.env.MARROW_HARNESS || process.env.MARROW_AGENT_CLIENT,
492
+ });
508
493
  }
509
494
  function clientOperationalPayload(toolName, value) {
510
495
  const payload = value && typeof value === 'object' && !Array.isArray(value)
@@ -575,6 +560,23 @@ if (process.argv[2] !== 'keys') {
575
560
  }
576
561
  catch { /* owner-only cache is best effort */ }
577
562
  }
563
+ function storeLastKnownStatus(status, source) {
564
+ try {
565
+ (0, status_cache_1.writeStatusCache)({
566
+ apiKey: API_KEY,
567
+ baseUrl: BASE_URL,
568
+ agentId: FLEET_AGENT_ID,
569
+ status,
570
+ source,
571
+ });
572
+ }
573
+ catch { /* owner-only cache is best effort */ }
574
+ }
575
+ function refreshStatusInBackground() {
576
+ void (0, index_1.marrowStatus)(API_KEY, BASE_URL, SESSION_ID, FLEET_AGENT_ID)
577
+ .then((status) => storeLastKnownStatus(status, 'status'))
578
+ .catch(() => undefined);
579
+ }
578
580
  // [FIX #6 & #7] Safe JSON response helper for memory API functions
579
581
  async function safeMemoryResponse(res) {
580
582
  if (!res.ok) {
@@ -820,7 +822,7 @@ if (process.argv[2] !== 'keys') {
820
822
  type: 'object',
821
823
  description: 'Optional required proof pack for gated work: summary, checks, outcome, blockers, commits_prs_shas, rollback_target, handoff_result_file, deployment_and_smoke.',
822
824
  },
823
- gate_receipt_id: { type: 'string', description: 'Receipt id from marrow_agent_runtime.gate_receipt.id for risky work.' },
825
+ gate_receipt_id: { type: 'string', description: 'Canonical receipt id from marrow_agent_runtime.runtime_authorization.id for risky work.' },
824
826
  arbitration_receipt_id: { type: 'string', description: 'Required for arbitrated work: use marrow_arbitrate.arbitration.receipt_id from the same runtime response.' },
825
827
  owner_approval_receipt_id: { type: 'string', description: 'Single-use owner approval receipt issued by authenticated dashboard review for review_required arbitration.' },
826
828
  action: { type: 'string', description: 'Optional original action. If provided and gate_receipt_id is omitted, MCP can fetch a matching runtime gate receipt before commit.' },
@@ -1233,6 +1235,8 @@ if (process.argv[2] !== 'keys') {
1233
1235
  name: 'marrow_agent_runtime',
1234
1236
  description: 'One-call agent-native Marrow loop. Returns passive status, decision brief, risk gate, relevant lessons, ' +
1235
1237
  'template suggestion, required proof pack, before-you-act instruction, and exact next action. ' +
1238
+ 'Its runtime_authorization is the authoritative gate receipt; it returns decision_id only when runtime actually creates a decision. ' +
1239
+ 'Use marrow_auto or marrow_think to create the decision_id required for outcome closure. ' +
1236
1240
  'Use this before meaningful work when you want Marrow to guide the whole action in one call.',
1237
1241
  inputSchema: {
1238
1242
  type: 'object',
@@ -1687,13 +1691,15 @@ if (process.argv[2] !== 'keys') {
1687
1691
  try {
1688
1692
  if (method === 'initialize') {
1689
1693
  initialized = true;
1694
+ const hostCapability = mcpHostCapability();
1690
1695
  success(id, {
1691
1696
  protocolVersion: '2024-11-05',
1692
1697
  capabilities: { tools: {}, prompts: {} },
1693
1698
  serverInfo: { name: 'marrow', version: hook_contract_1.MCP_ADAPTER_VERSION },
1694
1699
  ...(AUTO_ENROLL ? {
1695
- instructions: 'Use marrow_agent_runtime before consequential actions, obey fresh allow/warn/review_required/block results, use marrow_ask for relevant prior lessons, and close meaningful outcomes with marrow_commit. Infrastructure failures are not policy denials; continue only low-risk reversible work from the returned outage-safe brief, and require a fresh gate for high-risk work. MCP tools are on demand; do not claim passive or prompt-injection coverage unless the host separately configured trusted hooks and Marrow observed their receipts.',
1700
+ instructions: `Use marrow_agent_runtime before consequential actions, obey fresh allow/warn/review_required/block results, use marrow_ask for relevant prior lessons, and close meaningful outcomes with marrow_commit. Infrastructure failures are not policy denials; continue only low-risk reversible work from the returned outage-safe brief, and require a fresh gate for high-risk work. ${(0, host_capability_1.hostCapabilityInstructions)(hostCapability)}`,
1696
1701
  } : {}),
1702
+ _meta: { host_capability: hostCapability },
1697
1703
  });
1698
1704
  // Auto-enroll: emit enrollment notification on connection
1699
1705
  if (AUTO_ENROLL) {
@@ -1705,9 +1711,10 @@ if (process.argv[2] !== 'keys') {
1705
1711
  logger: 'marrow',
1706
1712
  data: {
1707
1713
  type: 'auto_enroll',
1708
- message: 'Marrow auto-enroll active. Call marrow_orient FIRST, then marrow_think before acting, marrow_commit after. Or use marrow_auto / marrow_run for one-call logging.',
1714
+ message: `Marrow operating contract available. MCP tools are on demand. ${hostCapability.exact_next_action}`,
1709
1715
  agentId: FLEET_AGENT_ID,
1710
1716
  client_update: (0, request_reliability_1.localClientUpdate)(),
1717
+ host_capability: hostCapability,
1711
1718
  },
1712
1719
  },
1713
1720
  });
@@ -1720,8 +1727,9 @@ if (process.argv[2] !== 'keys') {
1720
1727
  prompts: [
1721
1728
  {
1722
1729
  name: 'marrow-always-on',
1723
- description: 'Always-on Marrow control and proof loop. Applies before-action guidance, records compact lifecycle receipts, and keeps unfinished outcomes visible.',
1730
+ description: 'Marrow control and proof contract, qualified by verified host capability and observed lifecycle receipts.',
1724
1731
  arguments: [],
1732
+ _meta: { host_capability: mcpHostCapability() },
1725
1733
  },
1726
1734
  ],
1727
1735
  });
@@ -1737,8 +1745,10 @@ if (process.argv[2] !== 'keys') {
1737
1745
  error(id, -32602, 'Unknown prompt');
1738
1746
  return;
1739
1747
  }
1748
+ const hostCapability = mcpHostCapability();
1740
1749
  success(id, {
1741
- description: 'Always-on Marrow control and proof loop — automatic, zero-config',
1750
+ description: 'Marrow control and proof contract — coverage is capability-qualified and receipt-verified',
1751
+ _meta: { host_capability: hostCapability },
1742
1752
  messages: [
1743
1753
  {
1744
1754
  role: 'user',
@@ -1746,14 +1756,16 @@ if (process.argv[2] !== 'keys') {
1746
1756
  type: 'text',
1747
1757
  text: `You have Marrow — the agent control and proof layer around this workflow.
1748
1758
 
1749
- ## Passive operating contract
1759
+ ## Capability-qualified operating contract
1760
+
1761
+ ${(0, host_capability_1.hostCapabilityInstructions)(hostCapability)}
1750
1762
 
1751
- Installed hooks handle supported lifecycle capture automatically:
1752
- - UserPromptSubmit requests relevant policy, warnings, lessons, and a decision brief before risky work.
1753
- - PostToolUse records compact tool success or failure receipts.
1754
- - Stop keeps unfinished outcomes visible instead of silently treating session exit as success.
1763
+ When verified by observed receipts, native hooks can cover these bounded lifecycle stages:
1764
+ - UserPromptSubmit can request relevant policy, warnings, lessons, and a decision brief before risky work.
1765
+ - PostToolUse can record compact tool success or failure receipts.
1766
+ - Stop can keep unfinished outcomes visible instead of silently treating session exit as success.
1755
1767
 
1756
- Hooks never make a blocked action safe. Before a consequential action, respect the returned allow, warn, review_required, or block decision and its required proof. Call marrow_agent_runtime explicitly when the passive hook cannot cover the action.
1768
+ Hooks never make a blocked action safe. Before a consequential action, respect the returned allow, warn, review_required, or block decision and its required proof. Call marrow_agent_runtime explicitly when verified passive coverage cannot cover the action.
1757
1769
 
1758
1770
  When runtime/status returns a client_update notice, tell the operator and use its exact update and verification commands only when local change policy permits. Never silently change packages or configuration.
1759
1771
 
@@ -1802,7 +1814,7 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
1802
1814
  `2. Use marrow_agent_runtime when a risky action needs an explicit gate.\n` +
1803
1815
  `3. Close meaningful work with marrow_commit; a tool exit alone is not outcome proof.\n` +
1804
1816
  `4. Use marrow_decision_trace to explain the prior failure, lesson, gate, proof, workflow, and outcome path.\n\n` +
1805
- `Installed hooks cover supported passive lifecycle events. Leave unknown outcomes pending instead of inventing success.\n`;
1817
+ `${(0, host_capability_1.hostCapabilityInstructions)(mcpHostCapability())}\n\nLeave unknown outcomes pending instead of inventing success.\n`;
1806
1818
  const orientText = JSON.stringify(result, null, 2);
1807
1819
  success(id, {
1808
1820
  content: [{ type: 'text', text: enrollmentText + orientText }],
@@ -1969,7 +1981,12 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
1969
1981
  }, SESSION_ID, FLEET_AGENT_ID, signal), { highRisk: true, toolName: 'marrow_auto.runtime' });
1970
1982
  storeRuntimeGuidance(runtimeGate);
1971
1983
  }
1972
- const proofCanClose = !highRisk || (0, runtime_contract_1.highRiskRuntimeCanClose)(runtimeGate, suppliedProof, args.gate_receipt_id);
1984
+ const canonicalRuntimeReceiptId = (0, runtime_contract_1.runtimeAuthorizationReceiptId)(runtimeGate);
1985
+ const suppliedRuntimeReceiptId = typeof args.gate_receipt_id === 'string'
1986
+ ? args.gate_receipt_id
1987
+ : canonicalRuntimeReceiptId;
1988
+ const proofCanClose = !highRisk
1989
+ || (0, runtime_contract_1.highRiskRuntimeCanClose)(runtimeGate, suppliedProof, suppliedRuntimeReceiptId);
1973
1990
  const acceptedSuccess = proofCanClose ? outcomeSuccess : undefined;
1974
1991
  const delivery = () => (0, index_1.marrowAuto)(API_KEY, BASE_URL, {
1975
1992
  action,
@@ -1977,9 +1994,10 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
1977
1994
  success: acceptedSuccess,
1978
1995
  type,
1979
1996
  proof: suppliedProof,
1980
- gate_receipt_id: typeof args.gate_receipt_id === 'string'
1981
- ? args.gate_receipt_id
1982
- : runtimeGate?.gate_receipt?.id,
1997
+ gate_receipt_id: suppliedRuntimeReceiptId || undefined,
1998
+ // Low-risk one-shot capture does not need a policy gate. Consequential
1999
+ // work already obtained and validated canonical runtime authorization.
2000
+ auto_gate: highRisk,
1983
2001
  }, SESSION_ID, FLEET_AGENT_ID, 8_000);
1984
2002
  let delivered = null;
1985
2003
  let deliveryFailure = null;
@@ -2060,7 +2078,20 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
2060
2078
  return;
2061
2079
  }
2062
2080
  if (toolName === 'marrow_status') {
2081
+ let cached = null;
2082
+ try {
2083
+ cached = (0, status_cache_1.readStatusCache)({ apiKey: API_KEY, baseUrl: BASE_URL, agentId: FLEET_AGENT_ID });
2084
+ }
2085
+ catch { /* owner-only cache is best effort */ }
2086
+ if (cached) {
2087
+ const startedAt = Date.now();
2088
+ refreshStatusInBackground();
2089
+ (0, control_path_state_1.recordControlPathSample)('marrow_status', Date.now() - startedAt, true);
2090
+ toolSuccess(id, clientOperationalPayload('marrow_status', (0, status_cache_1.cachedStatusPayload)(cached)));
2091
+ return;
2092
+ }
2063
2093
  const result = await withControlDeadline((signal) => (0, index_1.marrowStatus)(API_KEY, BASE_URL, SESSION_ID, FLEET_AGENT_ID, signal), { cacheAware: false, toolName: 'marrow_status' });
2094
+ storeLastKnownStatus(result, 'status');
2064
2095
  toolSuccess(id, clientOperationalPayload('marrow_status', result));
2065
2096
  return;
2066
2097
  }
@@ -2257,6 +2288,7 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
2257
2288
  };
2258
2289
  const result = await withControlDeadline((signal) => (0, index_1.marrowAgentRuntime)(API_KEY, BASE_URL, runtimeInput, SESSION_ID, FLEET_AGENT_ID, signal), { highRisk: isHighRiskAction(runtimeInput.action, runtimeInput.type), toolName: 'marrow_agent_runtime' });
2259
2290
  storeRuntimeGuidance(result);
2291
+ storeLastKnownStatus(result.status, 'runtime');
2260
2292
  toolSuccess(id, clientOperationalPayload('marrow_agent_runtime', result));
2261
2293
  return;
2262
2294
  }