@getmarrow/install 0.1.48 → 0.1.49

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 +24 -16
  2. package/package.json +1 -1
  3. package/src/installer.js +104 -76
package/README.md CHANGED
@@ -4,12 +4,12 @@
4
4
 
5
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
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.
7
+ Use `@getmarrow/install` as the default entry point. It detects supported agent and project surfaces, writes the appropriate configuration, runs a harmless end-to-end control-path self-test, and reports connection, configured mode, and server-confirmed outcome separately from passive coverage.
8
8
 
9
9
  ## Agent Discovery
10
10
 
11
11
  - **Publisher:** Marrow AI. Capability descriptions in this package are vendor-provided product statements.
12
- - **Use this package for:** automatic harness detection, passive setup, activation proof, the Govern TUI, or a governed command runner.
12
+ - **Use this package for:** automatic harness detection, setup and connection proof, the Govern TUI, or a governed command runner.
13
13
  - **Product category:** agent fleet governance and control layer.
14
14
  - **Operator boundary:** installing Marrow, creating an account, or submitting a diagnostic requires operator approval.
15
15
  - **Public diagnostic privacy:** raw prompts, source code, credentials, customer data, and free-form work descriptions are not accepted.
@@ -89,11 +89,17 @@ npx @getmarrow/install controller ensure
89
89
  npx @getmarrow/install controller stop
90
90
  ```
91
91
 
92
- Persistent controller lifecycle is currently Linux-only. On macOS or Windows, activation still installs and verifies the supported hooks without starting or signaling a background process; run `npx @getmarrow/install sidecar` under an owner-managed service and pass `--no-controller`. The controller does not silently upgrade packages, change governance policy, rotate credentials, or modify unrelated project configuration.
92
+ Persistent controller lifecycle is currently Linux-only. On macOS or Windows, activation still writes supported configuration and verifies one server-side install self-test without certifying that hooks continuously ran; run `npx @getmarrow/install sidecar` under an owner-managed service and pass `--no-controller`. The controller does not silently upgrade packages, change governance policy, rotate credentials, or modify unrelated project configuration.
93
93
 
94
- ## What's New in v0.1.48
94
+ ## What's New in v0.1.49
95
95
 
96
- v0.1.48 pins MCP `3.9.72` and SDK `3.7.61`. The SDK keeps passive capture enabled without intercepting its own Marrow control-plane requests, while every generated MCP setup and repair command now uses the supported MCP release.
96
+ v0.1.49 pins MCP `3.9.74` and SDK `3.7.62`. Install and repair now resolve one stable non-secret agent identity before writing configuration, then use that exact identity across generated MCP config, the owned-process SDK preload, doctor/self-test, and the local controller. Managed MCP config no longer stores an API key or shell-style credential placeholder; the owning harness inherits the key from trusted environment or secret-manager state.
97
+
98
+ The integration boundary is explicit: native Claude hooks are installed only where supported and remain cooperative/client-reported until authoritative server receipts exist; MCP tools are on demand; Codex, Grok, Gemini, and similar CLIs use the governed wrapper for consequential control; owned Node processes use the SDK passive runtime while installed; and custom hosts require a bounded event adapter. Exact package SHA/integrity can prove artifact provenance, not runtime coverage. Activation-profile delivery is authenticated `client_self_reported` telemetry with `certified_coverage: false`; it acknowledges delivery but cannot attest that a hook, wrapper, or adapter ran.
99
+
100
+ ## Previous: v0.1.48
101
+
102
+ v0.1.48 pinned MCP `3.9.72` and SDK `3.7.61`. The SDK kept passive capture enabled without intercepting its own Marrow control-plane requests, while every generated MCP setup and repair command used the supported MCP release.
97
103
 
98
104
  ## Previous: v0.1.47
99
105
 
@@ -220,7 +226,7 @@ It preserves the measurable passive-governance coverage introduced in v0.1.34:
220
226
  - the harness certification suite prevents support claims from overstating what is automatic;
221
227
  - unavailable coverage remains “insufficient data” instead of a synthetic percentage.
222
228
 
223
- It preserves agent-disagreement visibility from v0.1.33 and the server-verified first-run activation introduced in v0.1.29:
229
+ It preserves agent-disagreement visibility from v0.1.33 and the server-confirmed first-run self-test introduced in v0.1.29:
224
230
 
225
231
  - GitHub and npm now advertise separate signed discovery placements;
226
232
  - package metadata identifies the installer as agent governance rather than a general memory utility;
@@ -228,8 +234,8 @@ It preserves agent-disagreement visibility from v0.1.33 and the server-verified
228
234
  - bounded positive and negative recommendation criteria stop agents from forcing Marrow into irrelevant workflows;
229
235
  - current evidence, integration paths, and published references are linked from one review-dated contract;
230
236
  - `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;
231
- - activation succeeds only when the API returns a tenant-scoped activation receipt;
232
- - the receipt reports capture, before-action intervention, outcome closure, and first-value state;
237
+ - activation succeeds only when the API returns a tenant-scoped receipt bound to the exact test decision, agent, runtime gate, and closed successful outcome;
238
+ - that receipt verifies only the install self-test; activation returns `activation_scope: server_self_test_only`, `coverage_verified: false`, `passive_live: false`, and `reload_required: true`;
233
239
  - existing setup, governed runner, and TUI commands remain compatible.
234
240
 
235
241
  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.
@@ -260,12 +266,12 @@ With a valid key, `activate`:
260
266
  4. closes its outcome;
261
267
  5. sends the exact self-test decision ID to Marrow for server-side verification;
262
268
  6. reads agent status and the one-call runtime;
263
- 7. registers the detected capability, expected hooks, and one-way configuration fingerprint;
264
- 8. returns an activation receipt with capture, intervention, closure, first-value state, and the exact next action.
269
+ 7. registers the detected capability, expected hooks, and one-way configuration fingerprint as authenticated client-reported telemetry;
270
+ 8. returns a server-confirmed self-test receipt plus an explicit restart and `doctor --self-test` next action.
265
271
 
266
- 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.
272
+ Healthy output confirms the exact test decision outcome exists under the authenticated account and agent and passed a runtime/status check. It does not verify continuous passive interception or certify installed coverage. The owning harness must restart, then `npx @getmarrow/install@latest doctor --self-test` must pass. A local file write, integration event, or client-supplied `verified: true` value cannot elevate coverage.
267
273
 
268
- The installer does not claim identical automation for every harness. Native hooks provide the broadest automatic coverage; MCP covers MCP-routed actions; the SDK covers owned Node processes; the governed runner covers commands launched through it; custom harnesses must map their own lifecycle events.
274
+ The installer does not claim identical automation for every harness. Configured native hooks remain cooperative/client-reported; MCP covers only on-demand MCP-routed actions; the SDK covers only owned Node processes where its runtime is installed; the governed runner covers only commands launched through it; custom harnesses must map their own lifecycle events.
269
275
 
270
276
  ## Govern TUI
271
277
 
@@ -333,7 +339,7 @@ The fleet view shows live agents, active workflows, agent disagreements and thei
333
339
  | --- | --- | --- |
334
340
  | Universal installer | You want Marrow to detect and wire the safest supported integration | Lowest |
335
341
  | Governed runner | You need control around existing shell, CI, deploy, publish, merge, or migration commands | Low |
336
- | MCP package | The agent client supports MCP and should use Marrow tools and hooks natively | Low |
342
+ | MCP package | The agent client supports MCP and should use Marrow tools on demand | Low |
337
343
  | SDK | You own the Node.js/TypeScript runtime and need programmatic control | Advanced |
338
344
  | Event contract | You have a custom harness that must map its lifecycle into Marrow | Advanced |
339
345
 
@@ -345,16 +351,16 @@ Run `npx @getmarrow/install integrations --json` for the machine-readable matrix
345
351
 
346
352
  | Harnesses | Prompt / pre-action / result | Closure and proof | Cached brief | Restart survival | Evidence adapter | Safe repair |
347
353
  | --- | --- | --- | --- | --- | --- | --- |
348
- | Claude Code | Automatic native hooks | Correlated when determinable; protected proof enforced | Owner-only bounded cache | Installed config and durable spool | Native hook evidence | Managed config after activation |
354
+ | Claude Code | Configured native hooks where supported; cooperative/client-reported until authoritative receipts exist | Correlated when determinable; proof is evaluated and is advisory or enforced according to plan policy | Owner-only bounded cache | Installed config and durable spool | Native hook evidence | Managed config after activation |
349
355
  | Cursor, Composer, Cline, Windsurf | MCP-routed only | MCP-routed; explicit or governed proof | Owner-only MCP cache | MCP config and durable spool | MCP lifecycle evidence | Managed config after activation |
350
- | Codex, OpenCode, Gemini, Grok, DeepSeek, Qwen, Kimi, MiniMax, GLM | Automatic only inside governed runner | Automatic when result is known; protected proof enforced | Runner/runtime cache | Activated controller and durable buffer | Command, test, deployment, or owner evidence | Managed config after activation |
356
+ | Codex, OpenCode, Gemini, Grok, DeepSeek, Qwen, Kimi, MiniMax, GLM | Automatic only inside governed runner | Automatic when result is known; proof is evaluated and is advisory or enforced according to plan policy | Runner/runtime cache | Activated controller and durable buffer | Command, test, deployment, or owner evidence | Managed config after activation |
351
357
  | Hermes, OpenClaw, custom harnesses | Lifecycle adapter required | Adapter or governed runner required | Adapter dependent | Adapter dependent | Adapter supplied | Adapter owned |
352
358
 
353
359
  For native hooks, a successful tool exit is not treated as a successful business outcome when proof is missing. MCP coverage includes only actions routed through that MCP client. Governed-runner coverage includes only commands launched through the runner. Event-contract integrations must emit the documented lifecycle themselves.
354
360
 
355
361
  ## Always-On Lifecycle
356
362
 
357
- 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.
363
+ When work actually passes through configured native hooks, an on-demand MCP call, an installed owned-process SDK runtime, the governed runner, or a bounded custom adapter, Marrow can 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. Work that bypasses those paths is not observed.
358
364
 
359
365
  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.
360
366
 
@@ -384,6 +390,8 @@ See the [Trust Center](https://getmarrow.ai/trust/) for implemented controls, cu
384
390
  | `MARROW_BASE_URL` | No | API base override |
385
391
  | `MARROW_FLEET_AGENT_ID` | No | Default agent identity |
386
392
 
393
+ Install and repair resolve one stable, non-secret agent identity before writing configuration. The managed MCP entry and owned-process SDK preload use that same identity and API base as doctor/self-test. The installer never copies `MARROW_API_KEY` into MCP configuration or generated runtime source; the owning harness must inherit the key from trusted environment or secret-manager configuration. Pass `--agent-id <id>` when an operator-approved identity must override the stable generated value.
394
+
387
395
  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.
388
396
 
389
397
  ## Documentation
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getmarrow/install",
3
- "version": "0.1.48",
3
+ "version": "0.1.49",
4
4
  "description": "Universal installer and governed runner for Marrow agent fleets.",
5
5
  "bin": {
6
6
  "marrow-install": "bin/marrow-install.js"
package/src/installer.js CHANGED
@@ -9,11 +9,10 @@ const { firstCapturePath, harnessReloadPlan } = require('./first-hour');
9
9
  const DEFAULT_BASE_URL = 'https://api.getmarrow.ai';
10
10
  const MARROW_BLOCK_START = '<!-- marrow:passive-start -->';
11
11
  const MARROW_BLOCK_END = '<!-- marrow:passive-end -->';
12
- const MCP_ADAPTER_VERSION = '3.9.72';
13
- const MCP_ADAPTER_SOURCE_SHA = '5203f96388849dfda12cb05d476b36b542639e16';
14
- const MCP_ADAPTER_INTEGRITY = 'sha512-FTs9ORn4WVwDIeJEjovriDK4X8WZVSGbbe8iz6GGYFy62jBRJOXOZ4aW8B4Ll+10i71o8RcEBJ0Px38Pc6v/qw==';
15
- const SDK_ADAPTER_VERSION = '3.7.61';
16
- const SDK_ADAPTER_INTEGRITY = 'sha512-1dXf/Px4mMN4lOehrRkBOF/dC6N9kjQ5R4eb8ohAACx5CvLTS32zSTelfMNjnRcdOknSKMjLnbh7aGFhulVo/Q==';
12
+ const MCP_ADAPTER_VERSION = '3.9.74';
13
+ const MCP_ADAPTER_SOURCE_SHA = 'a34a87ee1d6ea16d8ebcc26aa0e66bf7dcb5e23f';
14
+ const SDK_ADAPTER_VERSION = '3.7.62';
15
+ const SDK_ADAPTER_INTEGRITY = 'sha512-n1i6Be09TpAQ9BPNRKY7aCvA2iSUPpJfw8djw2MELwpNbBCtKiZ29Jji77BK/6EFLUpSIcTW/Gmdf/ccf0JRYQ==';
17
16
  const SDK_ADAPTER_TARBALL = `https://registry.npmjs.org/@getmarrow/sdk/-/sdk-${SDK_ADAPTER_VERSION}.tgz`;
18
17
  const MCP_PACKAGE_SPEC = `@getmarrow/mcp@${MCP_ADAPTER_VERSION}`;
19
18
  const ADAPTER_PROVENANCE = Object.freeze({
@@ -21,7 +20,8 @@ const ADAPTER_PROVENANCE = Object.freeze({
21
20
  package: '@getmarrow/mcp',
22
21
  version: MCP_ADAPTER_VERSION,
23
22
  source_sha: MCP_ADAPTER_SOURCE_SHA,
24
- integrity: MCP_ADAPTER_INTEGRITY,
23
+ integrity: null,
24
+ integrity_state: 'registry_unavailable_until_publish',
25
25
  }),
26
26
  sdk: Object.freeze({
27
27
  package: '@getmarrow/sdk',
@@ -340,7 +340,7 @@ Options:
340
340
  --dry-run Print planned changes without writing
341
341
  --doctor Check install health without writing
342
342
  --repair Write missing hooks/config, then run self-test and status check
343
- update Same as --repair: refresh certified MCP/SDK/install pins after owner approval
343
+ update Same as --repair: refresh exact MCP/SDK/install package pins after owner approval
344
344
  --yes, -y Write detected config files
345
345
  --mode <mode> auto, mcp, sdk, both, or md
346
346
  --key <key> Marrow API key for self-test. Prefer MARROW_API_KEY because CLI args can appear in process listings.
@@ -642,14 +642,18 @@ Required environment:
642
642
  ${MARROW_BLOCK_END}`;
643
643
  }
644
644
 
645
- function passiveRuntimeSource() {
645
+ function passiveRuntimeSource(options = {}) {
646
+ const installedAgentId = String(options.agentId || '').trim();
647
+ const installedBaseUrl = String(options.baseUrl || DEFAULT_BASE_URL).trim() || DEFAULT_BASE_URL;
646
648
  return `const apiKey = process.env.MARROW_API_KEY;
649
+ const installedAgentId = ${JSON.stringify(installedAgentId)};
650
+ const installedBaseUrl = ${JSON.stringify(installedBaseUrl)};
647
651
  if (apiKey && !globalThis.__MARROW_PASSIVE_RUNTIME__) {
648
652
  try {
649
653
  const { MarrowClient } = await import('@getmarrow/sdk');
650
654
  const marrow = new MarrowClient(apiKey, {
651
- baseUrl: process.env.MARROW_BASE_URL,
652
- agentId: process.env.MARROW_FLEET_AGENT_ID || process.env.MARROW_AGENT_ID,
655
+ baseUrl: installedBaseUrl,
656
+ agentId: installedAgentId || process.env.MARROW_FLEET_AGENT_ID || process.env.MARROW_AGENT_ID,
653
657
  sessionId: process.env.MARROW_SESSION_ID,
654
658
  mode: process.env.MARROW_ENFORCEMENT_MODE || 'auto',
655
659
  });
@@ -672,11 +676,14 @@ if (apiKey && !globalThis.__MARROW_PASSIVE_RUNTIME__) {
672
676
  `;
673
677
  }
674
678
 
675
- function envExample() {
679
+ function envExample(options = {}) {
680
+ const agentId = String(options.agentId || 'agent-or-fleet-id').trim() || 'agent-or-fleet-id';
681
+ const client = String(options.client || 'custom').trim() || 'custom';
682
+ const baseUrl = String(options.baseUrl || DEFAULT_BASE_URL).trim() || DEFAULT_BASE_URL;
676
683
  return `MARROW_API_KEY=mrw_live_replace_me
677
- MARROW_BASE_URL=${DEFAULT_BASE_URL}
678
- MARROW_FLEET_AGENT_ID=agent-or-fleet-id
679
- MARROW_CLIENT=codex
684
+ MARROW_BASE_URL=${JSON.stringify(baseUrl)}
685
+ MARROW_FLEET_AGENT_ID=${JSON.stringify(agentId)}
686
+ MARROW_CLIENT=${JSON.stringify(client)}
680
687
  MARROW_ENFORCEMENT_MODE=auto
681
688
  MARROW_PASSIVE_BRIEF=auto
682
689
  MARROW_PASSIVE_VALUE_REPORT=true
@@ -941,12 +948,18 @@ function activationProfile(detection, plan, changes, client) {
941
948
  config_fingerprint: configFingerprint,
942
949
  expected_hooks: expectedHooks,
943
950
  observed_hooks: observedHooks,
951
+ evidence_authority: 'client_self_reported',
952
+ coverage_verified: false,
953
+ configuration_complete: complete,
944
954
  complete,
945
955
  exact_fix: exactFix,
946
956
  };
947
957
  }
948
958
 
949
- function upsertMcpServerConfig(filePath) {
959
+ function upsertMcpServerConfig(filePath, options = {}) {
960
+ const agentId = String(options.agentId || '').trim();
961
+ if (!agentId) throw new Error('Refusing to generate Marrow MCP config without an agent identity');
962
+ const baseUrl = String(options.baseUrl || DEFAULT_BASE_URL).trim() || DEFAULT_BASE_URL;
950
963
  const config = parseJsonObject(filePath);
951
964
  const servers = config.mcpServers && typeof config.mcpServers === 'object' && !Array.isArray(config.mcpServers)
952
965
  ? config.mcpServers
@@ -955,9 +968,8 @@ function upsertMcpServerConfig(filePath) {
955
968
  command: 'npx',
956
969
  args: ['-y', `--package=${MCP_PACKAGE_SPEC}`, 'marrow-mcp'],
957
970
  env: {
958
- MARROW_API_KEY: '${MARROW_API_KEY}',
959
- MARROW_BASE_URL: '${MARROW_BASE_URL}',
960
- MARROW_FLEET_AGENT_ID: '${MARROW_FLEET_AGENT_ID}',
971
+ MARROW_BASE_URL: baseUrl,
972
+ MARROW_FLEET_AGENT_ID: agentId,
961
973
  },
962
974
  };
963
975
  config.mcpServers = servers;
@@ -1076,7 +1088,8 @@ function defaultHarnessInstallMatrix(detection = detectEnvironment(process.cwd()
1076
1088
  native_hooks: entry.capability_level === 'native_hooks' && Boolean(detection.claudeCode),
1077
1089
  governed_wrapper: entry.capability_level === 'governed_wrapper',
1078
1090
  },
1079
- verified_passive: detected && entry.automatic.length > 0,
1091
+ configured_locally: detected && entry.automatic.length > 0,
1092
+ verified_passive: false,
1080
1093
  unsupported_claim: entry.capability_level === 'event_contract'
1081
1094
  ? 'Needs a bounded event adapter. MCP tools remain on demand.'
1082
1095
  : null,
@@ -1085,6 +1098,9 @@ function defaultHarnessInstallMatrix(detection = detectEnvironment(process.cwd()
1085
1098
  }
1086
1099
 
1087
1100
  function buildPlan(detection, options) {
1101
+ const client = options.client || detectedClient(detection);
1102
+ const agentId = String(options.agentId || '').trim() || stableAgentId(detection.root, client);
1103
+ const baseUrl = String(options.baseUrl || DEFAULT_BASE_URL).trim() || DEFAULT_BASE_URL;
1088
1104
  const mode = options.mode === 'auto'
1089
1105
  ? detection.node ? 'both' : 'mcp'
1090
1106
  : options.mode;
@@ -1095,13 +1111,13 @@ function buildPlan(detection, options) {
1095
1111
  type: 'file',
1096
1112
  path: detection.paths.passiveRuntime,
1097
1113
  label: 'SDK passive runtime preload',
1098
- content: passiveRuntimeSource(),
1114
+ content: passiveRuntimeSource({ agentId, baseUrl }),
1099
1115
  });
1100
1116
  writes.push({
1101
1117
  type: 'file',
1102
1118
  path: detection.paths.passiveEnv,
1103
1119
  label: 'Marrow passive env example',
1104
- content: envExample(),
1120
+ content: envExample({ agentId, baseUrl, client }),
1105
1121
  overwrite: false,
1106
1122
  });
1107
1123
  }
@@ -1119,14 +1135,14 @@ function buildPlan(detection, options) {
1119
1135
  type: 'json-transform',
1120
1136
  path: detection.paths.mcpJson,
1121
1137
  label: 'Project MCP server config',
1122
- transform: upsertMcpServerConfig,
1138
+ transform: (filePath) => upsertMcpServerConfig(filePath, { agentId, baseUrl }),
1123
1139
  });
1124
1140
  if (detection.cursor) {
1125
1141
  writes.push({
1126
1142
  type: 'json-transform',
1127
1143
  path: detection.paths.cursorMcp,
1128
1144
  label: 'Cursor MCP server config',
1129
- transform: upsertMcpServerConfig,
1145
+ transform: (filePath) => upsertMcpServerConfig(filePath, { agentId, baseUrl }),
1130
1146
  });
1131
1147
  }
1132
1148
  }
@@ -1412,60 +1428,51 @@ async function runSelfTest(options) {
1412
1428
  if (!receiptValid) {
1413
1429
  throw new Error('activation receipt did not verify the exact self-test decision, agent, runtime gate, and closed successful outcome');
1414
1430
  }
1415
- activationProfileReceipt = await requestJson(`${baseUrl}/v1/agent/integrations/events`, {
1416
- method: 'POST',
1417
- headers,
1418
- body: JSON.stringify({
1419
- event_id: `activation-${activationConfigFingerprint.slice(0, 32)}`,
1420
- event_type: 'activation_profile_registered',
1421
- harness: options.activation.harness,
1422
- agent_id: options.agentId,
1423
- session_id: headers['x-marrow-session-id'],
1424
- adapter_version: activationAdapterVersion,
1425
- capability_level: activationCapabilityLevel,
1426
- config_fingerprint: activationConfigFingerprint,
1427
- expected_hooks: activationExpectedHooks,
1428
- action: 'passive integration activation profile registered',
1429
- occurred_at: new Date().toISOString(),
1430
- }),
1431
- });
1432
- const profileCoverage = activationProfileReceipt?.activation_coverage;
1433
- const profileReceipt = profileCoverage?.profile_receipt;
1434
- const accountBindingId = profileCoverage?.account_binding_id;
1435
- const expectedConfigurationBindingId = typeof accountBindingId === 'string'
1436
- ? crypto.createHash('sha256')
1437
- .update(`agent-config-receipt:v2:${accountBindingId}:${activationConfigFingerprint}`)
1438
- .digest('hex')
1439
- : null;
1440
- const expectedHooksMatch = Array.isArray(profileCoverage?.capture_coverage?.expected_hooks)
1441
- && [...profileCoverage.capture_coverage.expected_hooks].sort().join(',') === [...activationExpectedHooks].sort().join(',');
1442
- const profileBound = Boolean(
1443
- activationProfileReceipt?.accepted === true
1444
- && profileCoverage?.agent_id === options.agentId
1445
- && profileCoverage?.harness === options.activation.harness
1446
- && profileCoverage?.activation?.adapter_version === activationAdapterVersion
1447
- && profileCoverage?.activation?.capability_level === activationCapabilityLevel
1448
- && profileReceipt?.agent_id === options.agentId
1449
- && profileReceipt?.harness === options.activation.harness
1450
- && profileReceipt?.adapter_version === activationAdapterVersion
1451
- && profileReceipt?.capability_level === activationCapabilityLevel
1452
- && typeof accountBindingId === 'string'
1453
- && accountBindingId.length === 64
1454
- && profileReceipt?.account_binding_id === accountBindingId
1455
- && profileReceipt?.config_fingerprint_verified === true
1456
- && profileReceipt?.configuration_binding_id === expectedConfigurationBindingId
1457
- && profileReceipt?.expected_hooks_verified === true
1458
- && expectedHooksMatch
1431
+ let activationTelemetry;
1432
+ try {
1433
+ activationTelemetry = await requestJson(`${baseUrl}/v1/agent/integrations/events`, {
1434
+ method: 'POST',
1435
+ headers,
1436
+ body: JSON.stringify({
1437
+ event_id: `activation-${activationConfigFingerprint.slice(0, 32)}`,
1438
+ event_type: 'activation_profile_registered',
1439
+ harness: options.activation.harness,
1440
+ agent_id: options.agentId,
1441
+ session_id: headers['x-marrow-session-id'],
1442
+ adapter_version: activationAdapterVersion,
1443
+ capability_level: activationCapabilityLevel,
1444
+ config_fingerprint: activationConfigFingerprint,
1445
+ expected_hooks: activationExpectedHooks,
1446
+ action: 'integration activation profile registered as client self-reported telemetry',
1447
+ occurred_at: new Date().toISOString(),
1448
+ }),
1449
+ });
1450
+ } catch {
1451
+ throw new Error('activation telemetry delivery failed; retry activate after the integration-events endpoint accepts authenticated client_self_reported telemetry');
1452
+ }
1453
+ const telemetryAccepted = Boolean(
1454
+ activationTelemetry?.accepted === true
1455
+ && activationTelemetry?.evidence_authority === 'client_self_reported'
1459
1456
  );
1457
+ if (!telemetryAccepted) {
1458
+ throw new Error('activation telemetry was not acknowledged as authenticated client_self_reported delivery; update the backend/client compatibility pair, then retry activate');
1459
+ }
1460
+ // Never project response-supplied coverage or profile receipts into the install
1461
+ // result. This endpoint acknowledges bounded, authenticated client telemetry;
1462
+ // it does not certify that hooks, wrappers, or adapters continuously ran.
1463
+ activationProfileReceipt = {
1464
+ accepted: true,
1465
+ evidence_authority: 'client_self_reported',
1466
+ certified_coverage: false,
1467
+ };
1460
1468
  activationVerified = Boolean(
1461
1469
  firstValue.active
1462
1470
  && runtimeGateVerified(runtime)
1463
1471
  && (status.enabled ?? status.ok)
1464
- && options.activation.complete === true
1465
- && profileBound,
1472
+ && telemetryAccepted,
1466
1473
  );
1467
1474
  if (!activationVerified) {
1468
- throw new Error('activation prerequisites were not all verified by the server');
1475
+ throw new Error('activation self-test prerequisites were not all verified by the server');
1469
1476
  }
1470
1477
  }
1471
1478
  return {
@@ -1481,9 +1488,16 @@ async function runSelfTest(options) {
1481
1488
  runtime_exact_next_action: runtime.exact_next_action || null,
1482
1489
  runtime_before_you_act: runtime.before_you_act || null,
1483
1490
  activation_verified: activationVerified,
1491
+ activation_scope: options.activation ? 'server_self_test_only' : null,
1492
+ coverage_verified: false,
1493
+ passive_live: false,
1494
+ reload_required: Boolean(options.activation),
1495
+ activation_next_action: options.activation
1496
+ ? 'Restart the owning harness, then run npx @getmarrow/install@latest doctor --self-test.'
1497
+ : null,
1484
1498
  activation_receipt: activationReceipt,
1485
1499
  activation_profile_receipt: activationProfileReceipt,
1486
- activation_coverage: status.activation_coverage || firstValue.activation_coverage || null,
1500
+ activation_coverage: null,
1487
1501
  first_value: firstValue && firstValue.ok !== false ? firstValue : null,
1488
1502
  first_value_signal: firstValueSignal,
1489
1503
  install_value_moment: installValueMoment,
@@ -1636,7 +1650,13 @@ function printReport(report) {
1636
1650
  if (report.activation?.requested) {
1637
1651
  process.stdout.write('Activation:\n');
1638
1652
  process.stdout.write(`- agent: ${report.activation.agent_id}\n`);
1639
- process.stdout.write(`- server confirmed: ${report.activation.server_confirmed ? 'yes' : 'no'}\n\n`);
1653
+ process.stdout.write(`- self-test server confirmed: ${report.activation.server_confirmed ? 'yes' : 'no'}\n`);
1654
+ process.stdout.write(`- scope: ${report.activation.activation_scope}\n`);
1655
+ process.stdout.write(`- coverage verified: ${report.activation.coverage_verified ? 'yes' : 'no'}\n`);
1656
+ process.stdout.write(`- passive live in this process: ${report.activation.passive_live ? 'yes' : 'no'}\n`);
1657
+ process.stdout.write(`- reload required: ${report.activation.reload_required ? 'yes' : 'no'}\n`);
1658
+ if (report.activation.next_action) process.stdout.write(`- next action: ${report.activation.next_action}\n`);
1659
+ process.stdout.write('\n');
1640
1660
  }
1641
1661
 
1642
1662
  process.stdout.write('Detected:\n');
@@ -1814,12 +1834,12 @@ async function install(options) {
1814
1834
  throw new Error('repair requires explicit write authorization (--yes)');
1815
1835
  }
1816
1836
  const detection = detectEnvironment(options.cwd);
1837
+ const client = detectedClient(detection);
1838
+ options.client = client;
1839
+ options.agentId = String(options.agentId || '').trim() || stableAgentId(detection.root, client);
1817
1840
  const plan = buildPlan(detection, options);
1818
1841
  const writeMode = options.doctor ? 'doctor' : options.dryRun ? 'dry-run' : options.repair ? 'repair' : options.yes ? 'write' : 'dry-run';
1819
1842
  const changes = applyPlan(plan, options);
1820
- const client = detectedClient(detection);
1821
- options.client = client;
1822
- if (!options.agentId) options.agentId = stableAgentId(detection.root, client);
1823
1843
  const profile = activationProfile(detection, plan, changes, client);
1824
1844
  options.activation = options.activate ? {
1825
1845
  harness: client,
@@ -1830,7 +1850,10 @@ async function install(options) {
1830
1850
  .filter((change) => change.changed && change.applied && /hook|runtime|rule|instruction|config/i.test(change.label))
1831
1851
  .map((change) => change.label)
1832
1852
  .slice(0, 20),
1833
- capture_verified: changes.every((change) => change.applied || change.already_present),
1853
+ capture_verified: false,
1854
+ configuration_complete: changes.every((change) => change.applied || change.already_present),
1855
+ evidence_authority: 'client_self_reported',
1856
+ coverage_verified: false,
1834
1857
  adapter_version: profile.adapter_version,
1835
1858
  capability_level: profile.capability_level,
1836
1859
  config_fingerprint: profile.config_fingerprint,
@@ -1932,6 +1955,11 @@ async function install(options) {
1932
1955
  requested: options.activate,
1933
1956
  agent_id: options.agentId,
1934
1957
  server_confirmed: Boolean(selfTest.activation_verified),
1958
+ activation_scope: selfTest.activation_scope || null,
1959
+ coverage_verified: false,
1960
+ passive_live: false,
1961
+ reload_required: Boolean(options.activate),
1962
+ next_action: selfTest.activation_next_action || null,
1935
1963
  receipt: selfTest.activation_receipt || null,
1936
1964
  profile,
1937
1965
  },