@getmarrow/install 0.1.23 → 0.1.25
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 +126 -49
- package/bin/marrow-install.js +1 -1
- package/package.json +3 -1
- package/src/governed-runner.js +159 -41
- package/src/installer.js +22 -99
package/README.md
CHANGED
|
@@ -9,23 +9,89 @@ 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 fleet
|
|
13
|
+
npx @getmarrow/install integrations
|
|
14
|
+
npx @getmarrow/install hermes
|
|
15
|
+
npx @getmarrow/install openclaw
|
|
12
16
|
```
|
|
13
17
|
|
|
14
|
-
## What's New in v0.1.
|
|
18
|
+
## What's New in v0.1.25
|
|
15
19
|
|
|
16
|
-
v0.1.
|
|
20
|
+
v0.1.25 adds first-class harness add-on paths for Hermes Agent and OpenClaw.
|
|
17
21
|
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
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.
|
|
23
28
|
|
|
24
|
-
|
|
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.
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
Examples:
|
|
27
32
|
|
|
28
|
-
|
|
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
|
|
40
|
+
|
|
41
|
+
v0.1.20 adds the Fleet Operator TUI for teams running multiple agents under one Marrow account.
|
|
42
|
+
|
|
43
|
+
- `npx @getmarrow/install fleet` opens a terminal operator view when run in a real TTY.
|
|
44
|
+
- CI/headless agents can use `npx @getmarrow/install fleet --no-interactive` for a stable text snapshot or `--json` for machine-readable status.
|
|
45
|
+
- The view shows live agents, active workflows, risky actions waiting for proof, failed/stale outcomes, backpressure/capacity status, recent decisions, degraded hooks, and deploy/publish/merge gate posture.
|
|
46
|
+
- Operators can press Enter to inspect an agent and print the exact fix command when hooks, outcome closure, or status coverage is degraded.
|
|
47
|
+
- The command reads existing Marrow account endpoints and degrades gracefully when a route is unavailable, so it is safe to use during incidents.
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
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
|
+
```
|
|
56
|
+
|
|
57
|
+
## What's New in v0.1.14
|
|
58
|
+
|
|
59
|
+
v0.1.14 adds adaptive governance mode recommendations without silent auto-switching.
|
|
60
|
+
|
|
61
|
+
- `npx @getmarrow/install govern` now detects project signals such as `package.json`, deploy/publish scripts, `wrangler` config, GitHub workflows, migrations, Cursor/Codex/Claude files, and MCP config.
|
|
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.
|
|
66
|
+
|
|
67
|
+
Example recommendation:
|
|
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
|
+
```
|
|
77
|
+
|
|
78
|
+
## What's New in v0.1.13
|
|
79
|
+
|
|
80
|
+
v0.1.13 turns `npx @getmarrow/install govern` into an interactive terminal setup flow when run in a real TTY.
|
|
81
|
+
|
|
82
|
+
- Select Codex, Claude Code, Cursor, OpenCode, OpenClaw, CI scripts, or a custom command with arrow keys.
|
|
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`.
|
|
89
|
+
|
|
90
|
+
This keeps Marrow passive-first: install once, verify Marrow is active, then let agents use the runtime/gate path automatically for risky work.
|
|
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.
|
|
29
95
|
|
|
30
96
|
- `npx @getmarrow/install govern` prints a setup panel for detected harnesses and recommended protected commands.
|
|
31
97
|
- `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.
|
|
@@ -44,6 +110,12 @@ Preview the detected harnesses and protected command examples:
|
|
|
44
110
|
npx @getmarrow/install govern
|
|
45
111
|
```
|
|
46
112
|
|
|
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
|
+
|
|
47
119
|
Run a harmless command through Marrow:
|
|
48
120
|
|
|
49
121
|
```bash
|
|
@@ -69,6 +141,15 @@ MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install run \
|
|
|
69
141
|
|
|
70
142
|
Use `--policy warn` for pilot mode and `--fail-open` only for non-production local workflows where Marrow should never block execution.
|
|
71
143
|
|
|
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
|
+
|
|
72
153
|
## Which Install Path Should I Use?
|
|
73
154
|
|
|
74
155
|
Start here unless you already know you need a lower-level integration:
|
|
@@ -86,14 +167,6 @@ Use the lower-level packages only when you need direct control:
|
|
|
86
167
|
|
|
87
168
|
The three packages are not three competing onboarding paths. `@getmarrow/install` is the front door; SDK and MCP are the implementation paths underneath it.
|
|
88
169
|
|
|
89
|
-
For cleaner attribution, set an agent id and optional client label before install:
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
export MARROW_FLEET_AGENT_ID=codex-deploy-agent
|
|
93
|
-
export MARROW_CLIENT=codex
|
|
94
|
-
npx @getmarrow/install --yes
|
|
95
|
-
```
|
|
96
|
-
|
|
97
170
|
## Agent Value Proof Quickstart
|
|
98
171
|
|
|
99
172
|
One command should prove Marrow is active and useful:
|
|
@@ -109,9 +182,7 @@ Expected result:
|
|
|
109
182
|
- `/v1/agent/status` confirms capture health and missing hooks.
|
|
110
183
|
- `/v1/agent/runtime` verifies the one-call runtime gate and returns the before-action intervention contract.
|
|
111
184
|
- `/v1/agent/first-value` returns the five-minute proof payload used by installer, SDK, and MCP clients.
|
|
112
|
-
- `/v1/agent/value/proof` returns token value proof from passive model-usage capture.
|
|
113
185
|
- The installer prints: "Your agent is no longer starting from zero."
|
|
114
|
-
- The installer prints: "Token value proof" with capture status, observed model calls, token totals, estimated savings, confidence, and exact next action.
|
|
115
186
|
- Fresh accounts get a first useful action to try immediately.
|
|
116
187
|
- Accounts with history get proof such as avoided mistakes, reused winning decisions, prevented risky actions, or estimated time/token savings.
|
|
117
188
|
|
|
@@ -125,9 +196,43 @@ I am about to deploy to production. What should I check first?
|
|
|
125
196
|
|
|
126
197
|
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."
|
|
127
198
|
|
|
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
|
+
|
|
128
232
|
## What It Detects
|
|
129
233
|
|
|
130
234
|
- OpenClaw-style workspaces
|
|
235
|
+
- Hermes Agent config files
|
|
131
236
|
- Codex/agent instruction files such as `AGENTS.md`
|
|
132
237
|
- Claude Code settings and hooks
|
|
133
238
|
- Cursor project folders
|
|
@@ -157,7 +262,7 @@ npx @getmarrow/install --md --dry-run
|
|
|
157
262
|
|
|
158
263
|
## Self-Test
|
|
159
264
|
|
|
160
|
-
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,
|
|
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.
|
|
161
266
|
|
|
162
267
|
```bash
|
|
163
268
|
MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install --yes
|
|
@@ -191,34 +296,6 @@ npm install @getmarrow/sdk
|
|
|
191
296
|
|
|
192
297
|
The generated runtime now fails soft with an explicit warning if the SDK package is missing, so onboarding does not crash a user process.
|
|
193
298
|
|
|
194
|
-
## Passive Token Value Proof
|
|
195
|
-
|
|
196
|
-
Token value proof is default-on when the installer writes `.marrow/passive-runtime.mjs`.
|
|
197
|
-
|
|
198
|
-
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.
|
|
199
|
-
|
|
200
|
-
Captured fields are intentionally narrow:
|
|
201
|
-
|
|
202
|
-
- provider and model
|
|
203
|
-
- input, output, cached, and total token counts
|
|
204
|
-
- optional cost and latency estimates
|
|
205
|
-
- agent, workflow, session, and decision linkage
|
|
206
|
-
- Marrow intervention type, such as `runtime_gate`, `proof_pack`, or `before_you_act`
|
|
207
|
-
|
|
208
|
-
Marrow does not capture prompt text, completion text, raw tool output, command output, full environment values, or plaintext secrets for token value proof.
|
|
209
|
-
|
|
210
|
-
Disable capture only when a project requires it:
|
|
211
|
-
|
|
212
|
-
```bash
|
|
213
|
-
MARROW_PASSIVE_TOKEN_USAGE=false
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
Day-one behavior:
|
|
217
|
-
|
|
218
|
-
- Fresh install: `Token usage capture is ready; no model calls have been reported yet.`
|
|
219
|
-
- After observed usage: Marrow reports model calls, total tokens, estimated tokens saved, confidence, trend, and next action.
|
|
220
|
-
- 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.
|
|
221
|
-
|
|
222
299
|
|
|
223
300
|
## Trust and Data Boundaries
|
|
224
301
|
|
package/bin/marrow-install.js
CHANGED
|
@@ -4,7 +4,7 @@ const installer = require('../src/installer');
|
|
|
4
4
|
const governedRunner = require('../src/governed-runner');
|
|
5
5
|
|
|
6
6
|
const argv = process.argv.slice(2);
|
|
7
|
-
const governedCommands = new Set(['run', 'gate', 'proof', 'status', 'govern', 'fleet']);
|
|
7
|
+
const governedCommands = new Set(['run', 'gate', 'proof', 'status', 'govern', 'fleet', 'hermes', 'openclaw', 'integrations']);
|
|
8
8
|
const runCli = governedCommands.has(argv[0]) ? governedRunner.runCli : installer.runCli;
|
|
9
9
|
|
|
10
10
|
runCli(argv).catch((error) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getmarrow/install",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
4
4
|
"description": "Universal installer for Marrow passive agent setup.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"marrow-install": "bin/marrow-install.js"
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"marrow",
|
|
16
16
|
"mcp",
|
|
17
17
|
"installer",
|
|
18
|
+
"governance",
|
|
19
|
+
"runner",
|
|
18
20
|
"passive-runtime"
|
|
19
21
|
],
|
|
20
22
|
"repository": {
|
package/src/governed-runner.js
CHANGED
|
@@ -9,36 +9,6 @@ const DEFAULT_BASE_URL = 'https://api.getmarrow.ai';
|
|
|
9
9
|
const HIGH_RISK_TERMS = /\b(deploy|prod|production|publish|release|merge|migration|migrate|secret|token|key|cloudflare|wrangler|npm publish|gh pr merge|git push|terraform apply|kubectl apply|delete|destroy|drop)\b/i;
|
|
10
10
|
const GOVERN_TUI_ROW_COUNT = 7;
|
|
11
11
|
const FLEET_TUI_ROW_COUNT = 11;
|
|
12
|
-
const SOURCE_CLIENTS = new Set(['claude-code', 'cursor', 'windsurf', 'openclaw', 'codex', 'gemini', 'grok', 'deepseek', 'qwen', 'kimi', 'minimax', 'cline', 'opencode', 'hermes', 'glm', 'custom', 'unknown']);
|
|
13
|
-
|
|
14
|
-
function sourceClient() {
|
|
15
|
-
const raw = String(process.env.MARROW_CLIENT || process.env.MARROW_HARNESS || process.env.MARROW_AGENT_CLIENT || '').trim().toLowerCase().replace(/\s+/g, '-').replace(/^@/, '');
|
|
16
|
-
const aliases = {
|
|
17
|
-
claude: 'claude-code',
|
|
18
|
-
claude_code: 'claude-code',
|
|
19
|
-
'claude-code': 'claude-code',
|
|
20
|
-
cursor: 'cursor',
|
|
21
|
-
windsurf: 'windsurf',
|
|
22
|
-
openclaw: 'openclaw',
|
|
23
|
-
codex: 'codex',
|
|
24
|
-
'openai-codex': 'codex',
|
|
25
|
-
gemini: 'gemini',
|
|
26
|
-
google: 'gemini',
|
|
27
|
-
grok: 'grok',
|
|
28
|
-
deepseek: 'deepseek',
|
|
29
|
-
qwen: 'qwen',
|
|
30
|
-
kimi: 'kimi',
|
|
31
|
-
minimax: 'minimax',
|
|
32
|
-
cline: 'cline',
|
|
33
|
-
opencode: 'opencode',
|
|
34
|
-
'open-code': 'opencode',
|
|
35
|
-
hermes: 'hermes',
|
|
36
|
-
'hermes-agent': 'hermes',
|
|
37
|
-
glm: 'glm',
|
|
38
|
-
};
|
|
39
|
-
return aliases[raw] || (SOURCE_CLIENTS.has(raw) ? raw : 'custom');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
12
|
function usage() {
|
|
43
13
|
return `Usage:
|
|
44
14
|
npx @getmarrow/install run --agent deploy-agent -- npm test
|
|
@@ -49,6 +19,9 @@ function usage() {
|
|
|
49
19
|
npx @getmarrow/install govern
|
|
50
20
|
npx @getmarrow/install govern --no-interactive
|
|
51
21
|
npx @getmarrow/install fleet
|
|
22
|
+
npx @getmarrow/install integrations
|
|
23
|
+
npx @getmarrow/install hermes
|
|
24
|
+
npx @getmarrow/install openclaw
|
|
52
25
|
|
|
53
26
|
Commands:
|
|
54
27
|
run Run a command through Marrow pre-action gate and automatic outcome closure
|
|
@@ -57,6 +30,9 @@ Commands:
|
|
|
57
30
|
status Read /v1/agent/status
|
|
58
31
|
govern Interactive setup TUI when run in a terminal; text panel in CI/non-TTY
|
|
59
32
|
fleet Fleet operator TUI for live agents, workflows, gates, proof debt, and exact fixes
|
|
33
|
+
integrations List Marrow-supported harness add-ons
|
|
34
|
+
hermes Show and verify the Marrow add-on path for Hermes Agent
|
|
35
|
+
openclaw Show and verify the Marrow add-on path for OpenClaw
|
|
60
36
|
|
|
61
37
|
Options:
|
|
62
38
|
--agent <id> Agent identity. Defaults to MARROW_FLEET_AGENT_ID, MARROW_AGENT_ID, or local user
|
|
@@ -185,6 +161,12 @@ function detectProjectSignals(cwd = process.cwd()) {
|
|
|
185
161
|
addFile('CLAUDE.md', 'agent_instructions');
|
|
186
162
|
addFile('.mcp.json', 'mcp_config');
|
|
187
163
|
addFile('.cursor', 'cursor_project');
|
|
164
|
+
addFile('hermes.json', 'hermes_config');
|
|
165
|
+
addFile('hermes.yaml', 'hermes_config');
|
|
166
|
+
addFile('hermes.yml', 'hermes_config');
|
|
167
|
+
addFile('.hermes', 'hermes_config');
|
|
168
|
+
addFile('openclaw.json', 'openclaw_config');
|
|
169
|
+
addFile('.openclaw', 'openclaw_config');
|
|
188
170
|
|
|
189
171
|
for (const script of packageScripts) {
|
|
190
172
|
if (/\b(deploy|publish|release|migrate|migration|smoke|check|test)\b/i.test(script)) {
|
|
@@ -298,7 +280,7 @@ function parseArgs(argv) {
|
|
|
298
280
|
return { command, options };
|
|
299
281
|
}
|
|
300
282
|
|
|
301
|
-
if (command === 'status' || command === 'govern' || command === 'fleet') {
|
|
283
|
+
if (command === 'status' || command === 'govern' || command === 'fleet' || command === 'hermes' || command === 'openclaw' || command === 'integrations') {
|
|
302
284
|
const parsed = parseBaseOptions(argv, 1);
|
|
303
285
|
if (parsed.options.help) return { command: 'help' };
|
|
304
286
|
return { command, options: parsed.options };
|
|
@@ -313,7 +295,6 @@ function headers(options) {
|
|
|
313
295
|
'Content-Type': 'application/json',
|
|
314
296
|
'X-Marrow-Agent-Id': options.agentId,
|
|
315
297
|
'X-Marrow-Session-Id': options.sessionId,
|
|
316
|
-
'X-Marrow-Client': sourceClient(),
|
|
317
298
|
'User-Agent': '@getmarrow/install governed-runner',
|
|
318
299
|
};
|
|
319
300
|
return h;
|
|
@@ -480,11 +461,6 @@ async function createDecision(options, action, type) {
|
|
|
480
461
|
profile: options.profile,
|
|
481
462
|
governed: true,
|
|
482
463
|
},
|
|
483
|
-
source_meta: {
|
|
484
|
-
channel: 'cli',
|
|
485
|
-
client: sourceClient(),
|
|
486
|
-
user_intent: type === 'deploy' ? 'deploy' : 'operate',
|
|
487
|
-
},
|
|
488
464
|
});
|
|
489
465
|
}
|
|
490
466
|
|
|
@@ -724,13 +700,10 @@ function normalizeFixCommands(status, capacity) {
|
|
|
724
700
|
add(status.route_contract?.exact_fix);
|
|
725
701
|
add(status.auto_outcome_closure?.exact_fix);
|
|
726
702
|
add(status.capture_coverage?.exact_fix);
|
|
727
|
-
add(status.token_capture?.exact_fix);
|
|
728
|
-
add(status.token_capture?.fix_command, true);
|
|
729
703
|
add(capacity.exact_next_action, true);
|
|
730
704
|
add(capacity.next_action, true);
|
|
731
705
|
if (listValue(status.missed_hooks, status.degraded_hooks).length) add('npx @getmarrow/install --repair');
|
|
732
706
|
if (status.auto_outcome_closure?.status === 'degraded') add('npx @getmarrow/install --repair');
|
|
733
|
-
if (status.token_capture?.detected === false) add('npx @getmarrow/install --repair');
|
|
734
707
|
return commands.slice(0, 6);
|
|
735
708
|
}
|
|
736
709
|
|
|
@@ -884,6 +857,7 @@ function detectHarnesses(cwd = process.cwd()) {
|
|
|
884
857
|
{ name: 'Claude Code', command: 'claude -p', detected: fs.existsSync(path.join(cwd, 'CLAUDE.md')) || fs.existsSync(path.join(os.homedir(), '.claude.json')) },
|
|
885
858
|
{ name: 'Cursor', command: 'cursor', detected: fs.existsSync(path.join(cwd, '.cursor')) || fs.existsSync(path.join(os.homedir(), '.cursor')) },
|
|
886
859
|
{ name: 'OpenCode', command: 'opencode', detected: fs.existsSync(path.join(cwd, 'opencode.json')) || fs.existsSync(path.join(os.homedir(), '.opencode')) },
|
|
860
|
+
{ 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')) },
|
|
887
861
|
{ name: 'OpenClaw', command: 'openclaw agent', detected: fs.existsSync(path.join(os.homedir(), '.openclaw')) },
|
|
888
862
|
{ name: 'CI script', command: 'npm test', detected: fs.existsSync(path.join(cwd, 'package.json')) },
|
|
889
863
|
{ name: 'Custom command', command: '<your-agent-command>', detected: true },
|
|
@@ -891,6 +865,140 @@ function detectHarnesses(cwd = process.cwd()) {
|
|
|
891
865
|
return candidates;
|
|
892
866
|
}
|
|
893
867
|
|
|
868
|
+
function localIntegrationManifest(name) {
|
|
869
|
+
const key = String(name || '').toLowerCase().replace(/\s+/g, '-');
|
|
870
|
+
if (key === 'hermes' || key === 'hermes-agent') {
|
|
871
|
+
return {
|
|
872
|
+
integration: 'hermes-agent',
|
|
873
|
+
title: 'Marrow + Hermes Agent',
|
|
874
|
+
client_label: 'hermes',
|
|
875
|
+
command: 'hermes',
|
|
876
|
+
install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install hermes',
|
|
877
|
+
governed_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install run --agent hermes-prod --profile production --policy enforce -- hermes',
|
|
878
|
+
capture_points: [
|
|
879
|
+
'/goal -> Marrow completion contract',
|
|
880
|
+
'verification evidence -> Marrow proof pack',
|
|
881
|
+
'/learn -> outcome-ranked fleet lesson',
|
|
882
|
+
'/journey -> governance timeline',
|
|
883
|
+
'background subagents -> agent_id + source_meta.client=hermes',
|
|
884
|
+
],
|
|
885
|
+
exact_next_action: 'Run the Hermes add-on, then call Marrow runtime before deploy, merge, publish, migration, credential, or customer-facing work.',
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
if (key === 'openclaw' || key === 'open-claw') {
|
|
889
|
+
return {
|
|
890
|
+
integration: 'openclaw',
|
|
891
|
+
title: 'Marrow + OpenClaw',
|
|
892
|
+
client_label: 'openclaw',
|
|
893
|
+
command: 'openclaw agent',
|
|
894
|
+
install_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install openclaw',
|
|
895
|
+
governed_command: 'MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install run --agent openclaw-release --profile production --policy enforce -- openclaw agent',
|
|
896
|
+
capture_points: [
|
|
897
|
+
'agent sessions -> workflow sessions',
|
|
898
|
+
'handoff/result files -> proof packs',
|
|
899
|
+
'timeouts/silent quits -> failed or stale outcomes',
|
|
900
|
+
'release supervisor results -> deploy proof',
|
|
901
|
+
'watchdog checkpoints -> fleet handoff lessons',
|
|
902
|
+
],
|
|
903
|
+
exact_next_action: 'Run the OpenClaw add-on, then wrap release and handoff commands with the Marrow governed runner.',
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
return null;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
async function integrationManifest(options, name) {
|
|
910
|
+
const local = localIntegrationManifest(name);
|
|
911
|
+
if (!local) throw new Error(`Unsupported integration: ${name}`);
|
|
912
|
+
if (!options.apiKey) return { source: 'local', manifest: local };
|
|
913
|
+
const route = name === 'openclaw' ? '/v1/agent/integrations/openclaw' : '/v1/agent/integrations/hermes';
|
|
914
|
+
try {
|
|
915
|
+
const remote = await requestJson(options, 'GET', route);
|
|
916
|
+
return { source: 'api', manifest: remote };
|
|
917
|
+
} catch (error) {
|
|
918
|
+
return { source: 'local', manifest: { ...local, api_warning: error instanceof Error ? error.message : String(error) } };
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function renderIntegrationPanel(name, manifest, source = 'local', detectedHarnesses = detectHarnesses()) {
|
|
923
|
+
const title = manifest.title || (manifest.integration === 'openclaw' ? 'Marrow + OpenClaw' : 'Marrow + Hermes Agent');
|
|
924
|
+
const capturePoints = Array.isArray(manifest.capture_points)
|
|
925
|
+
? manifest.capture_points.map((point) => {
|
|
926
|
+
if (typeof point === 'string') return point;
|
|
927
|
+
return `${point.harness_surface || point.hermes_surface || 'harness event'} -> ${point.marrow_mapping || 'Marrow event'}`;
|
|
928
|
+
})
|
|
929
|
+
: [];
|
|
930
|
+
const installCommands = Array.isArray(manifest.install_commands)
|
|
931
|
+
? manifest.install_commands
|
|
932
|
+
: [manifest.install_command, manifest.governed_command].filter(Boolean);
|
|
933
|
+
const detected = detectedHarnesses.find((harness) => harness.name.toLowerCase().includes(name === 'openclaw' ? 'openclaw' : 'hermes'))?.detected;
|
|
934
|
+
const lines = [
|
|
935
|
+
title,
|
|
936
|
+
'',
|
|
937
|
+
`Status: ${manifest.status || 'supported'} (${source})`,
|
|
938
|
+
`Detected locally: ${detected ? 'yes' : 'not yet'}`,
|
|
939
|
+
`Client label: ${manifest.client_label}`,
|
|
940
|
+
'',
|
|
941
|
+
'What Marrow captures:',
|
|
942
|
+
...capturePoints.map((point) => ` - ${displayText(point, 110)}`),
|
|
943
|
+
'',
|
|
944
|
+
'Install / run:',
|
|
945
|
+
...installCommands.map((command) => ` ${displayText(command, 140)}`),
|
|
946
|
+
'',
|
|
947
|
+
'Why this matters:',
|
|
948
|
+
` ${displayText(manifest.marrow_positioning || 'Keep the harness. Add Marrow governance, proof packs, outcomes, and buyer-grade fleet value.', 140)}`,
|
|
949
|
+
'',
|
|
950
|
+
`Next: ${displayText(manifest.exact_next_action, 140)}`,
|
|
951
|
+
];
|
|
952
|
+
if (manifest.api_warning) lines.push('', `API warning: ${displayText(manifest.api_warning, 120)}`);
|
|
953
|
+
return lines.join('\n');
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
async function integrationOnly(parsed, name) {
|
|
957
|
+
const result = await integrationManifest(parsed.options, name);
|
|
958
|
+
const panel = renderIntegrationPanel(name, result.manifest, result.source);
|
|
959
|
+
if (parsed.options.json) {
|
|
960
|
+
return {
|
|
961
|
+
ok: true,
|
|
962
|
+
source: result.source,
|
|
963
|
+
integration: result.manifest,
|
|
964
|
+
panel,
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
process.stdout.write(`${panel}\n`);
|
|
968
|
+
return { ok: true, source: result.source, integration: result.manifest };
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
async function integrationsOnly(parsed) {
|
|
972
|
+
const local = {
|
|
973
|
+
integration_registry_version: 'local.harness-addons-v1',
|
|
974
|
+
supported_harnesses: ['hermes', 'openclaw'].map((name) => localIntegrationManifest(name)),
|
|
975
|
+
exact_next_action: 'Run npx @getmarrow/install hermes or npx @getmarrow/install openclaw for a harness-specific setup path.',
|
|
976
|
+
};
|
|
977
|
+
let registry = local;
|
|
978
|
+
let source = 'local';
|
|
979
|
+
if (parsed.options.apiKey) {
|
|
980
|
+
try {
|
|
981
|
+
registry = await requestJson(parsed.options, 'GET', '/v1/agent/integrations');
|
|
982
|
+
source = 'api';
|
|
983
|
+
} catch (error) {
|
|
984
|
+
registry = { ...local, api_warning: error instanceof Error ? error.message : String(error) };
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
if (parsed.options.json) return { ok: true, source, registry };
|
|
988
|
+
const harnesses = Array.isArray(registry.supported_harnesses) ? registry.supported_harnesses : [];
|
|
989
|
+
process.stdout.write([
|
|
990
|
+
'Marrow Harness Integrations',
|
|
991
|
+
'',
|
|
992
|
+
`Source: ${source}`,
|
|
993
|
+
'Supported add-ons:',
|
|
994
|
+
...harnesses.map((harness) => ` - ${displayText(harness.client_label || harness.integration || harness.title, 40)} ${displayText(harness.install_command || harness.install_commands?.[0] || '', 120)}`),
|
|
995
|
+
'',
|
|
996
|
+
`Next: ${displayText(registry.exact_next_action, 140)}`,
|
|
997
|
+
registry.api_warning ? `API warning: ${displayText(registry.api_warning, 120)}` : '',
|
|
998
|
+
].filter(Boolean).join('\n') + '\n');
|
|
999
|
+
return { ok: true, source, registry };
|
|
1000
|
+
}
|
|
1001
|
+
|
|
894
1002
|
function governPanel(options) {
|
|
895
1003
|
const rows = detectHarnesses();
|
|
896
1004
|
const project = detectProjectSignals();
|
|
@@ -1495,11 +1603,17 @@ async function runCli(argv) {
|
|
|
1495
1603
|
return;
|
|
1496
1604
|
} else if (parsed.command === 'fleet') {
|
|
1497
1605
|
result = await runFleetInteractive(parsed.options);
|
|
1606
|
+
} else if (parsed.command === 'hermes') {
|
|
1607
|
+
result = await integrationOnly(parsed, 'hermes');
|
|
1608
|
+
} else if (parsed.command === 'openclaw') {
|
|
1609
|
+
result = await integrationOnly(parsed, 'openclaw');
|
|
1610
|
+
} else if (parsed.command === 'integrations') {
|
|
1611
|
+
result = await integrationsOnly(parsed);
|
|
1498
1612
|
}
|
|
1499
1613
|
|
|
1500
1614
|
if (parsed.options?.json) process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
1501
1615
|
else if (result?.blocked) process.stderr.write(`BLOCKED: ${result.message || 'Marrow blocked this action.'}\n`);
|
|
1502
|
-
else if (
|
|
1616
|
+
else if (!['run', 'fleet', 'hermes', 'openclaw', 'integrations'].includes(parsed.command)) process.stdout.write('Marrow command completed.\n');
|
|
1503
1617
|
|
|
1504
1618
|
if (parsed.command === 'run' || result?.blocked) process.exitCode = result?.exitCode ?? (result?.ok === false ? 1 : 0);
|
|
1505
1619
|
}
|
|
@@ -1534,5 +1648,9 @@ module.exports = {
|
|
|
1534
1648
|
buildFleetState,
|
|
1535
1649
|
renderFleetTui,
|
|
1536
1650
|
runFleetInteractive,
|
|
1651
|
+
localIntegrationManifest,
|
|
1652
|
+
renderIntegrationPanel,
|
|
1653
|
+
integrationOnly,
|
|
1654
|
+
integrationsOnly,
|
|
1537
1655
|
runCli,
|
|
1538
1656
|
};
|
package/src/installer.js
CHANGED
|
@@ -6,35 +6,6 @@ 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
|
-
}
|
|
38
9
|
|
|
39
10
|
function parseArgs(argv) {
|
|
40
11
|
const options = {
|
|
@@ -134,6 +105,12 @@ function findUp(startDir, names, maxDepth = 8) {
|
|
|
134
105
|
}
|
|
135
106
|
|
|
136
107
|
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;
|
|
137
114
|
const marker = findUp(startDir, ['package.json', 'pyproject.toml', 'requirements.txt', '.git', 'AGENTS.md', 'CLAUDE.md']);
|
|
138
115
|
return marker ? path.dirname(marker) : path.resolve(startDir);
|
|
139
116
|
}
|
|
@@ -312,16 +289,14 @@ Marrow should run passively after install:
|
|
|
312
289
|
|
|
313
290
|
- Use MCP hooks when available: \`npx -y @getmarrow/mcp setup\`.
|
|
314
291
|
- Use SDK passive runtime in owned Node processes: \`createPassiveRuntime().install()\`.
|
|
315
|
-
-
|
|
316
|
-
- Before risky work, use Marrow's decision brief or passive prompt hook.
|
|
292
|
+
- Before risky work, use Marrow's before-action intervention from \`GET /v1/agent/status\` or \`POST /v1/agent/runtime\`.
|
|
317
293
|
- After meaningful work, record the outcome so future agents learn from it.
|
|
318
294
|
- Check health with \`marrow_agent_status\` or \`GET /v1/agent/status\`.
|
|
319
295
|
|
|
320
296
|
Required environment:
|
|
321
297
|
|
|
322
298
|
- \`MARROW_API_KEY\`
|
|
323
|
-
- Optional: \`MARROW_BASE_URL\`, \`MARROW_FLEET_AGENT_ID
|
|
324
|
-
- Optional: \`MARROW_PASSIVE_TOKEN_USAGE=false\` disables compact provider usage capture when needed.
|
|
299
|
+
- Optional: \`MARROW_BASE_URL\`, \`MARROW_FLEET_AGENT_ID\`
|
|
325
300
|
${MARROW_BLOCK_END}`;
|
|
326
301
|
}
|
|
327
302
|
|
|
@@ -343,7 +318,6 @@ if (apiKey && !globalThis.__MARROW_PASSIVE_RUNTIME__) {
|
|
|
343
318
|
useAgentRuntime: process.env.MARROW_AGENT_RUNTIME !== 'false',
|
|
344
319
|
useWorkflowGate: process.env.MARROW_WORKFLOW_GATE !== 'false',
|
|
345
320
|
requireOutcomeClosure: process.env.MARROW_REQUIRE_OUTCOME_CLOSURE !== 'false',
|
|
346
|
-
captureModelUsage: process.env.MARROW_PASSIVE_TOKEN_USAGE !== 'false',
|
|
347
321
|
});
|
|
348
322
|
|
|
349
323
|
runtime.install();
|
|
@@ -359,14 +333,12 @@ function envExample() {
|
|
|
359
333
|
return `MARROW_API_KEY=mrw_live_replace_me
|
|
360
334
|
MARROW_BASE_URL=${DEFAULT_BASE_URL}
|
|
361
335
|
MARROW_FLEET_AGENT_ID=agent-or-fleet-id
|
|
362
|
-
MARROW_CLIENT=codex
|
|
363
336
|
MARROW_ENFORCEMENT_MODE=auto
|
|
364
337
|
MARROW_PASSIVE_BRIEF=auto
|
|
365
338
|
MARROW_PASSIVE_VALUE_REPORT=true
|
|
366
339
|
MARROW_AGENT_RUNTIME=true
|
|
367
340
|
MARROW_WORKFLOW_GATE=true
|
|
368
341
|
MARROW_REQUIRE_OUTCOME_CLOSURE=true
|
|
369
|
-
MARROW_PASSIVE_TOKEN_USAGE=true
|
|
370
342
|
`;
|
|
371
343
|
}
|
|
372
344
|
|
|
@@ -595,7 +567,6 @@ async function runSelfTest(options) {
|
|
|
595
567
|
authorization: `Bearer ${options.apiKey}`,
|
|
596
568
|
'content-type': 'application/json',
|
|
597
569
|
'x-marrow-session-id': `install-${Date.now()}`,
|
|
598
|
-
'x-marrow-client': sourceClient(),
|
|
599
570
|
};
|
|
600
571
|
if (options.agentId) headers['x-marrow-agent-id'] = options.agentId;
|
|
601
572
|
|
|
@@ -606,11 +577,6 @@ async function runSelfTest(options) {
|
|
|
606
577
|
body: JSON.stringify({
|
|
607
578
|
type: 'process',
|
|
608
579
|
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
|
-
},
|
|
614
580
|
}),
|
|
615
581
|
});
|
|
616
582
|
|
|
@@ -667,14 +633,8 @@ async function runSelfTest(options) {
|
|
|
667
633
|
ok: false,
|
|
668
634
|
error: error instanceof Error ? error.message : String(error),
|
|
669
635
|
}));
|
|
670
|
-
const
|
|
671
|
-
|
|
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);
|
|
636
|
+
const firstValueSignal = buildFirstValueSignal(status, runtime, performance, firstValue);
|
|
637
|
+
const installValueMoment = buildInstallValueMoment(firstValueSignal, status, runtime, performance, firstValue);
|
|
678
638
|
return {
|
|
679
639
|
skipped: false,
|
|
680
640
|
decision_id: decisionId,
|
|
@@ -686,11 +646,11 @@ async function runSelfTest(options) {
|
|
|
686
646
|
auto_outcome_closure: status.auto_outcome_closure || null,
|
|
687
647
|
runtime_active: Boolean(runtime && runtime.ok !== false),
|
|
688
648
|
runtime_exact_next_action: runtime.exact_next_action || null,
|
|
689
|
-
runtime_before_you_act: runtime.before_you_act || null,
|
|
649
|
+
runtime_before_you_act: runtime.intervention?.agent_copy || runtime.intervention?.before_action || runtime.before_you_act || null,
|
|
650
|
+
runtime_intervention: runtime.intervention || null,
|
|
690
651
|
first_value: firstValue && firstValue.ok !== false ? firstValue : null,
|
|
691
652
|
first_value_signal: firstValueSignal,
|
|
692
653
|
install_value_moment: installValueMoment,
|
|
693
|
-
token_value_proof: tokenValueProof,
|
|
694
654
|
performance_proof: performance && performance.ok !== false ? {
|
|
695
655
|
avoided_mistakes: performance.avoided_mistakes ?? performance.avoided_repeated_mistakes ?? 0,
|
|
696
656
|
reused_winning_decisions: performance.reused_winning_decisions ?? 0,
|
|
@@ -702,30 +662,11 @@ async function runSelfTest(options) {
|
|
|
702
662
|
};
|
|
703
663
|
}
|
|
704
664
|
|
|
705
|
-
function
|
|
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) {
|
|
665
|
+
function buildInstallValueMoment(firstValueSignal = {}, status = {}, runtime = {}, performance = {}, firstValue = {}) {
|
|
723
666
|
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);
|
|
726
667
|
return {
|
|
727
668
|
headline: firstValue.headline || firstValue.first_value.headline || 'Your agent is no longer starting from zero.',
|
|
728
|
-
proof,
|
|
669
|
+
proof: Array.isArray(firstValue.first_value.proof) ? firstValue.first_value.proof : [],
|
|
729
670
|
fleet_signal: firstValue.history_signal?.summary || 'Fresh account: Marrow will build fleet memory from this first captured outcome.',
|
|
730
671
|
try_this_now: firstValue.first_value.try_this_now || 'Ask your agent: "I am about to deploy to production. What should I check first?"',
|
|
731
672
|
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.',
|
|
@@ -735,7 +676,9 @@ function buildInstallValueMoment(firstValueSignal = {}, status = {}, runtime = {
|
|
|
735
676
|
|
|
736
677
|
const proof = firstValueSignal.value_proof || [];
|
|
737
678
|
const hasFleetSignal = proof.length > 0;
|
|
738
|
-
const runtimeLesson = runtime.
|
|
679
|
+
const runtimeLesson = runtime.intervention?.agent_copy
|
|
680
|
+
|| runtime.intervention?.before_action
|
|
681
|
+
|| runtime.before_you_act
|
|
739
682
|
|| runtime.before_you_act_injection?.message
|
|
740
683
|
|| runtime.exact_next_action
|
|
741
684
|
|| firstValueSignal.first_lesson;
|
|
@@ -746,19 +689,18 @@ function buildInstallValueMoment(firstValueSignal = {}, status = {}, runtime = {
|
|
|
746
689
|
'Captured this setup decision',
|
|
747
690
|
'Closed the outcome successfully',
|
|
748
691
|
'Runtime gate is ' + (firstValueSignal.active ? 'active' : 'installed'),
|
|
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',
|
|
692
|
+
runtime.intervention?.must_use_before_action ? 'Before-action intervention is active for risky work' : runtimeLesson ? 'Future risky work now gets a pre-action brief' : 'Future risky work now gets checked before action',
|
|
751
693
|
],
|
|
752
694
|
fleet_signal: hasFleetSignal
|
|
753
695
|
? 'Marrow already found signal: ' + proof.join('; ') + '.'
|
|
754
696
|
: 'Fresh account: Marrow will start building fleet memory from this first captured outcome.',
|
|
755
697
|
try_this_now: 'Ask your agent: "I am about to deploy to production. What should I check first?"',
|
|
756
|
-
expected_response: 'Marrow should answer with
|
|
757
|
-
first_lesson: runtimeLesson || 'Marrow will
|
|
698
|
+
expected_response: 'Marrow should answer with proceed/warn/block, required proof, and any matching prior lesson/playbook before the agent acts.',
|
|
699
|
+
first_lesson: runtimeLesson || 'Marrow will stop agents before risky or repeated work and surface the prior lesson/playbook.',
|
|
758
700
|
};
|
|
759
701
|
}
|
|
760
702
|
|
|
761
|
-
function buildFirstValueSignal(status, runtime, performance, firstValue = {}
|
|
703
|
+
function buildFirstValueSignal(status, runtime, performance, firstValue = {}) {
|
|
762
704
|
if (firstValue && firstValue.ok !== false && firstValue.first_value) {
|
|
763
705
|
const capture = firstValue.capture || {};
|
|
764
706
|
const proof = firstValue.value_proof || {};
|
|
@@ -767,13 +709,11 @@ function buildFirstValueSignal(status, runtime, performance, firstValue = {}, to
|
|
|
767
709
|
if (Number(proof.reused_winning_decisions || 0) > 0) proofBits.push(`${proof.reused_winning_decisions} reused winning decision(s)`);
|
|
768
710
|
if (Number(proof.prevented_bad_actions || 0) > 0) proofBits.push(`${proof.prevented_bad_actions} prevented risky action(s)`);
|
|
769
711
|
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);
|
|
772
712
|
return {
|
|
773
713
|
active: Boolean(firstValue.active),
|
|
774
714
|
headline: `Marrow active: ${(capture.surfaces || ['decisions']).join(', ')} captured.`,
|
|
775
715
|
captured: capture.surfaces || ['decisions'],
|
|
776
|
-
first_lesson: firstValue.first_value.first_lesson,
|
|
716
|
+
first_lesson: firstValue.first_value.first_lesson || runtime?.intervention?.agent_copy,
|
|
777
717
|
value_proof: proofBits,
|
|
778
718
|
next_action: firstValue.next_action?.reason || 'Keep working; Marrow will capture outcomes and reuse lessons automatically.',
|
|
779
719
|
};
|
|
@@ -796,8 +736,6 @@ function buildFirstValueSignal(status, runtime, performance, firstValue = {}, to
|
|
|
796
736
|
if (Number(proof.prevented_bad_actions || 0) > 0) proofBits.push(`${proof.prevented_bad_actions} prevented risky action(s)`);
|
|
797
737
|
const tokens = proof.token_time_saved_estimate?.estimated_tokens_saved || 0;
|
|
798
738
|
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);
|
|
801
739
|
|
|
802
740
|
const firstLesson = runtime.before_you_act
|
|
803
741
|
|| runtime.before_you_act_injection?.message
|
|
@@ -861,20 +799,6 @@ function printReport(report) {
|
|
|
861
799
|
process.stdout.write(`- Next: ${report.selfTest.first_value_signal.next_action}\n`);
|
|
862
800
|
}
|
|
863
801
|
}
|
|
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
|
-
}
|
|
878
802
|
}
|
|
879
803
|
|
|
880
804
|
if (report.remediation) {
|
|
@@ -1025,5 +949,4 @@ module.exports = {
|
|
|
1025
949
|
inspectNpmTokenConfig,
|
|
1026
950
|
inspectSdkDependency,
|
|
1027
951
|
buildInstallValueMoment,
|
|
1028
|
-
buildTokenValueProof,
|
|
1029
952
|
};
|