@getmarrow/mcp 3.9.60 → 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.
Files changed (43) hide show
  1. package/README.md +40 -6
  2. package/dist/cli.js +94 -57
  3. package/dist/cli.js.map +1 -1
  4. package/dist/hook-context.d.ts +1 -1
  5. package/dist/hook-contract.d.ts +6 -6
  6. package/dist/hook-contract.js +1 -1
  7. package/dist/hook-pre-action.d.ts.map +1 -1
  8. package/dist/hook-pre-action.js +4 -2
  9. package/dist/hook-pre-action.js.map +1 -1
  10. package/dist/hook-session.d.ts +1 -1
  11. package/dist/hook.d.ts +1 -1
  12. package/dist/host-capability.d.ts +60 -0
  13. package/dist/host-capability.d.ts.map +1 -0
  14. package/dist/host-capability.js +118 -0
  15. package/dist/host-capability.js.map +1 -0
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +12 -7
  19. package/dist/index.js.map +1 -1
  20. package/dist/lifecycle-spool.d.ts +9 -0
  21. package/dist/lifecycle-spool.d.ts.map +1 -1
  22. package/dist/lifecycle-spool.js +94 -20
  23. package/dist/lifecycle-spool.js.map +1 -1
  24. package/dist/request-reliability.d.ts +3 -1
  25. package/dist/request-reliability.d.ts.map +1 -1
  26. package/dist/request-reliability.js +41 -11
  27. package/dist/request-reliability.js.map +1 -1
  28. package/dist/runtime-contract.d.ts +1 -0
  29. package/dist/runtime-contract.d.ts.map +1 -1
  30. package/dist/runtime-contract.js +72 -11
  31. package/dist/runtime-contract.js.map +1 -1
  32. package/dist/spool-command.d.ts +17 -0
  33. package/dist/spool-command.d.ts.map +1 -0
  34. package/dist/spool-command.js +26 -0
  35. package/dist/spool-command.js.map +1 -0
  36. package/dist/status-cache.d.ts +21 -0
  37. package/dist/status-cache.d.ts.map +1 -0
  38. package/dist/status-cache.js +177 -0
  39. package/dist/status-cache.js.map +1 -0
  40. package/dist/types.d.ts +20 -0
  41. package/dist/types.d.ts.map +1 -1
  42. package/package.json +1 -1
  43. package/server.json +2 -2
package/README.md CHANGED
@@ -104,7 +104,27 @@ 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.60
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
119
+
120
+ v3.9.61 keeps an authoritative proof-pack rejection distinct from a control-path outage:
121
+
122
+ - backend `MARROW_PROOF_PACK_INCOMPLETE` responses are reported as `validation` / `proof_required`, not infrastructure failures;
123
+ - the exact missing proof fields and backend repair instruction remain visible to the agent;
124
+ - live proof validation does not return a stale outage brief or unavailable authorization state;
125
+ - proof enforcement remains fail-closed, and successful commit behavior is unchanged.
126
+
127
+ ## Previous: v3.9.60
108
128
 
109
129
  v3.9.60 restores the complete control-and-proof loop for ordinary MCP clients:
110
130
 
@@ -116,7 +136,7 @@ v3.9.60 restores the complete control-and-proof loop for ordinary MCP clients:
116
136
  - a plan-gated handoff is reported as unavailable for the current plan, not as an API or authentication outage;
117
137
  - the release canary runs with the customer's default client deadlines instead of silently overriding them.
118
138
 
119
- 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.
120
140
 
121
141
  ## Previous: v3.9.59
122
142
 
@@ -129,7 +149,7 @@ v3.9.59 makes the six-tool control path reliable and honest across ordinary edge
129
149
  - a first-session outage returns a clearly labeled local safety brief, and infrastructure failures are never mislabeled as policy denials;
130
150
  - timeout errors return a concrete retry delay instead of an unresolved placeholder;
131
151
  - Cloudflare edge denials are separated from key-scope or Marrow policy rejections;
132
- - 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;
133
153
  - update, launch, setup, spool, and ping commands use the unambiguous `npx --package ... marrow-mcp` form;
134
154
  - explicit spool drains tolerate slow edge delivery without extending passive hook latency.
135
155
 
@@ -251,6 +271,8 @@ With installed native hooks, protected tool calls follow this automatically. For
251
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.
252
272
  5. Call `marrow_commit` with that `decision_id`, the outcome, gate receipt, and required proof.
253
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
+
254
276
  Example pre-action request:
255
277
 
256
278
  ```json
@@ -345,9 +367,21 @@ it does not run models, retain prompts, or infer a winner from labels.
345
367
 
346
368
  ## Passive Use
347
369
 
348
- `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.
349
383
 
350
- 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.
351
385
 
352
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:
353
387
 
@@ -356,7 +390,7 @@ npx -y --package=@getmarrow/mcp@latest marrow-mcp spool-status
356
390
  npx -y --package=@getmarrow/mcp@latest marrow-mcp drain-spool
357
391
  ```
358
392
 
359
- 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.
360
394
 
361
395
  Check the installed runtime:
362
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();
@@ -447,16 +445,21 @@ if (process.argv[2] !== 'keys') {
447
445
  }
448
446
  function toolFailure(toolName, failure) {
449
447
  const result = (0, request_reliability_1.structuredRequestFailure)(failure);
450
- const infrastructureFailure = !['authentication_required', 'permission_denied'].includes(failure.code);
448
+ const proofValidation = failure.code === 'proof_required';
449
+ const infrastructureFailure = !proofValidation && !['authentication_required', 'permission_denied'].includes(failure.code);
451
450
  const supportsStale = ['marrow_agent_runtime', 'marrow_orient', 'marrow_ask', 'marrow_handoff_status', 'marrow_runtime_status', 'marrow_status'].includes(toolName || '');
452
451
  const spool = (0, lifecycle_spool_1.lifecycleSpoolStatus)({ apiKey: API_KEY, agentId: FLEET_AGENT_ID });
453
- result.failure_kind = infrastructureFailure ? 'infrastructure' : 'authorization';
452
+ result.failure_kind = proofValidation ? 'validation' : infrastructureFailure ? 'infrastructure' : 'authorization';
454
453
  result.control_path = (0, control_path_state_1.controlPathStats)(toolName || 'marrow_control');
455
454
  result.lifecycle_spool = {
456
455
  ...spool,
457
456
  drain_command: 'npx -y --package=@getmarrow/mcp@latest marrow-mcp drain-spool',
458
457
  };
459
458
  result.host_capability = mcpHostCapability();
459
+ if (proofValidation) {
460
+ result.proof_required = true;
461
+ result.exact_next_action = failure.exactFix;
462
+ }
460
463
  if (infrastructureFailure && supportsStale) {
461
464
  let cached = null;
462
465
  try {
@@ -484,22 +487,9 @@ if (process.argv[2] !== 'keys') {
484
487
  return result;
485
488
  }
486
489
  function mcpHostCapability() {
487
- const raw = String(process.env.MARROW_CLIENT || process.env.MARROW_HARNESS || '').trim().toLowerCase();
488
- const host = ['grok', 'claude-code', 'codex', 'cursor', 'windsurf', 'gemini', 'kimi', 'qwen', 'deepseek']
489
- .includes(raw) ? raw : 'mcp-client';
490
- return {
491
- transport: 'mcp_stdio',
492
- host,
493
- tools_available: true,
494
- tool_invocation: 'on_demand',
495
- passive_hooks: {
496
- provided_by_mcp_transport: false,
497
- external_host_hook_state: 'unverified',
498
- observed_by_this_process: false,
499
- },
500
- always_on_state: 'host_dependent',
501
- 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.',
502
- };
490
+ return (0, host_capability_1.resolveHostCapability)({
491
+ hostLabel: process.env.MARROW_CLIENT || process.env.MARROW_HARNESS || process.env.MARROW_AGENT_CLIENT,
492
+ });
503
493
  }
504
494
  function clientOperationalPayload(toolName, value) {
505
495
  const payload = value && typeof value === 'object' && !Array.isArray(value)
@@ -570,6 +560,23 @@ if (process.argv[2] !== 'keys') {
570
560
  }
571
561
  catch { /* owner-only cache is best effort */ }
572
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
+ }
573
580
  // [FIX #6 & #7] Safe JSON response helper for memory API functions
574
581
  async function safeMemoryResponse(res) {
575
582
  if (!res.ok) {
@@ -815,7 +822,7 @@ if (process.argv[2] !== 'keys') {
815
822
  type: 'object',
816
823
  description: 'Optional required proof pack for gated work: summary, checks, outcome, blockers, commits_prs_shas, rollback_target, handoff_result_file, deployment_and_smoke.',
817
824
  },
818
- 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.' },
819
826
  arbitration_receipt_id: { type: 'string', description: 'Required for arbitrated work: use marrow_arbitrate.arbitration.receipt_id from the same runtime response.' },
820
827
  owner_approval_receipt_id: { type: 'string', description: 'Single-use owner approval receipt issued by authenticated dashboard review for review_required arbitration.' },
821
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.' },
@@ -1228,6 +1235,8 @@ if (process.argv[2] !== 'keys') {
1228
1235
  name: 'marrow_agent_runtime',
1229
1236
  description: 'One-call agent-native Marrow loop. Returns passive status, decision brief, risk gate, relevant lessons, ' +
1230
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. ' +
1231
1240
  'Use this before meaningful work when you want Marrow to guide the whole action in one call.',
1232
1241
  inputSchema: {
1233
1242
  type: 'object',
@@ -1682,13 +1691,15 @@ if (process.argv[2] !== 'keys') {
1682
1691
  try {
1683
1692
  if (method === 'initialize') {
1684
1693
  initialized = true;
1694
+ const hostCapability = mcpHostCapability();
1685
1695
  success(id, {
1686
1696
  protocolVersion: '2024-11-05',
1687
1697
  capabilities: { tools: {}, prompts: {} },
1688
1698
  serverInfo: { name: 'marrow', version: hook_contract_1.MCP_ADAPTER_VERSION },
1689
1699
  ...(AUTO_ENROLL ? {
1690
- 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)}`,
1691
1701
  } : {}),
1702
+ _meta: { host_capability: hostCapability },
1692
1703
  });
1693
1704
  // Auto-enroll: emit enrollment notification on connection
1694
1705
  if (AUTO_ENROLL) {
@@ -1700,9 +1711,10 @@ if (process.argv[2] !== 'keys') {
1700
1711
  logger: 'marrow',
1701
1712
  data: {
1702
1713
  type: 'auto_enroll',
1703
- 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}`,
1704
1715
  agentId: FLEET_AGENT_ID,
1705
1716
  client_update: (0, request_reliability_1.localClientUpdate)(),
1717
+ host_capability: hostCapability,
1706
1718
  },
1707
1719
  },
1708
1720
  });
@@ -1715,8 +1727,9 @@ if (process.argv[2] !== 'keys') {
1715
1727
  prompts: [
1716
1728
  {
1717
1729
  name: 'marrow-always-on',
1718
- 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.',
1719
1731
  arguments: [],
1732
+ _meta: { host_capability: mcpHostCapability() },
1720
1733
  },
1721
1734
  ],
1722
1735
  });
@@ -1732,8 +1745,10 @@ if (process.argv[2] !== 'keys') {
1732
1745
  error(id, -32602, 'Unknown prompt');
1733
1746
  return;
1734
1747
  }
1748
+ const hostCapability = mcpHostCapability();
1735
1749
  success(id, {
1736
- 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 },
1737
1752
  messages: [
1738
1753
  {
1739
1754
  role: 'user',
@@ -1741,14 +1756,16 @@ if (process.argv[2] !== 'keys') {
1741
1756
  type: 'text',
1742
1757
  text: `You have Marrow — the agent control and proof layer around this workflow.
1743
1758
 
1744
- ## Passive operating contract
1759
+ ## Capability-qualified operating contract
1760
+
1761
+ ${(0, host_capability_1.hostCapabilityInstructions)(hostCapability)}
1745
1762
 
1746
- Installed hooks handle supported lifecycle capture automatically:
1747
- - UserPromptSubmit requests relevant policy, warnings, lessons, and a decision brief before risky work.
1748
- - PostToolUse records compact tool success or failure receipts.
1749
- - 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.
1750
1767
 
1751
- 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.
1752
1769
 
1753
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.
1754
1771
 
@@ -1797,7 +1814,7 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
1797
1814
  `2. Use marrow_agent_runtime when a risky action needs an explicit gate.\n` +
1798
1815
  `3. Close meaningful work with marrow_commit; a tool exit alone is not outcome proof.\n` +
1799
1816
  `4. Use marrow_decision_trace to explain the prior failure, lesson, gate, proof, workflow, and outcome path.\n\n` +
1800
- `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`;
1801
1818
  const orientText = JSON.stringify(result, null, 2);
1802
1819
  success(id, {
1803
1820
  content: [{ type: 'text', text: enrollmentText + orientText }],
@@ -1964,7 +1981,12 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
1964
1981
  }, SESSION_ID, FLEET_AGENT_ID, signal), { highRisk: true, toolName: 'marrow_auto.runtime' });
1965
1982
  storeRuntimeGuidance(runtimeGate);
1966
1983
  }
1967
- 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);
1968
1990
  const acceptedSuccess = proofCanClose ? outcomeSuccess : undefined;
1969
1991
  const delivery = () => (0, index_1.marrowAuto)(API_KEY, BASE_URL, {
1970
1992
  action,
@@ -1972,9 +1994,10 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
1972
1994
  success: acceptedSuccess,
1973
1995
  type,
1974
1996
  proof: suppliedProof,
1975
- gate_receipt_id: typeof args.gate_receipt_id === 'string'
1976
- ? args.gate_receipt_id
1977
- : 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,
1978
2001
  }, SESSION_ID, FLEET_AGENT_ID, 8_000);
1979
2002
  let delivered = null;
1980
2003
  let deliveryFailure = null;
@@ -2055,7 +2078,20 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
2055
2078
  return;
2056
2079
  }
2057
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
+ }
2058
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');
2059
2095
  toolSuccess(id, clientOperationalPayload('marrow_status', result));
2060
2096
  return;
2061
2097
  }
@@ -2252,6 +2288,7 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
2252
2288
  };
2253
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' });
2254
2290
  storeRuntimeGuidance(result);
2291
+ storeLastKnownStatus(result.status, 'runtime');
2255
2292
  toolSuccess(id, clientOperationalPayload('marrow_agent_runtime', result));
2256
2293
  return;
2257
2294
  }