@getmarrow/mcp 2.7.0 → 2.9.0

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/dist/cli.js CHANGED
@@ -20,13 +20,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
20
20
  }) : function(o, v) {
21
21
  o["default"] = v;
22
22
  });
23
- var __importStar = (this && this.__importStar) || function (mod) {
24
- if (mod && mod.__esModule) return mod;
25
- var result = {};
26
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
27
- __setModuleDefault(result, mod);
28
- return result;
29
- };
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
30
40
  Object.defineProperty(exports, "__esModule", { value: true });
31
41
  const readline = __importStar(require("readline"));
32
42
  const index_1 = require("./index");
@@ -40,6 +50,26 @@ if (!API_KEY) {
40
50
  // Auto-orient on startup — cache warnings, inject into EVERY marrow_think response
41
51
  let cachedOrientWarnings = [];
42
52
  let thinkCallCount = 0;
53
+ const pendingDecisions = new Map();
54
+ const PENDING_TTL_MS = 30 * 60 * 1000; // 30 min TTL
55
+ function actionHash(action) {
56
+ const normalized = action.toLowerCase().trim().replace(/\s+/g, ' ');
57
+ // djb2 hash to prevent decision_id mismatches from normalization-only collisions
58
+ let h = 5381;
59
+ for (let i = 0; i < normalized.length; i++) {
60
+ h = ((h << 5) + h) ^ normalized.charCodeAt(i);
61
+ h = h >>> 0;
62
+ }
63
+ return h.toString(36) + '_' + normalized.slice(0, 32);
64
+ }
65
+ function cleanupPending() {
66
+ const now = Date.now();
67
+ for (const [key, val] of pendingDecisions) {
68
+ if (now - val.timestamp > PENDING_TTL_MS) {
69
+ pendingDecisions.delete(key);
70
+ }
71
+ }
72
+ }
43
73
  function formatWarningActionably(w) {
44
74
  const pct = Math.round(w.failureRate * 100);
45
75
  return `⚠️ ${w.type} has ${pct}% failure rate — check what went wrong last time before proceeding`;
@@ -49,11 +79,13 @@ async function refreshOrientWarnings() {
49
79
  const r = await (0, index_1.marrowOrient)(API_KEY, BASE_URL, undefined, SESSION_ID);
50
80
  cachedOrientWarnings = r.warnings;
51
81
  }
52
- catch { }
82
+ catch {
83
+ // ignore
84
+ }
53
85
  }
54
86
  // Initial orient
55
87
  refreshOrientWarnings().then(() => {
56
- if (cachedOrientWarnings.some(w => w.failureRate > 0.4)) {
88
+ if (cachedOrientWarnings.some((w) => w.failureRate > 0.4)) {
57
89
  process.stderr.write(`[marrow] ⚠️ High failure rate detected on startup — call marrow_orient for details before acting\n`);
58
90
  }
59
91
  });
@@ -72,7 +104,9 @@ async function autoCommitOnClose() {
72
104
  }, SESSION_ID);
73
105
  clearTimeout(timeout);
74
106
  }
75
- catch { }
107
+ catch {
108
+ // ignore
109
+ }
76
110
  }
77
111
  }
78
112
  process.on('SIGTERM', async () => {
@@ -81,7 +115,9 @@ process.on('SIGTERM', async () => {
81
115
  await autoCommitOnClose();
82
116
  process.exit(0);
83
117
  });
84
- process.stdin.on('end', async () => { await autoCommitOnClose(); });
118
+ process.stdin.on('end', async () => {
119
+ await autoCommitOnClose();
120
+ });
85
121
  function send(response) {
86
122
  process.stdout.write(JSON.stringify(response) + '\n');
87
123
  }
@@ -91,6 +127,157 @@ function success(id, result) {
91
127
  function error(id, code, message) {
92
128
  send({ jsonrpc: '2.0', id, error: { code, message } });
93
129
  }
130
+ // Memory API functions
131
+ async function marrowListMemories(apiKey, baseUrl, params, sessionId) {
132
+ const qs = new URLSearchParams();
133
+ if (params?.status)
134
+ qs.set('status', params.status);
135
+ if (params?.query)
136
+ qs.set('query', params.query);
137
+ if (params?.limit)
138
+ qs.set('limit', String(params.limit));
139
+ if (params?.agentId)
140
+ qs.set('agent_id', params.agentId);
141
+ const res = await fetch(`${baseUrl}/v1/memories?${qs.toString()}`, {
142
+ headers: {
143
+ Authorization: `Bearer ${apiKey}`,
144
+ ...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
145
+ },
146
+ });
147
+ const json = await res.json();
148
+ return json.data?.memories || [];
149
+ }
150
+ async function marrowGetMemory(apiKey, baseUrl, id, sessionId) {
151
+ const res = await fetch(`${baseUrl}/v1/memories/${id}`, {
152
+ headers: {
153
+ Authorization: `Bearer ${apiKey}`,
154
+ ...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
155
+ },
156
+ });
157
+ const json = await res.json();
158
+ return json.data?.memory || null;
159
+ }
160
+ async function marrowUpdateMemory(apiKey, baseUrl, id, patch, sessionId) {
161
+ const res = await fetch(`${baseUrl}/v1/memories/${id}`, {
162
+ method: 'PATCH',
163
+ headers: {
164
+ Authorization: `Bearer ${apiKey}`,
165
+ 'Content-Type': 'application/json',
166
+ ...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
167
+ },
168
+ body: JSON.stringify(patch),
169
+ });
170
+ const json = await res.json();
171
+ return json.data.memory;
172
+ }
173
+ async function marrowDeleteMemory(apiKey, baseUrl, id, meta, sessionId) {
174
+ const res = await fetch(`${baseUrl}/v1/memories/${id}`, {
175
+ method: 'DELETE',
176
+ headers: {
177
+ Authorization: `Bearer ${apiKey}`,
178
+ 'Content-Type': 'application/json',
179
+ ...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
180
+ },
181
+ body: JSON.stringify(meta || {}),
182
+ });
183
+ const json = await res.json();
184
+ return json.data.memory;
185
+ }
186
+ async function marrowMarkOutdated(apiKey, baseUrl, id, meta, sessionId) {
187
+ const res = await fetch(`${baseUrl}/v1/memories/${id}/outdated`, {
188
+ method: 'POST',
189
+ headers: {
190
+ Authorization: `Bearer ${apiKey}`,
191
+ 'Content-Type': 'application/json',
192
+ ...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
193
+ },
194
+ body: JSON.stringify(meta || {}),
195
+ });
196
+ const json = await res.json();
197
+ return json.data.memory;
198
+ }
199
+ async function marrowSupersedeMemory(apiKey, baseUrl, id, replacement, sessionId) {
200
+ const res = await fetch(`${baseUrl}/v1/memories/${id}/supersede`, {
201
+ method: 'POST',
202
+ headers: {
203
+ Authorization: `Bearer ${apiKey}`,
204
+ 'Content-Type': 'application/json',
205
+ ...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
206
+ },
207
+ body: JSON.stringify(replacement),
208
+ });
209
+ const json = await res.json();
210
+ return json.data;
211
+ }
212
+ async function marrowShareMemory(apiKey, baseUrl, id, agentIds, actor, sessionId) {
213
+ const res = await fetch(`${baseUrl}/v1/memories/${id}/share`, {
214
+ method: 'POST',
215
+ headers: {
216
+ Authorization: `Bearer ${apiKey}`,
217
+ 'Content-Type': 'application/json',
218
+ ...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
219
+ },
220
+ body: JSON.stringify({ agent_ids: agentIds, actor }),
221
+ });
222
+ const json = await res.json();
223
+ return json.data.memory;
224
+ }
225
+ async function marrowExportMemories(apiKey, baseUrl, params, sessionId) {
226
+ const qs = new URLSearchParams();
227
+ if (params?.format)
228
+ qs.set('format', params.format);
229
+ if (params?.status)
230
+ qs.set('status', params.status);
231
+ if (params?.tags)
232
+ qs.set('tags', params.tags);
233
+ const res = await fetch(`${baseUrl}/v1/memories/export?${qs.toString()}`, {
234
+ headers: {
235
+ Authorization: `Bearer ${apiKey}`,
236
+ ...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
237
+ },
238
+ });
239
+ const json = await res.json();
240
+ return json.data;
241
+ }
242
+ async function marrowImportMemories(apiKey, baseUrl, memories, mode, sessionId) {
243
+ const res = await fetch(`${baseUrl}/v1/memories/import`, {
244
+ method: 'POST',
245
+ headers: {
246
+ Authorization: `Bearer ${apiKey}`,
247
+ 'Content-Type': 'application/json',
248
+ ...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
249
+ },
250
+ body: JSON.stringify({ memories, mode }),
251
+ });
252
+ const json = await res.json();
253
+ return json.data;
254
+ }
255
+ async function marrowRetrieveMemories(apiKey, baseUrl, query, params, sessionId) {
256
+ const qs = new URLSearchParams();
257
+ qs.set('q', query);
258
+ if (params?.limit)
259
+ qs.set('limit', String(params.limit));
260
+ if (params?.from)
261
+ qs.set('from', params.from);
262
+ if (params?.to)
263
+ qs.set('to', params.to);
264
+ if (params?.tags)
265
+ qs.set('tags', params.tags);
266
+ if (params?.source)
267
+ qs.set('source', params.source);
268
+ if (params?.status)
269
+ qs.set('status', params.status);
270
+ if (params?.shared !== undefined)
271
+ qs.set('shared', String(params.shared));
272
+ const res = await fetch(`${baseUrl}/v1/memories/retrieve?${qs.toString()}`, {
273
+ headers: {
274
+ Authorization: `Bearer ${apiKey}`,
275
+ ...(sessionId ? { 'X-Marrow-Session-Id': sessionId } : {}),
276
+ },
277
+ });
278
+ const json = await res.json();
279
+ return json.data;
280
+ }
94
281
  // Tool definitions
95
282
  const TOOLS = [
96
283
  {
@@ -205,6 +392,36 @@ const TOOLS = [
205
392
  required: ['description', 'success', 'outcome'],
206
393
  },
207
394
  },
395
+ {
396
+ name: 'marrow_auto',
397
+ description: 'Zero-friction Marrow logging. One call for any action — Marrow handles everything in the background without blocking. ' +
398
+ 'Pass what you are about to do. Optionally pass outcome if already done. ' +
399
+ 'Use for ANY action: deploys, file writes, API calls, external sends. ' +
400
+ 'If you only have time for one call: pass action + outcome + success together — done in one shot.',
401
+ inputSchema: {
402
+ type: 'object',
403
+ properties: {
404
+ action: {
405
+ type: 'string',
406
+ description: 'What you are about to do or just did',
407
+ },
408
+ outcome: {
409
+ type: 'string',
410
+ description: 'What happened (if already done). Omit to log intent only.',
411
+ },
412
+ success: {
413
+ type: 'boolean',
414
+ description: 'Did it succeed (default: true)',
415
+ },
416
+ type: {
417
+ type: 'string',
418
+ enum: ['implementation', 'security', 'architecture', 'process', 'general'],
419
+ description: 'Type of action (default: general)',
420
+ },
421
+ },
422
+ required: ['action'],
423
+ },
424
+ },
208
425
  {
209
426
  name: 'marrow_ask',
210
427
  description: 'Query the collective hive in plain English. ' +
@@ -230,6 +447,145 @@ const TOOLS = [
230
447
  required: [],
231
448
  },
232
449
  },
450
+ {
451
+ name: 'marrow_list_memories',
452
+ description: 'List memories with optional filters (status, query, limit, agent_id for shared memories).',
453
+ inputSchema: {
454
+ type: 'object',
455
+ properties: {
456
+ status: { type: 'string', enum: ['active', 'outdated', 'deleted'], description: 'Filter by status' },
457
+ query: { type: 'string', description: 'Search query' },
458
+ limit: { type: 'number', description: 'Max results (default: 20)' },
459
+ agentId: { type: 'string', description: 'Agent ID for shared memories' },
460
+ },
461
+ required: [],
462
+ },
463
+ },
464
+ {
465
+ name: 'marrow_get_memory',
466
+ description: 'Get a single memory by ID.',
467
+ inputSchema: {
468
+ type: 'object',
469
+ properties: {
470
+ id: { type: 'string', description: 'Memory ID' },
471
+ },
472
+ required: ['id'],
473
+ },
474
+ },
475
+ {
476
+ name: 'marrow_update_memory',
477
+ description: 'Update memory text, tags, or metadata.',
478
+ inputSchema: {
479
+ type: 'object',
480
+ properties: {
481
+ id: { type: 'string', description: 'Memory ID' },
482
+ text: { type: 'string', description: 'New text' },
483
+ source: { type: 'string', description: 'Source' },
484
+ tags: { type: 'array', items: { type: 'string' }, description: 'Tags' },
485
+ actor: { type: 'string', description: 'Actor name' },
486
+ note: { type: 'string', description: 'Audit note' },
487
+ },
488
+ required: ['id'],
489
+ },
490
+ },
491
+ {
492
+ name: 'marrow_delete_memory',
493
+ description: 'Soft delete a memory.',
494
+ inputSchema: {
495
+ type: 'object',
496
+ properties: {
497
+ id: { type: 'string', description: 'Memory ID' },
498
+ actor: { type: 'string', description: 'Actor name' },
499
+ note: { type: 'string', description: 'Audit note' },
500
+ },
501
+ required: ['id'],
502
+ },
503
+ },
504
+ {
505
+ name: 'marrow_mark_outdated',
506
+ description: 'Mark a memory as outdated.',
507
+ inputSchema: {
508
+ type: 'object',
509
+ properties: {
510
+ id: { type: 'string', description: 'Memory ID' },
511
+ actor: { type: 'string', description: 'Actor name' },
512
+ note: { type: 'string', description: 'Audit note' },
513
+ },
514
+ required: ['id'],
515
+ },
516
+ },
517
+ {
518
+ name: 'marrow_supersede_memory',
519
+ description: 'Atomically replace a memory with a new version.',
520
+ inputSchema: {
521
+ type: 'object',
522
+ properties: {
523
+ id: { type: 'string', description: 'Memory ID to supersede' },
524
+ text: { type: 'string', description: 'New memory text' },
525
+ source: { type: 'string', description: 'Source' },
526
+ tags: { type: 'array', items: { type: 'string' }, description: 'Tags' },
527
+ actor: { type: 'string', description: 'Actor name' },
528
+ note: { type: 'string', description: 'Audit note' },
529
+ },
530
+ required: ['id', 'text'],
531
+ },
532
+ },
533
+ {
534
+ name: 'marrow_share_memory',
535
+ description: 'Share a memory with specific agents.',
536
+ inputSchema: {
537
+ type: 'object',
538
+ properties: {
539
+ id: { type: 'string', description: 'Memory ID' },
540
+ agentIds: { type: 'array', items: { type: 'string' }, description: 'Agent IDs to share with' },
541
+ actor: { type: 'string', description: 'Actor name' },
542
+ },
543
+ required: ['id', 'agentIds'],
544
+ },
545
+ },
546
+ {
547
+ name: 'marrow_export_memories',
548
+ description: 'Export memories to JSON or CSV.',
549
+ inputSchema: {
550
+ type: 'object',
551
+ properties: {
552
+ format: { type: 'string', enum: ['json', 'csv'], description: 'Export format' },
553
+ status: { type: 'string', enum: ['active', 'all'], description: 'Filter by status' },
554
+ tags: { type: 'string', description: 'Comma-separated tags' },
555
+ },
556
+ required: [],
557
+ },
558
+ },
559
+ {
560
+ name: 'marrow_import_memories',
561
+ description: 'Import memories with merge (dedup) or replace mode.',
562
+ inputSchema: {
563
+ type: 'object',
564
+ properties: {
565
+ memories: { type: 'array', items: { type: 'object', properties: { text: { type: 'string' }, source: { type: 'string' }, tags: { type: 'array', items: { type: 'string' } } } }, description: 'Memories to import' },
566
+ mode: { type: 'string', enum: ['merge', 'replace'], description: 'Import mode' },
567
+ },
568
+ required: ['memories', 'mode'],
569
+ },
570
+ },
571
+ {
572
+ name: 'marrow_retrieve_memories',
573
+ description: 'Full-text search memories with filters (from, to, tags, source, status, shared).',
574
+ inputSchema: {
575
+ type: 'object',
576
+ properties: {
577
+ query: { type: 'string', description: 'Search query' },
578
+ limit: { type: 'number', description: 'Max results' },
579
+ from: { type: 'string', description: 'From date (ISO-8601)' },
580
+ to: { type: 'string', description: 'To date (ISO-8601)' },
581
+ tags: { type: 'string', description: 'Comma-separated tags' },
582
+ source: { type: 'string', description: 'Source filter' },
583
+ status: { type: 'string', enum: ['active', 'outdated', 'deleted'], description: 'Status filter' },
584
+ shared: { type: 'boolean', description: 'Include shared memories' },
585
+ },
586
+ required: ['query'],
587
+ },
588
+ },
233
589
  ];
234
590
  // Request handler
235
591
  async function handleRequest(req) {
@@ -239,7 +595,7 @@ async function handleRequest(req) {
239
595
  success(id, {
240
596
  protocolVersion: '2024-11-05',
241
597
  capabilities: { tools: {}, prompts: {} },
242
- serverInfo: { name: 'marrow', version: '2.7.0' },
598
+ serverInfo: { name: 'marrow', version: '2.8.0' },
243
599
  });
244
600
  return;
245
601
  }
@@ -249,9 +605,9 @@ async function handleRequest(req) {
249
605
  {
250
606
  name: 'marrow-always-on',
251
607
  description: 'Always-on Marrow memory loop. Instructs the agent to orient at session start, log intent before meaningful actions, and commit outcomes after completion. Install once — works automatically.',
252
- arguments: []
253
- }
254
- ]
608
+ arguments: [],
609
+ },
610
+ ],
255
611
  });
256
612
  return;
257
613
  }
@@ -270,25 +626,19 @@ async function handleRequest(req) {
270
626
  type: 'text',
271
627
  text: `You have Marrow — a memory layer that tells you what went wrong last time before you do it again.
272
628
 
273
- ## MANDATORY: Call Marrow before every meaningful action
629
+ ## PRIMARY: Use marrow_auto for everything
274
630
 
275
- Before exec, write, deploy, publish, send, delete — call marrow_think. No exceptions.
276
- Marrow returns what broke last time, what worked, and what to watch for. Follow the recommendedNext field.
631
+ One tool. Two calls max. Zero loop management.
277
632
 
278
- ## Default: Use marrow_run for 90% of cases
633
+ The minimum loop:
634
+ 1. Before any external action: \`marrow_auto({ action: "...what you are about to do" })\`
635
+ 2. After the action: \`marrow_auto({ action: "...", outcome: "...what happened", success: true/false })\`
279
636
 
280
- One call. Done. No loop management.
281
- - description: what you did
282
- - success: true or false
283
- - outcome: one line of what happened
637
+ If you only have time for one call: \`marrow_auto({ action: "did X", outcome: "result", success: true })\` — that's the whole loop.
284
638
 
285
- marrow_run handles orient think commit in a single call. Use it unless you need fine-grained control.
639
+ You never need think + commit separately. \`marrow_auto\` handles everything.
286
640
 
287
- ## When you need more control
288
-
289
- 1. **Session start:** Call marrow_orient FIRST. It surfaces failure warnings so you avoid known mistakes.
290
- 2. **Before acting:** Call marrow_think with what you're about to do.
291
- 3. **After acting:** Call marrow_commit with what happened.
641
+ All Marrow API calls run in the background — \`marrow_auto\` returns instantly with cached warnings so you never wait.
292
642
 
293
643
  ## Query the hive
294
644
 
@@ -297,16 +647,27 @@ Use marrow_ask to query your decision history in plain English:
297
647
  - "what's our failure rate on security tasks?"
298
648
  - "what worked last time we published?"
299
649
 
650
+ ## Advanced mode: fine-grained control
651
+
652
+ For cases where you need explicit control over the orient → think → act → commit loop:
653
+
654
+ 1. **Session start:** Call marrow_orient FIRST. It surfaces failure warnings so you avoid known mistakes.
655
+ 2. **Before acting:** Call marrow_think with what you're about to do. Returns pattern insights and recommendedNext — follow it.
656
+ 3. **After acting:** Call marrow_commit with what happened.
657
+ 4. **Shortcut:** marrow_run handles orient → think → commit in a single awaited call.
658
+
659
+ Most agents never need advanced mode. \`marrow_auto\` covers 99% of cases.
660
+
300
661
  ## Why you must not skip this
301
662
 
302
663
  If you skip logging, the hive can't protect you or future agents from this mistake.
303
664
  Every decision you log compounds. Failure rates drop. Patterns emerge. The next session starts smarter.
304
665
  Marrow tells you what went wrong last time before you do it again. But only if you logged it.
305
666
 
306
- This is not optional overhead — it's how you stop repeating the same failures.`
307
- }
308
- }
309
- ]
667
+ This is not optional overhead — it's how you stop repeating the same failures.`,
668
+ },
669
+ },
670
+ ],
310
671
  });
311
672
  return;
312
673
  }
@@ -318,16 +679,9 @@ This is not optional overhead — it's how you stop repeating the same failures.
318
679
  const toolName = params?.name;
319
680
  const args = (params?.arguments || {});
320
681
  if (toolName === 'marrow_orient') {
321
- const result = await (0, index_1.marrowOrient)(API_KEY, BASE_URL, {
322
- taskType: args.taskType,
323
- }, SESSION_ID);
682
+ const result = await (0, index_1.marrowOrient)(API_KEY, BASE_URL, { taskType: args.taskType }, SESSION_ID);
324
683
  success(id, {
325
- content: [
326
- {
327
- type: 'text',
328
- text: JSON.stringify(result, null, 2),
329
- },
330
- ],
684
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
331
685
  });
332
686
  return;
333
687
  }
@@ -343,33 +697,27 @@ This is not optional overhead — it's how you stop repeating the same failures.
343
697
  // Refresh orient warnings every 5th think call
344
698
  thinkCallCount++;
345
699
  if (thinkCallCount % 5 === 0) {
346
- refreshOrientWarnings().catch(() => { });
700
+ refreshOrientWarnings();
347
701
  }
348
- // Inject orient warnings into EVERY think() response (not just first)
702
+ // Inject cached orient warnings into intelligence.insights
349
703
  if (cachedOrientWarnings.length > 0) {
350
- const intel = result.intelligence;
351
- const existing = intel.insights || [];
352
- intel.insights = [
353
- ...cachedOrientWarnings.map(w => ({
704
+ const existingInsights = result.intelligence?.insights || [];
705
+ result.intelligence.insights = [
706
+ ...cachedOrientWarnings.map((w) => ({
354
707
  type: 'failure_pattern',
355
- summary: formatWarningActionably(w),
708
+ summary: w.message,
356
709
  action: `Review past ${w.type} failures before proceeding`,
357
- severity: w.failureRate > 0.4 ? 'critical' : 'warning',
710
+ severity: (w.failureRate > 0.4 ? 'critical' : 'warning'),
358
711
  count: 0,
359
712
  })),
360
- ...existing,
713
+ ...existingInsights,
361
714
  ];
362
715
  }
363
- // Track for auto-commit on session close
716
+ // Track for auto-commit
364
717
  lastDecisionId = result.decision_id;
365
718
  lastCommitted = false;
366
719
  success(id, {
367
- content: [
368
- {
369
- type: 'text',
370
- text: JSON.stringify(result, null, 2),
371
- },
372
- ],
720
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
373
721
  });
374
722
  return;
375
723
  }
@@ -381,108 +729,209 @@ This is not optional overhead — it's how you stop repeating the same failures.
381
729
  caused_by: args.caused_by,
382
730
  }, SESSION_ID);
383
731
  lastCommitted = true;
732
+ lastDecisionId = null;
384
733
  success(id, {
385
- content: [
386
- {
387
- type: 'text',
388
- text: JSON.stringify(result, null, 2),
389
- },
390
- ],
734
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
391
735
  });
392
736
  return;
393
737
  }
394
738
  if (toolName === 'marrow_run') {
395
- const description = args.description;
396
- const type = args.type || 'general';
397
- const runSuccess = args.success;
398
- const outcome = args.outcome;
739
+ // marrow_run = orient + think + commit in one call
740
+ await (0, index_1.marrowOrient)(API_KEY, BASE_URL, undefined, SESSION_ID);
399
741
  const thinkResult = await (0, index_1.marrowThink)(API_KEY, BASE_URL, {
400
- action: description,
401
- type,
742
+ action: args.description,
743
+ type: args.type || 'general',
402
744
  }, SESSION_ID);
403
745
  const commitResult = await (0, index_1.marrowCommit)(API_KEY, BASE_URL, {
404
746
  decision_id: thinkResult.decision_id,
405
- success: runSuccess,
406
- outcome,
747
+ success: args.success ?? true,
748
+ outcome: args.outcome,
407
749
  }, SESSION_ID);
408
- lastCommitted = true;
409
- lastDecisionId = thinkResult.decision_id;
410
750
  success(id, {
411
751
  content: [
412
752
  {
413
753
  type: 'text',
414
- text: JSON.stringify({
415
- decision_id: thinkResult.decision_id,
416
- success_rate: commitResult.success_rate,
417
- insight: commitResult.insight,
418
- intelligence: thinkResult.intelligence,
419
- }, null, 2),
754
+ text: JSON.stringify({ think: thinkResult, commit: commitResult }, null, 2),
420
755
  },
421
756
  ],
422
757
  });
423
758
  return;
424
759
  }
760
+ if (toolName === 'marrow_auto') {
761
+ // marrow_auto = fire-and-forget background logging
762
+ // Return immediately with cached orient warnings, API calls happen in background
763
+ const action = args.action;
764
+ const outcome = args.outcome;
765
+ const outcomeSuccess = args.success ?? true;
766
+ const type = args.type || 'general';
767
+ // Return cached warnings immediately
768
+ const response = {
769
+ action,
770
+ outcome: outcome || 'pending',
771
+ warnings: cachedOrientWarnings.map(formatWarningActionably),
772
+ };
773
+ // Fire-and-forget the actual API calls
774
+ (async () => {
775
+ try {
776
+ if (!outcome) {
777
+ // Intent only
778
+ await (0, index_1.marrowThink)(API_KEY, BASE_URL, { action, type }, SESSION_ID);
779
+ }
780
+ else {
781
+ // Full loop
782
+ const thinkResult = await (0, index_1.marrowThink)(API_KEY, BASE_URL, { action, type }, SESSION_ID);
783
+ await (0, index_1.marrowCommit)(API_KEY, BASE_URL, {
784
+ decision_id: thinkResult.decision_id,
785
+ success: outcomeSuccess,
786
+ outcome,
787
+ }, SESSION_ID);
788
+ }
789
+ }
790
+ catch {
791
+ // Silently fail - auto is best-effort
792
+ }
793
+ })();
794
+ success(id, {
795
+ content: [{ type: 'text', text: JSON.stringify(response, null, 2) }],
796
+ });
797
+ return;
798
+ }
425
799
  if (toolName === 'marrow_ask') {
426
- const query = args.query;
427
- if (!query) {
428
- error(id, -32602, 'query parameter is required');
429
- return;
430
- }
431
- const result = await (0, index_1.marrowAsk)(API_KEY, BASE_URL, { query }, SESSION_ID);
800
+ const result = await (0, index_1.marrowAsk)(API_KEY, BASE_URL, { query: args.query }, SESSION_ID);
432
801
  success(id, {
433
- content: [
434
- {
435
- type: 'text',
436
- text: JSON.stringify(result, null, 2),
437
- },
438
- ],
802
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
439
803
  });
440
804
  return;
441
805
  }
442
806
  if (toolName === 'marrow_status') {
443
807
  const result = await (0, index_1.marrowStatus)(API_KEY, BASE_URL, SESSION_ID);
444
808
  success(id, {
445
- content: [
446
- {
447
- type: 'text',
448
- text: JSON.stringify(result, null, 2),
449
- },
450
- ],
809
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
451
810
  });
452
811
  return;
453
812
  }
454
- error(id, -32601, `Unknown tool: ${toolName}`);
813
+ // Memory control tools
814
+ if (toolName === 'marrow_list_memories') {
815
+ const result = await marrowListMemories(API_KEY, BASE_URL, {
816
+ status: args.status,
817
+ query: args.query,
818
+ limit: args.limit,
819
+ agentId: args.agentId,
820
+ }, SESSION_ID);
821
+ success(id, {
822
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
823
+ });
824
+ return;
825
+ }
826
+ if (toolName === 'marrow_get_memory') {
827
+ const result = await marrowGetMemory(API_KEY, BASE_URL, args.id, SESSION_ID);
828
+ success(id, {
829
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
830
+ });
831
+ return;
832
+ }
833
+ if (toolName === 'marrow_update_memory') {
834
+ const result = await marrowUpdateMemory(API_KEY, BASE_URL, args.id, {
835
+ text: args.text,
836
+ source: args.source,
837
+ tags: args.tags,
838
+ actor: args.actor,
839
+ note: args.note,
840
+ }, SESSION_ID);
841
+ success(id, {
842
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
843
+ });
844
+ return;
845
+ }
846
+ if (toolName === 'marrow_delete_memory') {
847
+ const result = await marrowDeleteMemory(API_KEY, BASE_URL, args.id, { actor: args.actor, note: args.note }, SESSION_ID);
848
+ success(id, {
849
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
850
+ });
851
+ return;
852
+ }
853
+ if (toolName === 'marrow_mark_outdated') {
854
+ const result = await marrowMarkOutdated(API_KEY, BASE_URL, args.id, { actor: args.actor, note: args.note }, SESSION_ID);
855
+ success(id, {
856
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
857
+ });
858
+ return;
859
+ }
860
+ if (toolName === 'marrow_supersede_memory') {
861
+ const result = await marrowSupersedeMemory(API_KEY, BASE_URL, args.id, {
862
+ text: args.text,
863
+ source: args.source,
864
+ tags: args.tags,
865
+ actor: args.actor,
866
+ note: args.note,
867
+ }, SESSION_ID);
868
+ success(id, {
869
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
870
+ });
871
+ return;
872
+ }
873
+ if (toolName === 'marrow_share_memory') {
874
+ const result = await marrowShareMemory(API_KEY, BASE_URL, args.id, args.agentIds || [], args.actor, SESSION_ID);
875
+ success(id, {
876
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
877
+ });
878
+ return;
879
+ }
880
+ if (toolName === 'marrow_export_memories') {
881
+ const result = await marrowExportMemories(API_KEY, BASE_URL, {
882
+ format: args.format,
883
+ status: args.status,
884
+ tags: args.tags,
885
+ }, SESSION_ID);
886
+ success(id, {
887
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
888
+ });
889
+ return;
890
+ }
891
+ if (toolName === 'marrow_import_memories') {
892
+ const result = await marrowImportMemories(API_KEY, BASE_URL, args.memories || [], args.mode || 'merge', SESSION_ID);
893
+ success(id, {
894
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
895
+ });
896
+ return;
897
+ }
898
+ if (toolName === 'marrow_retrieve_memories') {
899
+ const result = await marrowRetrieveMemories(API_KEY, BASE_URL, args.query, {
900
+ limit: args.limit,
901
+ from: args.from,
902
+ to: args.to,
903
+ tags: args.tags,
904
+ source: args.source,
905
+ status: args.status,
906
+ shared: args.shared,
907
+ }, SESSION_ID);
908
+ success(id, {
909
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
910
+ });
911
+ return;
912
+ }
913
+ error(id, -32601, `Method not found: ${toolName}`);
455
914
  return;
456
915
  }
457
- // Unknown method
458
916
  error(id, -32601, `Method not found: ${method}`);
459
917
  }
460
918
  catch (err) {
461
- const msg = err instanceof Error ? err.message.slice(0, 200) : 'Internal error';
462
- error(id, -32603, msg);
919
+ const message = err instanceof Error ? err.message : String(err);
920
+ error(id, -32000, message);
463
921
  }
464
922
  }
465
- // stdio server
923
+ // MCP stdio loop
466
924
  const rl = readline.createInterface({
467
925
  input: process.stdin,
468
926
  output: process.stdout,
469
- terminal: false,
470
927
  });
471
- rl.on('line', (line) => {
472
- const trimmed = line.trim();
473
- if (!trimmed)
474
- return;
928
+ rl.on('line', async (line) => {
475
929
  try {
476
- const req = JSON.parse(trimmed);
477
- handleRequest(req).catch((err) => {
478
- process.stderr.write(`Unhandled error: ${err}\n`);
479
- });
930
+ const msg = JSON.parse(line);
931
+ await handleRequest(msg);
480
932
  }
481
- catch {
482
- process.stderr.write(`Failed to parse request: ${trimmed}\n`);
933
+ catch (err) {
934
+ process.stderr.write(`[marrow] Parse error: ${err}\n`);
483
935
  }
484
936
  });
485
- rl.on('close', () => {
486
- process.exit(0);
487
- });
488
937
  //# sourceMappingURL=cli.js.map