@getmarrow/mcp 3.9.54 → 3.9.56

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
@@ -97,11 +97,33 @@ npx -y @getmarrow/install@latest doctor
97
97
 
98
98
  # Manual MCP-only setup
99
99
  npx -y @getmarrow/mcp@latest setup
100
+
101
+ # Verify live read latency, last success, and local backlog
102
+ npx -y @getmarrow/mcp@latest ping
100
103
  ```
101
104
 
102
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.
103
106
 
104
- ## What's New in v3.9.54
107
+ ## What's New in v3.9.56
108
+
109
+ v3.9.56 adds tenant-scoped coordination and evidence-only replay to the existing MCP governance surface:
110
+
111
+ - each normal user prompt performs one compact `/v1/agent/context` read; risky or mutating prompts perform one `/v1/agent/runtime` call instead;
112
+ - prompt lifecycle receipts are accepted into the owner-only local spool immediately and delivered asynchronously by later lifecycle activity;
113
+ - the prompt read deadline is 400 ms, injected guidance is limited to 3–8 concise lines, and raw prompts are not stored in the guidance cache;
114
+ - transient failures can use an owner-only, account/key/agent-scoped last-known brief for at most one hour, clearly labeled with its age;
115
+ - 401 and 403 responses never use cached guidance, and cached runtime guidance cannot authorize high-risk work;
116
+ - `marrow_ask` now maps to the canonical decision brief contract instead of a separate route;
117
+ - `npx -y @getmarrow/mcp@latest ping` reports current latency, rolling measured p50/p99, last success, and lifecycle backlog health;
118
+ - `marrow_coordinate` acquires/releases tenant-scoped resource leases and carries compact child proof packets without sharing transcripts;
119
+ - `marrow_replay_compare` compares already-recorded baseline and candidate outcomes with durable proof and never executes either model;
120
+ - both new tools preserve agent-bound key scope, reject unsafe path identifiers, and return unavailable or incomplete evidence rather than manufacturing a winner.
121
+
122
+ The package remains backward compatible with supported server aliases while advertising only implemented tools.
123
+
124
+ This release accepts `@getmarrow/sdk@^3.7.54` and is released after SDK `3.7.55`. The deterministic release order is SDK `3.7.55` first, MCP `3.9.56` second, and installer `0.1.40` last.
125
+
126
+ ## Previous: v3.9.54
105
127
 
106
128
  v3.9.54 makes Marrow's intervention visible through the existing decision-trace workflow:
107
129
 
@@ -111,8 +133,6 @@ v3.9.54 makes Marrow's intervention visible through the existing decision-trace
111
133
 
112
134
  It preserves the bounded MCP lifecycle recovery introduced in v3.9.53.
113
135
 
114
- This release is certified against `@getmarrow/sdk@3.7.53`. The deterministic release order is SDK `3.7.53` first, MCP `3.9.54` second, and installer `0.1.38` last.
115
-
116
136
  ## Previous: v3.9.53
117
137
 
118
138
  v3.9.53 adds exact lifecycle backlog visibility and bounded recovery for MCP-routed agent activity:
@@ -265,6 +285,16 @@ URLs, paths, credentials, or customer content. The arbitration response owns the
265
285
  session; pass its short-lived, single-use `owner_approval_receipt_id` to
266
286
  `marrow_commit`. An agent cannot authorize itself with a proof field.
267
287
 
288
+ Use `marrow_coordinate` when parallel agents could edit the same file, service,
289
+ deployment, or workflow. An acquired lease returns a one-time release capability.
290
+ Child agents can then create a compact proof packet containing only a bounded
291
+ summary and opaque durable evidence references. Complete is accepted only when
292
+ the linked outcome and required proof are actually closed.
293
+
294
+ Use `marrow_replay_compare` after two model or workflow variants have each
295
+ recorded an outcome. It compares that existing evidence under one tenant task;
296
+ it does not run models, retain prompts, or infer a winner from labels.
297
+
268
298
  ## Passive Use
269
299
 
270
300
  `npx @getmarrow/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.
@@ -294,6 +324,8 @@ Status diagnostics distinguish missing keys, invalid keys, wrong bound-agent ide
294
324
  | --- | --- |
295
325
  | `marrow_agent_runtime` | One-call pre-action status, policy gate, relevant lessons, proof requirements, and exact next action |
296
326
  | `marrow_arbitrate` | Resolve conflicting agent proposals before execution and return an explainable arbitration receipt |
327
+ | `marrow_coordinate` | Acquire/release resource leases and exchange compact child proof packets across tenant agents |
328
+ | `marrow_replay_compare` | Compare two existing proof-backed outcomes without executing a model |
297
329
  | `marrow_decision_brief` | Compact operating brief for meaningful work |
298
330
  | `marrow_think` | Record intent and retrieve relevant governance intelligence |
299
331
  | `marrow_commit` | Close an action with outcome, receipt, and proof |
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 ping_state_1 = require("./ping-state");
20
21
  const redact_1 = require("./redact");
21
22
  // Parse CLI args
22
23
  function parseArgs() {
@@ -48,9 +49,62 @@ function parseArgs() {
48
49
  if (args[i] === 'drain-spool' || args[i] === '--drain-spool') {
49
50
  result.drainSpool = true;
50
51
  }
52
+ if (args[i] === 'ping' || args[i] === '--ping') {
53
+ result.ping = true;
54
+ }
51
55
  }
52
56
  return result;
53
57
  }
58
+ async function runPingCommand() {
59
+ if (cliArgs.apiKey) {
60
+ process.stderr.write('Error: ping requires MARROW_API_KEY from trusted environment or owner configuration; --key is not accepted.\n');
61
+ process.exitCode = 1;
62
+ return;
63
+ }
64
+ const resolved = (0, env_1.resolveMarrowEnv)({ trustedOnly: true });
65
+ if (!resolved.apiKey) {
66
+ process.stdout.write(JSON.stringify({ ok: false, error: 'missing_key', exact_fix: resolved.exactFix }) + '\n');
67
+ process.exitCode = 1;
68
+ return;
69
+ }
70
+ const baseUrl = (0, index_1.validateBaseUrl)(resolved.baseUrl || 'https://api.getmarrow.ai');
71
+ const controller = new AbortController();
72
+ const timer = setTimeout(() => controller.abort(), 400);
73
+ timer.unref?.();
74
+ const started = Date.now();
75
+ try {
76
+ const status = await (0, index_1.marrowRuntimeStatus)(resolved.apiKey, baseUrl, true, resolved.sessionId, resolved.agentId, controller.signal);
77
+ const latencyMs = Date.now() - started;
78
+ const history = (0, ping_state_1.updatePingState)({ apiKey: resolved.apiKey, baseUrl, agentId: resolved.agentId, latencyMs, success: true });
79
+ process.stdout.write(JSON.stringify({
80
+ ok: status.ok === true,
81
+ health: status.health || 'available',
82
+ current_ms: latencyMs,
83
+ p50_ms: history.p50_ms,
84
+ p99_ms: history.p99_ms,
85
+ sample_count: history.sample_count,
86
+ last_success_at: history.last_success_at,
87
+ lifecycle_spool: (0, lifecycle_spool_1.lifecycleSpoolStatus)({ apiKey: resolved.apiKey, agentId: resolved.agentId }),
88
+ }, null, 2) + '\n');
89
+ }
90
+ catch (error) {
91
+ const history = (0, ping_state_1.updatePingState)({ apiKey: resolved.apiKey, baseUrl, agentId: resolved.agentId, success: false });
92
+ const message = error instanceof Error ? error.message : String(error);
93
+ process.stdout.write(JSON.stringify({
94
+ ok: false,
95
+ error: /401|unauthorized/i.test(message) ? 'authentication_failed' : /403|forbidden/i.test(message) ? 'permission_denied' : /abort|timeout/i.test(message) ? 'timeout' : 'unavailable',
96
+ p50_ms: history.p50_ms,
97
+ p99_ms: history.p99_ms,
98
+ sample_count: history.sample_count,
99
+ last_success_at: history.last_success_at,
100
+ lifecycle_spool: (0, lifecycle_spool_1.lifecycleSpoolStatus)({ apiKey: resolved.apiKey, agentId: resolved.agentId }),
101
+ }, null, 2) + '\n');
102
+ process.exitCode = 1;
103
+ }
104
+ finally {
105
+ clearTimeout(timer);
106
+ }
107
+ }
54
108
  async function runSpoolCommand(drain) {
55
109
  if (cliArgs.apiKey) {
56
110
  process.stderr.write('Error: lifecycle spool commands require MARROW_API_KEY from trusted environment or owner configuration; --key is not accepted.\n');
@@ -288,6 +342,9 @@ if (process.argv[2] !== 'keys') {
288
342
  else if (cliArgs.drainSpool) {
289
343
  void runSpoolCommand(true);
290
344
  }
345
+ else if (cliArgs.ping) {
346
+ void runPingCommand();
347
+ }
291
348
  else if (cliArgs.setup) {
292
349
  runSetup();
293
350
  }
@@ -1117,6 +1174,75 @@ if (process.argv[2] !== 'keys') {
1117
1174
  required: ['objective', 'proposals'],
1118
1175
  },
1119
1176
  },
1177
+ {
1178
+ name: 'marrow_coordinate',
1179
+ description: 'Coordinate tenant agents without sharing transcripts. Acquire or release a bounded resource lease, ' +
1180
+ 'list current leases, or create/list compact evidence-backed child proof packets for a parent agent.',
1181
+ inputSchema: {
1182
+ type: 'object',
1183
+ properties: {
1184
+ action: {
1185
+ type: 'string',
1186
+ enum: ['list_leases', 'acquire_lease', 'release_lease', 'list_proof_packets', 'create_proof_packet'],
1187
+ },
1188
+ resource_type: { type: 'string', enum: ['file', 'directory', 'service', 'workflow', 'deployment', 'custom'] },
1189
+ resource: { type: 'string', maxLength: 160, description: 'Bounded tenant-visible resource label. Do not send secrets.' },
1190
+ workflow_id: { type: 'string' },
1191
+ ttl_seconds: { type: 'number', minimum: 30, maximum: 3600 },
1192
+ lease_id: { type: 'string' },
1193
+ lease_token: { type: 'string', description: 'One-time lease capability returned by acquire_lease.' },
1194
+ status: { type: 'string', enum: ['active', 'released', 'expired', 'incomplete', 'complete', 'failed'] },
1195
+ limit: { type: 'number', minimum: 1, maximum: 100 },
1196
+ decision_id: { type: 'string' },
1197
+ proof_pack_id: { type: 'string' },
1198
+ summary: { type: 'string', maxLength: 280, description: 'Compact result summary; no raw transcript.' },
1199
+ evidence_refs: {
1200
+ type: 'array', maxItems: 20, items: { type: 'string' },
1201
+ description: 'Opaque durable evidence identifiers only.',
1202
+ },
1203
+ },
1204
+ required: ['action'],
1205
+ },
1206
+ },
1207
+ {
1208
+ name: 'marrow_replay_compare',
1209
+ description: 'Compare two already-recorded outcomes for the same tenant task using durable proof. ' +
1210
+ 'This does not run a model or replay customer content; it returns complete or insufficient_evidence.',
1211
+ inputSchema: {
1212
+ type: 'object',
1213
+ properties: {
1214
+ comparison_id: { type: 'string', description: 'Fetch a prior replay comparison by id.' },
1215
+ source_decision_id: { type: 'string', description: 'Original task decision used to bind the comparison.' },
1216
+ workspace_binding_id: { type: 'string', description: 'Optional privacy-safe workspace binding from runtime.' },
1217
+ constraints: {
1218
+ type: 'object',
1219
+ additionalProperties: false,
1220
+ maxProperties: 7,
1221
+ description: 'Allowlisted comparison labels only; prompts, code, transcripts, credentials, and nested content are rejected.',
1222
+ properties: {
1223
+ environment: { type: 'string', maxLength: 80 },
1224
+ tests: { type: 'string', maxLength: 80 },
1225
+ policy_profile_id: { type: 'string', maxLength: 80 },
1226
+ workflow_type: { type: 'string', maxLength: 80 },
1227
+ task_type: { type: 'string', maxLength: 80 },
1228
+ required_proof: { type: 'boolean' },
1229
+ same_workspace: { type: 'boolean' },
1230
+ },
1231
+ },
1232
+ baseline: {
1233
+ type: 'object',
1234
+ properties: { label: { type: 'string' }, decision_id: { type: 'string' } },
1235
+ required: ['decision_id'],
1236
+ },
1237
+ candidate: {
1238
+ type: 'object',
1239
+ properties: { label: { type: 'string' }, decision_id: { type: 'string' } },
1240
+ required: ['decision_id'],
1241
+ },
1242
+ },
1243
+ required: [],
1244
+ },
1245
+ },
1120
1246
  {
1121
1247
  name: 'marrow_governance_control_plane',
1122
1248
  description: 'Return Marrow control-plane proof: governance, runtime gates, proof packs, fleet intelligence, supported harnesses, and exact next action.',
@@ -1411,7 +1537,7 @@ if (process.argv[2] !== 'keys') {
1411
1537
  success(id, {
1412
1538
  protocolVersion: '2024-11-05',
1413
1539
  capabilities: { tools: {}, prompts: {} },
1414
- serverInfo: { name: 'marrow', version: '3.9.54' },
1540
+ serverInfo: { name: 'marrow', version: '3.9.56' },
1415
1541
  });
1416
1542
  // Auto-enroll: emit enrollment notification on connection
1417
1543
  if (AUTO_ENROLL) {
@@ -1962,6 +2088,19 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
1962
2088
  success(id, { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] });
1963
2089
  return;
1964
2090
  }
2091
+ if (toolName === 'marrow_coordinate') {
2092
+ const coordinationArgs = { ...args };
2093
+ delete coordinationArgs.agent_id;
2094
+ delete coordinationArgs.source_agent_id;
2095
+ const result = await (0, index_1.marrowCoordinate)(API_KEY, BASE_URL, coordinationArgs, SESSION_ID, FLEET_AGENT_ID);
2096
+ success(id, { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] });
2097
+ return;
2098
+ }
2099
+ if (toolName === 'marrow_replay_compare') {
2100
+ const result = await (0, index_1.marrowReplayCompare)(API_KEY, BASE_URL, args, SESSION_ID, FLEET_AGENT_ID);
2101
+ success(id, { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] });
2102
+ return;
2103
+ }
1965
2104
  if (toolName === 'marrow_governance_control_plane') {
1966
2105
  const result = await (0, index_1.marrowGovernanceControlPlane)(API_KEY, BASE_URL, SESSION_ID, FLEET_AGENT_ID);
1967
2106
  success(id, { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] });