@getmarrow/mcp 3.9.87 → 3.9.89

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
@@ -130,12 +130,22 @@ A direct `marrow_think` can receive a durable pending response before the backen
130
130
 
131
131
  A saved `observed_unverified` outcome is terminal observation evidence, not a committed outcome. Receipt expiry cannot retroactively authorize completed work. Preserve the original decision, receipt, proof and key; an already authorized durable checkpoint may finish through its existing exact recovery path. Do not repeat the action merely to obtain a fresh receipt.
132
132
 
133
- ## What's New in v3.9.87
133
+ ### Previous release: v3.9.87
134
134
 
135
135
  v3.9.87 hardens the authenticated control-path canary against single-sample transport blips so monitoring stops flapping on a healthy service. The canary now retries transport-class delivery failures (`request_failed`, `service_unavailable`, `connection_reset`, `dns_unavailable`, `tls_failure`, `edge_access_denied`, `rate_limited`) in-run with the same idempotent operation before declaring a failure, gives the asynchronous `marrow_auto` and `marrow_first_value` calls a separate deadline ceiling (thirty seconds via `MARROW_MCP_CANARY_ASYNC_TOOL_TIMEOUT_MS`), and raises the default total canary budget to forty-five seconds so bounded retries fit. Results recovered by a retry are annotated with `recovered_on_retry: true`. Authentication, authorization, contract, and package-identity failures remain immediate hard failures with no retry. Tool surface, client contracts, and request deadlines are unchanged.
136
136
 
137
137
  v3.9.86 was published with the adapter version constant still at `3.9.85`, so the strict canary identity check rejected it; it is deprecated — use `3.9.87`. SDK `3.7.62` and installer `0.1.56` are unchanged; install MCP `3.9.87`, reload the host, review hook trust, and verify before claiming the updated client is active.
138
138
 
139
+ ## What's New in v3.9.89
140
+
141
+ v3.9.89 adds a default-enabled, private local session loop guard for every supported native-hook installation, independent of Marrow plan or fleet entitlement. It hashes bounded operation inputs and results into owner-only state under `~/.marrow`, stops unchanged successful verification repeats, stops the third unchanged poll or failed attempt, resets after meaningful mutation or a new owner prompt, and clears the session at close. Routine read-only results remain local, so ordinary checks add no Marrow API or database writes; one compact client-reported block marker is emitted only when a configured hook actually denies a repeat. Official Marrow tools remain excluded. `MARROW_AUTO_HOOK=false` and the existing owner local-control disable remain the explicit opt-outs.
142
+
143
+ The pre-action path now reuses a valid runtime-created decision and calls Think only when the runtime completion contract explicitly requires decision creation. Setup reports the loop guard as configured without claiming live enforcement before host restart, trust review, and an observed hook invocation. `marrow-mcp loop-guard-self-test` verifies the local behavior against isolated temporary state without touching the user's ledger. SDK `3.7.62` and installer `0.1.57` are unchanged.
144
+
145
+ ### Earlier v3.9.88 changes
146
+
147
+ v3.9.88 makes the lifecycle spool self-healing so users never need a manual `drain-spool` for ordinary failures. Dead letters are now classified: authentication rejections (401/403) stay `attention_required` with credential-restore guidance and are never auto-retried; conflicts (409) are marked `server_owned` because the server already holds durable evidence for that event id, and are never replayed; every other dead letter (transport, schema, or legacy rows without a status) is `recoverable` and retried automatically by the passive nudge — at most 5 events per nudge, 3 recovery attempts each, with a 15-minute cooldown between attempts, inside the existing bounded nudge budget. Recovery bookkeeping stays local and never changes the server request. `spool-status` gains `recoverable`, `server_owned`, and `recovery_exhausted` counts, and `failed` now counts only auth-class dead letters that genuinely need the operator. Explicit `drain-spool` keeps full authority: it still retries every operator-fixable dead letter including the auth class, clears recovery exhaustion for a fresh budget, and skips server-owned events. SDK `3.7.62` and installer `0.1.56` are unchanged.
148
+
139
149
  ### Previous release: v3.9.85
140
150
 
141
151
  v3.9.85 keeps the existing Marrow Auto request and response deadlines active through response-body consumption and JSON parsing, so a server that sends headers and then stalls ends with the same typed bounded timeout as a stalled header response. The retry owner, four-second write-attempt ceiling, and eight-second Auto response budget are unchanged.
@@ -443,7 +453,7 @@ For an ordinary gate, continue only when the server declares `completion_contrac
443
453
 
444
454
  For arbitration, the ordinary proof marker is not accepted. Approve the exact arbitration decision in the authenticated Marrow dashboard, then call auto with the same operation ID, `arbitration_receipt_id`, and server-issued `owner_approval_receipt_id`. Proof or chat text cannot substitute for this dashboard receipt. A CLI closed response reports `phase: "closed"`, `live_delivery.committed: true`, and `resumable: false`; the library result uses `committed: true`. Neither an ordinary marker nor an outcome record changes a stopped action into an allowed action.
445
455
 
446
- The CLI's lifecycle `receipt.queued: true` means its stable event is stored locally for later bounded delivery; `receipt.accepted: false` must not be read as server acceptance. Transient failures preserve the queued event and retry schedule for later bounded delivery, including after restart. Server retry guidance is respected. Authentication and terminal delivery errors remain failed with explicit repair guidance. Process exit can interrupt the background nudge, leaving the event for a later run. `phase_timings_ms.total` measures core auto phases. `response_timings_ms` contains numeric `core`, `durable_enqueue`, and `full_response`; the last ends at response construction and excludes subsequent stdout drain and host processing. Canary `latency_ms` independently measures the MCP round trip, while `attempts` and `retry_wait_ms` describe measured outer tool retries, not internal database or network calls.
456
+ The CLI's lifecycle `receipt.queued: true` means its stable event is stored locally for later bounded delivery; `receipt.accepted: false` must not be read as server acceptance. Transient failures preserve the queued event and retry schedule for later bounded delivery, including after restart. Server retry guidance is respected. Authentication failures remain failed with explicit credential-repair guidance and are never auto-retried; conflicted events are server-owned evidence and are never replayed; every other dead letter self-heals through bounded automatic recovery during passive nudges. Process exit can interrupt the background nudge, leaving the event for a later run. `phase_timings_ms.total` measures core auto phases. `response_timings_ms` contains numeric `core`, `durable_enqueue`, and `full_response`; the last ends at response construction and excludes subsequent stdout drain and host processing. Canary `latency_ms` independently measures the MCP round trip, while `attempts` and `retry_wait_ms` describe measured outer tool retries, not internal database or network calls.
447
457
 
448
458
  Example pre-action request:
449
459
 
@@ -559,14 +569,14 @@ Setup installs distinct Claude Code and Grok hook entrypoints. The public entryp
559
569
 
560
570
  Claude Code hooks may cooperatively request guidance and apply the harness permission response, but that does not certify always-on control. Codex native hooks map both block and review-required gates to the supported synchronous deny response. Cursor and Composer use the same Cursor-native pre-action, result, failure, and stop adapters. Cline uses native pre-action and post-tool adapters plus TaskCancel closeout; its documented TaskComplete hook remains coming soon and is not claimed as observed coverage. Windsurf uses native pre-action, success-result, and response-closeout adapters and requires Restricted Mode to be off. Gemini CLI uses native BeforeTool, AfterTool, and AfterAgent adapters; AfterAgent is the deterministic per-turn closeout and no SessionEnd delivery is claimed. Grok uses global native PreToolUse, PostToolUse/PostToolUseFailure, and one nonblocking Stop closeout; Marrow's generated Grok file contains no duplicate SessionEnd closeout. These native paths keep MCP on demand and require restart plus host hook review. Cursor, Cline, Windsurf, Gemini, and Grok deny protected review-required or unavailable-control work when their native contracts cannot make a generic ask enforceable. All activity remains client-self-reported rather than verified host telemetry, and the governed wrapper remains an explicit bounded fallback. Unknown and custom hosts remain on demand unless they provide a bounded event adapter, whose activity is still not certification without an independent authority.
561
571
 
562
- 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:
572
+ Transient lifecycle receipts use a bounded owner-only spool and are retried with stable event IDs. Dead letters self-heal: recoverable failures are retried automatically with bounded attempts and cooldown during passive nudges, and conflicted events are recognized as server-owned evidence that is never replayed. Operators can inspect and drain it without exposing event content:
563
573
 
564
574
  ```bash
565
575
  npx -y --package=@getmarrow/mcp@latest marrow-mcp spool-status
566
576
  npx -y --package=@getmarrow/mcp@latest marrow-mcp drain-spool
567
577
  ```
568
578
 
569
- 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.
579
+ The output contains only state, bounded pending/failed/recoverable/server-owned/recovery-exhausted counts, oldest receipt timestamps, capacity, and an exact fix. Only authentication-class dead letters require the operator: restore the credential binding, then drain. 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. Authentication failures in the active namespace remain explicit durable failures with repair guidance; recovery-exhausted events stop cycling instead of retrying indefinitely, and `drain-spool` remains an explicit operator tool rather than a user requirement.
570
580
 
571
581
  Check the installed runtime:
572
582
 
package/dist/cli.js CHANGED
@@ -29,6 +29,7 @@ const redact_1 = require("./redact");
29
29
  const status_cache_1 = require("./status-cache");
30
30
  const habit_loop_copy_1 = require("./habit-loop-copy");
31
31
  const host_capability_1 = require("./host-capability");
32
+ const session_loop_guard_1 = require("./session-loop-guard");
32
33
  // Parse CLI args
33
34
  function parseArgs() {
34
35
  const args = process.argv.slice(2);
@@ -62,6 +63,9 @@ function parseArgs() {
62
63
  if (args[i] === 'ping' || args[i] === '--ping') {
63
64
  result.ping = true;
64
65
  }
66
+ if (args[i] === 'loop-guard-self-test' || args[i] === '--loop-guard-self-test') {
67
+ result.loopGuardSelfTest = true;
68
+ }
65
69
  }
66
70
  return result;
67
71
  }
@@ -71,7 +75,7 @@ function reportLifecycleSpool(input) {
71
75
  }
72
76
  catch { /* owner-only quarantine is best effort */ }
73
77
  const spool = (0, lifecycle_spool_1.lifecycleSpoolStatus)({ apiKey: input.apiKey, agentId: input.agentId });
74
- if (input.baseUrl && spool.pending > 0) {
78
+ if (input.baseUrl && (0, lifecycle_spool_1.shouldNudgeLifecycleSpool)(spool)) {
75
79
  void (0, lifecycle_spool_1.nudgeLifecycleSpool)({ apiKey: input.apiKey, baseUrl: input.baseUrl, agentId: input.agentId });
76
80
  }
77
81
  return spool;
@@ -256,6 +260,8 @@ ${MARROW_BLOCK_END}`;
256
260
  process.stdout.write(`Grok native hook configuration is present at ${grokHookInstall.settingsPath}. Restart Grok and inspect /hooks before relying on it. Activity remains client-self-reported and does not certify observed coverage.\n`);
257
261
  }
258
262
  process.stdout.write(`Hook settings: ${hookInstall.settingsPath}\n`);
263
+ process.stdout.write('Configured private local session loop guard for supported native hook events. It becomes active only after host restart/trust review and an actual hook invocation; setup alone does not prove enforcement.\n');
264
+ process.stdout.write('Offline verification: marrow-mcp loop-guard-self-test (uses isolated temporary state and does not touch the user ledger).\n');
259
265
  process.stdout.write('Set MARROW_AUTO_HOOK=false to disable passive hooks.\n');
260
266
  process.stdout.write('Set MARROW_PASSIVE_BRIEF=false to disable automatic decision briefs, or MARROW_PASSIVE_BRIEF=always to brief every prompt.\n');
261
267
  process.stdout.write('Set MARROW_HOOK_DEBUG=true for write-side hook diagnostics, or MARROW_CONTEXT_HOOK_DEBUG=true for prompt-context diagnostics.\n');
@@ -381,6 +387,9 @@ if (process.argv[2] !== 'keys') {
381
387
  else if (cliArgs.ping) {
382
388
  void runPingCommand();
383
389
  }
390
+ else if (cliArgs.loopGuardSelfTest) {
391
+ process.stdout.write(`${JSON.stringify((0, session_loop_guard_1.runSessionLoopGuardSelfTest)(), null, 2)}\n`);
392
+ }
384
393
  else if (cliArgs.setup) {
385
394
  runSetup();
386
395
  }