@getmarrow/mcp 3.9.80 → 3.9.82
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 +20 -4
- package/dist/cli.js +20 -5
- package/dist/cli.js.map +1 -1
- package/dist/hook-contract.d.ts +2 -2
- package/dist/hook-contract.js +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +54 -9
- package/dist/index.js.map +1 -1
- package/dist/runtime-contract.d.ts +10 -0
- package/dist/runtime-contract.d.ts.map +1 -1
- package/dist/runtime-contract.js +69 -0
- package/dist/runtime-contract.js.map +1 -1
- package/package.json +1 -1
- package/scripts/control-path-canary.cjs +265 -49
- package/scripts/mcp-registry-verify.cjs +37 -17
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -116,7 +116,17 @@ npx -y --package=@getmarrow/mcp@latest marrow-mcp ping
|
|
|
116
116
|
|
|
117
117
|
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.
|
|
118
118
|
|
|
119
|
-
## What's New in v3.9.
|
|
119
|
+
## What's New in v3.9.82
|
|
120
|
+
|
|
121
|
+
v3.9.82 batches client reliability fixes for `marrow_auto`. Continuations honor the server's finite retry delay within the existing eight-second core budget; when the delay cannot fit, the same operation remains pending with retry guidance. Gated auto reuses the server-created decision after checking its canonical scope, and ordinary owner approval can resume that decision when the backend explicitly declares the supported proof contract. The installed bytes of v3.9.81 cannot provide these client changes; update MCP for this behavior. SDK and installer versions are unchanged.
|
|
122
|
+
|
|
123
|
+
Auto now durably queues its lifecycle receipt before responding and starts the existing bounded background delivery afterward. A queued receipt is not server acceptance, while `live_delivery.committed` separately reports confirmed governed closure. New numeric response timings distinguish core work, durable enqueue, and response construction. The canary preserves these measurements and distinguishes pending completion, owner approval, and missing proof from malformed responses or transport failures; an uncommitted canary still fails. These changes do not promise fixed latency or eliminate outages.
|
|
124
|
+
|
|
125
|
+
### Previous release: v3.9.81
|
|
126
|
+
|
|
127
|
+
v3.9.81 adds bounded structured failure evidence to the authenticated eleven-tool control-path canary. Failed runs identify the observed stage, tool, error class, timing, and completed checks without retaining credentials, customer payloads, or arbitrary error text. Protocol and write failures observed after the final response or during shutdown now fail closed; the canary's own bounded cleanup remains compatible with a successful run. The eleven live-tool requirements, client deadlines, package identity checks, and retry limits are unchanged.
|
|
128
|
+
|
|
129
|
+
### Previous release: v3.9.80
|
|
120
130
|
|
|
121
131
|
v3.9.80 is a reliability patch for direct `marrow_think` and `marrow_commit` calls. Each invocation now carries one stable bounded idempotency key. Only the backend's documented pending-persistence states are reconciled, using the byte-identical request and key after a fixed one-second delay for at most three total attempts. A 202 response is never reported as successful completion; unknown, malformed, correlation-drifted, or exhausted pending responses fail closed with a structured error. Explicit caller-supplied idempotency keys remain unchanged, and durable `observed_unverified` outcomes retain their terminal, non-authorizing semantics.
|
|
122
132
|
|
|
@@ -379,15 +389,21 @@ Configured hooks can provide cooperative telemetry and context, but they are not
|
|
|
379
389
|
|
|
380
390
|
1. Call `marrow_agent_runtime` or `marrow_decision_brief`.
|
|
381
391
|
2. Stop when the returned decision is `block` or `review_required`; otherwise follow its prior lesson and proof contract.
|
|
382
|
-
3.
|
|
392
|
+
3. Reuse a server-created runtime `decision_id` when the completion contract identifies it. Call `marrow_think` or `marrow_auto` when decision creation is still required. Keep `marrow_agent_runtime.runtime_authorization.id` separate as the gate receipt for consequential work.
|
|
383
393
|
4. Perform the action only when its gate allows it. Codex, Grok, and Gemini use configured native hooks only after restart and host hook review. The governed wrapper remains an explicit bounded fallback: `npx @getmarrow/install run --agent <agent-id> -- -- <command>`.
|
|
384
394
|
5. Call `marrow_commit` with that `decision_id`, the outcome, gate receipt, and required proof.
|
|
385
395
|
|
|
386
|
-
`marrow_agent_runtime` returns `runtime_authorization` with the authoritative gate receipt.
|
|
396
|
+
`marrow_agent_runtime` returns `runtime_authorization` with the authoritative gate receipt. An ordinary or arbitrated runtime that creates a decision also returns its server-created `decision_id`; follow `completion_contract.decision_creation_required` and preserve the returned scope. When decision creation is required, call `marrow_think` or use `marrow_auto`. Auto requests the existing expanded runtime response to check receipt identity, decision, action, agent, session, and expiry before reusing that decision; it does not obtain a second authorization fetch.
|
|
387
397
|
|
|
388
398
|
A `review_required`, `block`, or `outcome_observation_only` result never permits the action. If the action already occurred and its real result must be preserved, `marrow_commit` can ask runtime to bind observation delivery to the existing decision, action, session, and agent. The exact backend `outcome_observation_only` correlation is non-durable and non-authorizing, so MCP never sends it as a gate, arbitration, or owner-approval receipt. An accepted observation reports `committed: false`, `outcome_state: "observed_unverified"`, `authorization_granted: false`, and `trusted_learning_applied: false`, plus the backend's `exact_next_action`. This is durable delivery, so do not retry or spool the same observation. To promote it into trusted learning, obtain the named authorization and proof, then make an explicit new commit attempt using the exact observed payload. Never synthesize a decision, receipt, approval, or authorization.
|
|
389
399
|
|
|
390
|
-
`marrow_auto` returns an `operation_id`, phase, and resumable state.
|
|
400
|
+
`marrow_auto` returns an `operation_id`, phase, and resumable state. For a resumable pending phase, respect `retry_after_ms` and use the same operation ID and unchanged scope; auto never shortens the requested finite delay to fit its core budget. When the phase is `proof_required`, supply the requested measured evidence before retrying that operation. Stable phase idempotency keys preserve the original decision and outcome across retries; the backend remains authoritative for acceptance and conflicts.
|
|
401
|
+
|
|
402
|
+
For an ordinary gate, continue only when the server declares `completion_contract.owner_approval.mode: "ordinary_non_arbitrated"`. Auto waits in `owner_approval_required` until the caller actually obtains explicit owner approval for the exact work and supplies `proof.owner_approval = { approved_by: "owner", reference: "approved-release-bundle" }` together with required measured proof. Then call auto once with the same operation ID and original decision/receipt scope. The object records actual approval; it is not permission to infer approval from action text, a model response, or `human_directed`. Unknown ordinary completion contracts remain stopped without automatically starting arbitration. The backend checks receipt ownership, scope, expiry, and proof before confirming closure.
|
|
403
|
+
|
|
404
|
+
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.
|
|
405
|
+
|
|
406
|
+
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. 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.
|
|
391
407
|
|
|
392
408
|
Example pre-action request:
|
|
393
409
|
|
package/dist/cli.js
CHANGED
|
@@ -850,14 +850,14 @@ if (process.argv[2] !== 'keys') {
|
|
|
850
850
|
{
|
|
851
851
|
name: 'marrow_commit',
|
|
852
852
|
description: 'Close a recorded action with success/failure, a specific outcome, and required proof. ' +
|
|
853
|
-
'decision_id comes from marrow_think, marrow_auto, or
|
|
853
|
+
'decision_id comes from marrow_think, marrow_auto, or a runtime that actually created a decision. ' +
|
|
854
854
|
'Use the gate receipt from marrow_agent_runtime for consequential work. ' +
|
|
855
855
|
'The exact non-authorizing outcome_observation_only runtime correlation may submit an observed_unverified result, but is never sent as receipt evidence and never authorizes action or trusted learning. ' +
|
|
856
856
|
'Only committed:true closes trusted outcome learning.',
|
|
857
857
|
inputSchema: {
|
|
858
858
|
type: 'object',
|
|
859
859
|
properties: {
|
|
860
|
-
decision_id: { type: 'string', description: 'decision_id from marrow_think, marrow_auto, or
|
|
860
|
+
decision_id: { type: 'string', description: 'decision_id from marrow_think, marrow_auto, or a runtime that created a decision' },
|
|
861
861
|
success: { type: 'boolean', description: 'Did the action succeed?' },
|
|
862
862
|
outcome: { type: 'string', description: 'What happened — be specific, this trains the hive' },
|
|
863
863
|
caused_by: { type: 'string', description: 'Optional: what caused this action' },
|
|
@@ -926,7 +926,7 @@ if (process.argv[2] !== 'keys') {
|
|
|
926
926
|
},
|
|
927
927
|
{
|
|
928
928
|
name: 'marrow_auto',
|
|
929
|
-
description: 'Durably capture
|
|
929
|
+
description: 'Durably capture activity with bounded core completion. Respect retry_after_ms and reuse operation_id for pending continuation. Server-declared ordinary approval requires actual owner approval supplied in proof; arbitration requires its dashboard receipt. Lifecycle receipt queued is separate from server acceptance. Risky completion requires a fresh gate and measured proof.',
|
|
930
930
|
inputSchema: {
|
|
931
931
|
type: 'object',
|
|
932
932
|
properties: {
|
|
@@ -1285,7 +1285,7 @@ if (process.argv[2] !== 'keys') {
|
|
|
1285
1285
|
description: 'One-call agent-native Marrow loop. Returns passive status, decision brief, risk gate, relevant lessons, ' +
|
|
1286
1286
|
'template suggestion, required proof pack, before-you-act instruction, and exact next action. ' +
|
|
1287
1287
|
'Its runtime_authorization is the authoritative gate receipt; it returns decision_id only when runtime actually creates a decision. ' +
|
|
1288
|
-
'
|
|
1288
|
+
'Reuse its server-created decision_id for outcome closure; use marrow_auto or marrow_think only when the completion contract requires decision creation. ' +
|
|
1289
1289
|
'Use this before meaningful work when you want Marrow to guide the whole action in one call.',
|
|
1290
1290
|
inputSchema: {
|
|
1291
1291
|
type: 'object',
|
|
@@ -1920,6 +1920,7 @@ if (process.argv[2] !== 'keys') {
|
|
|
1920
1920
|
// Request handler
|
|
1921
1921
|
async function handleRequest(req) {
|
|
1922
1922
|
const { id, method, params } = req;
|
|
1923
|
+
const responseStartedAt = performance.now();
|
|
1923
1924
|
// [FIX #15] Enforce initialize-first per MCP spec
|
|
1924
1925
|
if (!initialized && method !== 'initialize') {
|
|
1925
1926
|
error(id, -32002, 'Server not initialized. Send initialize first.');
|
|
@@ -2234,19 +2235,22 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
2234
2235
|
}, SESSION_ID, FLEET_AGENT_ID, 8_000);
|
|
2235
2236
|
let delivered = null;
|
|
2236
2237
|
let deliveryFailure = null;
|
|
2238
|
+
const coreStartedAt = performance.now();
|
|
2237
2239
|
try {
|
|
2238
2240
|
delivered = await delivery();
|
|
2239
2241
|
}
|
|
2240
2242
|
catch (err) {
|
|
2241
2243
|
deliveryFailure = (0, request_reliability_1.structuredRequestFailure)(err);
|
|
2242
2244
|
}
|
|
2245
|
+
const coreDurationMs = Math.floor(performance.now() - coreStartedAt);
|
|
2243
2246
|
const runtimeGate = delivered?.runtime_gate || null;
|
|
2244
2247
|
if (runtimeGate)
|
|
2245
2248
|
storeRuntimeGuidance(runtimeGate);
|
|
2249
|
+
const enqueueStartedAt = performance.now();
|
|
2246
2250
|
const receipt = await (0, lifecycle_spool_1.recordLifecycleEvent)({
|
|
2247
2251
|
apiKey: API_KEY,
|
|
2248
2252
|
baseUrl: BASE_URL,
|
|
2249
|
-
deferDelivery:
|
|
2253
|
+
deferDelivery: true,
|
|
2250
2254
|
event: {
|
|
2251
2255
|
...(delivered?.operation_id ? {
|
|
2252
2256
|
event_id: `auto_${delivered.committed ? 'closed' : 'pending'}_${delivered.operation_id}`,
|
|
@@ -2268,6 +2272,7 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
2268
2272
|
source: 'client_self_reported',
|
|
2269
2273
|
},
|
|
2270
2274
|
});
|
|
2275
|
+
const enqueueDurationMs = Math.floor(performance.now() - enqueueStartedAt);
|
|
2271
2276
|
const response = {
|
|
2272
2277
|
action,
|
|
2273
2278
|
outcome: outcome || 'pending',
|
|
@@ -2317,9 +2322,19 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
2317
2322
|
client_update: (0, request_reliability_1.localClientUpdate)(),
|
|
2318
2323
|
...(runtimeGate ? { runtime_gate: runtimeGate } : {}),
|
|
2319
2324
|
};
|
|
2325
|
+
// Measure through response construction; stdout/host consumption occurs
|
|
2326
|
+
// after this snapshot and is not represented as server phase latency.
|
|
2327
|
+
response.response_timings_ms = {
|
|
2328
|
+
core: coreDurationMs,
|
|
2329
|
+
durable_enqueue: enqueueDurationMs,
|
|
2330
|
+
full_response: Math.floor(performance.now() - responseStartedAt),
|
|
2331
|
+
};
|
|
2320
2332
|
success(id, {
|
|
2321
2333
|
content: [{ type: 'text', text: JSON.stringify(response, null, 2) }],
|
|
2322
2334
|
});
|
|
2335
|
+
// The existing bounded owner handles rejection and preserves queued
|
|
2336
|
+
// receipts across exit/restart. Network work never holds this response.
|
|
2337
|
+
void (0, lifecycle_spool_1.nudgeLifecycleSpool)({ apiKey: API_KEY, baseUrl: BASE_URL, agentId: FLEET_AGENT_ID });
|
|
2323
2338
|
return;
|
|
2324
2339
|
}
|
|
2325
2340
|
if (toolName === 'marrow_ask') {
|