@getmarrow/install 0.1.27 → 0.1.29

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.
Files changed (3) hide show
  1. package/README.md +100 -198
  2. package/package.json +3 -3
  3. package/src/installer.js +144 -20
package/README.md CHANGED
@@ -1,93 +1,86 @@
1
1
  # @getmarrow/install
2
2
 
3
- Universal installer for Marrow agent governance, proof, and passive control setup.
3
+ > Universal installer and governed runner for Marrow agent fleets.
4
4
 
5
- Use it when you want Marrow to detect the local agent/runtime environment and wire the safest passive governance integration automatically.
5
+ Marrow is the runtime control and proof layer for teams running AI agents. It applies policy and prior lessons before consequential actions, then records the evidence and outcome afterward.
6
+
7
+ Use `@getmarrow/install` as the default entry point. It detects supported agent and project surfaces, writes the appropriate passive configuration, runs a harmless end-to-end self-test, and reports whether policy, proof, attribution, and outcome capture are active.
8
+
9
+ ## Install
6
10
 
7
11
  ```bash
8
- npx @getmarrow/install --dry-run
9
12
  npx @getmarrow/install --yes
10
- npx @getmarrow/install --repair
11
- npx @getmarrow/install doctor
12
- npx @getmarrow/install govern
13
- npx @getmarrow/install fleet
14
- npx @getmarrow/install integrations
15
- npx @getmarrow/install hermes
16
- npx @getmarrow/install openclaw
17
13
  ```
18
14
 
19
- ## What's New in v0.1.26
15
+ Required secret:
20
16
 
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.
17
+ ```bash
18
+ export MARROW_API_KEY=mrw_live_...
19
+ ```
22
20
 
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.
21
+ ## What's New in v0.1.29
30
22
 
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.
23
+ v0.1.29 makes first-run activation server-verifiable instead of relying on local setup output:
32
24
 
33
- ## What's New in v0.1.24
25
+ - `npx @getmarrow/install activate` detects the current harness, writes supported passive controls, creates and closes a harmless decision, and asks Marrow to verify that exact outcome;
26
+ - activation succeeds only when the API returns a tenant-scoped activation receipt;
27
+ - the receipt reports capture, before-action intervention, outcome closure, and first-value state;
28
+ - existing setup, governed runner, and TUI commands remain compatible.
34
29
 
35
- v0.1.24 aligns the installer docs with Marrow's control-plane direction.
30
+ Use `activate` when you want one command with an explicit success contract. Use `--yes` when an existing automation already handles setup prompts and verification output.
36
31
 
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.
32
+ ```bash
33
+ npx @getmarrow/install activate
34
+ ```
41
35
 
42
- ## What's New in v0.1.23
36
+ ## What It Detects
43
37
 
44
- v0.1.23 improves attribution quality so Marrow can produce cleaner per-agent, per-harness, and per-workflow value reports.
38
+ The installer detects supported configuration and project signals for:
45
39
 
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.
40
+ - Codex, Claude Code, Cursor, Cursor Composer, Windsurf, Cline, OpenCode, Hermes, and OpenClaw;
41
+ - Gemini, Grok, DeepSeek, Qwen, Kimi, MiniMax, and GLM command-line or custom harness paths;
42
+ - MCP client configuration;
43
+ - Node.js and Python projects;
44
+ - CI, deploy, publish, merge, migration, and custom shell workflows.
51
45
 
52
- Business value: dashboards and reports can show which agents, harnesses, and workflow types are improving instead of grouping too much work into "unknown."
46
+ Marrow does not replace these models or harnesses. It adds a common business control, proof, and outcome layer around the actions they perform.
53
47
 
54
- ## Governed Runner
48
+ ## First-Run Activation
55
49
 
56
- The Marrow governed runner is for businesses that want agent governance without replacing their existing harness.
50
+ With a valid key, `activate`:
57
51
 
58
- - `npx @getmarrow/install govern` prints a setup panel for detected harnesses and recommended protected commands.
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.
60
- - Risky actions can fail closed by default when Marrow requires owner approval, blocks an action, or requires missing proof.
61
- - Successful and failed commands automatically close outcomes through `/v1/agent/commit` with a redacted proof pack.
62
- - The runner sends action and command metadata only; it does not upload command stdout, stderr, full environment values, or plaintext API keys.
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.
52
+ 1. detects the local integration surfaces;
53
+ 2. writes supported config and passive instructions;
54
+ 3. creates a harmless test decision;
55
+ 4. closes its outcome;
56
+ 5. sends the exact self-test decision ID to Marrow for server-side verification;
57
+ 6. reads agent status and the one-call runtime;
58
+ 7. returns an activation receipt with capture, intervention, closure, first-value state, and the exact next action.
64
59
 
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.
60
+ Healthy output confirms the exact decision outcome exists under the authenticated account and agent. A local file write or client-supplied `verified: true` value cannot produce an active receipt.
66
61
 
67
- ### Governed Runner Quickstart
62
+ ## Govern TUI
68
63
 
69
- Preview the detected harnesses and protected command examples:
64
+ Open the interactive setup panel:
70
65
 
71
66
  ```bash
72
67
  npx @getmarrow/install govern
73
68
  ```
74
69
 
75
- Run a harmless command through Marrow:
76
-
77
- ```bash
78
- MARROW_API_KEY=<your_marrow_key> npx @getmarrow/install run --agent codex-prod --profile production -- node -e "process.exit(0)"
79
- ```
70
+ The TUI shows detected harnesses and project risks, recommends passive, pilot, or enforce mode with reasons, lets the owner accept or override the recommendation, runs the self-test, and confirms the active controls. Use `Ctrl+C` to exit.
80
71
 
81
- Gate a production action before the agent executes it:
72
+ For non-interactive environments:
82
73
 
83
74
  ```bash
84
- MARROW_API_KEY=<your_marrow_key> npx @getmarrow/install gate "deploy production worker after tests pass"
75
+ npx @getmarrow/install govern --no-interactive
85
76
  ```
86
77
 
87
- Wrap a real deploy, publish, merge, or migration command only after the agent has the required proof:
78
+ ## Governed Runner
79
+
80
+ Place Marrow around an existing command without replacing the agent harness:
88
81
 
89
82
  ```bash
90
- MARROW_API_KEY=<your_marrow_key> npx @getmarrow/install run \
83
+ npx @getmarrow/install run \
91
84
  --agent deploy-agent \
92
85
  --type deploy \
93
86
  --profile production \
@@ -95,180 +88,89 @@ MARROW_API_KEY=<your_marrow_key> npx @getmarrow/install run \
95
88
  -- wrangler deploy
96
89
  ```
97
90
 
98
- Use `--policy warn` for pilot mode and `--fail-open` only for non-production local workflows where Marrow should never block execution.
91
+ The runner:
99
92
 
100
- ## Which Install Path Should I Use?
93
+ 1. requests the Marrow runtime gate;
94
+ 2. prints the decision, relevant lesson, owner-approval state, and required proof;
95
+ 3. blocks when policy requires it;
96
+ 4. runs the original command when allowed;
97
+ 5. records success or failure and attaches a redacted proof pack.
101
98
 
102
- Start here unless you already know you need a lower-level integration:
99
+ Useful commands:
103
100
 
104
101
  ```bash
105
- npx @getmarrow/install --yes
106
- ```
107
-
108
- The universal installer detects your local agent/runtime environment and wires the safest combination of MCP hooks, SDK passive runtime files, and agent instructions automatically. It also runs the self-test and prints first-run value proof.
109
-
110
- Use the lower-level packages only when you need direct control:
111
-
112
- - **SDK:** use `@getmarrow/sdk` when you are building a custom Node/TypeScript agent integration or wrapping your own tools, commands, deploys, and publishes in code.
113
- - **MCP:** use `@getmarrow/mcp` when you want manual MCP server/hook setup for Claude Code, Claude Desktop, Cursor, or another MCP-compatible client.
114
-
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.
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
-
125
- ## Agent Value Proof Quickstart
126
-
127
- One command should prove Marrow is active and useful:
128
-
129
- ```bash
130
- MARROW_API_KEY=<your_marrow_key> npx @getmarrow/install --yes
131
- ```
132
-
133
- Expected result:
134
-
135
- - Marrow writes the safest detected MCP/SDK/agent config.
136
- - A harmless setup decision is created and its outcome is committed.
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.
139
- - `/v1/agent/runtime` verifies the one-call runtime gate and returns the before-action intervention contract.
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.
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.
144
- - Fresh accounts get a first useful action to try immediately.
145
- - Accounts with history get proof such as avoided mistakes, reused winning decisions, prevented risky actions, or estimated time/token savings.
146
-
147
- ## First Five-Minute Proof
148
-
149
- After install, ask the agent:
150
-
151
- ```text
152
- I am about to deploy to production. What should I check first?
153
- ```
154
-
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."
156
-
157
- ## What It Detects
158
-
159
- - OpenClaw-style workspaces
160
- - Hermes Agent config files
161
- - Codex/agent instruction files such as `AGENTS.md`
162
- - Claude Code settings and hooks
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
166
- - MCP config files
167
- - Node projects
168
- - Python projects
169
-
170
- ## Install Modes
171
-
172
- ```bash
173
- npx @getmarrow/install --mcp --dry-run
174
- npx @getmarrow/install --sdk --dry-run
175
- npx @getmarrow/install --both --dry-run
176
- npx @getmarrow/install --md --dry-run
177
- ```
178
-
179
- `--dry-run` is the default unless `--yes` is passed.
180
-
181
- ## What It Writes
182
-
183
- - `.claude/settings.json` passive MCP hooks for tool outcomes and prompt context.
184
- - `.mcp.json` with the Marrow MCP server entry.
185
- - `.marrow/passive-runtime.mjs` for SDK passive runtime preload in Node processes.
186
- - `.marrow/env.example` with required environment variables.
187
- - `AGENTS.md` instructions for agents that rely on markdown/skills.
188
- - `.cursor/rules/marrow.mdc` when a Cursor project is detected.
189
-
190
- ## Self-Test
191
-
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.
193
-
194
- ```bash
195
- MARROW_API_KEY=<your_marrow_key> npx @getmarrow/install --yes
196
- ```
197
-
198
- Skip self-test:
199
-
200
- ```bash
201
- npx @getmarrow/install --yes --no-self-test
202
- ```
203
-
204
- Doctor check:
205
-
206
- ```bash
207
- MARROW_API_KEY=<your_marrow_key> npx @getmarrow/install doctor
102
+ npx @getmarrow/install gate --agent deploy-agent --type deploy --action "deploy production"
103
+ npx @getmarrow/install status
104
+ npx @getmarrow/install doctor
105
+ npx @getmarrow/install --repair
208
106
  ```
209
107
 
210
- Repair missing hooks/config:
108
+ ## Fleet Operator TUI
211
109
 
212
110
  ```bash
213
- MARROW_API_KEY=<your_marrow_key> npx @getmarrow/install --repair
111
+ npx @getmarrow/install fleet
214
112
  ```
215
113
 
216
- ## SDK Dependency
114
+ The fleet view shows live agents, active workflows, risky actions waiting for proof, stale or failed outcomes, capture health, recent decisions, and exact repair commands. It is an operator surface for the authenticated account, not a public status dashboard.
217
115
 
218
- When the installer writes `.marrow/passive-runtime.mjs`, the project should have `@getmarrow/sdk` installed:
116
+ ## Integration Paths
219
117
 
220
- ```bash
221
- npm install @getmarrow/sdk
222
- ```
118
+ | Path | Use it when | Owner effort |
119
+ | --- | --- | --- |
120
+ | Universal installer | You want Marrow to detect and wire the safest supported integration | Lowest |
121
+ | Governed runner | You need control around existing shell, CI, deploy, publish, merge, or migration commands | Low |
122
+ | MCP package | The agent client supports MCP and should use Marrow tools and hooks natively | Low |
123
+ | SDK | You own the Node.js/TypeScript runtime and need programmatic control | Advanced |
124
+ | Event contract | You have a custom harness that must map its lifecycle into Marrow | Advanced |
223
125
 
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.
126
+ These are integration surfaces for one Marrow product, not separate products.
225
127
 
226
- ## Passive Token Value Proof
128
+ ## Always-On Lifecycle
227
129
 
228
- Token value proof is default-on when the installer writes `.marrow/passive-runtime.mjs`.
130
+ Supported integrations capture a compact lifecycle without storing raw prompts, completions, command output, tool output, or credentials. Marrow recognizes prompt, goal, pre-action, tool/command result, verification evidence, workflow/session, subagent, handoff, proof-pack, and outcome events.
229
131
 
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.
132
+ Meaningful work opens an outcome-closure item. A tool exit or workflow completion does not silently count as a successful business outcome; an explicit outcome receipt closes it. Transient delivery failures are held in an owner-only local spool and retried with the same event ID so retries do not create duplicate lifecycle records.
231
133
 
232
- Captured fields are intentionally narrow:
134
+ Owners can inspect pending outcomes in Fleet Operations. Agents can retrieve a tenant-scoped causal trace for a decision to see the prior failure, lesson, gate, proof, workflow, and outcome path that changed the action.
233
135
 
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`
136
+ ## Passive Token and Value Proof
239
137
 
240
- Marrow does not capture prompt text, completion text, raw tool output, command output, full environment values, or plaintext secrets for token value proof.
138
+ When the installer writes `.marrow/passive-runtime.mjs` and the harness exposes usage metadata, Marrow can capture compact provider/model, token, latency, and optional cost counts. It does not require raw prompts, completions, command output, tool output, or plaintext secrets.
241
139
 
242
- Disable capture only when a project requires it:
140
+ After meaningful work, supported runtime and commit responses can return observed usage, trend direction, evidence confidence, and the next capture improvement. Savings are only reported when the available evidence supports them.
243
141
 
244
- ```bash
245
- MARROW_PASSIVE_TOKEN_USAGE=false
246
- ```
247
-
248
- Day-one behavior:
142
+ ## Trust and Data Boundaries
249
143
 
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.
144
+ - Private account, fleet, workflow, proof, and agent data remains tenant-scoped by default.
145
+ - Agent-bound keys can be restricted to an allowed identity and permission set.
146
+ - Sanitized aggregate contribution is optional and never means sharing raw prompts, code, secrets, proof packs, account identifiers, agent identifiers, or customer identities.
147
+ - The installer diagnoses key locations without printing secret values.
148
+ - Marrow returns guidance and policy data. Agents must not execute returned text as shell input.
253
149
 
150
+ See the [Trust Center](https://getmarrow.ai/trust/) for implemented controls, current limits, and roadmap status.
254
151
 
255
- ## Trust and Data Boundaries
152
+ ## Environment
256
153
 
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.
154
+ | Variable | Required | Purpose |
155
+ | --- | --- | --- |
156
+ | `MARROW_API_KEY` | Yes for live verification | Account or agent-bound API key |
157
+ | `MARROW_BASE_URL` | No | API base override |
158
+ | `MARROW_FLEET_AGENT_ID` | No | Default agent identity |
258
159
 
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.
160
+ Use the host's secret manager first. The shared resolver can also check documented Marrow and project env files for owned development environments. Run `doctor` when a key or hook cannot be found.
260
161
 
261
- For business pilots, review the live trust notes before production rollout: https://getmarrow.ai/docs#trust-boundaries
162
+ ## Documentation
262
163
 
263
- ## Trust Model
164
+ - [Source-of-truth docs](https://getmarrow.ai/docs/)
165
+ - [Trust Center](https://getmarrow.ai/trust/)
166
+ - [Status](https://getmarrow.ai/status/)
167
+ - [GitHub](https://github.com/getmarrow/marrow-install)
264
168
 
265
- This package is intended to be open source and auditable. It prints every file it will touch, requires `--yes` to write, does not store API keys in project files, and supports MCP-only, SDK-only, both, and markdown-only setups.
169
+ ## License
266
170
 
267
- ---
171
+ MIT
268
172
 
269
173
  ## Related Packages
270
174
 
271
- - **[@getmarrow/sdk](https://www.npmjs.com/package/@getmarrow/sdk)** TypeScript/Node.js SDK for custom agent integrations, passive runtime hooks, guarded actions, and direct API access.
272
- - **[@getmarrow/mcp](https://www.npmjs.com/package/@getmarrow/mcp)** MCP server for Claude Code, Claude Desktop, Cursor, and other MCP-compatible clients.
273
-
274
- **Docs:** [https://getmarrow.ai/docs](https://getmarrow.ai/docs)
175
+ - [@getmarrow/sdk](https://www.npmjs.com/package/@getmarrow/sdk) - Node.js and TypeScript integration for owned agent runtimes
176
+ - [@getmarrow/mcp](https://www.npmjs.com/package/@getmarrow/mcp) - MCP-native integration for compatible agent clients
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@getmarrow/install",
3
- "version": "0.1.27",
4
- "description": "Universal installer for Marrow agent governance, proof, and passive control setup.",
3
+ "version": "0.1.29",
4
+ "description": "Universal installer and governed runner for Marrow agent fleets.",
5
5
  "bin": {
6
6
  "marrow-install": "bin/marrow-install.js"
7
7
  },
@@ -22,7 +22,7 @@
22
22
  ],
23
23
  "repository": {
24
24
  "type": "git",
25
- "url": "https://github.com/getmarrow/marrow-install.git"
25
+ "url": "git+https://github.com/getmarrow/marrow-install.git"
26
26
  },
27
27
  "license": "MIT",
28
28
  "files": [
package/src/installer.js CHANGED
@@ -48,12 +48,19 @@ function parseArgs(argv) {
48
48
  baseUrl: process.env.MARROW_BASE_URL || DEFAULT_BASE_URL,
49
49
  agentId: process.env.MARROW_FLEET_AGENT_ID || process.env.MARROW_AGENT_ID || '',
50
50
  selfTest: true,
51
+ selfTestExplicitlyDisabled: false,
51
52
  json: false,
53
+ activate: false,
52
54
  };
53
55
 
54
56
  for (let i = 0; i < argv.length; i += 1) {
55
57
  const arg = argv[i];
56
- if (arg === '--yes' || arg === '-y') options.yes = true;
58
+ if (arg === 'activate' || arg === '--activate') {
59
+ options.activate = true;
60
+ options.yes = true;
61
+ options.selfTest = true;
62
+ }
63
+ else if (arg === '--yes' || arg === '-y') options.yes = true;
57
64
  else if (arg === '--repair' || arg === 'repair') {
58
65
  options.repair = true;
59
66
  options.yes = true;
@@ -61,7 +68,10 @@ function parseArgs(argv) {
61
68
  else if (arg === '--dry-run') options.dryRun = true;
62
69
  else if (arg === '--doctor' || arg === 'doctor' || arg === 'check') options.doctor = true;
63
70
  else if (arg === '--json') options.json = true;
64
- else if (arg === '--no-self-test') options.selfTest = false;
71
+ else if (arg === '--no-self-test') {
72
+ options.selfTest = false;
73
+ options.selfTestExplicitlyDisabled = true;
74
+ }
65
75
  else if (arg === '--self-test') options.selfTest = true;
66
76
  else if (arg === '--cwd') options.cwd = path.resolve(argv[++i] || options.cwd);
67
77
  else if (arg === '--mode') options.mode = argv[++i] || options.mode;
@@ -85,6 +95,12 @@ function parseArgs(argv) {
85
95
  if (!['auto', 'mcp', 'sdk', 'both', 'md'].includes(options.mode)) {
86
96
  throw new Error('--mode must be one of auto, mcp, sdk, both, md');
87
97
  }
98
+ if (options.activate && options.selfTestExplicitlyDisabled) {
99
+ throw new Error('activate cannot be combined with --no-self-test because server verification is required');
100
+ }
101
+ if (options.activate && options.dryRun) {
102
+ throw new Error('activate cannot be combined with --dry-run; use --dry-run without activate to preview changes');
103
+ }
88
104
 
89
105
  return options;
90
106
  }
@@ -92,6 +108,7 @@ function parseArgs(argv) {
92
108
  function usage() {
93
109
  return `Usage:
94
110
  npx @getmarrow/install --dry-run
111
+ npx @getmarrow/install activate
95
112
  npx @getmarrow/install --yes
96
113
  npx @getmarrow/install --repair
97
114
  npx @getmarrow/install doctor
@@ -99,6 +116,7 @@ function usage() {
99
116
  npx @getmarrow/install --sdk --yes
100
117
 
101
118
  Options:
119
+ activate Detect, install, self-test, and return a server-confirmed activation receipt
102
120
  --dry-run Print planned changes without writing
103
121
  --doctor Check install health without writing
104
122
  --repair Write missing hooks/config, then run self-test and status check
@@ -111,6 +129,20 @@ Options:
111
129
  `;
112
130
  }
113
131
 
132
+ function stableAgentId(root, client = sourceClient()) {
133
+ const identity = `${path.resolve(root)}:${os.hostname()}:${client}`;
134
+ return `${client}-${crypto.createHash('sha256').update(identity).digest('hex').slice(0, 12)}`;
135
+ }
136
+
137
+ function detectedClient(detection) {
138
+ if (sourceClient() !== 'custom') return sourceClient();
139
+ if (detection.openclaw) return 'openclaw';
140
+ if (detection.claudeCode) return 'claude-code';
141
+ if (detection.cursor) return 'cursor';
142
+ if (detection.codex) return 'codex';
143
+ return 'custom';
144
+ }
145
+
114
146
  function exists(filePath) {
115
147
  return fs.existsSync(filePath);
116
148
  }
@@ -556,8 +588,15 @@ function applyPlan(plan, options) {
556
588
  }
557
589
 
558
590
  const changed = before !== after;
559
- changes.push({ path: write.path, label: write.label, changed });
560
- if (changed && options.yes && !options.dryRun && !options.doctor) {
591
+ const writeApplied = Boolean(options.yes && !options.dryRun && !options.doctor);
592
+ changes.push({
593
+ path: write.path,
594
+ label: write.label,
595
+ changed,
596
+ applied: changed && writeApplied,
597
+ already_present: !changed,
598
+ });
599
+ if (changed && writeApplied) {
561
600
  fs.mkdirSync(path.dirname(write.path), { recursive: true });
562
601
  fs.writeFileSync(write.path, after);
563
602
  }
@@ -581,6 +620,22 @@ async function requestJson(url, options) {
581
620
  return json.data || json;
582
621
  }
583
622
 
623
+ function isCanonicalTimestamp(value) {
624
+ if (typeof value !== 'string' || value.length === 0) return false;
625
+ const parsed = Date.parse(value);
626
+ return Number.isFinite(parsed) && new Date(parsed).toISOString() === value;
627
+ }
628
+
629
+ function runtimeGateVerified(runtime) {
630
+ if (!runtime || typeof runtime !== 'object') return false;
631
+ if (runtime.ok === true) return true;
632
+ const gate = runtime.risk_gate;
633
+ if (!gate || typeof gate !== 'object') return false;
634
+ if (typeof gate.allow === 'boolean' || typeof gate.allowed === 'boolean') return true;
635
+ const decision = typeof gate.decision === 'string' ? gate.decision.toLowerCase() : '';
636
+ return ['allow', 'warn', 'review_required', 'block'].includes(decision);
637
+ }
638
+
584
639
  async function runSelfTest(options) {
585
640
  if (!options.selfTest) return { skipped: true, reason: 'disabled' };
586
641
  if (!options.apiKey) {
@@ -595,7 +650,7 @@ async function runSelfTest(options) {
595
650
  authorization: `Bearer ${options.apiKey}`,
596
651
  'content-type': 'application/json',
597
652
  'x-marrow-session-id': `install-${Date.now()}`,
598
- 'x-marrow-client': sourceClient(),
653
+ 'x-marrow-client': options.client || sourceClient(),
599
654
  };
600
655
  if (options.agentId) headers['x-marrow-agent-id'] = options.agentId;
601
656
 
@@ -608,7 +663,7 @@ async function runSelfTest(options) {
608
663
  action: 'Marrow passive install self-test: verify SDK/MCP hooks can record a harmless setup event',
609
664
  source_meta: {
610
665
  channel: 'cli',
611
- client: sourceClient(),
666
+ client: options.client || sourceClient(),
612
667
  user_intent: 'operate',
613
668
  },
614
669
  }),
@@ -641,10 +696,7 @@ async function runSelfTest(options) {
641
696
  outcome: 'self-test outcome committed',
642
697
  },
643
698
  }),
644
- }).catch((error) => ({
645
- ok: false,
646
- error: error instanceof Error ? error.message : String(error),
647
- }));
699
+ });
648
700
  const performance = await requestJson(`${baseUrl}/v1/analytics/agent-performance?period=7`, { headers })
649
701
  .catch((error) => ({
650
702
  ok: false,
@@ -662,11 +714,15 @@ async function runSelfTest(options) {
662
714
  checks: ['installer first-value self-test'],
663
715
  outcome: 'first-value endpoint reached',
664
716
  },
717
+ decision_id: decisionId,
718
+ agent_id: options.agentId,
719
+ activation: options.activation ? {
720
+ ...options.activation,
721
+ intervention_verified: runtimeGateVerified(runtime),
722
+ closure_verified: true,
723
+ } : undefined,
665
724
  }),
666
- }).catch((error) => ({
667
- ok: false,
668
- error: error instanceof Error ? error.message : String(error),
669
- }));
725
+ });
670
726
  const valueProof = await requestJson(`${baseUrl}/v1/agent/value/proof?period_days=30`, { headers })
671
727
  .catch((error) => ({
672
728
  ok: false,
@@ -675,6 +731,30 @@ async function runSelfTest(options) {
675
731
  const tokenValueProof = buildTokenValueProof(valueProof);
676
732
  const firstValueSignal = buildFirstValueSignal(status, runtime, performance, firstValue, tokenValueProof);
677
733
  const installValueMoment = buildInstallValueMoment(firstValueSignal, status, runtime, performance, firstValue, tokenValueProof);
734
+ let activationReceipt = null;
735
+ let activationVerified = false;
736
+ if (options.activation) {
737
+ activationReceipt = firstValue && firstValue.activation_receipt;
738
+ const receiptValid = activationReceipt
739
+ && typeof activationReceipt === 'object'
740
+ && typeof activationReceipt.id === 'string'
741
+ && activationReceipt.id.length > 0
742
+ && activationReceipt.decision_id === decisionId
743
+ && activationReceipt.agent_id === options.agentId
744
+ && activationReceipt.outcome_success === true
745
+ && isCanonicalTimestamp(activationReceipt.outcome_recorded_at)
746
+ && activationReceipt.server_confirmed === true
747
+ && activationReceipt.capture_verified === true
748
+ && activationReceipt.intervention_verified === true
749
+ && activationReceipt.closure_verified === true;
750
+ if (!receiptValid) {
751
+ throw new Error('activation receipt did not verify the exact self-test decision, agent, runtime gate, and closed successful outcome');
752
+ }
753
+ activationVerified = Boolean(firstValue.active && runtimeGateVerified(runtime) && (status.enabled ?? status.ok));
754
+ if (!activationVerified) {
755
+ throw new Error('activation prerequisites were not all verified by the server');
756
+ }
757
+ }
678
758
  return {
679
759
  skipped: false,
680
760
  decision_id: decisionId,
@@ -684,9 +764,11 @@ async function runSelfTest(options) {
684
764
  recommended_fix: status.recommended_fix || null,
685
765
  next_action: status.next_action || null,
686
766
  auto_outcome_closure: status.auto_outcome_closure || null,
687
- runtime_active: Boolean(runtime && runtime.ok !== false),
767
+ runtime_active: runtimeGateVerified(runtime),
688
768
  runtime_exact_next_action: runtime.exact_next_action || null,
689
769
  runtime_before_you_act: runtime.before_you_act || null,
770
+ activation_verified: activationVerified,
771
+ activation_receipt: activationReceipt,
690
772
  first_value: firstValue && firstValue.ok !== false ? firstValue : null,
691
773
  first_value_signal: firstValueSignal,
692
774
  install_value_moment: installValueMoment,
@@ -821,6 +903,12 @@ function printReport(report) {
821
903
  process.stdout.write(`Mode: ${report.mode}\n`);
822
904
  process.stdout.write(`Write mode: ${report.writeMode}\n\n`);
823
905
 
906
+ if (report.activation?.requested) {
907
+ process.stdout.write('Activation:\n');
908
+ process.stdout.write(`- agent: ${report.activation.agent_id}\n`);
909
+ process.stdout.write(`- server confirmed: ${report.activation.server_confirmed ? 'yes' : 'no'}\n\n`);
910
+ }
911
+
824
912
  process.stdout.write('Detected:\n');
825
913
  for (const [key, value] of Object.entries(report.detected)) {
826
914
  process.stdout.write(`- ${key}: ${value ? 'yes' : 'no'}\n`);
@@ -841,6 +929,7 @@ function printReport(report) {
841
929
  process.stdout.write(`- decision_id: ${report.selfTest.decision_id}\n`);
842
930
  process.stdout.write(`- health: ${report.selfTest.health || 'unknown'}\n`);
843
931
  process.stdout.write(`- one-call runtime: ${report.selfTest.runtime_active ? 'active' : 'not verified'}\n`);
932
+ if (report.selfTest.error) process.stdout.write(`- error: ${report.selfTest.error}\n`);
844
933
  if (report.selfTest.next_action) process.stdout.write(`- next action: ${report.selfTest.next_action}\n`);
845
934
  if (report.selfTest.first_value_signal) {
846
935
  process.stdout.write('\nFirst value:\n');
@@ -930,10 +1019,32 @@ function printReport(report) {
930
1019
  }
931
1020
 
932
1021
  async function install(options) {
1022
+ if (options.activate && (options.yes !== true || options.dryRun || options.doctor)) {
1023
+ throw new Error('activate requires write mode (--yes) because hooks must be installed during this run');
1024
+ }
1025
+ if (options.activate && options.selfTest === false) {
1026
+ throw new Error('activate requires the server self-test');
1027
+ }
933
1028
  const detection = detectEnvironment(options.cwd);
934
1029
  const plan = buildPlan(detection, options);
935
1030
  const writeMode = options.doctor ? 'doctor' : options.dryRun ? 'dry-run' : options.repair ? 'repair' : options.yes ? 'write' : 'dry-run';
936
1031
  const changes = applyPlan(plan, options);
1032
+ const client = detectedClient(detection);
1033
+ options.client = client;
1034
+ if (!options.agentId) options.agentId = stableAgentId(detection.root, client);
1035
+ options.activation = options.activate ? {
1036
+ harness: client,
1037
+ agent_id: options.agentId,
1038
+ install_surface: plan.mode,
1039
+ mode: options.governanceMode || 'passive',
1040
+ hooks_installed: changes
1041
+ .filter((change) => change.changed && change.applied && /hook|runtime|rule|instruction|config/i.test(change.label))
1042
+ .map((change) => change.label)
1043
+ .slice(0, 20),
1044
+ capture_verified: changes.every((change) => change.applied),
1045
+ intervention_verified: false,
1046
+ closure_verified: false,
1047
+ } : null;
937
1048
  const configInspection = inspectNpmTokenConfig();
938
1049
  const sdkDependency = inspectSdkDependency(detection);
939
1050
  const configDiagnostics = configInspection.safe;
@@ -941,11 +1052,17 @@ async function install(options) {
941
1052
  ? repairConfigDiagnostics(configDiagnostics)
942
1053
  : [];
943
1054
  const envHints = options.apiKey ? [] : findLikelyEnvFiles(detection);
944
- const selfTest = await runSelfTest(options).catch((error) => ({
945
- skipped: false,
946
- active: false,
947
- error: error instanceof Error ? error.message : String(error),
948
- }));
1055
+ let selfTest;
1056
+ try {
1057
+ selfTest = await runSelfTest(options);
1058
+ } catch (error) {
1059
+ const message = error instanceof Error ? error.message : String(error);
1060
+ if (options.activate) throw new Error(`Marrow activation failed: ${message}`);
1061
+ selfTest = { skipped: false, active: false, error: message };
1062
+ }
1063
+ if (options.activate && !selfTest.activation_verified) {
1064
+ throw new Error('Marrow activation failed: server confirmation was not returned');
1065
+ }
949
1066
  const changedConfig = changes.some((change) => change.changed) || configRepairs.some((repair) => repair.changed);
950
1067
  const selfTestPassed = Boolean(!selfTest.skipped && selfTest.active && !selfTest.error);
951
1068
  const remediation = options.repair
@@ -976,6 +1093,12 @@ async function install(options) {
976
1093
  openclaw: detection.openclaw,
977
1094
  mcpConfig: detection.mcpConfig,
978
1095
  },
1096
+ activation: {
1097
+ requested: options.activate,
1098
+ agent_id: options.agentId,
1099
+ server_confirmed: Boolean(selfTest.activation_verified),
1100
+ receipt: selfTest.activation_receipt || null,
1101
+ },
979
1102
  changes,
980
1103
  doctor: {
981
1104
  active: Boolean(!selfTest.skipped && selfTest.active),
@@ -1026,4 +1149,5 @@ module.exports = {
1026
1149
  inspectSdkDependency,
1027
1150
  buildInstallValueMoment,
1028
1151
  buildTokenValueProof,
1152
+ stableAgentId,
1029
1153
  };