@getmarrow/mcp 3.9.58 → 3.9.60
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 +38 -9
- package/dist/cli.js +178 -96
- package/dist/cli.js.map +1 -1
- package/dist/control-path-state.d.ts +14 -0
- package/dist/control-path-state.d.ts.map +1 -0
- package/dist/control-path-state.js +46 -0
- package/dist/control-path-state.js.map +1 -0
- package/dist/env.js +1 -1
- package/dist/env.js.map +1 -1
- package/dist/hook-context.d.ts +1 -1
- package/dist/hook-context.d.ts.map +1 -1
- package/dist/hook-context.js +3 -3
- package/dist/hook-context.js.map +1 -1
- package/dist/hook-contract.d.ts +6 -6
- package/dist/hook-contract.d.ts.map +1 -1
- package/dist/hook-contract.js +6 -6
- package/dist/hook-contract.js.map +1 -1
- package/dist/hook-session.d.ts +1 -1
- package/dist/hook-session.d.ts.map +1 -1
- package/dist/hook.d.ts +1 -1
- package/dist/hook.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -7
- package/dist/index.js.map +1 -1
- package/dist/lifecycle-spool.d.ts +9 -0
- package/dist/lifecycle-spool.d.ts.map +1 -1
- package/dist/lifecycle-spool.js +70 -9
- package/dist/lifecycle-spool.js.map +1 -1
- package/dist/ping-state.d.ts +1 -0
- package/dist/ping-state.d.ts.map +1 -1
- package/dist/ping-state.js +7 -0
- package/dist/ping-state.js.map +1 -1
- package/dist/request-reliability.d.ts +5 -1
- package/dist/request-reliability.d.ts.map +1 -1
- package/dist/request-reliability.js +44 -21
- package/dist/request-reliability.js.map +1 -1
- package/dist/runtime-contract.d.ts +2 -1
- package/dist/runtime-contract.d.ts.map +1 -1
- package/dist/runtime-contract.js +124 -2
- package/dist/runtime-contract.js.map +1 -1
- package/dist/types.d.ts +23 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/scripts/control-path-canary.cjs +7 -4
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ Verify current claims through the [public evidence manifest](https://getmarrow.a
|
|
|
57
57
|
## Install
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
|
-
npx
|
|
60
|
+
npx -y --package=@getmarrow/mcp@latest marrow-mcp setup
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
Set the key through trusted secret storage:
|
|
@@ -75,7 +75,7 @@ Then configure the MCP server:
|
|
|
75
75
|
"mcpServers": {
|
|
76
76
|
"marrow": {
|
|
77
77
|
"command": "npx",
|
|
78
|
-
"args": ["-y", "
|
|
78
|
+
"args": ["-y", "--package=@getmarrow/mcp@latest", "marrow-mcp"]
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
}
|
|
@@ -96,15 +96,44 @@ npx -y @getmarrow/install@latest activate
|
|
|
96
96
|
npx -y @getmarrow/install@latest doctor
|
|
97
97
|
|
|
98
98
|
# Manual MCP-only setup
|
|
99
|
-
npx -y
|
|
99
|
+
npx -y --package=@getmarrow/mcp@latest marrow-mcp setup
|
|
100
100
|
|
|
101
101
|
# Verify live read latency, last success, and local backlog
|
|
102
|
-
npx -y
|
|
102
|
+
npx -y --package=@getmarrow/mcp@latest marrow-mcp ping
|
|
103
103
|
```
|
|
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.
|
|
107
|
+
## What's New in v3.9.60
|
|
108
|
+
|
|
109
|
+
v3.9.60 restores the complete control-and-proof loop for ordinary MCP clients:
|
|
110
|
+
|
|
111
|
+
- cached guidance no longer cuts live status, ask, runtime, or handoff reads down to an impossible 500 ms deadline;
|
|
112
|
+
- `marrow_auto` waits for the bounded think-and-commit path and reports the live decision and proof result in-band;
|
|
113
|
+
- `marrow_commit` now shares the same abort and deadline contract as the other control calls;
|
|
114
|
+
- transient retries use a one-second delay so a slow edge path is not immediately hit again;
|
|
115
|
+
- spool status surfaces backlogs under older credential namespaces without replaying them across an unverified tenant boundary;
|
|
116
|
+
- a plan-gated handoff is reported as unavailable for the current plan, not as an API or authentication outage;
|
|
117
|
+
- the release canary runs with the customer's default client deadlines instead of silently overriding them.
|
|
118
|
+
|
|
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.
|
|
120
|
+
|
|
121
|
+
## Previous: v3.9.59
|
|
122
|
+
|
|
123
|
+
v3.9.59 makes the six-tool control path reliable and honest across ordinary edge and geographic latency:
|
|
124
|
+
|
|
125
|
+
- the default health deadline is 2.5 seconds instead of an unrealistically narrow 400 ms;
|
|
126
|
+
- `MARROW_PING_TIMEOUT_MS` can tune the probe between 500 ms and 5 seconds;
|
|
127
|
+
- authenticated control reads tolerate cold network/TLS paths while cached reads still return quickly;
|
|
128
|
+
- status, ask, runtime, and handoff responses report measured current/p50/p99 latency plus owner-only queue health;
|
|
129
|
+
- a first-session outage returns a clearly labeled local safety brief, and infrastructure failures are never mislabeled as policy denials;
|
|
130
|
+
- timeout errors return a concrete retry delay instead of an unresolved placeholder;
|
|
131
|
+
- 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;
|
|
133
|
+
- update, launch, setup, spool, and ping commands use the unambiguous `npx --package ... marrow-mcp` form;
|
|
134
|
+
- explicit spool drains tolerate slow edge delivery without extending passive hook latency.
|
|
135
|
+
|
|
136
|
+
## Previous: v3.9.58
|
|
108
137
|
|
|
109
138
|
v3.9.58 makes latency evidence accurate by reusing one initialized MCP process for the complete control-path canary:
|
|
110
139
|
|
|
@@ -133,7 +162,7 @@ v3.9.56 adds tenant-scoped coordination and evidence-only replay to the existing
|
|
|
133
162
|
- transient failures can use an owner-only, account/key/agent-scoped last-known brief for at most one hour, clearly labeled with its age;
|
|
134
163
|
- 401 and 403 responses never use cached guidance, and cached runtime guidance cannot authorize high-risk work;
|
|
135
164
|
- `marrow_ask` now maps to the canonical decision brief contract instead of a separate route;
|
|
136
|
-
- `npx -y
|
|
165
|
+
- `npx -y --package=@getmarrow/mcp@latest marrow-mcp ping` reports current latency, rolling measured p50/p99, last success, and lifecycle backlog health;
|
|
137
166
|
- `marrow_coordinate` acquires/releases tenant-scoped resource leases and carries compact child proof packets without sharing transcripts;
|
|
138
167
|
- `marrow_replay_compare` compares already-recorded baseline and candidate outcomes with durable proof and never executes either model;
|
|
139
168
|
- both new tools preserve agent-bound key scope, reject unsafe path identifiers, and return unavailable or incomplete evidence rather than manufacturing a winner.
|
|
@@ -316,15 +345,15 @@ it does not run models, retain prompts, or infer a winner from labels.
|
|
|
316
345
|
|
|
317
346
|
## Passive Use
|
|
318
347
|
|
|
319
|
-
`npx
|
|
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.
|
|
320
349
|
|
|
321
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.
|
|
322
351
|
|
|
323
352
|
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:
|
|
324
353
|
|
|
325
354
|
```bash
|
|
326
|
-
npx
|
|
327
|
-
npx
|
|
355
|
+
npx -y --package=@getmarrow/mcp@latest marrow-mcp spool-status
|
|
356
|
+
npx -y --package=@getmarrow/mcp@latest marrow-mcp drain-spool
|
|
328
357
|
```
|
|
329
358
|
|
|
330
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.
|
package/dist/cli.js
CHANGED
|
@@ -21,6 +21,7 @@ const guidance_cache_1 = require("./guidance-cache");
|
|
|
21
21
|
const request_reliability_1 = require("./request-reliability");
|
|
22
22
|
const hook_contract_1 = require("./hook-contract");
|
|
23
23
|
const ping_state_1 = require("./ping-state");
|
|
24
|
+
const control_path_state_1 = require("./control-path-state");
|
|
24
25
|
const redact_1 = require("./redact");
|
|
25
26
|
const runtime_contract_1 = require("./runtime-contract");
|
|
26
27
|
// Parse CLI args
|
|
@@ -73,7 +74,7 @@ async function runPingCommand() {
|
|
|
73
74
|
}
|
|
74
75
|
const baseUrl = (0, index_1.validateBaseUrl)(resolved.baseUrl || 'https://api.getmarrow.ai');
|
|
75
76
|
const controller = new AbortController();
|
|
76
|
-
const timer = setTimeout(() => controller.abort(),
|
|
77
|
+
const timer = setTimeout(() => controller.abort(), (0, ping_state_1.resolvePingTimeoutMs)(process.env.MARROW_PING_TIMEOUT_MS));
|
|
77
78
|
timer.unref?.();
|
|
78
79
|
const started = Date.now();
|
|
79
80
|
try {
|
|
@@ -126,12 +127,17 @@ async function runSpoolCommand(drain) {
|
|
|
126
127
|
const status = drain
|
|
127
128
|
? await (0, lifecycle_spool_1.drainLifecycleSpool)({ apiKey, baseUrl, agentId: resolved.agentId || undefined })
|
|
128
129
|
: (0, lifecycle_spool_1.lifecycleSpoolStatus)({ apiKey, agentId: resolved.agentId || undefined });
|
|
130
|
+
const namespaceAttention = status.other_namespaces.state === 'attention_required';
|
|
129
131
|
process.stdout.write(`${JSON.stringify({
|
|
130
|
-
ok:
|
|
132
|
+
ok: drain
|
|
133
|
+
? status.state === 'clear' && !namespaceAttention
|
|
134
|
+
: status.state !== 'attention_required' && !namespaceAttention,
|
|
131
135
|
lifecycle_spool: status,
|
|
132
136
|
}, null, 2)}\n`);
|
|
133
|
-
if (status.state === 'attention_required')
|
|
137
|
+
if (status.state === 'attention_required' || namespaceAttention)
|
|
134
138
|
process.exitCode = 2;
|
|
139
|
+
else if (drain && status.state !== 'clear')
|
|
140
|
+
process.exitCode = 1;
|
|
135
141
|
}
|
|
136
142
|
// ─── Setup command: inject Marrow instructions into CLAUDE.md ───
|
|
137
143
|
function runSetup() {
|
|
@@ -441,27 +447,76 @@ if (process.argv[2] !== 'keys') {
|
|
|
441
447
|
}
|
|
442
448
|
function toolFailure(toolName, failure) {
|
|
443
449
|
const result = (0, request_reliability_1.structuredRequestFailure)(failure);
|
|
444
|
-
const
|
|
445
|
-
const supportsStale = ['marrow_agent_runtime', 'marrow_orient', 'marrow_ask', 'marrow_runtime_status', 'marrow_status'].includes(toolName || '');
|
|
446
|
-
|
|
450
|
+
const infrastructureFailure = !['authentication_required', 'permission_denied'].includes(failure.code);
|
|
451
|
+
const supportsStale = ['marrow_agent_runtime', 'marrow_orient', 'marrow_ask', 'marrow_handoff_status', 'marrow_runtime_status', 'marrow_status'].includes(toolName || '');
|
|
452
|
+
const spool = (0, lifecycle_spool_1.lifecycleSpoolStatus)({ apiKey: API_KEY, agentId: FLEET_AGENT_ID });
|
|
453
|
+
result.failure_kind = infrastructureFailure ? 'infrastructure' : 'authorization';
|
|
454
|
+
result.control_path = (0, control_path_state_1.controlPathStats)(toolName || 'marrow_control');
|
|
455
|
+
result.lifecycle_spool = {
|
|
456
|
+
...spool,
|
|
457
|
+
drain_command: 'npx -y --package=@getmarrow/mcp@latest marrow-mcp drain-spool',
|
|
458
|
+
};
|
|
459
|
+
result.host_capability = mcpHostCapability();
|
|
460
|
+
if (infrastructureFailure && supportsStale) {
|
|
447
461
|
let cached = null;
|
|
448
462
|
try {
|
|
449
463
|
cached = (0, guidance_cache_1.readGuidanceCache)({ apiKey: API_KEY, baseUrl: BASE_URL, agentId: FLEET_AGENT_ID });
|
|
450
464
|
}
|
|
451
465
|
catch { /* owner-only cache is best effort */ }
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
466
|
+
result.stale_brief = cached?.context || [
|
|
467
|
+
'## Marrow control-path outage brief',
|
|
468
|
+
'- This is an infrastructure failure; Marrow returned no policy decision and did not deny the action.',
|
|
469
|
+
'- Preserve local evidence and continue only low-risk, reversible work under the owner\'s existing safeguards.',
|
|
470
|
+
'- Do not perform high-risk work until a fresh Marrow runtime gate returns allow, warn, review_required, or block.',
|
|
471
|
+
].join('\n');
|
|
472
|
+
result.stale_source = cached ? 'last_known_guidance' : 'local_outage_safety';
|
|
473
|
+
result.stale_ms = cached?.stale_ms ?? null;
|
|
474
|
+
result.authorization_state = 'unavailable';
|
|
475
|
+
result.gate_obtained = false;
|
|
458
476
|
result.stale_can_authorize_high_risk = false;
|
|
477
|
+
const retryAfterMs = failure.retryAfterMs;
|
|
459
478
|
result.exact_next_action = cached
|
|
460
479
|
? 'Use this last-known brief for low-risk context only. Obtain a fresh Marrow runtime gate before high-risk work.'
|
|
461
|
-
:
|
|
480
|
+
: retryAfterMs != null
|
|
481
|
+
? `Wait ${retryAfterMs} ms, then retry once. Do not perform high-risk work without a fresh Marrow runtime gate.`
|
|
482
|
+
: failure.exactFix;
|
|
462
483
|
}
|
|
463
484
|
return result;
|
|
464
485
|
}
|
|
486
|
+
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
|
+
};
|
|
503
|
+
}
|
|
504
|
+
function clientOperationalPayload(toolName, value) {
|
|
505
|
+
const payload = value && typeof value === 'object' && !Array.isArray(value)
|
|
506
|
+
? value
|
|
507
|
+
: { data: value };
|
|
508
|
+
const spool = (0, lifecycle_spool_1.lifecycleSpoolStatus)({ apiKey: API_KEY, agentId: FLEET_AGENT_ID });
|
|
509
|
+
return {
|
|
510
|
+
...payload,
|
|
511
|
+
host_capability: payload.host_capability || mcpHostCapability(),
|
|
512
|
+
client_update: payload.client_update || (0, request_reliability_1.localClientUpdate)(),
|
|
513
|
+
control_path: (0, control_path_state_1.controlPathStats)(toolName),
|
|
514
|
+
lifecycle_spool: {
|
|
515
|
+
...spool,
|
|
516
|
+
drain_command: 'npx -y --package=@getmarrow/mcp@latest marrow-mcp drain-spool',
|
|
517
|
+
},
|
|
518
|
+
};
|
|
519
|
+
}
|
|
465
520
|
// [FIX #9] Runtime validation helper for required string params
|
|
466
521
|
function requireString(args, name) {
|
|
467
522
|
const val = args[name];
|
|
@@ -480,25 +535,25 @@ if (process.argv[2] !== 'keys') {
|
|
|
480
535
|
function isHighRiskAction(action, type) {
|
|
481
536
|
return HIGH_RISK_ACTION.test(`${String(type || '')} ${String(action || '')}`);
|
|
482
537
|
}
|
|
483
|
-
function cachedGuidance() {
|
|
484
|
-
try {
|
|
485
|
-
return (0, guidance_cache_1.readGuidanceCache)({ apiKey: API_KEY, baseUrl: BASE_URL, agentId: FLEET_AGENT_ID });
|
|
486
|
-
}
|
|
487
|
-
catch {
|
|
488
|
-
return null;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
538
|
async function withControlDeadline(operation, options = {}) {
|
|
492
|
-
const
|
|
493
|
-
const
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
|
|
539
|
+
const toolName = options.toolName || 'marrow_control';
|
|
540
|
+
const configuredTimeoutMs = Number(process.env.MARROW_REQUEST_TIMEOUT_MS);
|
|
541
|
+
const runtimeBudget = toolName === 'marrow_agent_runtime' || toolName === 'marrow_auto.runtime';
|
|
542
|
+
const timeoutMs = Number.isFinite(configuredTimeoutMs)
|
|
543
|
+
? Math.min(10_000, Math.max(150, Math.floor(configuredTimeoutMs)))
|
|
544
|
+
: options.highRisk || runtimeBudget ? 4_500 : 4_000;
|
|
497
545
|
const controller = new AbortController();
|
|
498
546
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
499
547
|
timer.unref?.();
|
|
548
|
+
const startedAt = Date.now();
|
|
500
549
|
try {
|
|
501
|
-
|
|
550
|
+
const result = await operation(controller.signal);
|
|
551
|
+
(0, control_path_state_1.recordControlPathSample)(toolName, Date.now() - startedAt, true);
|
|
552
|
+
return result;
|
|
553
|
+
}
|
|
554
|
+
catch (error) {
|
|
555
|
+
(0, control_path_state_1.recordControlPathSample)(toolName, Date.now() - startedAt, false);
|
|
556
|
+
throw error;
|
|
502
557
|
}
|
|
503
558
|
finally {
|
|
504
559
|
clearTimeout(timer);
|
|
@@ -1631,6 +1686,9 @@ if (process.argv[2] !== 'keys') {
|
|
|
1631
1686
|
protocolVersion: '2024-11-05',
|
|
1632
1687
|
capabilities: { tools: {}, prompts: {} },
|
|
1633
1688
|
serverInfo: { name: 'marrow', version: hook_contract_1.MCP_ADAPTER_VERSION },
|
|
1689
|
+
...(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.',
|
|
1691
|
+
} : {}),
|
|
1634
1692
|
});
|
|
1635
1693
|
// Auto-enroll: emit enrollment notification on connection
|
|
1636
1694
|
if (AUTO_ENROLL) {
|
|
@@ -1731,7 +1789,7 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
1731
1789
|
orientCallCount++;
|
|
1732
1790
|
const wantAutoWarn = args.autoWarn ?? true;
|
|
1733
1791
|
const taskType = args.taskType;
|
|
1734
|
-
const result = await withControlDeadline((signal) => (0, index_1.marrowOrient)(API_KEY, BASE_URL, { taskType, autoWarn: wantAutoWarn }, SESSION_ID, FLEET_AGENT_ID, signal), { highRisk: isHighRiskAction(`Orient before ${taskType || 'general'} work`, taskType) });
|
|
1792
|
+
const result = await withControlDeadline((signal) => (0, index_1.marrowOrient)(API_KEY, BASE_URL, { taskType, autoWarn: wantAutoWarn }, SESSION_ID, FLEET_AGENT_ID, signal), { highRisk: isHighRiskAction(`Orient before ${taskType || 'general'} work`, taskType), toolName: 'marrow_orient' });
|
|
1735
1793
|
if (AUTO_ENROLL && orientCallCount === 1) {
|
|
1736
1794
|
const enrollmentText = `\n\n**Marrow control and proof active**\n\n` +
|
|
1737
1795
|
`Marrow applies relevant policy and prior lessons before consequential actions, then records evidence and the real outcome afterward.\n\n` +
|
|
@@ -1798,12 +1856,10 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
1798
1856
|
// [FIX #9] Validate required params
|
|
1799
1857
|
const decision_id = requireString(args, 'decision_id');
|
|
1800
1858
|
const outcome = requireString(args, 'outcome');
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
}
|
|
1804
|
-
const result = await (0, index_1.marrowCommit)(API_KEY, BASE_URL, {
|
|
1859
|
+
const commitSuccess = requireBoolean(args, 'success');
|
|
1860
|
+
const result = await withControlDeadline((signal) => (0, index_1.marrowCommit)(API_KEY, BASE_URL, {
|
|
1805
1861
|
decision_id,
|
|
1806
|
-
success:
|
|
1862
|
+
success: commitSuccess,
|
|
1807
1863
|
outcome,
|
|
1808
1864
|
caused_by: args.caused_by,
|
|
1809
1865
|
proof: args.proof,
|
|
@@ -1815,10 +1871,10 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
1815
1871
|
surfaces: args.surfaces,
|
|
1816
1872
|
auto_gate: args.auto_gate,
|
|
1817
1873
|
model_usage: args.model_usage,
|
|
1818
|
-
}, SESSION_ID, FLEET_AGENT_ID);
|
|
1874
|
+
}, SESSION_ID, FLEET_AGENT_ID, signal), { highRisk: true, cacheAware: false, toolName: 'marrow_commit' });
|
|
1819
1875
|
const commitResult = { ...result, narrative: result.narrative ?? null };
|
|
1820
|
-
lastCommitted =
|
|
1821
|
-
lastDecisionId = null;
|
|
1876
|
+
lastCommitted = result.committed;
|
|
1877
|
+
lastDecisionId = result.committed ? null : decision_id;
|
|
1822
1878
|
success(id, {
|
|
1823
1879
|
content: [{ type: 'text', text: JSON.stringify(commitResult, null, 2) }],
|
|
1824
1880
|
});
|
|
@@ -1905,23 +1961,48 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
1905
1961
|
session_id: SESSION_ID,
|
|
1906
1962
|
proof: suppliedProof,
|
|
1907
1963
|
context: { source: 'mcp_auto_risk_upgrade' },
|
|
1908
|
-
}, SESSION_ID, FLEET_AGENT_ID, signal), { highRisk: true });
|
|
1964
|
+
}, SESSION_ID, FLEET_AGENT_ID, signal), { highRisk: true, toolName: 'marrow_auto.runtime' });
|
|
1909
1965
|
storeRuntimeGuidance(runtimeGate);
|
|
1910
1966
|
}
|
|
1911
1967
|
const proofCanClose = !highRisk || (0, runtime_contract_1.highRiskRuntimeCanClose)(runtimeGate, suppliedProof, args.gate_receipt_id);
|
|
1912
1968
|
const acceptedSuccess = proofCanClose ? outcomeSuccess : undefined;
|
|
1969
|
+
const delivery = () => (0, index_1.marrowAuto)(API_KEY, BASE_URL, {
|
|
1970
|
+
action,
|
|
1971
|
+
outcome,
|
|
1972
|
+
success: acceptedSuccess,
|
|
1973
|
+
type,
|
|
1974
|
+
proof: suppliedProof,
|
|
1975
|
+
gate_receipt_id: typeof args.gate_receipt_id === 'string'
|
|
1976
|
+
? args.gate_receipt_id
|
|
1977
|
+
: runtimeGate?.gate_receipt?.id,
|
|
1978
|
+
}, SESSION_ID, FLEET_AGENT_ID, 8_000);
|
|
1979
|
+
let delivered = null;
|
|
1980
|
+
let deliveryFailure = null;
|
|
1981
|
+
try {
|
|
1982
|
+
delivered = await delivery();
|
|
1983
|
+
}
|
|
1984
|
+
catch (err) {
|
|
1985
|
+
deliveryFailure = (0, request_reliability_1.structuredRequestFailure)(err);
|
|
1986
|
+
}
|
|
1913
1987
|
const receipt = await (0, lifecycle_spool_1.recordLifecycleEvent)({
|
|
1914
1988
|
apiKey: API_KEY,
|
|
1915
1989
|
baseUrl: BASE_URL,
|
|
1916
|
-
deferDelivery:
|
|
1990
|
+
deferDelivery: false,
|
|
1917
1991
|
event: {
|
|
1918
|
-
event_type:
|
|
1992
|
+
event_type: delivered?.committed
|
|
1993
|
+
? 'outcome_committed'
|
|
1994
|
+
: !highRisk && acceptedSuccess === false
|
|
1995
|
+
? 'tool_failed'
|
|
1996
|
+
: !highRisk && acceptedSuccess === true
|
|
1997
|
+
? 'tool_completed'
|
|
1998
|
+
: 'goal_started',
|
|
1919
1999
|
harness: process.env.MARROW_CLIENT || process.env.MARROW_HARNESS || 'mcp',
|
|
1920
2000
|
agent_id: FLEET_AGENT_ID,
|
|
1921
2001
|
session_id: SESSION_ID,
|
|
2002
|
+
decision_id: delivered?.decision_id,
|
|
1922
2003
|
action,
|
|
1923
|
-
outcome_state: 'pending',
|
|
1924
|
-
success: highRisk ? undefined : acceptedSuccess,
|
|
2004
|
+
outcome_state: delivered?.committed ? 'closed' : 'pending',
|
|
2005
|
+
success: delivered?.committed ? acceptedSuccess : highRisk ? undefined : acceptedSuccess,
|
|
1925
2006
|
adapter_version: hook_contract_1.MCP_ADAPTER_VERSION,
|
|
1926
2007
|
capability_level: 'mcp',
|
|
1927
2008
|
},
|
|
@@ -1930,48 +2011,29 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
1930
2011
|
action,
|
|
1931
2012
|
outcome: outcome || 'pending',
|
|
1932
2013
|
warnings: cachedOrientWarnings.map(formatWarningActionably),
|
|
1933
|
-
logging:
|
|
2014
|
+
logging: delivered?.committed
|
|
2015
|
+
? 'governed_commit_confirmed'
|
|
2016
|
+
: delivered
|
|
2017
|
+
? 'intent_confirmed'
|
|
2018
|
+
: 'durably_queued',
|
|
1934
2019
|
receipt,
|
|
1935
|
-
completion_state:
|
|
2020
|
+
completion_state: delivered?.committed
|
|
2021
|
+
? 'closed_with_proof'
|
|
2022
|
+
: !proofCanClose
|
|
2023
|
+
? 'pending_required_proof'
|
|
2024
|
+
: outcomeSuccess === undefined
|
|
2025
|
+
? 'pending_evidence'
|
|
2026
|
+
: 'delivery_pending',
|
|
2027
|
+
decision_id: delivered?.decision_id || null,
|
|
2028
|
+
live_delivery: {
|
|
2029
|
+
accepted: Boolean(delivered),
|
|
2030
|
+
committed: Boolean(delivered?.committed),
|
|
2031
|
+
...(deliveryFailure ? { failure: deliveryFailure } : {}),
|
|
2032
|
+
},
|
|
2033
|
+
host_capability: mcpHostCapability(),
|
|
1936
2034
|
client_update: (0, request_reliability_1.localClientUpdate)(),
|
|
1937
|
-
...(runtimeGate ? {
|
|
1938
|
-
runtime_gate: runtimeGate,
|
|
1939
|
-
completion_state: proofCanClose ? 'governed_delivery_pending' : 'pending_required_proof',
|
|
1940
|
-
} : {}),
|
|
2035
|
+
...(runtimeGate ? { runtime_gate: runtimeGate } : {}),
|
|
1941
2036
|
};
|
|
1942
|
-
const delivery = () => (0, index_1.marrowAuto)(API_KEY, BASE_URL, {
|
|
1943
|
-
action,
|
|
1944
|
-
outcome,
|
|
1945
|
-
success: acceptedSuccess,
|
|
1946
|
-
type,
|
|
1947
|
-
proof: suppliedProof,
|
|
1948
|
-
gate_receipt_id: typeof args.gate_receipt_id === 'string'
|
|
1949
|
-
? args.gate_receipt_id
|
|
1950
|
-
: runtimeGate?.gate_receipt?.id,
|
|
1951
|
-
}, SESSION_ID, FLEET_AGENT_ID, 1_500);
|
|
1952
|
-
if (highRisk && acceptedSuccess !== undefined) {
|
|
1953
|
-
const delivered = await delivery();
|
|
1954
|
-
if (!delivered.committed) {
|
|
1955
|
-
throw new request_reliability_1.MarrowRequestError({
|
|
1956
|
-
code: 'invalid_response',
|
|
1957
|
-
message: 'Marrow did not confirm the governed outcome commit',
|
|
1958
|
-
retryable: true,
|
|
1959
|
-
exactFix: 'Keep the outcome pending and retry marrow_commit with the fresh gate receipt and required proof.',
|
|
1960
|
-
});
|
|
1961
|
-
}
|
|
1962
|
-
response.logging = 'governed_commit_confirmed';
|
|
1963
|
-
response.completion_state = 'closed_with_proof';
|
|
1964
|
-
response.decision_id = delivered.decision_id;
|
|
1965
|
-
}
|
|
1966
|
-
else if (!highRisk) {
|
|
1967
|
-
void delivery().catch((err) => {
|
|
1968
|
-
const failure = (0, request_reliability_1.structuredRequestFailure)(err);
|
|
1969
|
-
const errorCode = failure.error && typeof failure.error === 'object'
|
|
1970
|
-
? String(failure.error.code || 'request_failed')
|
|
1971
|
-
: 'request_failed';
|
|
1972
|
-
process.stderr.write(`[marrow] marrow_auto queued; live delivery unavailable (${errorCode})\n`);
|
|
1973
|
-
});
|
|
1974
|
-
}
|
|
1975
2037
|
success(id, {
|
|
1976
2038
|
content: [{ type: 'text', text: JSON.stringify(response, null, 2) }],
|
|
1977
2039
|
});
|
|
@@ -1979,7 +2041,7 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
1979
2041
|
}
|
|
1980
2042
|
if (toolName === 'marrow_ask') {
|
|
1981
2043
|
const query = requireString(args, 'query');
|
|
1982
|
-
const result = await withControlDeadline((signal) => (0, index_1.marrowAsk)(API_KEY, BASE_URL, { query }, SESSION_ID, FLEET_AGENT_ID, signal));
|
|
2044
|
+
const result = await withControlDeadline((signal) => (0, index_1.marrowAsk)(API_KEY, BASE_URL, { query }, SESSION_ID, FLEET_AGENT_ID, signal), { toolName: 'marrow_ask' });
|
|
1983
2045
|
try {
|
|
1984
2046
|
(0, guidance_cache_1.writeGuidanceCache)({
|
|
1985
2047
|
apiKey: API_KEY,
|
|
@@ -1989,15 +2051,12 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
1989
2051
|
});
|
|
1990
2052
|
}
|
|
1991
2053
|
catch { /* owner-only cache is best effort */ }
|
|
1992
|
-
|
|
1993
|
-
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
1994
|
-
});
|
|
2054
|
+
toolSuccess(id, clientOperationalPayload('marrow_ask', result));
|
|
1995
2055
|
return;
|
|
1996
2056
|
}
|
|
1997
2057
|
if (toolName === 'marrow_status') {
|
|
1998
|
-
const result = await withControlDeadline((signal) => (0, index_1.marrowStatus)(API_KEY, BASE_URL, SESSION_ID, FLEET_AGENT_ID, signal), { cacheAware: false });
|
|
1999
|
-
|
|
2000
|
-
toolSuccess(id, { ...statusRecord, client_update: statusRecord.client_update || (0, request_reliability_1.localClientUpdate)() });
|
|
2058
|
+
const result = await withControlDeadline((signal) => (0, index_1.marrowStatus)(API_KEY, BASE_URL, SESSION_ID, FLEET_AGENT_ID, signal), { cacheAware: false, toolName: 'marrow_status' });
|
|
2059
|
+
toolSuccess(id, clientOperationalPayload('marrow_status', result));
|
|
2001
2060
|
return;
|
|
2002
2061
|
}
|
|
2003
2062
|
if (toolName === 'marrow_create_key') {
|
|
@@ -2135,7 +2194,7 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
2135
2194
|
return;
|
|
2136
2195
|
}
|
|
2137
2196
|
if (toolName === 'marrow_runtime_status') {
|
|
2138
|
-
const result = await withControlDeadline((signal) => (0, index_1.marrowRuntimeStatus)(API_KEY, BASE_URL, args.fast !== false, SESSION_ID, FLEET_AGENT_ID, signal), { cacheAware: false });
|
|
2197
|
+
const result = await withControlDeadline((signal) => (0, index_1.marrowRuntimeStatus)(API_KEY, BASE_URL, args.fast !== false, SESSION_ID, FLEET_AGENT_ID, signal), { cacheAware: false, toolName: 'marrow_runtime_status' });
|
|
2139
2198
|
success(id, { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] });
|
|
2140
2199
|
return;
|
|
2141
2200
|
}
|
|
@@ -2191,9 +2250,9 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
2191
2250
|
: undefined,
|
|
2192
2251
|
period: args.period,
|
|
2193
2252
|
};
|
|
2194
|
-
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) });
|
|
2253
|
+
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' });
|
|
2195
2254
|
storeRuntimeGuidance(result);
|
|
2196
|
-
|
|
2255
|
+
toolSuccess(id, clientOperationalPayload('marrow_agent_runtime', result));
|
|
2197
2256
|
return;
|
|
2198
2257
|
}
|
|
2199
2258
|
if (toolName === 'marrow_arbitrate') {
|
|
@@ -2391,12 +2450,35 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
2391
2450
|
return;
|
|
2392
2451
|
}
|
|
2393
2452
|
if (toolName === 'marrow_handoff_status') {
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2453
|
+
try {
|
|
2454
|
+
const result = await withControlDeadline((signal) => (0, index_1.marrowHandoffStatus)(API_KEY, BASE_URL, {
|
|
2455
|
+
status: args.status,
|
|
2456
|
+
agentId: args.agentId || FLEET_AGENT_ID,
|
|
2457
|
+
limit: args.limit,
|
|
2458
|
+
}, SESSION_ID, FLEET_AGENT_ID, signal), { cacheAware: false, toolName: 'marrow_handoff_status' });
|
|
2459
|
+
toolSuccess(id, clientOperationalPayload('marrow_handoff_status', result));
|
|
2460
|
+
}
|
|
2461
|
+
catch (error) {
|
|
2462
|
+
if (error instanceof request_reliability_1.MarrowRequestError && error.backendCode === 'MARROW_PLAN_UPGRADE_REQUIRED') {
|
|
2463
|
+
toolSuccess(id, clientOperationalPayload('marrow_handoff_status', {
|
|
2464
|
+
ok: true,
|
|
2465
|
+
available: false,
|
|
2466
|
+
state: 'not_entitled',
|
|
2467
|
+
failure_kind: 'entitlement',
|
|
2468
|
+
authorization_state: 'plan_limited',
|
|
2469
|
+
credential_valid: true,
|
|
2470
|
+
feature: 'fleet_handoff_status',
|
|
2471
|
+
current_plan: error.currentPlan,
|
|
2472
|
+
required_plan: 'team',
|
|
2473
|
+
required_feature: error.requiredFeature || 'fleet_learning',
|
|
2474
|
+
service_reachable: true,
|
|
2475
|
+
exact_next_action: 'Use status, ask, runtime, auto, and commit on the current plan. Upgrade to Team before relying on fleet handoff status.',
|
|
2476
|
+
}));
|
|
2477
|
+
}
|
|
2478
|
+
else {
|
|
2479
|
+
throw error;
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2400
2482
|
return;
|
|
2401
2483
|
}
|
|
2402
2484
|
if (toolName === 'marrow_session_end') {
|