@getmarrow/install 0.1.25 → 0.1.27
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 +82 -125
- package/package.json +5 -4
- package/src/governed-runner.js +123 -5
- package/src/installer.js +99 -22
package/README.md
CHANGED
|
@@ -1,104 +1,66 @@
|
|
|
1
1
|
# @getmarrow/install
|
|
2
2
|
|
|
3
|
-
Universal installer for Marrow passive
|
|
3
|
+
Universal installer for Marrow agent governance, proof, and passive control setup.
|
|
4
4
|
|
|
5
|
-
Use it when you want Marrow to detect the local agent/runtime environment and wire the safest passive integration automatically.
|
|
5
|
+
Use it when you want Marrow to detect the local agent/runtime environment and wire the safest passive governance integration automatically.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npx @getmarrow/install --dry-run
|
|
9
9
|
npx @getmarrow/install --yes
|
|
10
10
|
npx @getmarrow/install --repair
|
|
11
11
|
npx @getmarrow/install doctor
|
|
12
|
+
npx @getmarrow/install govern
|
|
12
13
|
npx @getmarrow/install fleet
|
|
13
14
|
npx @getmarrow/install integrations
|
|
14
15
|
npx @getmarrow/install hermes
|
|
15
16
|
npx @getmarrow/install openclaw
|
|
16
17
|
```
|
|
17
18
|
|
|
18
|
-
## What's New in v0.1.
|
|
19
|
-
|
|
20
|
-
v0.1.25 adds first-class harness add-on paths for Hermes Agent and OpenClaw.
|
|
21
|
-
|
|
22
|
-
- `npx @getmarrow/install integrations` lists Marrow-supported harness add-ons.
|
|
23
|
-
- `npx @getmarrow/install hermes` shows the Hermes attachment path and, when `MARROW_API_KEY` is present, verifies the backend integration contract.
|
|
24
|
-
- `npx @getmarrow/install openclaw` shows the OpenClaw attachment path and verifies the backend integration contract.
|
|
25
|
-
- `npx @getmarrow/install govern` now detects Hermes config files alongside Codex, Claude Code, Cursor, OpenCode, OpenClaw, CI scripts, and custom commands.
|
|
26
|
-
- Hermes `/goal`, verification evidence, `/learn`, `/journey`, and background subagents map into Marrow runtime gates, proof packs, fleet lessons, governance timeline, and outcome closure.
|
|
27
|
-
- OpenClaw sessions, handoff/result files, watchdog checkpoints, silent exits, and release supervisor results map into Marrow workflow sessions, proof packs, stale-outcome detection, and reusable handoff lessons.
|
|
28
|
-
|
|
29
|
-
This is the intended positioning: keep the harness your team already uses, and add Marrow as the governance, proof, and fleet-intelligence layer around it.
|
|
30
|
-
|
|
31
|
-
Examples:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install hermes
|
|
35
|
-
MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install openclaw
|
|
36
|
-
MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install integrations --json
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## What's New in v0.1.20
|
|
19
|
+
## What's New in v0.1.26
|
|
40
20
|
|
|
41
|
-
v0.1.
|
|
21
|
+
v0.1.26 expands Marrow's supported harness registry so teams can attach governance to the agent tools, IDE agents, model CLIs, MCP clients, CI runners, and custom harnesses they already use.
|
|
42
22
|
|
|
43
|
-
- `npx @getmarrow/install
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
23
|
+
- `npx @getmarrow/install integrations` lists supported surfaces, support level, install command, capture path, and detection signals.
|
|
24
|
+
- `GET /v1/agent/data-quality` lets agents verify attribution coverage after install, including agent ID, source metadata, harness/client, workflow type, outcome closure, and exact repair guidance.
|
|
25
|
+
- `GET /v1/agent/integrations/{client}` gives supported harnesses a per-client setup guide with install command, capture points, source metadata, runtime/commit/batch mapping, and exact next action.
|
|
26
|
+
- `npx @getmarrow/install govern` detects Codex, Claude Code, Cursor, Cursor Composer, Windsurf, Cline, OpenCode, Hermes, OpenClaw, Gemini, Grok, DeepSeek, Qwen, Kimi, MiniMax, GLM, MCP clients, CI scripts, and custom commands.
|
|
27
|
+
- Hermes Agent and OpenClaw remain first-class add-ons.
|
|
28
|
+
- Codex, Claude Code, Cursor, Cursor Composer, Windsurf, Cline, OpenCode, Gemini, Grok, DeepSeek, Qwen, Kimi, MiniMax, GLM, MCP clients, CI scripts, and custom shell/API harnesses can use MCP/SDK hooks, the governed runner, or `POST /v1/agent/integrations/events`.
|
|
29
|
+
- Custom harness events should include `harness`, `event_type`, `agent_id`, and `action`, so Marrow can return the right runtime, commit, or batch-ingest next step without storing unsupported or under-attributed raw payloads.
|
|
48
30
|
|
|
49
|
-
|
|
31
|
+
The positioning is simple: keep the harness your team already uses, and add Marrow as the governance, proof, and fleet-intelligence layer around it.
|
|
50
32
|
|
|
51
|
-
|
|
52
|
-
MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install fleet
|
|
53
|
-
MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install fleet --no-interactive
|
|
54
|
-
MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install fleet --json
|
|
55
|
-
```
|
|
33
|
+
## What's New in v0.1.24
|
|
56
34
|
|
|
57
|
-
|
|
35
|
+
v0.1.24 aligns the installer docs with Marrow's control-plane direction.
|
|
58
36
|
|
|
59
|
-
|
|
37
|
+
- Marrow is positioned as an agent governance, proof, and intelligence layer, not a single-agent memory app.
|
|
38
|
+
- The recommended setup path remains passive by default: install once, then let agents call runtime gates, completion contracts, and outcome closure as they work.
|
|
39
|
+
- Hermes Agent is a supported client label and integration target; Marrow maps Hermes goals, verification evidence, learning, journey, and subagent outcomes into fleet governance proof.
|
|
40
|
+
- New backend surfaces include governance control-plane proof, completion-contract evaluation, fleet governance timeline, and buyer-grade value proof.
|
|
60
41
|
|
|
61
|
-
|
|
62
|
-
- When `MARROW_API_KEY` is present, the TUI asks Marrow for a recommended mode: `passive`, `pilot`, or `enforce`.
|
|
63
|
-
- The TUI shows the exact reasons, confidence, and selected command before the user applies anything.
|
|
64
|
-
- User choice is explicit. Marrow logs whether the recommendation was accepted or overridden, but it does not silently switch modes.
|
|
65
|
-
- Policy profiles are supported by the backend/SDK/MCP so businesses can define rules like local=passive, staging=pilot, production deploys=enforce.
|
|
42
|
+
## What's New in v0.1.23
|
|
66
43
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
```text
|
|
70
|
-
Recommended mode: pilot
|
|
71
|
-
Reason:
|
|
72
|
-
- Node project detected
|
|
73
|
-
- Cloudflare Worker detected
|
|
74
|
-
- GitHub workflow detected
|
|
75
|
-
- No owner approval policy configured yet
|
|
76
|
-
```
|
|
44
|
+
v0.1.23 improves attribution quality so Marrow can produce cleaner per-agent, per-harness, and per-workflow value reports.
|
|
77
45
|
|
|
78
|
-
|
|
46
|
+
- Installer self-tests and governed runner calls attach `source_meta.channel`, `source_meta.client`, and inferred workflow intent by default.
|
|
47
|
+
- Generated passive runtime keeps using SDK defaults for `agent_id`, harness/client, source channel, and user intent attribution.
|
|
48
|
+
- Set `MARROW_CLIENT`, `MARROW_HARNESS`, or `MARROW_AGENT_CLIENT` to labels such as `codex`, `claude-code`, `cursor`, `gemini`, `qwen`, `opencode`, `hermes`, `openclaw`, or `custom`.
|
|
49
|
+
- Invalid or unknown client labels fall back to `custom` instead of breaking onboarding.
|
|
50
|
+
- Marrow still does not store prompts, completions, tool stdout/stderr, plaintext API keys, or raw model output for this feature.
|
|
79
51
|
|
|
80
|
-
|
|
52
|
+
Business value: dashboards and reports can show which agents, harnesses, and workflow types are improving instead of grouping too much work into "unknown."
|
|
81
53
|
|
|
82
|
-
|
|
83
|
-
- Choose passive setup, governed pilot mode, or governed enforce mode.
|
|
84
|
-
- Run passive setup + self-test from the TUI after explicit confirmation.
|
|
85
|
-
- Check Marrow status and test the before-action gate from the same screen.
|
|
86
|
-
- Print the exact command for the selected harness/mode so users know what to run next.
|
|
87
|
-
- Exit cleanly with `q`, `Esc`, or `Ctrl+C`.
|
|
88
|
-
- CI/non-TTY usage remains stable with `npx @getmarrow/install govern --no-interactive`.
|
|
54
|
+
## Governed Runner
|
|
89
55
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
## What's New in v0.1.12
|
|
93
|
-
|
|
94
|
-
v0.1.12 adds the Marrow governed runner for businesses that want agent governance without replacing their existing harness.
|
|
56
|
+
The Marrow governed runner is for businesses that want agent governance without replacing their existing harness.
|
|
95
57
|
|
|
96
58
|
- `npx @getmarrow/install govern` prints a setup panel for detected harnesses and recommended protected commands.
|
|
97
59
|
- `npx @getmarrow/install run --agent <agent-id> -- <command>` wraps existing agent, deploy, merge, publish, migration, and verification commands with Marrow's pre-action runtime gate.
|
|
98
60
|
- Risky actions can fail closed by default when Marrow requires owner approval, blocks an action, or requires missing proof.
|
|
99
61
|
- Successful and failed commands automatically close outcomes through `/v1/agent/commit` with a redacted proof pack.
|
|
100
62
|
- The runner sends action and command metadata only; it does not upload command stdout, stderr, full environment values, or plaintext API keys.
|
|
101
|
-
- This gives teams a thin governance path for Codex, Claude Code, OpenClaw, OpenCode,
|
|
63
|
+
- This gives teams a thin governance path for Codex, Claude Code, Cursor, Cursor Composer, Windsurf, Cline, OpenClaw, Hermes, OpenCode, Gemini, Grok, DeepSeek, Qwen, Kimi, MiniMax, GLM, MCP clients, CI scripts, and custom shell-based agents.
|
|
102
64
|
|
|
103
65
|
Business value: Marrow can sit in front of the commands that matter most, tell the agent what prior lesson or proof is required before action, and produce an audit-ready outcome trail after the command finishes.
|
|
104
66
|
|
|
@@ -110,28 +72,22 @@ Preview the detected harnesses and protected command examples:
|
|
|
110
72
|
npx @getmarrow/install govern
|
|
111
73
|
```
|
|
112
74
|
|
|
113
|
-
In a real terminal, this opens the interactive setup flow. In CI or scripts, use:
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
npx @getmarrow/install govern --no-interactive
|
|
117
|
-
```
|
|
118
|
-
|
|
119
75
|
Run a harmless command through Marrow:
|
|
120
76
|
|
|
121
77
|
```bash
|
|
122
|
-
MARROW_API_KEY
|
|
78
|
+
MARROW_API_KEY=<your_marrow_key> npx @getmarrow/install run --agent codex-prod --profile production -- node -e "process.exit(0)"
|
|
123
79
|
```
|
|
124
80
|
|
|
125
81
|
Gate a production action before the agent executes it:
|
|
126
82
|
|
|
127
83
|
```bash
|
|
128
|
-
MARROW_API_KEY
|
|
84
|
+
MARROW_API_KEY=<your_marrow_key> npx @getmarrow/install gate "deploy production worker after tests pass"
|
|
129
85
|
```
|
|
130
86
|
|
|
131
87
|
Wrap a real deploy, publish, merge, or migration command only after the agent has the required proof:
|
|
132
88
|
|
|
133
89
|
```bash
|
|
134
|
-
MARROW_API_KEY
|
|
90
|
+
MARROW_API_KEY=<your_marrow_key> npx @getmarrow/install run \
|
|
135
91
|
--agent deploy-agent \
|
|
136
92
|
--type deploy \
|
|
137
93
|
--profile production \
|
|
@@ -141,15 +97,6 @@ MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install run \
|
|
|
141
97
|
|
|
142
98
|
Use `--policy warn` for pilot mode and `--fail-open` only for non-production local workflows where Marrow should never block execution.
|
|
143
99
|
|
|
144
|
-
## What's New in v0.1.10
|
|
145
|
-
|
|
146
|
-
- First-run output now explains the value in agent/user language: your agent is no longer starting from zero.
|
|
147
|
-
- Self-test prints first proof: setup decision captured, outcome closed, runtime gate active, and risky work now gets a pre-action brief.
|
|
148
|
-
- Fresh accounts get a guided prompt to try immediately: "I am about to deploy to production. What should I check first?"
|
|
149
|
-
- Existing accounts/fleets show stronger proof when available: avoided mistakes, reused winning decisions, prevented risky actions, and token/time savings.
|
|
150
|
-
- Generated SDK passive runtime now fails soft if `@getmarrow/sdk` is missing and the installer prints the exact dependency fix.
|
|
151
|
-
- Docs now make the universal installer the default path; SDK and MCP are advanced/manual integration paths.
|
|
152
|
-
|
|
153
100
|
## Which Install Path Should I Use?
|
|
154
101
|
|
|
155
102
|
Start here unless you already know you need a lower-level integration:
|
|
@@ -167,12 +114,20 @@ Use the lower-level packages only when you need direct control:
|
|
|
167
114
|
|
|
168
115
|
The three packages are not three competing onboarding paths. `@getmarrow/install` is the front door; SDK and MCP are the implementation paths underneath it.
|
|
169
116
|
|
|
117
|
+
For cleaner attribution, set an agent id and optional client label before install:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
export MARROW_FLEET_AGENT_ID=codex-deploy-agent
|
|
121
|
+
export MARROW_CLIENT=codex
|
|
122
|
+
npx @getmarrow/install --yes
|
|
123
|
+
```
|
|
124
|
+
|
|
170
125
|
## Agent Value Proof Quickstart
|
|
171
126
|
|
|
172
127
|
One command should prove Marrow is active and useful:
|
|
173
128
|
|
|
174
129
|
```bash
|
|
175
|
-
MARROW_API_KEY
|
|
130
|
+
MARROW_API_KEY=<your_marrow_key> npx @getmarrow/install --yes
|
|
176
131
|
```
|
|
177
132
|
|
|
178
133
|
Expected result:
|
|
@@ -180,9 +135,12 @@ Expected result:
|
|
|
180
135
|
- Marrow writes the safest detected MCP/SDK/agent config.
|
|
181
136
|
- A harmless setup decision is created and its outcome is committed.
|
|
182
137
|
- `/v1/agent/status` confirms capture health and missing hooks.
|
|
138
|
+
- `/v1/agent/data-quality` confirms whether attribution is clean enough for per-agent, per-harness, and per-workflow value proof.
|
|
183
139
|
- `/v1/agent/runtime` verifies the one-call runtime gate and returns the before-action intervention contract.
|
|
184
140
|
- `/v1/agent/first-value` returns the five-minute proof payload used by installer, SDK, and MCP clients.
|
|
141
|
+
- `/v1/agent/value/proof` returns token value proof from passive model-usage capture.
|
|
185
142
|
- The installer prints: "Your agent is no longer starting from zero."
|
|
143
|
+
- The installer prints: "Token value proof" with capture status, observed model calls, token totals, estimated savings, confidence, and exact next action.
|
|
186
144
|
- Fresh accounts get a first useful action to try immediately.
|
|
187
145
|
- Accounts with history get proof such as avoided mistakes, reused winning decisions, prevented risky actions, or estimated time/token savings.
|
|
188
146
|
|
|
@@ -196,39 +154,6 @@ I am about to deploy to production. What should I check first?
|
|
|
196
154
|
|
|
197
155
|
Marrow should answer with `proceed`, `warn`, `block`, or `owner_approval_required`, plus required proof and matching fleet lessons/playbooks before the agent acts. This is the first product moment: not just "hooks installed", but "the agent is being warned before risky work."
|
|
198
156
|
|
|
199
|
-
## Fleet Scale Startup Guidance
|
|
200
|
-
|
|
201
|
-
For business fleets, agents can read `GET /v1/agent/scale/capacity-contract` after install or during startup. The capacity contract tells the agent whether the account is under backpressure, when low-risk runtime guidance can be reused, when high-risk actions must still call Marrow, and how to batch low-risk telemetry through `/v1/agent/ingest/batch`.
|
|
202
|
-
|
|
203
|
-
This keeps Marrow passive without making it chatty. The current contract models the common `50 businesses x 50 agents = 2,500 agents` rollout shape and gives agents an exact next action: cache low-risk runtime guidance briefly, batch command/tool telemetry, and preserve proof-gated checks for deploys, publishes, merges, migrations, secrets, billing, and destructive work.
|
|
204
|
-
|
|
205
|
-
## Fleet Operator TUI
|
|
206
|
-
|
|
207
|
-
Use the operator TUI when a human owner or orchestrator agent needs to understand fleet health before acting:
|
|
208
|
-
|
|
209
|
-
```bash
|
|
210
|
-
MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install fleet
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
It shows:
|
|
214
|
-
|
|
215
|
-
- live agents and selected agent inspection
|
|
216
|
-
- active workflows
|
|
217
|
-
- risky actions waiting for proof
|
|
218
|
-
- failed or stale outcomes
|
|
219
|
-
- backpressure and capacity guidance
|
|
220
|
-
- recent decisions
|
|
221
|
-
- degraded hooks
|
|
222
|
-
- deploy, publish, and merge gate status
|
|
223
|
-
- exact fix commands for common degraded states
|
|
224
|
-
|
|
225
|
-
For automated reports or headless agents:
|
|
226
|
-
|
|
227
|
-
```bash
|
|
228
|
-
MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install fleet --no-interactive
|
|
229
|
-
MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install fleet --json
|
|
230
|
-
```
|
|
231
|
-
|
|
232
157
|
## What It Detects
|
|
233
158
|
|
|
234
159
|
- OpenClaw-style workspaces
|
|
@@ -236,6 +161,8 @@ MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install fleet --json
|
|
|
236
161
|
- Codex/agent instruction files such as `AGENTS.md`
|
|
237
162
|
- Claude Code settings and hooks
|
|
238
163
|
- Cursor project folders
|
|
164
|
+
- Cursor Composer, Windsurf, Cline, and OpenCode project markers
|
|
165
|
+
- Gemini, Grok, DeepSeek, Qwen, Kimi, MiniMax, and GLM command/profile markers
|
|
239
166
|
- MCP config files
|
|
240
167
|
- Node projects
|
|
241
168
|
- Python projects
|
|
@@ -262,10 +189,10 @@ npx @getmarrow/install --md --dry-run
|
|
|
262
189
|
|
|
263
190
|
## Self-Test
|
|
264
191
|
|
|
265
|
-
When `MARROW_API_KEY` is present, the installer creates a harmless test decision, commits the outcome, reads `/v1/agent/status`, calls the one-call runtime, and prints the first useful Marrow signal.
|
|
192
|
+
When `MARROW_API_KEY` is present, the installer creates a harmless test decision, commits the outcome, reads `/v1/agent/status`, calls the one-call runtime, reads `/v1/agent/value/proof`, and prints the first useful Marrow signal plus token value proof.
|
|
266
193
|
|
|
267
194
|
```bash
|
|
268
|
-
MARROW_API_KEY
|
|
195
|
+
MARROW_API_KEY=<your_marrow_key> npx @getmarrow/install --yes
|
|
269
196
|
```
|
|
270
197
|
|
|
271
198
|
Skip self-test:
|
|
@@ -277,13 +204,13 @@ npx @getmarrow/install --yes --no-self-test
|
|
|
277
204
|
Doctor check:
|
|
278
205
|
|
|
279
206
|
```bash
|
|
280
|
-
MARROW_API_KEY
|
|
207
|
+
MARROW_API_KEY=<your_marrow_key> npx @getmarrow/install doctor
|
|
281
208
|
```
|
|
282
209
|
|
|
283
210
|
Repair missing hooks/config:
|
|
284
211
|
|
|
285
212
|
```bash
|
|
286
|
-
MARROW_API_KEY
|
|
213
|
+
MARROW_API_KEY=<your_marrow_key> npx @getmarrow/install --repair
|
|
287
214
|
```
|
|
288
215
|
|
|
289
216
|
## SDK Dependency
|
|
@@ -296,10 +223,40 @@ npm install @getmarrow/sdk
|
|
|
296
223
|
|
|
297
224
|
The generated runtime now fails soft with an explicit warning if the SDK package is missing, so onboarding does not crash a user process.
|
|
298
225
|
|
|
226
|
+
## Passive Token Value Proof
|
|
227
|
+
|
|
228
|
+
Token value proof is default-on when the installer writes `.marrow/passive-runtime.mjs`.
|
|
229
|
+
|
|
230
|
+
In supported Node/TypeScript agents, the SDK passive runtime wraps outbound model-provider `fetch` responses and captures compact `usage` blocks when providers return them. MCP and harness integrations can also attach `model_usage` to `marrow_commit` or call `marrow_model_usage` when the harness exposes usage metadata.
|
|
231
|
+
|
|
232
|
+
Captured fields are intentionally narrow:
|
|
233
|
+
|
|
234
|
+
- provider and model
|
|
235
|
+
- input, output, cached, and total token counts
|
|
236
|
+
- optional cost and latency estimates
|
|
237
|
+
- agent, workflow, session, and decision linkage
|
|
238
|
+
- Marrow intervention type, such as `runtime_gate`, `proof_pack`, or `before_you_act`
|
|
239
|
+
|
|
240
|
+
Marrow does not capture prompt text, completion text, raw tool output, command output, full environment values, or plaintext secrets for token value proof.
|
|
241
|
+
|
|
242
|
+
Disable capture only when a project requires it:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
MARROW_PASSIVE_TOKEN_USAGE=false
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Day-one behavior:
|
|
249
|
+
|
|
250
|
+
- Fresh install: `Token usage capture is ready; no model calls have been reported yet.`
|
|
251
|
+
- After observed usage: Marrow reports model calls, total tokens, estimated tokens saved, confidence, trend, and next action.
|
|
252
|
+
- After workflows complete: agents should show the returned `token_value_signal` or value proof in owner updates so users see savings without opening a dashboard.
|
|
253
|
+
|
|
299
254
|
|
|
300
255
|
## Trust and Data Boundaries
|
|
301
256
|
|
|
302
|
-
Marrow is tenant-aware by design. Private account, fleet,
|
|
257
|
+
Marrow is tenant-aware by design. Private account, fleet, workflow, proof-pack, and agent data stays scoped to the authenticated account and authorized agent-bound keys.
|
|
258
|
+
|
|
259
|
+
Enterprise tenants receive a strong private governance baseline from day one: risk gates, proof requirements, workflow templates, private/account learning, and exact next actions. Teams that enable sanitized aggregate contribution unlock richer k-anonymous collective workflow guidance. Contribution never means raw prompts, decisions, proof packs, code, secrets, account identifiers, agent identifiers, or customer identities.
|
|
303
260
|
|
|
304
261
|
For business pilots, review the live trust notes before production rollout: https://getmarrow.ai/docs#trust-boundaries
|
|
305
262
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getmarrow/install",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Universal installer for Marrow passive
|
|
3
|
+
"version": "0.1.27",
|
|
4
|
+
"description": "Universal installer for Marrow agent governance, proof, and passive control setup.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"marrow-install": "bin/marrow-install.js"
|
|
7
7
|
},
|
|
@@ -15,9 +15,10 @@
|
|
|
15
15
|
"marrow",
|
|
16
16
|
"mcp",
|
|
17
17
|
"installer",
|
|
18
|
+
"passive-runtime",
|
|
18
19
|
"governance",
|
|
19
|
-
"
|
|
20
|
-
"
|
|
20
|
+
"proof-packs",
|
|
21
|
+
"agent-control-plane"
|
|
21
22
|
],
|
|
22
23
|
"repository": {
|
|
23
24
|
"type": "git",
|
package/src/governed-runner.js
CHANGED
|
@@ -45,6 +45,7 @@ Options:
|
|
|
45
45
|
--fail-closed If Marrow is unreachable, block the command
|
|
46
46
|
--owner-approved <ref> Owner approval reference for review-required gates
|
|
47
47
|
--proof-file <path> JSON proof to include on outcome commit
|
|
48
|
+
--client <label> Harness/client label. Defaults to MARROW_CLIENT, MARROW_HARNESS, or MARROW_AGENT_CLIENT
|
|
48
49
|
--base-url <url> Marrow API base URL
|
|
49
50
|
--key <key> Marrow API key. Prefer MARROW_API_KEY
|
|
50
51
|
--json Print machine-readable result after completion
|
|
@@ -77,6 +78,40 @@ function redactedCommand(command) {
|
|
|
77
78
|
return command.map((part) => shellQuote(redact(part))).join(' ');
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
function normalizeClientLabel(value) {
|
|
82
|
+
const raw = String(value || '').trim().toLowerCase();
|
|
83
|
+
if (!raw) return '';
|
|
84
|
+
const normalized = raw
|
|
85
|
+
.replace(/[^a-z0-9._-]+/g, '-')
|
|
86
|
+
.replace(/^-+|-+$/g, '')
|
|
87
|
+
.slice(0, 64);
|
|
88
|
+
const aliases = {
|
|
89
|
+
claude: 'claude-code',
|
|
90
|
+
claude_code: 'claude-code',
|
|
91
|
+
'cursor-composer': 'composer',
|
|
92
|
+
'openai-codex': 'codex',
|
|
93
|
+
openai: 'codex',
|
|
94
|
+
'gemini-cli': 'gemini',
|
|
95
|
+
'grok-cli': 'grok',
|
|
96
|
+
'deepseek-cli': 'deepseek',
|
|
97
|
+
'qwen-cli': 'qwen',
|
|
98
|
+
'kimi-cli': 'kimi',
|
|
99
|
+
'minimax-cli': 'minimax',
|
|
100
|
+
'glm-cli': 'glm',
|
|
101
|
+
};
|
|
102
|
+
return aliases[normalized] || normalized || 'custom';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function sourceClient(value) {
|
|
106
|
+
return normalizeClientLabel(
|
|
107
|
+
value
|
|
108
|
+
|| process.env.MARROW_CLIENT
|
|
109
|
+
|| process.env.MARROW_HARNESS
|
|
110
|
+
|| process.env.MARROW_AGENT_CLIENT
|
|
111
|
+
|| '@getmarrow/install',
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
80
115
|
function displayText(value, maxLength = 120) {
|
|
81
116
|
const text = redact(String(value || ''))
|
|
82
117
|
.replace(/\u001b\][^\u0007]*(?:\u0007|\u001b\\)/g, '')
|
|
@@ -160,13 +195,23 @@ function detectProjectSignals(cwd = process.cwd()) {
|
|
|
160
195
|
addFile('AGENTS.md', 'agent_instructions');
|
|
161
196
|
addFile('CLAUDE.md', 'agent_instructions');
|
|
162
197
|
addFile('.mcp.json', 'mcp_config');
|
|
198
|
+
addFile('mcp.json', 'mcp_config');
|
|
163
199
|
addFile('.cursor', 'cursor_project');
|
|
200
|
+
addFile('.windsurf', 'windsurf_project');
|
|
201
|
+
addFile('.cline', 'cline_project');
|
|
164
202
|
addFile('hermes.json', 'hermes_config');
|
|
165
203
|
addFile('hermes.yaml', 'hermes_config');
|
|
166
204
|
addFile('hermes.yml', 'hermes_config');
|
|
167
205
|
addFile('.hermes', 'hermes_config');
|
|
168
206
|
addFile('openclaw.json', 'openclaw_config');
|
|
169
207
|
addFile('.openclaw', 'openclaw_config');
|
|
208
|
+
addFile('.gemini', 'gemini_profile');
|
|
209
|
+
addFile('.grok', 'grok_profile');
|
|
210
|
+
addFile('.deepseek', 'deepseek_profile');
|
|
211
|
+
addFile('.qwen', 'qwen_profile');
|
|
212
|
+
addFile('.kimi', 'kimi_profile');
|
|
213
|
+
addFile('.minimax', 'minimax_profile');
|
|
214
|
+
addFile('.glm', 'glm_profile');
|
|
170
215
|
|
|
171
216
|
for (const script of packageScripts) {
|
|
172
217
|
if (/\b(deploy|publish|release|migrate|migration|smoke|check|test)\b/i.test(script)) {
|
|
@@ -204,6 +249,7 @@ function parseBaseOptions(argv, startIndex = 0) {
|
|
|
204
249
|
proofFile: '',
|
|
205
250
|
type: '',
|
|
206
251
|
action: '',
|
|
252
|
+
client: sourceClient(),
|
|
207
253
|
interactive: null,
|
|
208
254
|
};
|
|
209
255
|
let i = startIndex;
|
|
@@ -224,6 +270,7 @@ function parseBaseOptions(argv, startIndex = 0) {
|
|
|
224
270
|
options.failOpen = false;
|
|
225
271
|
} else if (arg === '--owner-approved') options.ownerApproval = argv[++i] || options.ownerApproval;
|
|
226
272
|
else if (arg === '--proof-file') options.proofFile = argv[++i] || options.proofFile;
|
|
273
|
+
else if (arg === '--client' || arg === '--harness') options.client = sourceClient(argv[++i] || options.client);
|
|
227
274
|
else if (arg === '--base-url') options.baseUrl = argv[++i] || options.baseUrl;
|
|
228
275
|
else if (arg === '--key') {
|
|
229
276
|
options.apiKey = argv[++i] || options.apiKey;
|
|
@@ -295,11 +342,28 @@ function headers(options) {
|
|
|
295
342
|
'Content-Type': 'application/json',
|
|
296
343
|
'X-Marrow-Agent-Id': options.agentId,
|
|
297
344
|
'X-Marrow-Session-Id': options.sessionId,
|
|
345
|
+
'X-Marrow-Client': sourceClient(options.client),
|
|
298
346
|
'User-Agent': '@getmarrow/install governed-runner',
|
|
299
347
|
};
|
|
300
348
|
return h;
|
|
301
349
|
}
|
|
302
350
|
|
|
351
|
+
function sourceMeta(options, channel, extra = {}) {
|
|
352
|
+
const client = sourceClient(options.client);
|
|
353
|
+
return {
|
|
354
|
+
channel,
|
|
355
|
+
client,
|
|
356
|
+
harness: client,
|
|
357
|
+
runner: '@getmarrow/install',
|
|
358
|
+
agent_id: options.agentId,
|
|
359
|
+
session_id: options.sessionId,
|
|
360
|
+
profile: options.profile,
|
|
361
|
+
governed: true,
|
|
362
|
+
...(extra.action ? { user_intent: displayText(extra.action, 160) } : {}),
|
|
363
|
+
...extra,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
303
367
|
function dataOf(json) {
|
|
304
368
|
return json && typeof json === 'object' && json.data && typeof json.data === 'object' ? json.data : json;
|
|
305
369
|
}
|
|
@@ -340,22 +404,26 @@ function defaultProof(input) {
|
|
|
340
404
|
exit_code: input.exitCode,
|
|
341
405
|
runner: '@getmarrow/install run',
|
|
342
406
|
profile: input.options.profile,
|
|
407
|
+
source_meta: sourceMeta(input.options, 'proof', { action: input.action }),
|
|
343
408
|
...(input.options.ownerApproval ? { owner_approval: { approved_by: 'owner', reference: input.options.ownerApproval } } : {}),
|
|
344
409
|
...proof,
|
|
345
410
|
};
|
|
346
411
|
}
|
|
347
412
|
|
|
348
413
|
async function preflightRuntime(options, action, type, commandText) {
|
|
414
|
+
const meta = sourceMeta(options, 'runtime', { action, command: commandText, action_type: type });
|
|
349
415
|
return requestJson(options, 'POST', '/v1/agent/runtime', {
|
|
350
416
|
action,
|
|
351
417
|
type,
|
|
352
418
|
surfaces: inferSurfaces(commandText || action),
|
|
419
|
+
source_meta: meta,
|
|
353
420
|
context: {
|
|
354
421
|
runner: '@getmarrow/install run',
|
|
355
422
|
profile: options.profile,
|
|
356
423
|
command: commandText,
|
|
357
424
|
policy: options.policy,
|
|
358
425
|
governed: true,
|
|
426
|
+
source_meta: meta,
|
|
359
427
|
},
|
|
360
428
|
});
|
|
361
429
|
}
|
|
@@ -383,6 +451,7 @@ async function recommendGovernanceMode(options, project = detectProjectSignals()
|
|
|
383
451
|
id: options.agentId,
|
|
384
452
|
role: 'setup',
|
|
385
453
|
},
|
|
454
|
+
source_meta: sourceMeta(options, 'mode_recommend', { action }),
|
|
386
455
|
});
|
|
387
456
|
}
|
|
388
457
|
|
|
@@ -402,6 +471,7 @@ async function recordGovernanceModeSelection(options, state) {
|
|
|
402
471
|
},
|
|
403
472
|
selected_mode: selected,
|
|
404
473
|
selection_source: selected === state.recommendation.recommended_mode ? 'accepted' : 'overridden',
|
|
474
|
+
source_meta: sourceMeta(options, 'mode_selection', { action: 'selected Marrow governance mode for this project' }),
|
|
405
475
|
});
|
|
406
476
|
}
|
|
407
477
|
|
|
@@ -453,13 +523,16 @@ function runChild(command, env = process.env) {
|
|
|
453
523
|
}
|
|
454
524
|
|
|
455
525
|
async function createDecision(options, action, type) {
|
|
526
|
+
const meta = sourceMeta(options, 'think', { action, action_type: type });
|
|
456
527
|
return requestJson(options, 'POST', '/v1/agent/think', {
|
|
457
528
|
action,
|
|
458
529
|
type,
|
|
530
|
+
source_meta: meta,
|
|
459
531
|
context: {
|
|
460
532
|
runner: '@getmarrow/install run',
|
|
461
533
|
profile: options.profile,
|
|
462
534
|
governed: true,
|
|
535
|
+
source_meta: meta,
|
|
463
536
|
},
|
|
464
537
|
});
|
|
465
538
|
}
|
|
@@ -470,6 +543,7 @@ async function commitOutcome(options, decisionId, success, outcome, proof, gateR
|
|
|
470
543
|
success,
|
|
471
544
|
outcome,
|
|
472
545
|
proof,
|
|
546
|
+
source_meta: sourceMeta(options, 'commit', { action: outcome }),
|
|
473
547
|
};
|
|
474
548
|
if (gateReceiptId) body.gate_receipt_id = gateReceiptId;
|
|
475
549
|
return requestJson(options, 'POST', '/v1/agent/commit', body);
|
|
@@ -851,14 +925,51 @@ function fleetPanel(snapshot) {
|
|
|
851
925
|
].filter(Boolean).join('\n');
|
|
852
926
|
}
|
|
853
927
|
|
|
928
|
+
const GENERIC_GOVERNED_COMMAND = 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install run --agent <agent-id> --profile production --policy warn -- <harness-command>';
|
|
929
|
+
|
|
930
|
+
function localSupportedHarnesses() {
|
|
931
|
+
return [
|
|
932
|
+
{ display_name: 'OpenAI Codex', client_label: 'codex', category: 'agent_harness', support_level: 'governed_runner', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install run --agent codex-prod -- codex' },
|
|
933
|
+
{ display_name: 'Claude Code', client_label: 'claude-code', category: 'agent_harness', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/mcp setup' },
|
|
934
|
+
{ display_name: 'Cursor', client_label: 'cursor', category: 'ide_agent', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/mcp setup' },
|
|
935
|
+
{ display_name: 'Cursor Composer', client_label: 'composer', category: 'ide_agent', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/mcp setup' },
|
|
936
|
+
{ display_name: 'Windsurf', client_label: 'windsurf', category: 'ide_agent', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/mcp setup' },
|
|
937
|
+
{ display_name: 'Cline', client_label: 'cline', category: 'ide_agent', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/mcp setup' },
|
|
938
|
+
{ display_name: 'OpenCode', client_label: 'opencode', category: 'agent_harness', support_level: 'governed_runner', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install run --agent opencode-prod -- opencode' },
|
|
939
|
+
{ display_name: 'Hermes Agent', client_label: 'hermes', category: 'agent_harness', support_level: 'first_class_addon', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install hermes' },
|
|
940
|
+
{ display_name: 'OpenClaw', client_label: 'openclaw', category: 'agent_harness', support_level: 'first_class_addon', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install openclaw' },
|
|
941
|
+
{ display_name: 'Gemini CLI', client_label: 'gemini', category: 'model_cli', support_level: 'governed_runner', install_command: GENERIC_GOVERNED_COMMAND },
|
|
942
|
+
{ display_name: 'Grok CLI', client_label: 'grok', category: 'model_cli', support_level: 'governed_runner', install_command: GENERIC_GOVERNED_COMMAND },
|
|
943
|
+
{ display_name: 'DeepSeek', client_label: 'deepseek', category: 'model_cli', support_level: 'governed_runner', install_command: GENERIC_GOVERNED_COMMAND },
|
|
944
|
+
{ display_name: 'Qwen', client_label: 'qwen', category: 'model_cli', support_level: 'governed_runner', install_command: GENERIC_GOVERNED_COMMAND },
|
|
945
|
+
{ display_name: 'Kimi', client_label: 'kimi', category: 'model_cli', support_level: 'governed_runner', install_command: GENERIC_GOVERNED_COMMAND },
|
|
946
|
+
{ display_name: 'MiniMax', client_label: 'minimax', category: 'model_cli', support_level: 'governed_runner', install_command: GENERIC_GOVERNED_COMMAND },
|
|
947
|
+
{ display_name: 'GLM', client_label: 'glm', category: 'model_cli', support_level: 'governed_runner', install_command: GENERIC_GOVERNED_COMMAND },
|
|
948
|
+
{ display_name: 'MCP-compatible clients', client_label: 'mcp', category: 'mcp_client', support_level: 'native_mcp_or_sdk', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/mcp setup' },
|
|
949
|
+
{ display_name: 'CI scripts and deploy runners', client_label: 'ci', category: 'ci_runner', support_level: 'governed_runner', install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install run --agent ci-release --profile production --policy enforce -- <ci-or-deploy-command>' },
|
|
950
|
+
{ display_name: 'Custom shell/API harness', client_label: 'custom', category: 'custom_runner', support_level: 'event_contract', install_command: 'POST /v1/agent/integrations/events with harness, event_type, agent_id, and action' },
|
|
951
|
+
];
|
|
952
|
+
}
|
|
953
|
+
|
|
854
954
|
function detectHarnesses(cwd = process.cwd()) {
|
|
855
955
|
const candidates = [
|
|
856
956
|
{ name: 'Codex', command: 'codex', detected: fs.existsSync(path.join(cwd, 'AGENTS.md')) || fs.existsSync(path.join(os.homedir(), '.codex')) },
|
|
857
957
|
{ name: 'Claude Code', command: 'claude -p', detected: fs.existsSync(path.join(cwd, 'CLAUDE.md')) || fs.existsSync(path.join(os.homedir(), '.claude.json')) },
|
|
858
958
|
{ name: 'Cursor', command: 'cursor', detected: fs.existsSync(path.join(cwd, '.cursor')) || fs.existsSync(path.join(os.homedir(), '.cursor')) },
|
|
959
|
+
{ name: 'Cursor Composer', command: 'cursor composer', detected: fs.existsSync(path.join(cwd, '.cursor')) || fs.existsSync(path.join(os.homedir(), '.cursor')) },
|
|
960
|
+
{ name: 'Windsurf', command: 'windsurf', detected: fs.existsSync(path.join(cwd, '.windsurf')) || fs.existsSync(path.join(os.homedir(), '.windsurf')) },
|
|
961
|
+
{ name: 'Cline', command: 'cline', detected: fs.existsSync(path.join(cwd, '.cline')) || fs.existsSync(path.join(cwd, '.vscode')) },
|
|
859
962
|
{ name: 'OpenCode', command: 'opencode', detected: fs.existsSync(path.join(cwd, 'opencode.json')) || fs.existsSync(path.join(os.homedir(), '.opencode')) },
|
|
860
963
|
{ name: 'Hermes Agent', command: 'hermes', detected: fs.existsSync(path.join(cwd, 'hermes.json')) || fs.existsSync(path.join(cwd, '.hermes')) || fs.existsSync(path.join(os.homedir(), '.hermes')) || fs.existsSync(path.join(os.homedir(), '.hermes-agent')) },
|
|
861
964
|
{ name: 'OpenClaw', command: 'openclaw agent', detected: fs.existsSync(path.join(os.homedir(), '.openclaw')) },
|
|
965
|
+
{ name: 'Gemini CLI', command: 'gemini', detected: fs.existsSync(path.join(cwd, '.gemini')) || fs.existsSync(path.join(os.homedir(), '.gemini')) },
|
|
966
|
+
{ name: 'Grok CLI', command: 'grok', detected: fs.existsSync(path.join(cwd, '.grok')) || fs.existsSync(path.join(os.homedir(), '.grok')) },
|
|
967
|
+
{ name: 'DeepSeek', command: 'deepseek', detected: fs.existsSync(path.join(cwd, '.deepseek')) || fs.existsSync(path.join(os.homedir(), '.deepseek')) },
|
|
968
|
+
{ name: 'Qwen', command: 'qwen', detected: fs.existsSync(path.join(cwd, '.qwen')) || fs.existsSync(path.join(os.homedir(), '.qwen')) },
|
|
969
|
+
{ name: 'Kimi', command: 'kimi', detected: fs.existsSync(path.join(cwd, '.kimi')) || fs.existsSync(path.join(os.homedir(), '.kimi')) },
|
|
970
|
+
{ name: 'MiniMax', command: 'minimax', detected: fs.existsSync(path.join(cwd, '.minimax')) || fs.existsSync(path.join(os.homedir(), '.minimax')) },
|
|
971
|
+
{ name: 'GLM', command: 'glm', detected: fs.existsSync(path.join(cwd, '.glm')) || fs.existsSync(path.join(os.homedir(), '.glm')) },
|
|
972
|
+
{ name: 'MCP-compatible client', command: 'mcp client', detected: fs.existsSync(path.join(cwd, '.mcp.json')) || fs.existsSync(path.join(cwd, 'mcp.json')) },
|
|
862
973
|
{ name: 'CI script', command: 'npm test', detected: fs.existsSync(path.join(cwd, 'package.json')) },
|
|
863
974
|
{ name: 'Custom command', command: '<your-agent-command>', detected: true },
|
|
864
975
|
];
|
|
@@ -970,9 +1081,10 @@ async function integrationOnly(parsed, name) {
|
|
|
970
1081
|
|
|
971
1082
|
async function integrationsOnly(parsed) {
|
|
972
1083
|
const local = {
|
|
973
|
-
integration_registry_version: 'local.harness-
|
|
974
|
-
|
|
975
|
-
|
|
1084
|
+
integration_registry_version: 'local.broad-harness-support-v2',
|
|
1085
|
+
first_class_addons: ['hermes', 'openclaw'].map((name) => localIntegrationManifest(name)),
|
|
1086
|
+
supported_harnesses: localSupportedHarnesses(),
|
|
1087
|
+
exact_next_action: 'Pick the harness your team already uses. Use the install_command shown here, or send compact events to /v1/agent/integrations/events.',
|
|
976
1088
|
};
|
|
977
1089
|
let registry = local;
|
|
978
1090
|
let source = 'local';
|
|
@@ -990,9 +1102,10 @@ async function integrationsOnly(parsed) {
|
|
|
990
1102
|
'Marrow Harness Integrations',
|
|
991
1103
|
'',
|
|
992
1104
|
`Source: ${source}`,
|
|
993
|
-
'Supported
|
|
994
|
-
...harnesses.map((harness) => ` - ${displayText(harness.client_label || harness.integration || harness.title, 40)} ${displayText(harness.install_command || harness.install_commands?.[0] || '', 120)}`),
|
|
1105
|
+
'Supported harnesses and model CLIs:',
|
|
1106
|
+
...harnesses.map((harness) => ` - ${displayText(harness.display_name || harness.client_label || harness.integration || harness.title, 40)} [${displayText(harness.support_level || 'supported', 24)}] ${displayText(harness.install_command || harness.install_commands?.[0] || '', 120)}`),
|
|
995
1107
|
'',
|
|
1108
|
+
'First-class add-on guides: hermes, openclaw',
|
|
996
1109
|
`Next: ${displayText(registry.exact_next_action, 140)}`,
|
|
997
1110
|
registry.api_warning ? `API warning: ${displayText(registry.api_warning, 120)}` : '',
|
|
998
1111
|
].filter(Boolean).join('\n') + '\n');
|
|
@@ -1622,6 +1735,10 @@ module.exports = {
|
|
|
1622
1735
|
parseArgs,
|
|
1623
1736
|
redact,
|
|
1624
1737
|
redactedCommand,
|
|
1738
|
+
normalizeClientLabel,
|
|
1739
|
+
sourceClient,
|
|
1740
|
+
sourceMeta,
|
|
1741
|
+
headers,
|
|
1625
1742
|
inferType,
|
|
1626
1743
|
inferSurfaces,
|
|
1627
1744
|
commandForSelection,
|
|
@@ -1648,6 +1765,7 @@ module.exports = {
|
|
|
1648
1765
|
buildFleetState,
|
|
1649
1766
|
renderFleetTui,
|
|
1650
1767
|
runFleetInteractive,
|
|
1768
|
+
localSupportedHarnesses,
|
|
1651
1769
|
localIntegrationManifest,
|
|
1652
1770
|
renderIntegrationPanel,
|
|
1653
1771
|
integrationOnly,
|
package/src/installer.js
CHANGED
|
@@ -6,6 +6,35 @@ const crypto = require('node:crypto');
|
|
|
6
6
|
const DEFAULT_BASE_URL = 'https://api.getmarrow.ai';
|
|
7
7
|
const MARROW_BLOCK_START = '<!-- marrow:passive-start -->';
|
|
8
8
|
const MARROW_BLOCK_END = '<!-- marrow:passive-end -->';
|
|
9
|
+
const SOURCE_CLIENTS = new Set(['claude-code', 'cursor', 'windsurf', 'openclaw', 'codex', 'gemini', 'grok', 'deepseek', 'qwen', 'kimi', 'minimax', 'cline', 'opencode', 'hermes', 'glm', 'custom', 'unknown']);
|
|
10
|
+
|
|
11
|
+
function sourceClient() {
|
|
12
|
+
const raw = String(process.env.MARROW_CLIENT || process.env.MARROW_HARNESS || process.env.MARROW_AGENT_CLIENT || '').trim().toLowerCase().replace(/\s+/g, '-').replace(/^@/, '');
|
|
13
|
+
const aliases = {
|
|
14
|
+
claude: 'claude-code',
|
|
15
|
+
claude_code: 'claude-code',
|
|
16
|
+
'claude-code': 'claude-code',
|
|
17
|
+
cursor: 'cursor',
|
|
18
|
+
windsurf: 'windsurf',
|
|
19
|
+
openclaw: 'openclaw',
|
|
20
|
+
codex: 'codex',
|
|
21
|
+
'openai-codex': 'codex',
|
|
22
|
+
gemini: 'gemini',
|
|
23
|
+
google: 'gemini',
|
|
24
|
+
grok: 'grok',
|
|
25
|
+
deepseek: 'deepseek',
|
|
26
|
+
qwen: 'qwen',
|
|
27
|
+
kimi: 'kimi',
|
|
28
|
+
minimax: 'minimax',
|
|
29
|
+
cline: 'cline',
|
|
30
|
+
opencode: 'opencode',
|
|
31
|
+
'open-code': 'opencode',
|
|
32
|
+
hermes: 'hermes',
|
|
33
|
+
'hermes-agent': 'hermes',
|
|
34
|
+
glm: 'glm',
|
|
35
|
+
};
|
|
36
|
+
return aliases[raw] || (SOURCE_CLIENTS.has(raw) ? raw : 'custom');
|
|
37
|
+
}
|
|
9
38
|
|
|
10
39
|
function parseArgs(argv) {
|
|
11
40
|
const options = {
|
|
@@ -105,12 +134,6 @@ function findUp(startDir, names, maxDepth = 8) {
|
|
|
105
134
|
}
|
|
106
135
|
|
|
107
136
|
function projectRoot(startDir) {
|
|
108
|
-
const resolved = path.resolve(startDir);
|
|
109
|
-
if (path.basename(resolved) === '.marrow') return path.dirname(resolved);
|
|
110
|
-
if (path.basename(resolved) === 'env' && path.basename(path.dirname(resolved)) === '.marrow') {
|
|
111
|
-
return path.dirname(path.dirname(resolved));
|
|
112
|
-
}
|
|
113
|
-
if (exists(path.join(resolved, '.marrow'))) return resolved;
|
|
114
137
|
const marker = findUp(startDir, ['package.json', 'pyproject.toml', 'requirements.txt', '.git', 'AGENTS.md', 'CLAUDE.md']);
|
|
115
138
|
return marker ? path.dirname(marker) : path.resolve(startDir);
|
|
116
139
|
}
|
|
@@ -289,14 +312,16 @@ Marrow should run passively after install:
|
|
|
289
312
|
|
|
290
313
|
- Use MCP hooks when available: \`npx -y @getmarrow/mcp setup\`.
|
|
291
314
|
- Use SDK passive runtime in owned Node processes: \`createPassiveRuntime().install()\`.
|
|
292
|
-
-
|
|
315
|
+
- Keep passive token/model usage proof enabled so Marrow can show token, cost, latency, and workflow savings after real work completes.
|
|
316
|
+
- Before risky work, use Marrow's decision brief or passive prompt hook.
|
|
293
317
|
- After meaningful work, record the outcome so future agents learn from it.
|
|
294
318
|
- Check health with \`marrow_agent_status\` or \`GET /v1/agent/status\`.
|
|
295
319
|
|
|
296
320
|
Required environment:
|
|
297
321
|
|
|
298
322
|
- \`MARROW_API_KEY\`
|
|
299
|
-
- Optional: \`MARROW_BASE_URL\`, \`MARROW_FLEET_AGENT_ID\`
|
|
323
|
+
- Optional: \`MARROW_BASE_URL\`, \`MARROW_FLEET_AGENT_ID\`, \`MARROW_CLIENT\`
|
|
324
|
+
- Optional: \`MARROW_PASSIVE_TOKEN_USAGE=false\` disables compact provider usage capture when needed.
|
|
300
325
|
${MARROW_BLOCK_END}`;
|
|
301
326
|
}
|
|
302
327
|
|
|
@@ -318,6 +343,7 @@ if (apiKey && !globalThis.__MARROW_PASSIVE_RUNTIME__) {
|
|
|
318
343
|
useAgentRuntime: process.env.MARROW_AGENT_RUNTIME !== 'false',
|
|
319
344
|
useWorkflowGate: process.env.MARROW_WORKFLOW_GATE !== 'false',
|
|
320
345
|
requireOutcomeClosure: process.env.MARROW_REQUIRE_OUTCOME_CLOSURE !== 'false',
|
|
346
|
+
captureModelUsage: process.env.MARROW_PASSIVE_TOKEN_USAGE !== 'false',
|
|
321
347
|
});
|
|
322
348
|
|
|
323
349
|
runtime.install();
|
|
@@ -333,12 +359,14 @@ function envExample() {
|
|
|
333
359
|
return `MARROW_API_KEY=mrw_live_replace_me
|
|
334
360
|
MARROW_BASE_URL=${DEFAULT_BASE_URL}
|
|
335
361
|
MARROW_FLEET_AGENT_ID=agent-or-fleet-id
|
|
362
|
+
MARROW_CLIENT=codex
|
|
336
363
|
MARROW_ENFORCEMENT_MODE=auto
|
|
337
364
|
MARROW_PASSIVE_BRIEF=auto
|
|
338
365
|
MARROW_PASSIVE_VALUE_REPORT=true
|
|
339
366
|
MARROW_AGENT_RUNTIME=true
|
|
340
367
|
MARROW_WORKFLOW_GATE=true
|
|
341
368
|
MARROW_REQUIRE_OUTCOME_CLOSURE=true
|
|
369
|
+
MARROW_PASSIVE_TOKEN_USAGE=true
|
|
342
370
|
`;
|
|
343
371
|
}
|
|
344
372
|
|
|
@@ -567,6 +595,7 @@ async function runSelfTest(options) {
|
|
|
567
595
|
authorization: `Bearer ${options.apiKey}`,
|
|
568
596
|
'content-type': 'application/json',
|
|
569
597
|
'x-marrow-session-id': `install-${Date.now()}`,
|
|
598
|
+
'x-marrow-client': sourceClient(),
|
|
570
599
|
};
|
|
571
600
|
if (options.agentId) headers['x-marrow-agent-id'] = options.agentId;
|
|
572
601
|
|
|
@@ -577,6 +606,11 @@ async function runSelfTest(options) {
|
|
|
577
606
|
body: JSON.stringify({
|
|
578
607
|
type: 'process',
|
|
579
608
|
action: 'Marrow passive install self-test: verify SDK/MCP hooks can record a harmless setup event',
|
|
609
|
+
source_meta: {
|
|
610
|
+
channel: 'cli',
|
|
611
|
+
client: sourceClient(),
|
|
612
|
+
user_intent: 'operate',
|
|
613
|
+
},
|
|
580
614
|
}),
|
|
581
615
|
});
|
|
582
616
|
|
|
@@ -633,8 +667,14 @@ async function runSelfTest(options) {
|
|
|
633
667
|
ok: false,
|
|
634
668
|
error: error instanceof Error ? error.message : String(error),
|
|
635
669
|
}));
|
|
636
|
-
const
|
|
637
|
-
|
|
670
|
+
const valueProof = await requestJson(`${baseUrl}/v1/agent/value/proof?period_days=30`, { headers })
|
|
671
|
+
.catch((error) => ({
|
|
672
|
+
ok: false,
|
|
673
|
+
error: error instanceof Error ? error.message : String(error),
|
|
674
|
+
}));
|
|
675
|
+
const tokenValueProof = buildTokenValueProof(valueProof);
|
|
676
|
+
const firstValueSignal = buildFirstValueSignal(status, runtime, performance, firstValue, tokenValueProof);
|
|
677
|
+
const installValueMoment = buildInstallValueMoment(firstValueSignal, status, runtime, performance, firstValue, tokenValueProof);
|
|
638
678
|
return {
|
|
639
679
|
skipped: false,
|
|
640
680
|
decision_id: decisionId,
|
|
@@ -646,11 +686,11 @@ async function runSelfTest(options) {
|
|
|
646
686
|
auto_outcome_closure: status.auto_outcome_closure || null,
|
|
647
687
|
runtime_active: Boolean(runtime && runtime.ok !== false),
|
|
648
688
|
runtime_exact_next_action: runtime.exact_next_action || null,
|
|
649
|
-
runtime_before_you_act: runtime.
|
|
650
|
-
runtime_intervention: runtime.intervention || null,
|
|
689
|
+
runtime_before_you_act: runtime.before_you_act || null,
|
|
651
690
|
first_value: firstValue && firstValue.ok !== false ? firstValue : null,
|
|
652
691
|
first_value_signal: firstValueSignal,
|
|
653
692
|
install_value_moment: installValueMoment,
|
|
693
|
+
token_value_proof: tokenValueProof,
|
|
654
694
|
performance_proof: performance && performance.ok !== false ? {
|
|
655
695
|
avoided_mistakes: performance.avoided_mistakes ?? performance.avoided_repeated_mistakes ?? 0,
|
|
656
696
|
reused_winning_decisions: performance.reused_winning_decisions ?? 0,
|
|
@@ -662,11 +702,30 @@ async function runSelfTest(options) {
|
|
|
662
702
|
};
|
|
663
703
|
}
|
|
664
704
|
|
|
665
|
-
function
|
|
705
|
+
function buildTokenValueProof(valueProof = {}) {
|
|
706
|
+
const modelUsage = valueProof && valueProof.ok !== false
|
|
707
|
+
? valueProof.model_usage || valueProof.token_value_signal || valueProof
|
|
708
|
+
: null;
|
|
709
|
+
if (!modelUsage || typeof modelUsage !== 'object') {
|
|
710
|
+
return {
|
|
711
|
+
enabled: true,
|
|
712
|
+
capture_default: 'on_when_sdk_mcp_or_installer_hooks_available',
|
|
713
|
+
observed: { model_calls: 0, tokens: { total: 0 } },
|
|
714
|
+
savings: { estimated_tokens_saved: 0, estimated_minutes_saved: 0, confidence: 'none', method: 'warming_up' },
|
|
715
|
+
proof_line: 'Token usage capture is ready; no model calls have been reported yet.',
|
|
716
|
+
exact_next_action: 'Keep passive token capture enabled so Marrow can attach usage proof after real model calls complete.',
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
return modelUsage;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function buildInstallValueMoment(firstValueSignal = {}, status = {}, runtime = {}, performance = {}, firstValue = {}, tokenValueProof = null) {
|
|
666
723
|
if (firstValue && firstValue.ok !== false && firstValue.first_value) {
|
|
724
|
+
const proof = Array.isArray(firstValue.first_value.proof) ? [...firstValue.first_value.proof] : [];
|
|
725
|
+
if (tokenValueProof?.proof_line && !proof.includes(tokenValueProof.proof_line)) proof.push(tokenValueProof.proof_line);
|
|
667
726
|
return {
|
|
668
727
|
headline: firstValue.headline || firstValue.first_value.headline || 'Your agent is no longer starting from zero.',
|
|
669
|
-
proof
|
|
728
|
+
proof,
|
|
670
729
|
fleet_signal: firstValue.history_signal?.summary || 'Fresh account: Marrow will build fleet memory from this first captured outcome.',
|
|
671
730
|
try_this_now: firstValue.first_value.try_this_now || 'Ask your agent: "I am about to deploy to production. What should I check first?"',
|
|
672
731
|
expected_response: firstValue.first_value.expected_response || 'Marrow should answer with a risk gate, required proof, and any matching fleet lessons before the agent acts.',
|
|
@@ -676,9 +735,7 @@ function buildInstallValueMoment(firstValueSignal = {}, status = {}, runtime = {
|
|
|
676
735
|
|
|
677
736
|
const proof = firstValueSignal.value_proof || [];
|
|
678
737
|
const hasFleetSignal = proof.length > 0;
|
|
679
|
-
const runtimeLesson = runtime.
|
|
680
|
-
|| runtime.intervention?.before_action
|
|
681
|
-
|| runtime.before_you_act
|
|
738
|
+
const runtimeLesson = runtime.before_you_act
|
|
682
739
|
|| runtime.before_you_act_injection?.message
|
|
683
740
|
|| runtime.exact_next_action
|
|
684
741
|
|| firstValueSignal.first_lesson;
|
|
@@ -689,18 +746,19 @@ function buildInstallValueMoment(firstValueSignal = {}, status = {}, runtime = {
|
|
|
689
746
|
'Captured this setup decision',
|
|
690
747
|
'Closed the outcome successfully',
|
|
691
748
|
'Runtime gate is ' + (firstValueSignal.active ? 'active' : 'installed'),
|
|
692
|
-
|
|
749
|
+
runtimeLesson ? 'Future risky work now gets a pre-action brief' : 'Future risky work now gets checked before action',
|
|
750
|
+
tokenValueProof?.proof_line || 'Token usage proof is active and warming up after the first model call',
|
|
693
751
|
],
|
|
694
752
|
fleet_signal: hasFleetSignal
|
|
695
753
|
? 'Marrow already found signal: ' + proof.join('; ') + '.'
|
|
696
754
|
: 'Fresh account: Marrow will start building fleet memory from this first captured outcome.',
|
|
697
755
|
try_this_now: 'Ask your agent: "I am about to deploy to production. What should I check first?"',
|
|
698
|
-
expected_response: 'Marrow should answer with
|
|
699
|
-
first_lesson: runtimeLesson || 'Marrow will
|
|
756
|
+
expected_response: 'Marrow should answer with a risk gate, required proof, and any matching fleet lessons before the agent acts.',
|
|
757
|
+
first_lesson: runtimeLesson || 'Marrow will surface prior lessons before risky or repeated work.',
|
|
700
758
|
};
|
|
701
759
|
}
|
|
702
760
|
|
|
703
|
-
function buildFirstValueSignal(status, runtime, performance, firstValue = {}) {
|
|
761
|
+
function buildFirstValueSignal(status, runtime, performance, firstValue = {}, tokenValueProof = null) {
|
|
704
762
|
if (firstValue && firstValue.ok !== false && firstValue.first_value) {
|
|
705
763
|
const capture = firstValue.capture || {};
|
|
706
764
|
const proof = firstValue.value_proof || {};
|
|
@@ -709,11 +767,13 @@ function buildFirstValueSignal(status, runtime, performance, firstValue = {}) {
|
|
|
709
767
|
if (Number(proof.reused_winning_decisions || 0) > 0) proofBits.push(`${proof.reused_winning_decisions} reused winning decision(s)`);
|
|
710
768
|
if (Number(proof.prevented_bad_actions || 0) > 0) proofBits.push(`${proof.prevented_bad_actions} prevented risky action(s)`);
|
|
711
769
|
if (Number(proof.estimated_tokens_saved || 0) > 0) proofBits.push(`~${proof.estimated_tokens_saved} tokens saved`);
|
|
770
|
+
if (Number(tokenValueProof?.savings?.estimated_tokens_saved || 0) > 0) proofBits.push(`~${tokenValueProof.savings.estimated_tokens_saved} measured model tokens saved`);
|
|
771
|
+
else if (tokenValueProof?.proof_line) proofBits.push(tokenValueProof.proof_line);
|
|
712
772
|
return {
|
|
713
773
|
active: Boolean(firstValue.active),
|
|
714
774
|
headline: `Marrow active: ${(capture.surfaces || ['decisions']).join(', ')} captured.`,
|
|
715
775
|
captured: capture.surfaces || ['decisions'],
|
|
716
|
-
first_lesson: firstValue.first_value.first_lesson
|
|
776
|
+
first_lesson: firstValue.first_value.first_lesson,
|
|
717
777
|
value_proof: proofBits,
|
|
718
778
|
next_action: firstValue.next_action?.reason || 'Keep working; Marrow will capture outcomes and reuse lessons automatically.',
|
|
719
779
|
};
|
|
@@ -736,6 +796,8 @@ function buildFirstValueSignal(status, runtime, performance, firstValue = {}) {
|
|
|
736
796
|
if (Number(proof.prevented_bad_actions || 0) > 0) proofBits.push(`${proof.prevented_bad_actions} prevented risky action(s)`);
|
|
737
797
|
const tokens = proof.token_time_saved_estimate?.estimated_tokens_saved || 0;
|
|
738
798
|
if (tokens > 0) proofBits.push(`~${tokens} tokens saved`);
|
|
799
|
+
if (Number(tokenValueProof?.savings?.estimated_tokens_saved || 0) > 0) proofBits.push(`~${tokenValueProof.savings.estimated_tokens_saved} measured model tokens saved`);
|
|
800
|
+
else if (tokenValueProof?.proof_line) proofBits.push(tokenValueProof.proof_line);
|
|
739
801
|
|
|
740
802
|
const firstLesson = runtime.before_you_act
|
|
741
803
|
|| runtime.before_you_act_injection?.message
|
|
@@ -799,6 +861,20 @@ function printReport(report) {
|
|
|
799
861
|
process.stdout.write(`- Next: ${report.selfTest.first_value_signal.next_action}\n`);
|
|
800
862
|
}
|
|
801
863
|
}
|
|
864
|
+
if (report.selfTest.token_value_proof) {
|
|
865
|
+
const proof = report.selfTest.token_value_proof;
|
|
866
|
+
const observed = proof.observed || {};
|
|
867
|
+
const savings = proof.savings || {};
|
|
868
|
+
const tokens = observed.tokens || {};
|
|
869
|
+
process.stdout.write('\nToken value proof:\n');
|
|
870
|
+
process.stdout.write(`- passive capture: ${proof.enabled ? 'on' : 'unknown'}\n`);
|
|
871
|
+
process.stdout.write(`- model calls observed: ${observed.model_calls || 0}\n`);
|
|
872
|
+
process.stdout.write(`- tokens observed: ${tokens.total || 0}\n`);
|
|
873
|
+
process.stdout.write(`- estimated tokens saved: ${savings.estimated_tokens_saved || 0}\n`);
|
|
874
|
+
if (savings.confidence) process.stdout.write(`- confidence: ${savings.confidence}\n`);
|
|
875
|
+
if (proof.proof_line) process.stdout.write(`- proof: ${proof.proof_line}\n`);
|
|
876
|
+
if (proof.exact_next_action) process.stdout.write(`- next: ${proof.exact_next_action}\n`);
|
|
877
|
+
}
|
|
802
878
|
}
|
|
803
879
|
|
|
804
880
|
if (report.remediation) {
|
|
@@ -949,4 +1025,5 @@ module.exports = {
|
|
|
949
1025
|
inspectNpmTokenConfig,
|
|
950
1026
|
inspectSdkDependency,
|
|
951
1027
|
buildInstallValueMoment,
|
|
1028
|
+
buildTokenValueProof,
|
|
952
1029
|
};
|