@getmarrow/mcp 2.3.3 → 2.3.4
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 +28 -261
- package/dist/client.d.ts +86 -0
- package/dist/client.js +233 -0
- package/dist/index.d.ts +2 -109
- package/dist/index.js +125 -83
- package/dist/tools/check.d.ts +23 -0
- package/dist/tools/check.js +26 -0
- package/dist/tools/commit.d.ts +51 -0
- package/dist/tools/commit.js +46 -0
- package/dist/tools/orient.d.ts +33 -0
- package/dist/tools/orient.js +30 -0
- package/dist/tools/patterns.d.ts +15 -0
- package/dist/tools/patterns.js +21 -0
- package/dist/tools/think.d.ts +67 -0
- package/dist/tools/think.js +57 -0
- package/package.json +22 -8
- package/dist/cli.d.ts +0 -7
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -284
- package/dist/cli.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,287 +1,54 @@
|
|
|
1
|
-
#
|
|
1
|
+
# marrow-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
MCP server for [Marrow](https://getmarrow.ai) — decision intelligence for AI agents.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## What's New in v2.3.4
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
### Loop-enforcement tools for real agent operating discipline
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
This release adds the MCP surface for Marrow's live loop workflow so agents can orient, think, act, check, and commit inside a single session instead of treating memory as an afterthought.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
New/updated tool flow:
|
|
12
|
+
- `marrow_orient` / `orient` — start the session with loop state, recent lessons, and a recommended next step
|
|
13
|
+
- `marrow_think` / `think` — log intent and get pattern intelligence + loop metadata back
|
|
14
|
+
- `marrow_check` / `check` — inspect current loop state and recommended next step mid-session
|
|
15
|
+
- `marrow_commit` / `commit` — close the loop cleanly with outcome logging
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
---
|
|
17
|
+
What changed in practice:
|
|
18
|
+
- session-local loop state now persists across MCP tool calls
|
|
19
|
+
- agents get a canonical session-start nudge toward `marrow_think`
|
|
20
|
+
- commit now correctly returns a closed-loop state (`done` / `outcome_logged`)
|
|
21
|
+
- package is publish-ready as `@getmarrow/mcp`
|
|
20
22
|
|
|
21
|
-
##
|
|
22
|
-
|
|
23
|
-
Add to your `claude_desktop_config.json`:
|
|
23
|
+
## Quick Start
|
|
24
24
|
|
|
25
25
|
```json
|
|
26
|
+
// claude_desktop_config.json
|
|
26
27
|
{
|
|
27
28
|
"mcpServers": {
|
|
28
29
|
"marrow": {
|
|
29
30
|
"command": "npx",
|
|
30
31
|
"args": ["@getmarrow/mcp"],
|
|
31
|
-
"env": {
|
|
32
|
-
"MARROW_API_KEY": "your_key_from_getmarrow.ai"
|
|
33
|
-
}
|
|
32
|
+
"env": { "MARROW_API_KEY": "mrw_your_key_here" }
|
|
34
33
|
}
|
|
35
34
|
}
|
|
36
35
|
}
|
|
37
36
|
```
|
|
38
37
|
|
|
39
|
-
Restart Claude. That's it — Marrow is now available as a tool in every conversation.
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## Setup — Any MCP-Compatible Agent
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
export MARROW_API_KEY=your_key_from_getmarrow.ai # get your key at getmarrow.ai/dashboard
|
|
47
|
-
npx @getmarrow/mcp
|
|
48
|
-
# Listens on stdin, outputs JSON-RPC on stdout
|
|
49
|
-
# Drop into any MCP framework
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
## How It Works
|
|
55
|
-
|
|
56
|
-
Your agent calls `marrow_think` before acting. Marrow queries the hive — collective intelligence from every agent that's ever done something similar — and returns what worked, what patterns emerged, what playbooks exist. When your agent is done, that experience gets committed back to the hive. Every agent gets smarter. Forever.
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
## Quick Start Pattern
|
|
61
|
-
|
|
62
|
-
```
|
|
63
|
-
Session start → marrow_orient() ← reads from hive, surfaces warnings
|
|
64
|
-
Before each action → marrow_think() ← queries + opens memory session
|
|
65
|
-
After each action → marrow_commit() ← writes result back to hive
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
**orient() is what makes Marrow compound.** Without it, you're writing to a database no one reads. With it, your agent automatically avoids mistakes it's made before.
|
|
69
|
-
|
|
70
|
-
---
|
|
71
|
-
|
|
72
38
|
## Tools
|
|
73
39
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
**
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
{
|
|
80
|
-
"taskType": "implementation" // optional: filter to specific task type
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
**Response:**
|
|
85
|
-
```json
|
|
86
|
-
{
|
|
87
|
-
"warnings": [
|
|
88
|
-
{
|
|
89
|
-
"type": "process",
|
|
90
|
-
"failureRate": 0.26,
|
|
91
|
-
"message": "process has 26% failure rate over 27 decisions — review lessons before acting"
|
|
92
|
-
}
|
|
93
|
-
],
|
|
94
|
-
"shouldPause": false // true if any failure rate >40% — stop and query lessons first
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
If `shouldPause` is true, query your lessons before proceeding. If `warnings` is empty, you're clear.
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
### `marrow_think` — The core tool
|
|
103
|
-
|
|
104
|
-
Call this before every significant action. Returns collective intelligence from the hive.
|
|
105
|
-
|
|
106
|
-
**Input:**
|
|
107
|
-
```json
|
|
108
|
-
{
|
|
109
|
-
"action": "What the agent is about to do",
|
|
110
|
-
"type": "implementation | security | architecture | process | general",
|
|
111
|
-
"previous_outcome": "What happened last time (auto-commits previous session)",
|
|
112
|
-
"previous_success": true
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
**Returns:**
|
|
117
|
-
```json
|
|
118
|
-
{
|
|
119
|
-
"decision_id": "save this for the next call",
|
|
120
|
-
"intelligence": {
|
|
121
|
-
"similar": [
|
|
122
|
-
{ "outcome": "Used incremental approach. Shipped in 2 days, 0 rollbacks.", "confidence": 0.91 }
|
|
123
|
-
],
|
|
124
|
-
"similar_count": 3,
|
|
125
|
-
"patterns": [
|
|
126
|
-
{ "pattern_id": "a1b2c3", "decision_type": "implementation", "frequency": 84, "confidence": 0.9 }
|
|
127
|
-
],
|
|
128
|
-
"patterns_count": 2,
|
|
129
|
-
"templates": [
|
|
130
|
-
{ "steps": ["Plan", "Spec", "Build", "Test", "Deploy"], "success_rate": 0.89 }
|
|
131
|
-
],
|
|
132
|
-
"success_rate": 0.87,
|
|
133
|
-
"priority_score": 0.7,
|
|
134
|
-
"insight": "Workflow gap detected — barvis_audit missing after build",
|
|
135
|
-
"insights": [
|
|
136
|
-
{
|
|
137
|
-
"type": "workflow_gap",
|
|
138
|
-
"summary": "audit not logged after build (3 consecutive times)",
|
|
139
|
-
"action": "Run audit before reporting done",
|
|
140
|
-
"severity": "critical",
|
|
141
|
-
"count": 3
|
|
142
|
-
}
|
|
143
|
-
],
|
|
144
|
-
"cluster_id": "818df2fa-6365-49f6-b478-bc3dcb748469"
|
|
145
|
-
},
|
|
146
|
-
"stream_url": "/v1/stream?format=sse"
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
**Example prompt to Claude:**
|
|
151
|
-
|
|
152
|
-
> Before starting this refactor, call `marrow_think` with action "Refactoring auth module to support OAuth 2.0" and type "implementation". Use the intelligence to guide your approach.
|
|
153
|
-
|
|
154
|
-
Claude will automatically:
|
|
155
|
-
1. Call `marrow_think`
|
|
156
|
-
2. Read what worked for other agents doing similar refactors
|
|
157
|
-
3. Apply those patterns to its approach
|
|
158
|
-
4. On the next `marrow_think` call, commit this outcome back to the hive
|
|
159
|
-
|
|
160
|
-
---
|
|
161
|
-
|
|
162
|
-
### `marrow_commit` — Explicit commit
|
|
163
|
-
|
|
164
|
-
Commit an outcome explicitly when you need more control. `marrow_think` auto-commits on the next call, so this is optional.
|
|
165
|
-
|
|
166
|
-
**Input:**
|
|
167
|
-
```json
|
|
168
|
-
{
|
|
169
|
-
"decision_id": "from previous marrow_think",
|
|
170
|
-
"success": true,
|
|
171
|
-
"outcome": "Auth refactor complete. OAuth 2.0 working. Zero breaking changes. 2 day turnaround.",
|
|
172
|
-
"caused_by": "optional: previous decision_id to link causally"
|
|
173
|
-
}
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
---
|
|
177
|
-
|
|
178
|
-
### `marrow_status` — Platform health
|
|
179
|
-
|
|
180
|
-
Check your agent's current performance across the hive.
|
|
181
|
-
|
|
182
|
-
**Returns:**
|
|
183
|
-
```json
|
|
184
|
-
{
|
|
185
|
-
"success_rate": 0.87,
|
|
186
|
-
"decision_velocity": 142,
|
|
187
|
-
"patterns_discovered": 23,
|
|
188
|
-
"hive_consensus": 0.91
|
|
189
|
-
}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
---
|
|
193
|
-
|
|
194
|
-
## Real Example — Claude Coding Agent
|
|
195
|
-
|
|
196
|
-
**System prompt addition:**
|
|
197
|
-
```
|
|
198
|
-
Before starting any significant task, call marrow_think with a description of what you're about to do.
|
|
199
|
-
After completing a task, the next marrow_think call will automatically record the outcome.
|
|
200
|
-
Use the intelligence returned to guide your approach — especially `similar` and `patterns`.
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
**What happens in practice:**
|
|
204
|
-
|
|
205
|
-
User: *"Refactor the payment service to add retry logic"*
|
|
206
|
-
|
|
207
|
-
Claude calls `marrow_think`:
|
|
208
|
-
```json
|
|
209
|
-
{ "action": "Adding retry logic to payment service", "type": "implementation" }
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
Marrow returns:
|
|
213
|
-
```json
|
|
214
|
-
{
|
|
215
|
-
"intelligence": {
|
|
216
|
-
"similar": [
|
|
217
|
-
{ "outcome": "Exponential backoff with jitter. 3 retries max. Idempotency key required. Zero double-charges in 6 months.", "confidence": 0.96 }
|
|
218
|
-
],
|
|
219
|
-
"patterns": [
|
|
220
|
-
{ "pattern": "Retry without idempotency key causes duplicate transactions in 23% of cases", "frequency": 156 }
|
|
221
|
-
]
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
Claude now knows — from thousands of other agents' experience — exactly what to do and what to avoid. Without Marrow, it would have guessed.
|
|
227
|
-
|
|
228
|
-
---
|
|
229
|
-
|
|
230
|
-
## Why Marrow for MCP?
|
|
231
|
-
|
|
232
|
-
- **Zero setup friction** — one config entry, works instantly
|
|
233
|
-
- **Model agnostic** — Claude, GPT, Gemini, any MCP-compatible model
|
|
234
|
-
- **Compounds automatically** — every session adds to the hive, no manual curation
|
|
235
|
-
- **Private by default** — your agent's decisions are anonymized before entering the hive
|
|
236
|
-
- **Persistent across sessions** — memory survives context resets, model upgrades, new conversations
|
|
237
|
-
|
|
238
|
-
---
|
|
239
|
-
|
|
240
|
-
## Get Started
|
|
241
|
-
|
|
242
|
-
```bash
|
|
243
|
-
npm install -g @getmarrow/mcp
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
1. Get your API key at [getmarrow.ai](https://getmarrow.ai)
|
|
247
|
-
2. Add the MCP server config above
|
|
248
|
-
3. Tell your agent to call `marrow_think` before acting
|
|
249
|
-
4. Watch collective intelligence flow in from the first call
|
|
250
|
-
|
|
251
|
-
**Your agent inherits the experience of every agent that came before it.**
|
|
252
|
-
|
|
253
|
-
---
|
|
254
|
-
|
|
255
|
-
*Compatible with Claude Desktop, Claude API, and any MCP-compatible framework.*
|
|
256
|
-
|
|
257
|
-
---
|
|
258
|
-
|
|
259
|
-
## Privacy & Data
|
|
260
|
-
|
|
261
|
-
**What Marrow collects:**
|
|
262
|
-
- The `action` string and `type` you pass to `marrow_think` and `marrow_commit`
|
|
263
|
-
- The `outcome` you report — used to train collective intelligence
|
|
264
|
-
- Decision metadata: timestamps, success/failure, confidence scores
|
|
40
|
+
- **marrow_orient** / `orient` — Start the session with loop state, recent lessons, and a recommended next step
|
|
41
|
+
- **marrow_think** / `think` — Log a decision and get pattern intelligence, loop metadata, and useful next-step guidance back
|
|
42
|
+
- **marrow_commit** / `commit` — Commit task outcome to the hive and close the loop
|
|
43
|
+
- **marrow_check** / `check` — Inspect current loop state for this MCP session
|
|
44
|
+
- **patterns** — Get accumulated decision patterns
|
|
265
45
|
|
|
266
|
-
|
|
267
|
-
- No PII (names, emails, user IDs, IP addresses)
|
|
268
|
-
- No conversation content or message history
|
|
269
|
-
- No code, credentials, or file contents
|
|
270
|
-
- No agent identity beyond your anonymous API key hash
|
|
46
|
+
## Session hint
|
|
271
47
|
|
|
272
|
-
|
|
273
|
-
- All inputs are stripped of PII patterns before storage (emails, phone numbers, UUIDs matching user ID formats, IP addresses)
|
|
274
|
-
- Your API key is stored as a SHA-256 hash — never in plaintext
|
|
275
|
-
- Hive intelligence is aggregated across agents — individual decisions are never exposed to other users
|
|
276
|
-
- You can opt out of hive contribution at any time by setting `contributeToHive: false` in your MCP config
|
|
48
|
+
At session start, Marrow nudges clients with the canonical reminder:
|
|
277
49
|
|
|
278
|
-
|
|
279
|
-
- Decisions you log contribute anonymously to collective patterns
|
|
280
|
-
- No one can trace a pattern back to your agent or your account
|
|
281
|
-
- Enterprise plans include private org hive mode — your data never leaves your org
|
|
50
|
+
> Tip: log plans, decisions, and outcomes to Marrow so your agent improves over time.
|
|
282
51
|
|
|
283
|
-
|
|
284
|
-
- Decision data: 30 days on Free tier, 90 days on Pro, unlimited on Enterprise
|
|
285
|
-
- You can delete all your data at any time via the dashboard or API
|
|
52
|
+
## Get an API key
|
|
286
53
|
|
|
287
|
-
|
|
54
|
+
Sign up at [getmarrow.ai](https://getmarrow.ai)
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marrow API client — thin HTTP wrapper
|
|
3
|
+
*/
|
|
4
|
+
export type MarrowLoopRecommendation = 'orient' | 'think' | 'act' | 'commit' | 'done';
|
|
5
|
+
export interface MarrowLoopState {
|
|
6
|
+
orientedAt: string | null;
|
|
7
|
+
lastThinkAt: string | null;
|
|
8
|
+
lastOutcomeAt: string | null;
|
|
9
|
+
hasIntentLog: boolean;
|
|
10
|
+
hasOutcomeLog: boolean;
|
|
11
|
+
actionCountSinceLastThink: number;
|
|
12
|
+
externalActionCountSinceLastThink: number;
|
|
13
|
+
lastDecisionId: string | null;
|
|
14
|
+
pendingDecisionId: string | null;
|
|
15
|
+
recommendedNext: MarrowLoopRecommendation;
|
|
16
|
+
loopState: 'idle' | 'oriented' | 'intent_logged' | 'acting' | 'outcome_logged';
|
|
17
|
+
message: string | null;
|
|
18
|
+
hints: string[];
|
|
19
|
+
}
|
|
20
|
+
export declare class MarrowClient {
|
|
21
|
+
private apiKey;
|
|
22
|
+
private baseUrl;
|
|
23
|
+
private currentDecisionId;
|
|
24
|
+
private loopState;
|
|
25
|
+
constructor();
|
|
26
|
+
check(): {
|
|
27
|
+
ok: boolean;
|
|
28
|
+
recommendedNext: MarrowLoopRecommendation;
|
|
29
|
+
state: {
|
|
30
|
+
hints: string[];
|
|
31
|
+
orientedAt: string | null;
|
|
32
|
+
lastThinkAt: string | null;
|
|
33
|
+
lastOutcomeAt: string | null;
|
|
34
|
+
hasIntentLog: boolean;
|
|
35
|
+
hasOutcomeLog: boolean;
|
|
36
|
+
actionCountSinceLastThink: number;
|
|
37
|
+
externalActionCountSinceLastThink: number;
|
|
38
|
+
lastDecisionId: string | null;
|
|
39
|
+
pendingDecisionId: string | null;
|
|
40
|
+
recommendedNext: MarrowLoopRecommendation;
|
|
41
|
+
loopState: "idle" | "oriented" | "intent_logged" | "acting" | "outcome_logged";
|
|
42
|
+
message: string | null;
|
|
43
|
+
};
|
|
44
|
+
warnings: string[];
|
|
45
|
+
};
|
|
46
|
+
orient(params?: {
|
|
47
|
+
task_type?: string;
|
|
48
|
+
}): Promise<Record<string, unknown>>;
|
|
49
|
+
think(params: {
|
|
50
|
+
action: string;
|
|
51
|
+
type: string;
|
|
52
|
+
previous_decision_id?: string;
|
|
53
|
+
previous_success?: boolean;
|
|
54
|
+
previous_outcome?: string;
|
|
55
|
+
}): Promise<Record<string, unknown>>;
|
|
56
|
+
commit(params: {
|
|
57
|
+
decision_id: string;
|
|
58
|
+
success: boolean;
|
|
59
|
+
outcome: string;
|
|
60
|
+
}): Promise<Record<string, unknown>>;
|
|
61
|
+
recordAction(meta?: {
|
|
62
|
+
external?: boolean;
|
|
63
|
+
}): {
|
|
64
|
+
ok: boolean;
|
|
65
|
+
recommendedNext: MarrowLoopRecommendation;
|
|
66
|
+
state: {
|
|
67
|
+
hints: string[];
|
|
68
|
+
orientedAt: string | null;
|
|
69
|
+
lastThinkAt: string | null;
|
|
70
|
+
lastOutcomeAt: string | null;
|
|
71
|
+
hasIntentLog: boolean;
|
|
72
|
+
hasOutcomeLog: boolean;
|
|
73
|
+
actionCountSinceLastThink: number;
|
|
74
|
+
externalActionCountSinceLastThink: number;
|
|
75
|
+
lastDecisionId: string | null;
|
|
76
|
+
pendingDecisionId: string | null;
|
|
77
|
+
recommendedNext: MarrowLoopRecommendation;
|
|
78
|
+
loopState: "idle" | "oriented" | "intent_logged" | "acting" | "outcome_logged";
|
|
79
|
+
message: string | null;
|
|
80
|
+
};
|
|
81
|
+
warnings: string[];
|
|
82
|
+
};
|
|
83
|
+
patterns(): Promise<Record<string, unknown>[]>;
|
|
84
|
+
private post;
|
|
85
|
+
private get;
|
|
86
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Marrow API client — thin HTTP wrapper
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MarrowClient = void 0;
|
|
7
|
+
const DEFAULT_BASE_URL = 'https://api.getmarrow.ai';
|
|
8
|
+
const SESSION_START_HINT = 'Tip: log plans, decisions, and outcomes to Marrow so your agent improves over time.';
|
|
9
|
+
const POST_ORIENT_NUDGE = 'You have not logged any decisions yet this session. Before acting, call marrow_think.';
|
|
10
|
+
const PRE_EXIT_REMINDER = 'Before ending the session, log the outcome to Marrow so the loop closes cleanly.';
|
|
11
|
+
function nowIso() {
|
|
12
|
+
return new Date().toISOString();
|
|
13
|
+
}
|
|
14
|
+
class MarrowClient {
|
|
15
|
+
apiKey;
|
|
16
|
+
baseUrl;
|
|
17
|
+
currentDecisionId = null;
|
|
18
|
+
loopState = {
|
|
19
|
+
orientedAt: null,
|
|
20
|
+
lastThinkAt: null,
|
|
21
|
+
lastOutcomeAt: null,
|
|
22
|
+
hasIntentLog: false,
|
|
23
|
+
hasOutcomeLog: false,
|
|
24
|
+
actionCountSinceLastThink: 0,
|
|
25
|
+
externalActionCountSinceLastThink: 0,
|
|
26
|
+
lastDecisionId: null,
|
|
27
|
+
pendingDecisionId: null,
|
|
28
|
+
recommendedNext: 'orient',
|
|
29
|
+
loopState: 'idle',
|
|
30
|
+
message: SESSION_START_HINT,
|
|
31
|
+
hints: [SESSION_START_HINT],
|
|
32
|
+
};
|
|
33
|
+
constructor() {
|
|
34
|
+
const key = process.env.MARROW_API_KEY;
|
|
35
|
+
if (!key) {
|
|
36
|
+
throw new Error('MARROW_API_KEY environment variable is required');
|
|
37
|
+
}
|
|
38
|
+
this.apiKey = key;
|
|
39
|
+
this.baseUrl = process.env.MARROW_BASE_URL || DEFAULT_BASE_URL;
|
|
40
|
+
}
|
|
41
|
+
check() {
|
|
42
|
+
if (!this.loopState.orientedAt) {
|
|
43
|
+
this.loopState.recommendedNext = 'orient';
|
|
44
|
+
this.loopState.loopState = 'idle';
|
|
45
|
+
this.loopState.message = SESSION_START_HINT;
|
|
46
|
+
this.loopState.hints = [SESSION_START_HINT];
|
|
47
|
+
}
|
|
48
|
+
else if (this.loopState.hasOutcomeLog) {
|
|
49
|
+
this.loopState.recommendedNext = 'done';
|
|
50
|
+
this.loopState.loopState = 'outcome_logged';
|
|
51
|
+
this.loopState.message = 'Loop closed. Ready for the next task.';
|
|
52
|
+
this.loopState.hints = ['Loop closed. Ready for the next task.'];
|
|
53
|
+
}
|
|
54
|
+
else if (!this.loopState.hasIntentLog) {
|
|
55
|
+
this.loopState.recommendedNext = 'think';
|
|
56
|
+
this.loopState.loopState = 'oriented';
|
|
57
|
+
this.loopState.message = POST_ORIENT_NUDGE;
|
|
58
|
+
this.loopState.hints = [SESSION_START_HINT, POST_ORIENT_NUDGE];
|
|
59
|
+
}
|
|
60
|
+
else if (!this.loopState.hasOutcomeLog && this.loopState.actionCountSinceLastThink > 0) {
|
|
61
|
+
this.loopState.recommendedNext = 'commit';
|
|
62
|
+
this.loopState.loopState = 'acting';
|
|
63
|
+
this.loopState.message = PRE_EXIT_REMINDER;
|
|
64
|
+
this.loopState.hints = [PRE_EXIT_REMINDER];
|
|
65
|
+
}
|
|
66
|
+
else if (!this.loopState.hasOutcomeLog) {
|
|
67
|
+
this.loopState.recommendedNext = 'act';
|
|
68
|
+
this.loopState.loopState = 'intent_logged';
|
|
69
|
+
this.loopState.message = 'Intent logged. Act, then log the outcome.';
|
|
70
|
+
this.loopState.hints = ['Intent logged. Act, then log the outcome.'];
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
this.loopState.recommendedNext = 'done';
|
|
74
|
+
this.loopState.loopState = 'outcome_logged';
|
|
75
|
+
this.loopState.message = 'Loop closed. Ready for the next task.';
|
|
76
|
+
this.loopState.hints = ['Loop closed. Ready for the next task.'];
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
ok: true,
|
|
80
|
+
recommendedNext: this.loopState.recommendedNext,
|
|
81
|
+
state: { ...this.loopState, hints: [...this.loopState.hints] },
|
|
82
|
+
warnings: [...this.loopState.hints],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
async orient(params) {
|
|
86
|
+
const patterns = await this.get(`/v1/agent/patterns${params?.task_type ? `?type=${encodeURIComponent(params.task_type)}` : ''}`);
|
|
87
|
+
let lessons = [];
|
|
88
|
+
try {
|
|
89
|
+
const lessonRes = await this.get('/v1/agent/think/history?type=lesson&limit=5');
|
|
90
|
+
lessons = (lessonRes.data
|
|
91
|
+
|| lessonRes.items
|
|
92
|
+
|| lessonRes.decisions
|
|
93
|
+
|| []);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
lessons = [];
|
|
97
|
+
}
|
|
98
|
+
this.loopState.orientedAt = nowIso();
|
|
99
|
+
const loop = this.check();
|
|
100
|
+
const failurePatterns = (patterns.data?.failure_patterns
|
|
101
|
+
|| patterns.failure_patterns
|
|
102
|
+
|| []);
|
|
103
|
+
return {
|
|
104
|
+
session_start_hint: SESSION_START_HINT,
|
|
105
|
+
nudge: this.loopState.hasIntentLog ? null : POST_ORIENT_NUDGE,
|
|
106
|
+
recommended_next: loop.recommendedNext,
|
|
107
|
+
loop: loop.state,
|
|
108
|
+
warnings: failurePatterns
|
|
109
|
+
.filter((item) => Number(item.failureRate || item.failure_rate || 0) > 0.15)
|
|
110
|
+
.map((item) => ({
|
|
111
|
+
type: String(item.decisionType || item.decision_type || 'general'),
|
|
112
|
+
failure_rate: Number(item.failureRate || item.failure_rate || 0),
|
|
113
|
+
message: `${String(item.decisionType || item.decision_type || 'general')} has ${Math.round(Number(item.failureRate || item.failure_rate || 0) * 100)}% failure rate over ${Number(item.count || 0)} decisions — check lessons before proceeding`,
|
|
114
|
+
})),
|
|
115
|
+
lessons: lessons.map((lesson) => ({
|
|
116
|
+
summary: String(lesson.action || lesson.summary || ''),
|
|
117
|
+
severity: 'info',
|
|
118
|
+
})),
|
|
119
|
+
text: [SESSION_START_HINT, POST_ORIENT_NUDGE, `Recommended next step: ${loop.recommendedNext}.`].join(' '),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
async think(params) {
|
|
123
|
+
if (params.previous_decision_id !== undefined && !params.previous_decision_id.trim()) {
|
|
124
|
+
throw new Error('decision_id must be a non-empty string');
|
|
125
|
+
}
|
|
126
|
+
const result = await this.post('/v1/agent/think', params);
|
|
127
|
+
const data = result.data || result;
|
|
128
|
+
const intelligence = data.intelligence || {};
|
|
129
|
+
const decisionId = typeof data.decision_id === 'string' && data.decision_id.trim()
|
|
130
|
+
? data.decision_id
|
|
131
|
+
: null;
|
|
132
|
+
this.loopState.orientedAt = this.loopState.orientedAt || nowIso();
|
|
133
|
+
this.loopState.lastThinkAt = nowIso();
|
|
134
|
+
this.loopState.hasIntentLog = true;
|
|
135
|
+
this.loopState.hasOutcomeLog = false;
|
|
136
|
+
this.loopState.actionCountSinceLastThink = 0;
|
|
137
|
+
this.loopState.externalActionCountSinceLastThink = 0;
|
|
138
|
+
this.currentDecisionId = decisionId;
|
|
139
|
+
this.loopState.pendingDecisionId = decisionId;
|
|
140
|
+
this.loopState.lastDecisionId = decisionId;
|
|
141
|
+
const loop = this.check();
|
|
142
|
+
return {
|
|
143
|
+
...result,
|
|
144
|
+
data: {
|
|
145
|
+
...data,
|
|
146
|
+
accepted_as: 'intent',
|
|
147
|
+
warnings: loop.warnings,
|
|
148
|
+
recommended_next: loop.recommendedNext,
|
|
149
|
+
loop: loop.state,
|
|
150
|
+
summary: [
|
|
151
|
+
'Intent logged to Marrow.',
|
|
152
|
+
intelligence.insight ? `Pattern hint: ${String(intelligence.insight)}` : null,
|
|
153
|
+
`Recommended next step: ${loop.recommendedNext}.`,
|
|
154
|
+
].filter(Boolean).join(' '),
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
async commit(params) {
|
|
159
|
+
const decisionId = params.decision_id.trim();
|
|
160
|
+
if (!decisionId) {
|
|
161
|
+
throw new Error('decision_id must be a non-empty string');
|
|
162
|
+
}
|
|
163
|
+
if (!this.currentDecisionId) {
|
|
164
|
+
throw new Error('No active decision. Call marrow_think first.');
|
|
165
|
+
}
|
|
166
|
+
if (decisionId !== this.currentDecisionId) {
|
|
167
|
+
throw new Error(`decision_id mismatch: expected ${this.currentDecisionId}`);
|
|
168
|
+
}
|
|
169
|
+
const result = await this.post('/v1/agent/commit', {
|
|
170
|
+
...params,
|
|
171
|
+
decision_id: decisionId,
|
|
172
|
+
});
|
|
173
|
+
const data = result.data || result;
|
|
174
|
+
this.loopState.lastOutcomeAt = nowIso();
|
|
175
|
+
this.loopState.hasIntentLog = false;
|
|
176
|
+
this.loopState.hasOutcomeLog = true;
|
|
177
|
+
this.loopState.actionCountSinceLastThink = 0;
|
|
178
|
+
this.loopState.externalActionCountSinceLastThink = 0;
|
|
179
|
+
this.loopState.pendingDecisionId = null;
|
|
180
|
+
this.loopState.lastDecisionId = decisionId;
|
|
181
|
+
this.currentDecisionId = null;
|
|
182
|
+
const loop = this.check();
|
|
183
|
+
return {
|
|
184
|
+
...result,
|
|
185
|
+
data: {
|
|
186
|
+
...data,
|
|
187
|
+
accepted_as: 'outcome',
|
|
188
|
+
recommended_next: loop.recommendedNext,
|
|
189
|
+
loop: loop.state,
|
|
190
|
+
summary: 'Outcome logged to Marrow. Loop closed.',
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
recordAction(meta) {
|
|
195
|
+
this.loopState.actionCountSinceLastThink += 1;
|
|
196
|
+
if (meta?.external)
|
|
197
|
+
this.loopState.externalActionCountSinceLastThink += 1;
|
|
198
|
+
return this.check();
|
|
199
|
+
}
|
|
200
|
+
async patterns() {
|
|
201
|
+
const res = await this.get('/v1/patterns');
|
|
202
|
+
return res?.data || [];
|
|
203
|
+
}
|
|
204
|
+
async post(path, body) {
|
|
205
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
206
|
+
method: 'POST',
|
|
207
|
+
headers: {
|
|
208
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
209
|
+
'Content-Type': 'application/json',
|
|
210
|
+
},
|
|
211
|
+
body: JSON.stringify(body),
|
|
212
|
+
});
|
|
213
|
+
if (!res.ok) {
|
|
214
|
+
const text = (await res.text()).slice(0, 200);
|
|
215
|
+
throw new Error(`Marrow API error (${res.status}): ${text}`);
|
|
216
|
+
}
|
|
217
|
+
return res.json();
|
|
218
|
+
}
|
|
219
|
+
async get(path) {
|
|
220
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
221
|
+
method: 'GET',
|
|
222
|
+
headers: {
|
|
223
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
if (!res.ok) {
|
|
227
|
+
const text = (await res.text()).slice(0, 200);
|
|
228
|
+
throw new Error(`Marrow API error (${res.status}): ${text}`);
|
|
229
|
+
}
|
|
230
|
+
return res.json();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
exports.MarrowClient = MarrowClient;
|