@getmarrow/mcp 3.9.59 → 3.9.61
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 +24 -1
- package/dist/cli.js +119 -72
- package/dist/cli.js.map +1 -1
- package/dist/hook-context.d.ts +1 -1
- package/dist/hook-contract.d.ts +6 -6
- package/dist/hook-contract.js +1 -1
- package/dist/hook-session.d.ts +1 -1
- package/dist/hook.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -6
- package/dist/index.js.map +1 -1
- package/dist/lifecycle-spool.d.ts +9 -0
- package/dist/lifecycle-spool.d.ts.map +1 -1
- package/dist/lifecycle-spool.js +63 -3
- package/dist/lifecycle-spool.js.map +1 -1
- package/dist/request-reliability.d.ts +7 -1
- package/dist/request-reliability.d.ts.map +1 -1
- package/dist/request-reliability.js +58 -15
- package/dist/request-reliability.js.map +1 -1
- package/dist/runtime-contract.d.ts +2 -1
- package/dist/runtime-contract.d.ts.map +1 -1
- package/dist/runtime-contract.js +124 -2
- package/dist/runtime-contract.js.map +1 -1
- package/dist/types.d.ts +23 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/scripts/control-path-canary.cjs +7 -4
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -104,7 +104,30 @@ npx -y --package=@getmarrow/mcp@latest marrow-mcp ping
|
|
|
104
104
|
|
|
105
105
|
Detection and notification are automatic. After explicit installer activation, the local controller may restore only Marrow-managed hooks/configuration. Package upgrades, owner policy, credentials, and unrelated configuration remain explicit and subject to the operator's normal change policy.
|
|
106
106
|
|
|
107
|
-
## What's New in v3.9.
|
|
107
|
+
## What's New in v3.9.61
|
|
108
|
+
|
|
109
|
+
v3.9.61 keeps an authoritative proof-pack rejection distinct from a control-path outage:
|
|
110
|
+
|
|
111
|
+
- backend `MARROW_PROOF_PACK_INCOMPLETE` responses are reported as `validation` / `proof_required`, not infrastructure failures;
|
|
112
|
+
- the exact missing proof fields and backend repair instruction remain visible to the agent;
|
|
113
|
+
- live proof validation does not return a stale outage brief or unavailable authorization state;
|
|
114
|
+
- proof enforcement remains fail-closed, and successful commit behavior is unchanged.
|
|
115
|
+
|
|
116
|
+
## Previous: v3.9.60
|
|
117
|
+
|
|
118
|
+
v3.9.60 restores the complete control-and-proof loop for ordinary MCP clients:
|
|
119
|
+
|
|
120
|
+
- cached guidance no longer cuts live status, ask, runtime, or handoff reads down to an impossible 500 ms deadline;
|
|
121
|
+
- `marrow_auto` waits for the bounded think-and-commit path and reports the live decision and proof result in-band;
|
|
122
|
+
- `marrow_commit` now shares the same abort and deadline contract as the other control calls;
|
|
123
|
+
- transient retries use a one-second delay so a slow edge path is not immediately hit again;
|
|
124
|
+
- spool status surfaces backlogs under older credential namespaces without replaying them across an unverified tenant boundary;
|
|
125
|
+
- a plan-gated handoff is reported as unavailable for the current plan, not as an API or authentication outage;
|
|
126
|
+
- the release canary runs with the customer's default client deadlines instead of silently overriding them.
|
|
127
|
+
|
|
128
|
+
MCP-only hosts, including Grok today, receive Marrow's control instructions but cannot expose native always-on lifecycle hooks unless the host implements them. Marrow reports that capability boundary explicitly instead of claiming hook coverage it cannot observe.
|
|
129
|
+
|
|
130
|
+
## Previous: v3.9.59
|
|
108
131
|
|
|
109
132
|
v3.9.59 makes the six-tool control path reliable and honest across ordinary edge and geographic latency:
|
|
110
133
|
|
package/dist/cli.js
CHANGED
|
@@ -127,11 +127,14 @@ async function runSpoolCommand(drain) {
|
|
|
127
127
|
const status = drain
|
|
128
128
|
? await (0, lifecycle_spool_1.drainLifecycleSpool)({ apiKey, baseUrl, agentId: resolved.agentId || undefined })
|
|
129
129
|
: (0, lifecycle_spool_1.lifecycleSpoolStatus)({ apiKey, agentId: resolved.agentId || undefined });
|
|
130
|
+
const namespaceAttention = status.other_namespaces.state === 'attention_required';
|
|
130
131
|
process.stdout.write(`${JSON.stringify({
|
|
131
|
-
ok: drain
|
|
132
|
+
ok: drain
|
|
133
|
+
? status.state === 'clear' && !namespaceAttention
|
|
134
|
+
: status.state !== 'attention_required' && !namespaceAttention,
|
|
132
135
|
lifecycle_spool: status,
|
|
133
136
|
}, null, 2)}\n`);
|
|
134
|
-
if (status.state === 'attention_required')
|
|
137
|
+
if (status.state === 'attention_required' || namespaceAttention)
|
|
135
138
|
process.exitCode = 2;
|
|
136
139
|
else if (drain && status.state !== 'clear')
|
|
137
140
|
process.exitCode = 1;
|
|
@@ -444,15 +447,21 @@ if (process.argv[2] !== 'keys') {
|
|
|
444
447
|
}
|
|
445
448
|
function toolFailure(toolName, failure) {
|
|
446
449
|
const result = (0, request_reliability_1.structuredRequestFailure)(failure);
|
|
447
|
-
const
|
|
450
|
+
const proofValidation = failure.code === 'proof_required';
|
|
451
|
+
const infrastructureFailure = !proofValidation && !['authentication_required', 'permission_denied'].includes(failure.code);
|
|
448
452
|
const supportsStale = ['marrow_agent_runtime', 'marrow_orient', 'marrow_ask', 'marrow_handoff_status', 'marrow_runtime_status', 'marrow_status'].includes(toolName || '');
|
|
449
453
|
const spool = (0, lifecycle_spool_1.lifecycleSpoolStatus)({ apiKey: API_KEY, agentId: FLEET_AGENT_ID });
|
|
450
|
-
result.failure_kind = infrastructureFailure ? 'infrastructure' : 'authorization';
|
|
454
|
+
result.failure_kind = proofValidation ? 'validation' : infrastructureFailure ? 'infrastructure' : 'authorization';
|
|
451
455
|
result.control_path = (0, control_path_state_1.controlPathStats)(toolName || 'marrow_control');
|
|
452
456
|
result.lifecycle_spool = {
|
|
453
457
|
...spool,
|
|
454
458
|
drain_command: 'npx -y --package=@getmarrow/mcp@latest marrow-mcp drain-spool',
|
|
455
459
|
};
|
|
460
|
+
result.host_capability = mcpHostCapability();
|
|
461
|
+
if (proofValidation) {
|
|
462
|
+
result.proof_required = true;
|
|
463
|
+
result.exact_next_action = failure.exactFix;
|
|
464
|
+
}
|
|
456
465
|
if (infrastructureFailure && supportsStale) {
|
|
457
466
|
let cached = null;
|
|
458
467
|
try {
|
|
@@ -479,6 +488,24 @@ if (process.argv[2] !== 'keys') {
|
|
|
479
488
|
}
|
|
480
489
|
return result;
|
|
481
490
|
}
|
|
491
|
+
function mcpHostCapability() {
|
|
492
|
+
const raw = String(process.env.MARROW_CLIENT || process.env.MARROW_HARNESS || '').trim().toLowerCase();
|
|
493
|
+
const host = ['grok', 'claude-code', 'codex', 'cursor', 'windsurf', 'gemini', 'kimi', 'qwen', 'deepseek']
|
|
494
|
+
.includes(raw) ? raw : 'mcp-client';
|
|
495
|
+
return {
|
|
496
|
+
transport: 'mcp_stdio',
|
|
497
|
+
host,
|
|
498
|
+
tools_available: true,
|
|
499
|
+
tool_invocation: 'on_demand',
|
|
500
|
+
passive_hooks: {
|
|
501
|
+
provided_by_mcp_transport: false,
|
|
502
|
+
external_host_hook_state: 'unverified',
|
|
503
|
+
observed_by_this_process: false,
|
|
504
|
+
},
|
|
505
|
+
always_on_state: 'host_dependent',
|
|
506
|
+
exact_next_action: 'Use the Marrow MCP tools on demand. Claim passive coverage only when the host separately configures trusted hooks and Marrow receives their lifecycle receipts.',
|
|
507
|
+
};
|
|
508
|
+
}
|
|
482
509
|
function clientOperationalPayload(toolName, value) {
|
|
483
510
|
const payload = value && typeof value === 'object' && !Array.isArray(value)
|
|
484
511
|
? value
|
|
@@ -486,6 +513,7 @@ if (process.argv[2] !== 'keys') {
|
|
|
486
513
|
const spool = (0, lifecycle_spool_1.lifecycleSpoolStatus)({ apiKey: API_KEY, agentId: FLEET_AGENT_ID });
|
|
487
514
|
return {
|
|
488
515
|
...payload,
|
|
516
|
+
host_capability: payload.host_capability || mcpHostCapability(),
|
|
489
517
|
client_update: payload.client_update || (0, request_reliability_1.localClientUpdate)(),
|
|
490
518
|
control_path: (0, control_path_state_1.controlPathStats)(toolName),
|
|
491
519
|
lifecycle_spool: {
|
|
@@ -512,21 +540,13 @@ if (process.argv[2] !== 'keys') {
|
|
|
512
540
|
function isHighRiskAction(action, type) {
|
|
513
541
|
return HIGH_RISK_ACTION.test(`${String(type || '')} ${String(action || '')}`);
|
|
514
542
|
}
|
|
515
|
-
function cachedGuidance() {
|
|
516
|
-
try {
|
|
517
|
-
return (0, guidance_cache_1.readGuidanceCache)({ apiKey: API_KEY, baseUrl: BASE_URL, agentId: FLEET_AGENT_ID });
|
|
518
|
-
}
|
|
519
|
-
catch {
|
|
520
|
-
return null;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
543
|
async function withControlDeadline(operation, options = {}) {
|
|
524
544
|
const toolName = options.toolName || 'marrow_control';
|
|
525
|
-
const hasCache = options.cacheAware !== false && Boolean(cachedGuidance());
|
|
526
545
|
const configuredTimeoutMs = Number(process.env.MARROW_REQUEST_TIMEOUT_MS);
|
|
546
|
+
const runtimeBudget = toolName === 'marrow_agent_runtime' || toolName === 'marrow_auto.runtime';
|
|
527
547
|
const timeoutMs = Number.isFinite(configuredTimeoutMs)
|
|
528
548
|
? Math.min(10_000, Math.max(150, Math.floor(configuredTimeoutMs)))
|
|
529
|
-
: options.highRisk
|
|
549
|
+
: options.highRisk || runtimeBudget ? 4_500 : 4_000;
|
|
530
550
|
const controller = new AbortController();
|
|
531
551
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
532
552
|
timer.unref?.();
|
|
@@ -1672,7 +1692,7 @@ if (process.argv[2] !== 'keys') {
|
|
|
1672
1692
|
capabilities: { tools: {}, prompts: {} },
|
|
1673
1693
|
serverInfo: { name: 'marrow', version: hook_contract_1.MCP_ADAPTER_VERSION },
|
|
1674
1694
|
...(AUTO_ENROLL ? {
|
|
1675
|
-
instructions: 'Use marrow_agent_runtime before consequential actions, obey fresh allow/warn/review_required/block results, use marrow_ask for relevant prior lessons, and close meaningful outcomes with marrow_commit. Infrastructure failures are not policy denials; continue only low-risk reversible work from the returned outage-safe brief, and require a fresh gate for high-risk work.',
|
|
1695
|
+
instructions: 'Use marrow_agent_runtime before consequential actions, obey fresh allow/warn/review_required/block results, use marrow_ask for relevant prior lessons, and close meaningful outcomes with marrow_commit. Infrastructure failures are not policy denials; continue only low-risk reversible work from the returned outage-safe brief, and require a fresh gate for high-risk work. MCP tools are on demand; do not claim passive or prompt-injection coverage unless the host separately configured trusted hooks and Marrow observed their receipts.',
|
|
1676
1696
|
} : {}),
|
|
1677
1697
|
});
|
|
1678
1698
|
// Auto-enroll: emit enrollment notification on connection
|
|
@@ -1841,12 +1861,10 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
1841
1861
|
// [FIX #9] Validate required params
|
|
1842
1862
|
const decision_id = requireString(args, 'decision_id');
|
|
1843
1863
|
const outcome = requireString(args, 'outcome');
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
}
|
|
1847
|
-
const result = await (0, index_1.marrowCommit)(API_KEY, BASE_URL, {
|
|
1864
|
+
const commitSuccess = requireBoolean(args, 'success');
|
|
1865
|
+
const result = await withControlDeadline((signal) => (0, index_1.marrowCommit)(API_KEY, BASE_URL, {
|
|
1848
1866
|
decision_id,
|
|
1849
|
-
success:
|
|
1867
|
+
success: commitSuccess,
|
|
1850
1868
|
outcome,
|
|
1851
1869
|
caused_by: args.caused_by,
|
|
1852
1870
|
proof: args.proof,
|
|
@@ -1858,10 +1876,10 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
1858
1876
|
surfaces: args.surfaces,
|
|
1859
1877
|
auto_gate: args.auto_gate,
|
|
1860
1878
|
model_usage: args.model_usage,
|
|
1861
|
-
}, SESSION_ID, FLEET_AGENT_ID);
|
|
1879
|
+
}, SESSION_ID, FLEET_AGENT_ID, signal), { highRisk: true, cacheAware: false, toolName: 'marrow_commit' });
|
|
1862
1880
|
const commitResult = { ...result, narrative: result.narrative ?? null };
|
|
1863
|
-
lastCommitted =
|
|
1864
|
-
lastDecisionId = null;
|
|
1881
|
+
lastCommitted = result.committed;
|
|
1882
|
+
lastDecisionId = result.committed ? null : decision_id;
|
|
1865
1883
|
success(id, {
|
|
1866
1884
|
content: [{ type: 'text', text: JSON.stringify(commitResult, null, 2) }],
|
|
1867
1885
|
});
|
|
@@ -1953,18 +1971,43 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
1953
1971
|
}
|
|
1954
1972
|
const proofCanClose = !highRisk || (0, runtime_contract_1.highRiskRuntimeCanClose)(runtimeGate, suppliedProof, args.gate_receipt_id);
|
|
1955
1973
|
const acceptedSuccess = proofCanClose ? outcomeSuccess : undefined;
|
|
1974
|
+
const delivery = () => (0, index_1.marrowAuto)(API_KEY, BASE_URL, {
|
|
1975
|
+
action,
|
|
1976
|
+
outcome,
|
|
1977
|
+
success: acceptedSuccess,
|
|
1978
|
+
type,
|
|
1979
|
+
proof: suppliedProof,
|
|
1980
|
+
gate_receipt_id: typeof args.gate_receipt_id === 'string'
|
|
1981
|
+
? args.gate_receipt_id
|
|
1982
|
+
: runtimeGate?.gate_receipt?.id,
|
|
1983
|
+
}, SESSION_ID, FLEET_AGENT_ID, 8_000);
|
|
1984
|
+
let delivered = null;
|
|
1985
|
+
let deliveryFailure = null;
|
|
1986
|
+
try {
|
|
1987
|
+
delivered = await delivery();
|
|
1988
|
+
}
|
|
1989
|
+
catch (err) {
|
|
1990
|
+
deliveryFailure = (0, request_reliability_1.structuredRequestFailure)(err);
|
|
1991
|
+
}
|
|
1956
1992
|
const receipt = await (0, lifecycle_spool_1.recordLifecycleEvent)({
|
|
1957
1993
|
apiKey: API_KEY,
|
|
1958
1994
|
baseUrl: BASE_URL,
|
|
1959
|
-
deferDelivery:
|
|
1995
|
+
deferDelivery: false,
|
|
1960
1996
|
event: {
|
|
1961
|
-
event_type:
|
|
1997
|
+
event_type: delivered?.committed
|
|
1998
|
+
? 'outcome_committed'
|
|
1999
|
+
: !highRisk && acceptedSuccess === false
|
|
2000
|
+
? 'tool_failed'
|
|
2001
|
+
: !highRisk && acceptedSuccess === true
|
|
2002
|
+
? 'tool_completed'
|
|
2003
|
+
: 'goal_started',
|
|
1962
2004
|
harness: process.env.MARROW_CLIENT || process.env.MARROW_HARNESS || 'mcp',
|
|
1963
2005
|
agent_id: FLEET_AGENT_ID,
|
|
1964
2006
|
session_id: SESSION_ID,
|
|
2007
|
+
decision_id: delivered?.decision_id,
|
|
1965
2008
|
action,
|
|
1966
|
-
outcome_state: 'pending',
|
|
1967
|
-
success: highRisk ? undefined : acceptedSuccess,
|
|
2009
|
+
outcome_state: delivered?.committed ? 'closed' : 'pending',
|
|
2010
|
+
success: delivered?.committed ? acceptedSuccess : highRisk ? undefined : acceptedSuccess,
|
|
1968
2011
|
adapter_version: hook_contract_1.MCP_ADAPTER_VERSION,
|
|
1969
2012
|
capability_level: 'mcp',
|
|
1970
2013
|
},
|
|
@@ -1973,48 +2016,29 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
1973
2016
|
action,
|
|
1974
2017
|
outcome: outcome || 'pending',
|
|
1975
2018
|
warnings: cachedOrientWarnings.map(formatWarningActionably),
|
|
1976
|
-
logging:
|
|
2019
|
+
logging: delivered?.committed
|
|
2020
|
+
? 'governed_commit_confirmed'
|
|
2021
|
+
: delivered
|
|
2022
|
+
? 'intent_confirmed'
|
|
2023
|
+
: 'durably_queued',
|
|
1977
2024
|
receipt,
|
|
1978
|
-
completion_state:
|
|
2025
|
+
completion_state: delivered?.committed
|
|
2026
|
+
? 'closed_with_proof'
|
|
2027
|
+
: !proofCanClose
|
|
2028
|
+
? 'pending_required_proof'
|
|
2029
|
+
: outcomeSuccess === undefined
|
|
2030
|
+
? 'pending_evidence'
|
|
2031
|
+
: 'delivery_pending',
|
|
2032
|
+
decision_id: delivered?.decision_id || null,
|
|
2033
|
+
live_delivery: {
|
|
2034
|
+
accepted: Boolean(delivered),
|
|
2035
|
+
committed: Boolean(delivered?.committed),
|
|
2036
|
+
...(deliveryFailure ? { failure: deliveryFailure } : {}),
|
|
2037
|
+
},
|
|
2038
|
+
host_capability: mcpHostCapability(),
|
|
1979
2039
|
client_update: (0, request_reliability_1.localClientUpdate)(),
|
|
1980
|
-
...(runtimeGate ? {
|
|
1981
|
-
runtime_gate: runtimeGate,
|
|
1982
|
-
completion_state: proofCanClose ? 'governed_delivery_pending' : 'pending_required_proof',
|
|
1983
|
-
} : {}),
|
|
2040
|
+
...(runtimeGate ? { runtime_gate: runtimeGate } : {}),
|
|
1984
2041
|
};
|
|
1985
|
-
const delivery = () => (0, index_1.marrowAuto)(API_KEY, BASE_URL, {
|
|
1986
|
-
action,
|
|
1987
|
-
outcome,
|
|
1988
|
-
success: acceptedSuccess,
|
|
1989
|
-
type,
|
|
1990
|
-
proof: suppliedProof,
|
|
1991
|
-
gate_receipt_id: typeof args.gate_receipt_id === 'string'
|
|
1992
|
-
? args.gate_receipt_id
|
|
1993
|
-
: runtimeGate?.gate_receipt?.id,
|
|
1994
|
-
}, SESSION_ID, FLEET_AGENT_ID, 1_500);
|
|
1995
|
-
if (highRisk && acceptedSuccess !== undefined) {
|
|
1996
|
-
const delivered = await delivery();
|
|
1997
|
-
if (!delivered.committed) {
|
|
1998
|
-
throw new request_reliability_1.MarrowRequestError({
|
|
1999
|
-
code: 'invalid_response',
|
|
2000
|
-
message: 'Marrow did not confirm the governed outcome commit',
|
|
2001
|
-
retryable: true,
|
|
2002
|
-
exactFix: 'Keep the outcome pending and retry marrow_commit with the fresh gate receipt and required proof.',
|
|
2003
|
-
});
|
|
2004
|
-
}
|
|
2005
|
-
response.logging = 'governed_commit_confirmed';
|
|
2006
|
-
response.completion_state = 'closed_with_proof';
|
|
2007
|
-
response.decision_id = delivered.decision_id;
|
|
2008
|
-
}
|
|
2009
|
-
else if (!highRisk) {
|
|
2010
|
-
void delivery().catch((err) => {
|
|
2011
|
-
const failure = (0, request_reliability_1.structuredRequestFailure)(err);
|
|
2012
|
-
const errorCode = failure.error && typeof failure.error === 'object'
|
|
2013
|
-
? String(failure.error.code || 'request_failed')
|
|
2014
|
-
: 'request_failed';
|
|
2015
|
-
process.stderr.write(`[marrow] marrow_auto queued; live delivery unavailable (${errorCode})\n`);
|
|
2016
|
-
});
|
|
2017
|
-
}
|
|
2018
2042
|
success(id, {
|
|
2019
2043
|
content: [{ type: 'text', text: JSON.stringify(response, null, 2) }],
|
|
2020
2044
|
});
|
|
@@ -2431,12 +2455,35 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
|
|
|
2431
2455
|
return;
|
|
2432
2456
|
}
|
|
2433
2457
|
if (toolName === 'marrow_handoff_status') {
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2458
|
+
try {
|
|
2459
|
+
const result = await withControlDeadline((signal) => (0, index_1.marrowHandoffStatus)(API_KEY, BASE_URL, {
|
|
2460
|
+
status: args.status,
|
|
2461
|
+
agentId: args.agentId || FLEET_AGENT_ID,
|
|
2462
|
+
limit: args.limit,
|
|
2463
|
+
}, SESSION_ID, FLEET_AGENT_ID, signal), { cacheAware: false, toolName: 'marrow_handoff_status' });
|
|
2464
|
+
toolSuccess(id, clientOperationalPayload('marrow_handoff_status', result));
|
|
2465
|
+
}
|
|
2466
|
+
catch (error) {
|
|
2467
|
+
if (error instanceof request_reliability_1.MarrowRequestError && error.backendCode === 'MARROW_PLAN_UPGRADE_REQUIRED') {
|
|
2468
|
+
toolSuccess(id, clientOperationalPayload('marrow_handoff_status', {
|
|
2469
|
+
ok: true,
|
|
2470
|
+
available: false,
|
|
2471
|
+
state: 'not_entitled',
|
|
2472
|
+
failure_kind: 'entitlement',
|
|
2473
|
+
authorization_state: 'plan_limited',
|
|
2474
|
+
credential_valid: true,
|
|
2475
|
+
feature: 'fleet_handoff_status',
|
|
2476
|
+
current_plan: error.currentPlan,
|
|
2477
|
+
required_plan: 'team',
|
|
2478
|
+
required_feature: error.requiredFeature || 'fleet_learning',
|
|
2479
|
+
service_reachable: true,
|
|
2480
|
+
exact_next_action: 'Use status, ask, runtime, auto, and commit on the current plan. Upgrade to Team before relying on fleet handoff status.',
|
|
2481
|
+
}));
|
|
2482
|
+
}
|
|
2483
|
+
else {
|
|
2484
|
+
throw error;
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2440
2487
|
return;
|
|
2441
2488
|
}
|
|
2442
2489
|
if (toolName === 'marrow_session_end') {
|