@getmarrow/mcp 3.9.48 → 3.9.50
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 +57 -5
- package/dist/cli.js +97 -23
- package/dist/cli.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 +15 -56
- package/dist/hook-context.js.map +1 -1
- package/dist/hook-contract.d.ts +39 -0
- package/dist/hook-contract.d.ts.map +1 -0
- package/dist/hook-contract.js +240 -0
- package/dist/hook-contract.js.map +1 -0
- package/dist/hook-pre-action.d.ts +15 -0
- package/dist/hook-pre-action.d.ts.map +1 -0
- package/dist/hook-pre-action.js +168 -0
- package/dist/hook-pre-action.js.map +1 -0
- package/dist/hook-session.d.ts +1 -1
- package/dist/hook-session.d.ts.map +1 -1
- package/dist/hook-session.js +12 -38
- package/dist/hook-session.js.map +1 -1
- package/dist/hook.d.ts +3 -1
- package/dist/hook.d.ts.map +1 -1
- package/dist/hook.js +17 -100
- package/dist/hook.js.map +1 -1
- package/dist/index.d.ts +17 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +74 -1
- package/dist/index.js.map +1 -1
- package/dist/lifecycle-spool.d.ts +9 -1
- package/dist/lifecycle-spool.d.ts.map +1 -1
- package/dist/lifecycle-spool.js +49 -1
- package/dist/lifecycle-spool.js.map +1 -1
- package/dist/types.d.ts +42 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -82,26 +82,40 @@ Then configure the MCP server:
|
|
|
82
82
|
For most new installations, start with the universal installer instead:
|
|
83
83
|
|
|
84
84
|
```bash
|
|
85
|
-
npx @getmarrow/install
|
|
85
|
+
npx @getmarrow/install activate
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
## What's New in v3.9.
|
|
88
|
+
## What's New in v3.9.50
|
|
89
89
|
|
|
90
|
-
v3.9.
|
|
90
|
+
v3.9.50 makes native passive-hook coverage verifiable. `UserPromptSubmit`, `PreToolUse`, `PostToolUse`/`PostToolUseFailure`, and `Stop` attach bounded evidence for the hook that actually ran, the native-hook capability level, the adapter version, and a one-way fingerprint of the exact installed hook contract:
|
|
91
|
+
|
|
92
|
+
- agents and owners can distinguish “MCP configured” from “pre-action, result, and session hooks actually observed”;
|
|
93
|
+
- `PreToolUse` requests the Marrow runtime gate before matched actions and maps `block` to deny and `review_required` to operator review;
|
|
94
|
+
- matching `PreToolUse` and result hooks share Claude Code's tool-use correlation while the session shares one workflow identity;
|
|
95
|
+
- generic integration events cannot claim native-hook coverage unless the native adapter supplies that evidence;
|
|
96
|
+
- retries preserve correlation and capability evidence in the owner-only durable spool;
|
|
97
|
+
- configuration drift can be diagnosed without sending configuration contents;
|
|
98
|
+
- missing outcome closure remains visible rather than treating tool or session exit as business success;
|
|
99
|
+
- existing MCP tools and lifecycle inputs remain compatible.
|
|
100
|
+
|
|
101
|
+
It preserves `marrow_arbitrate` from v3.9.49, the session-orientation hardening introduced in v3.9.48, and the always-on lifecycle introduced in v3.9.44:
|
|
91
102
|
|
|
92
103
|
- `server.json` and `mcpName` identify the stdio server, required secret, source repository, and package version for registry consumers;
|
|
93
104
|
- GitHub, npm, and MCP registry surfaces use separate signed discovery placements;
|
|
94
105
|
- package language consistently describes agent governance, runtime control, proof, and fleet intelligence;
|
|
95
106
|
- bounded positive and negative recommendation criteria stop agents from forcing Marrow into irrelevant workflows;
|
|
96
107
|
- current evidence, integration paths, and published references are linked from one review-dated contract;
|
|
97
|
-
- `UserPromptSubmit` obtains relevant
|
|
98
|
-
- `
|
|
108
|
+
- `UserPromptSubmit` obtains relevant task guidance without storing raw prompt text;
|
|
109
|
+
- `PreToolUse` checks matched tool actions before execution without sending raw tool input;
|
|
110
|
+
- `PostToolUse` and `PostToolUseFailure` record compact result receipts;
|
|
99
111
|
- `Stop` keeps unfinished outcomes visible instead of silently treating a session exit as success;
|
|
100
112
|
- transient lifecycle delivery failures use an owner-only, bounded local spool with stable event IDs;
|
|
101
113
|
- `marrow_decision_trace` explains the tenant-scoped path from prior failure and lesson through gate, proof, workflow, and outcome.
|
|
102
114
|
|
|
103
115
|
Existing MCP tools and stable context API names remain compatible. Authentication, policy, proof, and validation failures are surfaced rather than retried as network failures.
|
|
104
116
|
|
|
117
|
+
Coverage percentages are produced only when Marrow has exact observed receipts. An installed config without observed hooks is shown as warming up or degraded, with `npx @getmarrow/install --repair` as the bounded repair path.
|
|
118
|
+
|
|
105
119
|
## Governed Action Flow
|
|
106
120
|
|
|
107
121
|
Before deploys, merges, publishes, migrations, credential changes, financial operations, or customer-impacting work:
|
|
@@ -157,6 +171,43 @@ Example closeout:
|
|
|
157
171
|
|
|
158
172
|
High-risk work can be allowed, warned, held for review, or blocked according to account policy. Low-risk work can use passive guidance and bounded cached state where the runtime contract permits it.
|
|
159
173
|
|
|
174
|
+
When two or more agents disagree on the next action, call `marrow_arbitrate`
|
|
175
|
+
before either proposal executes. It uses the same `/v1/agent/runtime` control
|
|
176
|
+
plane and returns `selected`, `synthesized`, `review_required`, or `blocked`
|
|
177
|
+
with a durable tenant-scoped receipt explaining the policy, evidence, authority,
|
|
178
|
+
risk, and dissent behind the result.
|
|
179
|
+
|
|
180
|
+
```json
|
|
181
|
+
{
|
|
182
|
+
"tool": "marrow_arbitrate",
|
|
183
|
+
"arguments": {
|
|
184
|
+
"objective": "Release the audited backend change safely",
|
|
185
|
+
"ownerIntent": "Production deploys require independent audit proof",
|
|
186
|
+
"proposals": [
|
|
187
|
+
{
|
|
188
|
+
"proposal_id": "deploy-now",
|
|
189
|
+
"agent_id": "jarvis",
|
|
190
|
+
"action": "Deploy the tested commit now",
|
|
191
|
+
"risk_level": "high"
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"proposal_id": "audit-first",
|
|
195
|
+
"agent_id": "barvis",
|
|
196
|
+
"action": "Audit the exact commit, then release only if it passes"
|
|
197
|
+
}
|
|
198
|
+
]
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Marrow resolves agent roles from the account rather than trusting caller claims.
|
|
204
|
+
Evidence references must be opaque identifiers; do not send raw prompts, logs,
|
|
205
|
+
URLs, paths, credentials, or customer content. The arbitration response owns the
|
|
206
|
+
`decision_id`, gate receipt, and arbitration receipt used at commit. A
|
|
207
|
+
`review_required` result must be approved from an authenticated Marrow dashboard
|
|
208
|
+
session; pass its short-lived, single-use `owner_approval_receipt_id` to
|
|
209
|
+
`marrow_commit`. An agent cannot authorize itself with a proof field.
|
|
210
|
+
|
|
160
211
|
## Passive Use
|
|
161
212
|
|
|
162
213
|
`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.
|
|
@@ -176,6 +227,7 @@ Status diagnostics distinguish missing keys, invalid keys, wrong bound-agent ide
|
|
|
176
227
|
| Tool | Purpose |
|
|
177
228
|
| --- | --- |
|
|
178
229
|
| `marrow_agent_runtime` | One-call pre-action status, policy gate, relevant lessons, proof requirements, and exact next action |
|
|
230
|
+
| `marrow_arbitrate` | Resolve conflicting agent proposals before execution and return an explainable arbitration receipt |
|
|
179
231
|
| `marrow_decision_brief` | Compact operating brief for meaningful work |
|
|
180
232
|
| `marrow_think` | Record intent and retrieve relevant governance intelligence |
|
|
181
233
|
| `marrow_commit` | Close an action with outcome, receipt, and proof |
|
package/dist/cli.js
CHANGED
|
@@ -14,6 +14,7 @@ const index_1 = require("./index");
|
|
|
14
14
|
const hook_1 = require("./hook");
|
|
15
15
|
const hook_context_1 = require("./hook-context");
|
|
16
16
|
const hook_session_1 = require("./hook-session");
|
|
17
|
+
const hook_pre_action_1 = require("./hook-pre-action");
|
|
17
18
|
const env_1 = require("./env");
|
|
18
19
|
const redact_1 = require("./redact");
|
|
19
20
|
// Parse CLI args
|
|
@@ -34,6 +35,9 @@ function parseArgs() {
|
|
|
34
35
|
if (args[i] === 'context-hook' || args[i] === '--context-hook') {
|
|
35
36
|
result.contextHook = true;
|
|
36
37
|
}
|
|
38
|
+
if (args[i] === 'pre-action-hook' || args[i] === '--pre-action-hook') {
|
|
39
|
+
result.preActionHook = true;
|
|
40
|
+
}
|
|
37
41
|
if (args[i] === 'session-hook' || args[i] === '--session-hook') {
|
|
38
42
|
result.sessionHook = true;
|
|
39
43
|
}
|
|
@@ -115,6 +119,13 @@ ${MARROW_BLOCK_END}`;
|
|
|
115
119
|
else {
|
|
116
120
|
process.stdout.write('UserPromptSubmit hook already installed — Marrow context and passive decision briefs are injected on matching prompts.\n');
|
|
117
121
|
}
|
|
122
|
+
const preActionHookInstall = (0, hook_pre_action_1.installPreActionHook)(process.cwd());
|
|
123
|
+
if (preActionHookInstall.installed) {
|
|
124
|
+
process.stdout.write('Installed PreToolUse hook — Marrow now checks each matched action before execution.\n');
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
process.stdout.write('PreToolUse hook already installed — matched actions are checked before execution.\n');
|
|
128
|
+
}
|
|
118
129
|
const sessionHookInstall = (0, hook_session_1.installSessionEndHook)(process.cwd());
|
|
119
130
|
if (sessionHookInstall.installed) {
|
|
120
131
|
process.stdout.write('Installed Stop hook — unfinished outcomes remain visible for reconciliation.\n');
|
|
@@ -230,6 +241,9 @@ if (process.argv[2] !== 'keys') {
|
|
|
230
241
|
else if (cliArgs.contextHook) {
|
|
231
242
|
void (0, hook_context_1.runContextHookCommand)();
|
|
232
243
|
}
|
|
244
|
+
else if (cliArgs.preActionHook) {
|
|
245
|
+
void (0, hook_pre_action_1.runPreActionHookCommand)();
|
|
246
|
+
}
|
|
233
247
|
else if (cliArgs.sessionHook) {
|
|
234
248
|
void (0, hook_session_1.runSessionHookCommand)();
|
|
235
249
|
}
|
|
@@ -262,26 +276,6 @@ if (process.argv[2] !== 'keys') {
|
|
|
262
276
|
let thinkCallCount = 0;
|
|
263
277
|
let orientCallCount = 0;
|
|
264
278
|
let initialized = false;
|
|
265
|
-
const pendingDecisions = new Map();
|
|
266
|
-
const PENDING_TTL_MS = 30 * 60 * 1000; // 30 min TTL
|
|
267
|
-
function actionHash(action) {
|
|
268
|
-
const normalized = action.toLowerCase().trim().replace(/\s+/g, ' ');
|
|
269
|
-
let h = 5381;
|
|
270
|
-
for (let i = 0; i < normalized.length; i++) {
|
|
271
|
-
h = ((h << 5) + h) ^ normalized.charCodeAt(i);
|
|
272
|
-
h = h >>> 0;
|
|
273
|
-
}
|
|
274
|
-
return h.toString(36) + '_' + normalized.slice(0, 32);
|
|
275
|
-
}
|
|
276
|
-
// [FIX #11] Actually call cleanupPending to prevent unbounded map growth
|
|
277
|
-
function cleanupPending() {
|
|
278
|
-
const now = Date.now();
|
|
279
|
-
for (const [key, val] of pendingDecisions) {
|
|
280
|
-
if (now - val.timestamp > PENDING_TTL_MS) {
|
|
281
|
-
pendingDecisions.delete(key);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
279
|
function formatWarningActionably(w) {
|
|
286
280
|
const pct = Math.round(w.failureRate * 100);
|
|
287
281
|
return `⚠️ ${w.type} has ${pct}% failure rate — check what went wrong last time before proceeding`;
|
|
@@ -594,6 +588,8 @@ if (process.argv[2] !== 'keys') {
|
|
|
594
588
|
description: 'Optional required proof pack for gated work: summary, checks, outcome, blockers, commits_prs_shas, rollback_target, handoff_result_file, deployment_and_smoke.',
|
|
595
589
|
},
|
|
596
590
|
gate_receipt_id: { type: 'string', description: 'Receipt id from marrow_agent_runtime.gate_receipt.id for risky work.' },
|
|
591
|
+
arbitration_receipt_id: { type: 'string', description: 'Required for arbitrated work: use marrow_arbitrate.arbitration.receipt_id from the same runtime response.' },
|
|
592
|
+
owner_approval_receipt_id: { type: 'string', description: 'Single-use owner approval receipt issued by authenticated dashboard review for review_required arbitration.' },
|
|
597
593
|
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.' },
|
|
598
594
|
type: { type: 'string', description: 'Optional original action type for auto gate lookup, e.g. deploy, publish, merge, handoff, implementation.' },
|
|
599
595
|
surfaces: { type: 'array', items: { type: 'string' }, description: 'Optional surfaces for auto gate receipt, e.g. github, cloudflare, npm, production.' },
|
|
@@ -1025,6 +1021,61 @@ if (process.argv[2] !== 'keys') {
|
|
|
1025
1021
|
required: ['action'],
|
|
1026
1022
|
},
|
|
1027
1023
|
},
|
|
1024
|
+
{
|
|
1025
|
+
name: 'marrow_arbitrate',
|
|
1026
|
+
description: 'Resolve conflicting next-step proposals from two or more tenant agents before execution. ' +
|
|
1027
|
+
'Uses the existing Marrow runtime gate and returns a durable arbitration receipt with the selected, ' +
|
|
1028
|
+
'synthesized, review-required, or blocked action and exactly why it changed.',
|
|
1029
|
+
inputSchema: {
|
|
1030
|
+
type: 'object',
|
|
1031
|
+
properties: {
|
|
1032
|
+
objective: { type: 'string', description: 'The shared owner or workflow objective.' },
|
|
1033
|
+
ownerIntent: { type: 'string', description: 'Optional bounded owner intent used for deterministic alignment.' },
|
|
1034
|
+
conflictType: {
|
|
1035
|
+
type: 'string',
|
|
1036
|
+
enum: ['action_conflict', 'policy_conflict', 'evidence_conflict', 'authority_conflict', 'risk_conflict'],
|
|
1037
|
+
},
|
|
1038
|
+
proposals: {
|
|
1039
|
+
type: 'array',
|
|
1040
|
+
minItems: 2,
|
|
1041
|
+
maxItems: 8,
|
|
1042
|
+
items: {
|
|
1043
|
+
type: 'object',
|
|
1044
|
+
properties: {
|
|
1045
|
+
proposal_id: { type: 'string', description: 'Stable opaque proposal id.' },
|
|
1046
|
+
agent_id: { type: 'string', description: 'Existing agent id in this Marrow account.' },
|
|
1047
|
+
action: { type: 'string', description: 'Proposed next action.' },
|
|
1048
|
+
rationale: { type: 'string', description: 'Optional concise rationale; redacted before transport.' },
|
|
1049
|
+
confidence: { type: 'number', minimum: 0, maximum: 1 },
|
|
1050
|
+
risk_level: { type: 'string', enum: ['low', 'medium', 'high'] },
|
|
1051
|
+
requires_owner_approval: { type: 'boolean' },
|
|
1052
|
+
evidence: {
|
|
1053
|
+
type: 'array',
|
|
1054
|
+
maxItems: 8,
|
|
1055
|
+
items: {
|
|
1056
|
+
type: 'object',
|
|
1057
|
+
properties: {
|
|
1058
|
+
kind: { type: 'string' },
|
|
1059
|
+
reference: { type: 'string', description: 'Opaque identifier only; no URL, path, or raw evidence.' },
|
|
1060
|
+
},
|
|
1061
|
+
required: ['kind', 'reference'],
|
|
1062
|
+
},
|
|
1063
|
+
},
|
|
1064
|
+
},
|
|
1065
|
+
required: ['proposal_id', 'agent_id', 'action'],
|
|
1066
|
+
},
|
|
1067
|
+
},
|
|
1068
|
+
action: { type: 'string', description: 'Optional runtime action label.' },
|
|
1069
|
+
type: { type: 'string', description: 'Optional runtime action type. Defaults to coordination.' },
|
|
1070
|
+
agentId: { type: 'string', description: 'Requesting agent id. Defaults to MARROW_AGENT_ID.' },
|
|
1071
|
+
sessionId: { type: 'string', description: 'Optional workflow session id.' },
|
|
1072
|
+
surfaces: { type: 'array', items: { type: 'string' } },
|
|
1073
|
+
context: { type: 'object', description: 'Optional non-sensitive runtime metadata.' },
|
|
1074
|
+
proof: { type: 'object', description: 'Optional proof already available to the runtime gate.' },
|
|
1075
|
+
},
|
|
1076
|
+
required: ['objective', 'proposals'],
|
|
1077
|
+
},
|
|
1078
|
+
},
|
|
1028
1079
|
{
|
|
1029
1080
|
name: 'marrow_governance_control_plane',
|
|
1030
1081
|
description: 'Return Marrow control-plane proof: governance, runtime gates, proof packs, fleet intelligence, supported harnesses, and exact next action.',
|
|
@@ -1319,7 +1370,7 @@ if (process.argv[2] !== 'keys') {
|
|
|
1319
1370
|
success(id, {
|
|
1320
1371
|
protocolVersion: '2024-11-05',
|
|
1321
1372
|
capabilities: { tools: {}, prompts: {} },
|
|
1322
|
-
serverInfo: { name: 'marrow', version: '3.9.
|
|
1373
|
+
serverInfo: { name: 'marrow', version: '3.9.50' },
|
|
1323
1374
|
});
|
|
1324
1375
|
// Auto-enroll: emit enrollment notification on connection
|
|
1325
1376
|
if (AUTO_ENROLL) {
|
|
@@ -1502,6 +1553,8 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
1502
1553
|
caused_by: args.caused_by,
|
|
1503
1554
|
proof: args.proof,
|
|
1504
1555
|
gate_receipt_id: args.gate_receipt_id,
|
|
1556
|
+
arbitration_receipt_id: args.arbitration_receipt_id,
|
|
1557
|
+
owner_approval_receipt_id: args.owner_approval_receipt_id,
|
|
1505
1558
|
action: args.action,
|
|
1506
1559
|
type: args.type,
|
|
1507
1560
|
surfaces: args.surfaces,
|
|
@@ -1590,8 +1643,6 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
1590
1643
|
const outcome = args.outcome;
|
|
1591
1644
|
const outcomeSuccess = args.success ?? true;
|
|
1592
1645
|
const type = args.type || 'general';
|
|
1593
|
-
// [FIX #11] Cleanup pending decisions on each auto call
|
|
1594
|
-
cleanupPending();
|
|
1595
1646
|
// [FIX #8] Include pending flag so agent knows logging is deferred
|
|
1596
1647
|
const response = {
|
|
1597
1648
|
action,
|
|
@@ -1845,6 +1896,29 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
1845
1896
|
success(id, { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] });
|
|
1846
1897
|
return;
|
|
1847
1898
|
}
|
|
1899
|
+
if (toolName === 'marrow_arbitrate') {
|
|
1900
|
+
const result = await (0, index_1.marrowArbitrate)(API_KEY, BASE_URL, {
|
|
1901
|
+
objective: (0, redact_1.redactSensitiveText)(args.objective),
|
|
1902
|
+
owner_intent: typeof args.ownerIntent === 'string' ? (0, redact_1.redactSensitiveText)(args.ownerIntent) : undefined,
|
|
1903
|
+
conflict_type: args.conflictType,
|
|
1904
|
+
proposals: Array.isArray(args.proposals)
|
|
1905
|
+
? args.proposals
|
|
1906
|
+
: [],
|
|
1907
|
+
action: typeof args.action === 'string' ? (0, redact_1.redactSensitiveText)(args.action) : undefined,
|
|
1908
|
+
type: args.type,
|
|
1909
|
+
agent_id: args.agentId || AGENT_ID,
|
|
1910
|
+
session_id: args.sessionId || SESSION_ID,
|
|
1911
|
+
surfaces: Array.isArray(args.surfaces) ? args.surfaces : undefined,
|
|
1912
|
+
context: args.context && typeof args.context === 'object' && !Array.isArray(args.context)
|
|
1913
|
+
? (0, redact_1.redactSensitiveValue)(args.context)
|
|
1914
|
+
: undefined,
|
|
1915
|
+
proof: args.proof && typeof args.proof === 'object' && !Array.isArray(args.proof)
|
|
1916
|
+
? (0, redact_1.redactSensitiveValue)(args.proof)
|
|
1917
|
+
: undefined,
|
|
1918
|
+
}, SESSION_ID, FLEET_AGENT_ID);
|
|
1919
|
+
success(id, { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] });
|
|
1920
|
+
return;
|
|
1921
|
+
}
|
|
1848
1922
|
if (toolName === 'marrow_governance_control_plane') {
|
|
1849
1923
|
const result = await (0, index_1.marrowGovernanceControlPlane)(API_KEY, BASE_URL, SESSION_ID, FLEET_AGENT_ID);
|
|
1850
1924
|
success(id, { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] });
|