@getmarrow/install 0.1.53 → 0.1.54
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 +11 -1
- package/package.json +1 -1
- package/src/installer.js +246 -17
package/README.md
CHANGED
|
@@ -64,6 +64,12 @@ export MARROW_API_KEY=mrw_live_...
|
|
|
64
64
|
|
|
65
65
|
The bare command is the default-on path: it detects and byte-idempotently writes managed configuration, runs the authenticated activation self-test, and starts the supported persistent controller. The key stays process-only and is never written to generated configuration or controller state. Use `--dry-run` for a non-writing preview, `doctor` for a read-only health check, or `--no-controller` to install and self-test without starting the controller. The explicit `activate` command remains equivalent and supported.
|
|
66
66
|
|
|
67
|
+
### MCP tool profiles
|
|
68
|
+
|
|
69
|
+
Ordinary setup leaves `MARROW_TOOL_PROFILE` unset, which selects the documented 17-tool `primary` surface. Set `MARROW_TOOL_PROFILE=core` only for the legacy seven-tool minimal surface, or `MARROW_TOOL_PROFILE=full` for the complete advanced/legacy catalog. Explicit `primary`, `core`, and `full` values are accepted; any other value fails with an exact bounded repair and never falls back to a broader profile.
|
|
70
|
+
|
|
71
|
+
Tool visibility is not authorization. Every visible call still reaches Marrow's backend authentication, tenant, key-permission, plan, proof, and policy enforcement. `doctor --self-test` reports the configured and effective profile, the expected visible count, reloaded MCP visible names/count, and non-authorizing backend entitlement/upgrade projection. Until the owning harness restarts and matching MCP status is observed, actual visibility stays unavailable and the profile remains not-live. The self-test reads backend availability status; it does not invoke paid write tools to discover access.
|
|
72
|
+
|
|
67
73
|
## Keeping Marrow Current
|
|
68
74
|
|
|
69
75
|
Marrow's hosted API, website, and dashboard update automatically; local SDK dependencies, generated runtime files, MCP hooks/configuration, and pinned package versions do not silently rewrite themselves. Keeping them current delivers new client-side features, compatibility improvements, and any published security fixes. Supported clients report their package version during authenticated status/runtime activity, and Marrow returns a `client_update` notice with the exact action when the version is behind or unknown.
|
|
@@ -93,7 +99,11 @@ npx @getmarrow/install controller stop
|
|
|
93
99
|
|
|
94
100
|
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.
|
|
95
101
|
|
|
96
|
-
## What's New in v0.1.
|
|
102
|
+
## What's New in v0.1.54
|
|
103
|
+
|
|
104
|
+
v0.1.54 pins sealed MCP candidate `3.9.77` from source `1e782d8ba6bbb54bfaa322f0300565c7176f1969` and makes ordinary setup use the primary tool surface without writing a profile variable. Explicit `core` and `full` selections remain preserved, invalid values fail closed with a bounded repair, and human/JSON self-test output distinguishes configured/effective profile, expected visibility, actual post-reload visibility, and backend-projected entitlement state. Backend projections are status evidence only and always report `authorizes_calls: false`.
|
|
105
|
+
|
|
106
|
+
## Previous: v0.1.53
|
|
97
107
|
|
|
98
108
|
v0.1.53 pins the native-gate MCP candidate `3.9.75` and reconciles only Marrow-owned native hook surfaces. Codex uses `.codex/hooks.json`; Cursor and Composer use `.cursor/hooks.json`; Cline uses bounded executables under `.clinerules/hooks/`; Windsurf uses `.windsurf/hooks.json`; Gemini CLI receives named BeforeTool, AfterTool, and AfterAgent groups in `.gemini/settings.json`; Grok receives trusted global hooks in `~/.grok/hooks/marrow.json`. Grok PreToolUse validates strict private allow/deny JSON and fails closed with exit `2` when the child cannot provide an exact decision; PostToolUse/PostToolUseFailure emit compact results; one nonblocking Stop hook closes the turn with no duplicate SessionEnd hook. Grok hooks remain user-toggleable, so restart plus `/hooks` inspection is required and configuration never proves observed coverage.
|
|
99
109
|
|
package/package.json
CHANGED
package/src/installer.js
CHANGED
|
@@ -10,8 +10,9 @@ const { evidence: localControlEvidence } = require('./control-state');
|
|
|
10
10
|
const DEFAULT_BASE_URL = 'https://api.getmarrow.ai';
|
|
11
11
|
const MARROW_BLOCK_START = '<!-- marrow:passive-start -->';
|
|
12
12
|
const MARROW_BLOCK_END = '<!-- marrow:passive-end -->';
|
|
13
|
-
const MCP_ADAPTER_VERSION = '3.9.
|
|
14
|
-
const MCP_ADAPTER_SOURCE_SHA = '
|
|
13
|
+
const MCP_ADAPTER_VERSION = '3.9.77';
|
|
14
|
+
const MCP_ADAPTER_SOURCE_SHA = '1e782d8ba6bbb54bfaa322f0300565c7176f1969';
|
|
15
|
+
const MCP_ADAPTER_INTEGRITY = 'sha512-4SVvnyTaDh/mhVUry1LaGCYKXSB/mt5NCHijIOLP336jILSqe7ezjZ5/EwEOMTQ2JvLm6w0QE/jmGbcksqe86w==';
|
|
15
16
|
const SDK_ADAPTER_VERSION = '3.7.62';
|
|
16
17
|
const SDK_ADAPTER_INTEGRITY = 'sha512-n1i6Be09TpAQ9BPNRKY7aCvA2iSUPpJfw8djw2MELwpNbBCtKiZ29Jji77BK/6EFLUpSIcTW/Gmdf/ccf0JRYQ==';
|
|
17
18
|
const SDK_ADAPTER_TARBALL = `https://registry.npmjs.org/@getmarrow/sdk/-/sdk-${SDK_ADAPTER_VERSION}.tgz`;
|
|
@@ -21,8 +22,8 @@ const ADAPTER_PROVENANCE = Object.freeze({
|
|
|
21
22
|
package: '@getmarrow/mcp',
|
|
22
23
|
version: MCP_ADAPTER_VERSION,
|
|
23
24
|
source_sha: MCP_ADAPTER_SOURCE_SHA,
|
|
24
|
-
integrity:
|
|
25
|
-
integrity_state: '
|
|
25
|
+
integrity: MCP_ADAPTER_INTEGRITY,
|
|
26
|
+
integrity_state: 'sealed_local_candidate',
|
|
26
27
|
}),
|
|
27
28
|
sdk: Object.freeze({
|
|
28
29
|
package: '@getmarrow/sdk',
|
|
@@ -91,6 +92,28 @@ const GEMINI_HOOK_TIMEOUT_MS = 5000;
|
|
|
91
92
|
const GEMINI_CLOSEOUT_TIMEOUT_MS = 3000;
|
|
92
93
|
const NATIVE_EXPECTED_HOOKS = ['prompt', 'pre_action', 'action_result', 'session_end'];
|
|
93
94
|
const SOURCE_CLIENTS = new Set(['claude-code', 'cursor', 'composer', 'windsurf', 'openclaw', 'codex', 'gemini', 'grok', 'deepseek', 'qwen', 'kimi', 'minimax', 'cline', 'opencode', 'hermes', 'glm', 'mcp', 'ci', 'custom', 'unknown']);
|
|
95
|
+
const TOOL_PROFILES = new Set(['primary', 'core', 'full']);
|
|
96
|
+
const TOOL_PROFILE_EXPECTED_COUNTS = Object.freeze({ primary: 17, core: 7, full: null });
|
|
97
|
+
const TOOL_PROFILE_EXACT_FIX = 'Unset MARROW_TOOL_PROFILE to use primary, or set MARROW_TOOL_PROFILE=core or MARROW_TOOL_PROFILE=full, then restart the owning harness and run npx @getmarrow/install@latest doctor --self-test.';
|
|
98
|
+
const PRIMARY_TOOL_NAMES = Object.freeze([
|
|
99
|
+
'marrow_agent_runtime',
|
|
100
|
+
'marrow_arbitrate',
|
|
101
|
+
'marrow_coordinate',
|
|
102
|
+
'marrow_replay_compare',
|
|
103
|
+
'marrow_decision_brief',
|
|
104
|
+
'marrow_think',
|
|
105
|
+
'marrow_commit',
|
|
106
|
+
'marrow_workflow_gate',
|
|
107
|
+
'marrow_completion_contracts',
|
|
108
|
+
'marrow_evaluate_completion_contract',
|
|
109
|
+
'marrow_agent_status',
|
|
110
|
+
'marrow_value_report',
|
|
111
|
+
'marrow_buyer_proof',
|
|
112
|
+
'marrow_governance_timeline',
|
|
113
|
+
'marrow_decision_trace',
|
|
114
|
+
'marrow_fleet_lessons',
|
|
115
|
+
'marrow_model_usage',
|
|
116
|
+
]);
|
|
94
117
|
const HARNESS_CAPABILITY_REGISTRY = Object.freeze([
|
|
95
118
|
{ client: 'claude-code', capability_level: 'native_hooks', automatic: ['prompt', 'pre_action', 'action_result', 'session_end'], install_surface: 'mcp' },
|
|
96
119
|
{ client: 'cursor', capability_level: 'native_hooks', automatic: ['pre_action', 'action_result', 'outcome_closure'], install_surface: 'mcp' },
|
|
@@ -118,6 +141,148 @@ function explicitMcpVersion(command) {
|
|
|
118
141
|
return match ? match[1] : null;
|
|
119
142
|
}
|
|
120
143
|
|
|
144
|
+
function resolveToolProfile(value) {
|
|
145
|
+
const structured = value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
146
|
+
const candidate = structured
|
|
147
|
+
? value.configured_profile
|
|
148
|
+
: value;
|
|
149
|
+
const absent = !structured && candidate === undefined;
|
|
150
|
+
const structuredUnset = structured && candidate === 'unset';
|
|
151
|
+
const configuredProfile = absent || structuredUnset
|
|
152
|
+
? 'unset'
|
|
153
|
+
: candidate;
|
|
154
|
+
if (!absent && !structuredUnset && !TOOL_PROFILES.has(configuredProfile)) {
|
|
155
|
+
throw new Error(`Invalid MARROW_TOOL_PROFILE. ${TOOL_PROFILE_EXACT_FIX}`);
|
|
156
|
+
}
|
|
157
|
+
const effectiveProfile = configuredProfile === 'unset' ? 'primary' : configuredProfile;
|
|
158
|
+
return {
|
|
159
|
+
configured_profile: configuredProfile,
|
|
160
|
+
effective_profile: effectiveProfile,
|
|
161
|
+
expected_visible_count: TOOL_PROFILE_EXPECTED_COUNTS[effectiveProfile],
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function normalizePrimaryToolAvailability(value) {
|
|
166
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) || value.profile !== 'primary') return null;
|
|
167
|
+
const evidence = value.entitlement_evidence;
|
|
168
|
+
const counts = value.counts;
|
|
169
|
+
const tools = Array.isArray(value.tools) ? value.tools : [];
|
|
170
|
+
if (!evidence || typeof evidence !== 'object' || evidence.authorizing !== false) return null;
|
|
171
|
+
if (!['available', 'unavailable'].includes(evidence.state)) return null;
|
|
172
|
+
if (!counts || counts.total !== 17 || !Number.isInteger(counts.entitled) || !Number.isInteger(counts.upgrade_required)) return null;
|
|
173
|
+
if (counts.entitled + counts.upgrade_required !== counts.total || tools.length !== counts.total) return null;
|
|
174
|
+
const normalizedTools = [];
|
|
175
|
+
const seen = new Set();
|
|
176
|
+
for (const tool of tools) {
|
|
177
|
+
if (!tool || typeof tool !== 'object' || !PRIMARY_TOOL_NAMES.includes(tool.name) || seen.has(tool.name)) return null;
|
|
178
|
+
if (!['entitled', 'upgrade_required'].includes(tool.state) || typeof tool.always_available !== 'boolean') return null;
|
|
179
|
+
seen.add(tool.name);
|
|
180
|
+
normalizedTools.push({
|
|
181
|
+
name: tool.name,
|
|
182
|
+
state: tool.state,
|
|
183
|
+
always_available: tool.always_available,
|
|
184
|
+
plan_feature: typeof tool.plan_feature === 'string' ? tool.plan_feature : null,
|
|
185
|
+
minimum_plan: typeof tool.minimum_plan === 'string' ? tool.minimum_plan : null,
|
|
186
|
+
owner_management_url: typeof tool.owner_management_url === 'string' ? tool.owner_management_url : '',
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
if (PRIMARY_TOOL_NAMES.some((name) => !seen.has(name))) return null;
|
|
190
|
+
const entitled = normalizedTools.filter((tool) => tool.state === 'entitled').length;
|
|
191
|
+
const upgradeRequired = normalizedTools.filter((tool) => tool.state === 'upgrade_required').length;
|
|
192
|
+
if (entitled !== counts.entitled || upgradeRequired !== counts.upgrade_required) return null;
|
|
193
|
+
return {
|
|
194
|
+
profile: 'primary',
|
|
195
|
+
current_plan: typeof value.current_plan === 'string' ? value.current_plan : null,
|
|
196
|
+
owner_management_url: typeof value.owner_management_url === 'string' ? value.owner_management_url : '',
|
|
197
|
+
entitlement_evidence: {
|
|
198
|
+
state: evidence.state,
|
|
199
|
+
source: typeof evidence.source === 'string' ? evidence.source : 'entitlement_read_unavailable',
|
|
200
|
+
authoritative: evidence.authoritative === true,
|
|
201
|
+
authorizing: false,
|
|
202
|
+
},
|
|
203
|
+
counts: { total: 17, entitled, upgrade_required: upgradeRequired },
|
|
204
|
+
tools: normalizedTools,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function backendEntitlementProjection(statusProfile, contextProjection) {
|
|
209
|
+
const freshProjection = normalizePrimaryToolAvailability(contextProjection);
|
|
210
|
+
if (freshProjection) {
|
|
211
|
+
return {
|
|
212
|
+
evidence_state: freshProjection.entitlement_evidence.state,
|
|
213
|
+
source: 'authenticated_backend',
|
|
214
|
+
authorizes_calls: false,
|
|
215
|
+
primary_tool_availability: freshProjection,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
const envelope = statusProfile?.backend_entitlement_projection;
|
|
219
|
+
const projected = normalizePrimaryToolAvailability(envelope?.primary_tool_availability);
|
|
220
|
+
const source = ['authenticated_backend', 'cached_or_stale_status', 'backend_projection_not_provided'].includes(envelope?.source)
|
|
221
|
+
? envelope.source
|
|
222
|
+
: 'backend_projection_not_provided';
|
|
223
|
+
const available = envelope?.authorizes_calls === false
|
|
224
|
+
&& envelope?.evidence_state === 'available'
|
|
225
|
+
&& source === 'authenticated_backend'
|
|
226
|
+
&& projected?.entitlement_evidence.state === 'available';
|
|
227
|
+
return {
|
|
228
|
+
evidence_state: available ? 'available' : 'unavailable',
|
|
229
|
+
source,
|
|
230
|
+
authorizes_calls: false,
|
|
231
|
+
primary_tool_availability: projected,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function buildMcpToolProfileReport(value, statusProfile = null, contextProjection = null, forceReload = false) {
|
|
236
|
+
const expected = resolveToolProfile(value);
|
|
237
|
+
const reportedNames = Array.isArray(statusProfile?.visible_tool_names)
|
|
238
|
+
? statusProfile.visible_tool_names.filter((name) => typeof name === 'string')
|
|
239
|
+
: [];
|
|
240
|
+
const reportedCount = statusProfile?.visible_tool_count;
|
|
241
|
+
const reportedConfigured = statusProfile?.configured_profile;
|
|
242
|
+
const reportedEffective = statusProfile?.effective_profile;
|
|
243
|
+
const uniqueNames = new Set(reportedNames);
|
|
244
|
+
const profileIdentityMatches = reportedConfigured === expected.configured_profile
|
|
245
|
+
&& reportedEffective === expected.effective_profile;
|
|
246
|
+
const reportedCatalogIsConsistent = Number.isInteger(reportedCount)
|
|
247
|
+
&& reportedCount >= 0
|
|
248
|
+
&& reportedNames.length === reportedCount
|
|
249
|
+
&& uniqueNames.size === reportedCount;
|
|
250
|
+
const expectedCount = expected.effective_profile === 'full'
|
|
251
|
+
&& profileIdentityMatches
|
|
252
|
+
&& reportedCatalogIsConsistent
|
|
253
|
+
? reportedCount
|
|
254
|
+
: expected.expected_visible_count;
|
|
255
|
+
const expectedPrimaryNames = expected.effective_profile !== 'primary'
|
|
256
|
+
|| (reportedNames.length === PRIMARY_TOOL_NAMES.length
|
|
257
|
+
&& PRIMARY_TOOL_NAMES.every((name) => uniqueNames.has(name)));
|
|
258
|
+
const visibilityLive = !forceReload
|
|
259
|
+
&& profileIdentityMatches
|
|
260
|
+
&& statusProfile?.local_visibility_grants_entitlement === false
|
|
261
|
+
&& reportedCatalogIsConsistent
|
|
262
|
+
&& reportedCount === expectedCount
|
|
263
|
+
&& expectedPrimaryNames;
|
|
264
|
+
return {
|
|
265
|
+
configured_profile: expected.configured_profile,
|
|
266
|
+
effective_profile: expected.effective_profile,
|
|
267
|
+
expected_visible_count: expectedCount,
|
|
268
|
+
visible_tool_count: visibilityLive ? reportedCount : null,
|
|
269
|
+
actual_visible_count: visibilityLive ? reportedCount : null,
|
|
270
|
+
visible_tool_names: visibilityLive ? reportedNames : [],
|
|
271
|
+
local_visibility_grants_entitlement: false,
|
|
272
|
+
visibility_live: visibilityLive,
|
|
273
|
+
reload_required: !visibilityLive,
|
|
274
|
+
reported_configured_profile: typeof reportedConfigured === 'string' ? reportedConfigured : null,
|
|
275
|
+
reported_effective_profile: typeof reportedEffective === 'string' ? reportedEffective : null,
|
|
276
|
+
backend_entitlement_projection: backendEntitlementProjection(statusProfile, contextProjection),
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function initialToolProfileReport(value) {
|
|
281
|
+
return {
|
|
282
|
+
...buildMcpToolProfileReport(value),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
121
286
|
function readMcpPackageVersion(packageRoot) {
|
|
122
287
|
try {
|
|
123
288
|
const pkg = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
|
|
@@ -307,7 +472,7 @@ function sourceClient() {
|
|
|
307
472
|
return aliases[raw] || (SOURCE_CLIENTS.has(raw) ? raw : 'custom');
|
|
308
473
|
}
|
|
309
474
|
|
|
310
|
-
function parseArgs(argv) {
|
|
475
|
+
function parseArgs(argv, env = process.env) {
|
|
311
476
|
const options = {
|
|
312
477
|
cwd: process.cwd(),
|
|
313
478
|
yes: false,
|
|
@@ -315,9 +480,10 @@ function parseArgs(argv) {
|
|
|
315
480
|
doctor: false,
|
|
316
481
|
repair: false,
|
|
317
482
|
mode: 'auto',
|
|
318
|
-
apiKey:
|
|
319
|
-
baseUrl:
|
|
320
|
-
agentId:
|
|
483
|
+
apiKey: env.MARROW_API_KEY || '',
|
|
484
|
+
baseUrl: env.MARROW_BASE_URL || DEFAULT_BASE_URL,
|
|
485
|
+
agentId: env.MARROW_FLEET_AGENT_ID || env.MARROW_AGENT_ID || '',
|
|
486
|
+
toolProfile: resolveToolProfile(env.MARROW_TOOL_PROFILE),
|
|
321
487
|
selfTest: true,
|
|
322
488
|
selfTestExplicitlyDisabled: false,
|
|
323
489
|
json: false,
|
|
@@ -439,6 +605,10 @@ Options:
|
|
|
439
605
|
--agent-id <id> Agent/fleet id for self-test headers
|
|
440
606
|
--no-controller Do not start the local background controller during install/repair
|
|
441
607
|
--no-self-test Skip API smoke/self-test
|
|
608
|
+
|
|
609
|
+
Environment:
|
|
610
|
+
MARROW_TOOL_PROFILE Leave unset for primary (17 tools), or explicitly set primary, core, or full.
|
|
611
|
+
Visibility never grants entitlement; backend plans and permissions authorize calls.
|
|
442
612
|
`;
|
|
443
613
|
}
|
|
444
614
|
|
|
@@ -727,6 +897,7 @@ function passiveInstructions() {
|
|
|
727
897
|
Marrow should run passively after install:
|
|
728
898
|
|
|
729
899
|
- Use MCP plus these instructions in every workspace: \`npx -y --package=${MCP_PACKAGE_SPEC} marrow-mcp setup\`.
|
|
900
|
+
- Leave \`MARROW_TOOL_PROFILE\` unset for the 17-tool primary surface. Set \`MARROW_TOOL_PROFILE=core\` or \`MARROW_TOOL_PROFILE=full\` only as an explicit opt-in; backend plans and permissions still enforce access to every visible tool.
|
|
730
901
|
- Use SDK passive runtime in owned Node processes: \`createPassiveRuntime().install()\`.
|
|
731
902
|
- Native Claude hooks install only when \`.claude\` is present. Codex native hooks install into \`.codex/hooks.json\`; Cursor and Composer use \`.cursor/hooks.json\`; Cline uses non-overwriting executable files under \`.clinerules/hooks/\`; Windsurf uses \`.windsurf/hooks.json\`; Gemini CLI uses \`.gemini/settings.json\`; Grok uses trusted global \`~/.grok/hooks/marrow.json\`. Restart the host, enable/review hooks, disable Windsurf Restricted Mode where native hooks are required, and trust the workspace before claiming runtime coverage. The governed wrapper remains an explicit bounded fallback. Hermes, OpenClaw, and custom hosts need a bounded event adapter.
|
|
732
903
|
- Keep passive token/model usage proof enabled. Empty savings stay zero until observed model usage lands. Do not invent token, cost, or time savings.
|
|
@@ -790,6 +961,7 @@ function envExample(options = {}) {
|
|
|
790
961
|
MARROW_BASE_URL=${JSON.stringify(baseUrl)}
|
|
791
962
|
MARROW_FLEET_AGENT_ID=${JSON.stringify(agentId)}
|
|
792
963
|
MARROW_CLIENT=${JSON.stringify(client)}
|
|
964
|
+
# MARROW_TOOL_PROFILE is intentionally unset: ordinary setup uses primary. Set core or full only as an explicit opt-in.
|
|
793
965
|
MARROW_ENFORCEMENT_MODE=auto
|
|
794
966
|
MARROW_PASSIVE_BRIEF=auto
|
|
795
967
|
MARROW_PASSIVE_VALUE_REPORT=true
|
|
@@ -1622,13 +1794,18 @@ function upsertMcpServerConfig(filePath, options = {}) {
|
|
|
1622
1794
|
const servers = config.mcpServers && typeof config.mcpServers === 'object' && !Array.isArray(config.mcpServers)
|
|
1623
1795
|
? config.mcpServers
|
|
1624
1796
|
: {};
|
|
1797
|
+
const existingProfile = resolveToolProfile(servers.marrow?.env?.MARROW_TOOL_PROFILE).configured_profile;
|
|
1798
|
+
const requestedProfile = resolveToolProfile(options.toolProfile).configured_profile;
|
|
1799
|
+
const configuredProfile = requestedProfile === 'unset' ? existingProfile : requestedProfile;
|
|
1800
|
+
const env = {
|
|
1801
|
+
MARROW_BASE_URL: baseUrl,
|
|
1802
|
+
MARROW_FLEET_AGENT_ID: agentId,
|
|
1803
|
+
};
|
|
1804
|
+
if (configuredProfile !== 'unset') env.MARROW_TOOL_PROFILE = configuredProfile;
|
|
1625
1805
|
servers.marrow = {
|
|
1626
1806
|
command: 'npx',
|
|
1627
1807
|
args: ['-y', `--package=${MCP_PACKAGE_SPEC}`, 'marrow-mcp'],
|
|
1628
|
-
env
|
|
1629
|
-
MARROW_BASE_URL: baseUrl,
|
|
1630
|
-
MARROW_FLEET_AGENT_ID: agentId,
|
|
1631
|
-
},
|
|
1808
|
+
env,
|
|
1632
1809
|
};
|
|
1633
1810
|
config.mcpServers = servers;
|
|
1634
1811
|
return JSON.stringify(config, null, 2) + '\n';
|
|
@@ -1919,7 +2096,7 @@ function buildPlan(detection, options) {
|
|
|
1919
2096
|
type: 'json-transform',
|
|
1920
2097
|
path: detection.paths.mcpJson,
|
|
1921
2098
|
label: 'Project MCP server config',
|
|
1922
|
-
transform: (filePath) => upsertMcpServerConfig(filePath, { agentId, baseUrl }),
|
|
2099
|
+
transform: (filePath) => upsertMcpServerConfig(filePath, { agentId, baseUrl, toolProfile: options.toolProfile }),
|
|
1923
2100
|
});
|
|
1924
2101
|
if (detection.cursor) {
|
|
1925
2102
|
writes.push({
|
|
@@ -1932,7 +2109,7 @@ function buildPlan(detection, options) {
|
|
|
1932
2109
|
type: 'json-transform',
|
|
1933
2110
|
path: detection.paths.cursorMcp,
|
|
1934
2111
|
label: 'Cursor MCP server config',
|
|
1935
|
-
transform: (filePath) => upsertMcpServerConfig(filePath, { agentId, baseUrl }),
|
|
2112
|
+
transform: (filePath) => upsertMcpServerConfig(filePath, { agentId, baseUrl, toolProfile: options.toolProfile }),
|
|
1936
2113
|
});
|
|
1937
2114
|
}
|
|
1938
2115
|
}
|
|
@@ -2106,12 +2283,14 @@ function runtimeGateVerified(runtime) {
|
|
|
2106
2283
|
}
|
|
2107
2284
|
|
|
2108
2285
|
async function runSelfTest(options) {
|
|
2109
|
-
|
|
2286
|
+
const initialProfile = initialToolProfileReport(options.toolProfile);
|
|
2287
|
+
if (!options.selfTest) return { skipped: true, reason: 'disabled', mcp_tool_profile: initialProfile };
|
|
2110
2288
|
if (!options.apiKey) {
|
|
2111
2289
|
return {
|
|
2112
2290
|
skipped: true,
|
|
2113
2291
|
reason: 'missing MARROW_API_KEY',
|
|
2114
2292
|
exact_fix: 'export MARROW_API_KEY=mrw_live_... && npx @getmarrow/install --repair',
|
|
2293
|
+
mcp_tool_profile: initialProfile,
|
|
2115
2294
|
};
|
|
2116
2295
|
}
|
|
2117
2296
|
|
|
@@ -2157,6 +2336,14 @@ async function runSelfTest(options) {
|
|
|
2157
2336
|
});
|
|
2158
2337
|
|
|
2159
2338
|
const status = await requestJson(`${baseUrl}/v1/agent/status`, { headers });
|
|
2339
|
+
const context = await requestJson(`${baseUrl}/v1/agent/context`, { headers })
|
|
2340
|
+
.catch(() => null);
|
|
2341
|
+
const toolProfile = buildMcpToolProfileReport(
|
|
2342
|
+
options.toolProfile,
|
|
2343
|
+
status.mcp_tool_profile,
|
|
2344
|
+
context?.primary_tool_availability,
|
|
2345
|
+
Boolean(options.activation),
|
|
2346
|
+
);
|
|
2160
2347
|
const runtime = await requestJson(`${baseUrl}/v1/agent/runtime`, {
|
|
2161
2348
|
method: 'POST',
|
|
2162
2349
|
headers,
|
|
@@ -2291,6 +2478,7 @@ async function runSelfTest(options) {
|
|
|
2291
2478
|
}
|
|
2292
2479
|
return {
|
|
2293
2480
|
skipped: false,
|
|
2481
|
+
mcp_tool_profile: toolProfile,
|
|
2294
2482
|
decision_id: decisionId,
|
|
2295
2483
|
active: Boolean(status.enabled ?? status.ok),
|
|
2296
2484
|
health: status.health || null,
|
|
@@ -2485,6 +2673,24 @@ function printReport(report) {
|
|
|
2485
2673
|
}
|
|
2486
2674
|
|
|
2487
2675
|
process.stdout.write('\nSelf-test:\n');
|
|
2676
|
+
const toolProfile = report.selfTest.mcp_tool_profile || report.toolProfile;
|
|
2677
|
+
if (toolProfile) {
|
|
2678
|
+
process.stdout.write(`- configured tool profile: ${toolProfile.configured_profile}\n`);
|
|
2679
|
+
process.stdout.write(`- effective tool profile: ${toolProfile.effective_profile}\n`);
|
|
2680
|
+
process.stdout.write(`- expected visible tools: ${toolProfile.expected_visible_count == null ? 'complete catalog (awaiting reloaded MCP count)' : toolProfile.expected_visible_count}\n`);
|
|
2681
|
+
process.stdout.write(`- actual visible tools: ${toolProfile.actual_visible_count == null ? 'unavailable until process reload' : toolProfile.actual_visible_count}\n`);
|
|
2682
|
+
process.stdout.write(`- visible tool names: ${toolProfile.visibility_live ? toolProfile.visible_tool_names.join(', ') : 'unavailable until process reload'}\n`);
|
|
2683
|
+
process.stdout.write(`- profile live: ${toolProfile.visibility_live ? 'yes' : 'no'}\n`);
|
|
2684
|
+
const projection = toolProfile.backend_entitlement_projection;
|
|
2685
|
+
const availability = projection?.primary_tool_availability;
|
|
2686
|
+
if (projection?.evidence_state === 'available' && availability?.entitlement_evidence?.state === 'available') {
|
|
2687
|
+
process.stdout.write(`- backend-projected entitled tools: ${availability.counts.entitled}\n`);
|
|
2688
|
+
process.stdout.write(`- backend-projected upgrade-required tools: ${availability.counts.upgrade_required}\n`);
|
|
2689
|
+
process.stdout.write(`- backend projection source: ${projection.source}; authorizes calls: no\n`);
|
|
2690
|
+
} else {
|
|
2691
|
+
process.stdout.write(`- backend-projected entitlements: unavailable (source: ${projection?.source || 'backend_projection_not_provided'}; non-authorizing)\n`);
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2488
2694
|
if (report.selfTest.skipped) {
|
|
2489
2695
|
process.stdout.write(`- skipped: ${report.selfTest.reason}\n`);
|
|
2490
2696
|
if (report.selfTest.exact_fix) process.stdout.write(`- exact fix: ${report.selfTest.exact_fix}\n`);
|
|
@@ -2650,6 +2856,9 @@ async function install(options) {
|
|
|
2650
2856
|
if (options.repair && options.yes !== true && !options.dryRun && !options.doctor) {
|
|
2651
2857
|
throw new Error('repair requires explicit write authorization (--yes)');
|
|
2652
2858
|
}
|
|
2859
|
+
options.toolProfile = resolveToolProfile(options.toolProfile === undefined
|
|
2860
|
+
? process.env.MARROW_TOOL_PROFILE
|
|
2861
|
+
: options.toolProfile);
|
|
2653
2862
|
const detection = detectEnvironment(options.cwd);
|
|
2654
2863
|
const client = detectedClient(detection);
|
|
2655
2864
|
options.client = client;
|
|
@@ -2696,11 +2905,27 @@ async function install(options) {
|
|
|
2696
2905
|
} catch (error) {
|
|
2697
2906
|
const message = error instanceof Error ? error.message : String(error);
|
|
2698
2907
|
if (options.activate) throw new Error(`Marrow activation failed: ${message}`);
|
|
2699
|
-
selfTest = {
|
|
2908
|
+
selfTest = {
|
|
2909
|
+
skipped: false,
|
|
2910
|
+
active: false,
|
|
2911
|
+
error: message,
|
|
2912
|
+
mcp_tool_profile: initialToolProfileReport(options.toolProfile),
|
|
2913
|
+
};
|
|
2700
2914
|
}
|
|
2701
2915
|
if (options.activate && !selfTest.activation_verified) {
|
|
2702
2916
|
throw new Error('Marrow activation failed: server confirmation was not returned');
|
|
2703
2917
|
}
|
|
2918
|
+
const harnessReload = harnessReloadPlan(detection, changes);
|
|
2919
|
+
if (harnessReload.required && selfTest.mcp_tool_profile) {
|
|
2920
|
+
selfTest.mcp_tool_profile = {
|
|
2921
|
+
...selfTest.mcp_tool_profile,
|
|
2922
|
+
visible_tool_count: null,
|
|
2923
|
+
actual_visible_count: null,
|
|
2924
|
+
visible_tool_names: [],
|
|
2925
|
+
visibility_live: false,
|
|
2926
|
+
reload_required: true,
|
|
2927
|
+
};
|
|
2928
|
+
}
|
|
2704
2929
|
const changedConfig = changes.some((change) => change.applied) || configRepairs.some((repair) => repair.changed);
|
|
2705
2930
|
const selfTestPassed = Boolean(!selfTest.skipped && selfTest.active && !selfTest.error);
|
|
2706
2931
|
const controllerPlatform = options.controllerPlatform || process.platform;
|
|
@@ -2762,6 +2987,7 @@ async function install(options) {
|
|
|
2762
2987
|
adapterProvenance: ADAPTER_PROVENANCE,
|
|
2763
2988
|
mode: plan.mode,
|
|
2764
2989
|
writeMode,
|
|
2990
|
+
toolProfile: selfTest.mcp_tool_profile || initialToolProfileReport(options.toolProfile),
|
|
2765
2991
|
detected: {
|
|
2766
2992
|
node: detection.node,
|
|
2767
2993
|
python: detection.python,
|
|
@@ -2784,7 +3010,7 @@ async function install(options) {
|
|
|
2784
3010
|
receipt: selfTest.activation_receipt || null,
|
|
2785
3011
|
profile,
|
|
2786
3012
|
},
|
|
2787
|
-
harnessReload
|
|
3013
|
+
harnessReload,
|
|
2788
3014
|
firstCapture: firstCapturePath(detection, options.agentId),
|
|
2789
3015
|
changes,
|
|
2790
3016
|
doctor: {
|
|
@@ -2866,6 +3092,9 @@ module.exports = {
|
|
|
2866
3092
|
GROK_SESSION_END_HOOK_COMMAND,
|
|
2867
3093
|
GROK_NATIVE_HOOK_MATCHER,
|
|
2868
3094
|
printReport,
|
|
3095
|
+
buildMcpToolProfileReport,
|
|
3096
|
+
resolveToolProfile,
|
|
3097
|
+
PRIMARY_TOOL_NAMES,
|
|
2869
3098
|
ADAPTER_PROVENANCE,
|
|
2870
3099
|
HARNESS_CAPABILITY_REGISTRY,
|
|
2871
3100
|
defaultHarnessInstallMatrix,
|