@livx.cc/agentx 0.99.44 → 0.99.46
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/dist/{Agent-f4um2q8E.d.ts → Agent-OcAg2yxA.d.ts} +15 -1
- package/dist/cli.d.ts +5 -4
- package/dist/cli.js +68 -13
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +66 -12
- package/dist/index.js.map +1 -1
- package/dist/{mcp-D5eN0JyQ.d.ts → mcp-kgG6rtsd.d.ts} +1 -1
- package/dist/mcp.client.d.ts +2 -2
- package/dist/{tools-S-ArIR3o.d.ts → tools-BqL8Lk4J.d.ts} +4 -0
- package/dist/tools.shell.d.ts +1 -1
- package/dist/tools.shell.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { a as AgentOptions, H as Hooks, i as RunResult, A as Agent } from './Agent-
|
|
2
|
-
export { C as ChatFragment, b as CompactResult, D as DEFAULT_MUTATING, c as Decision, P as PermissionOptions, d as PermissionPolicy, e as PermissionRule, f as PreToolUseDecision, R as ReasoningEffort, g as RecordingHooks, h as RecordingLifecycle, T as ToolUse, j as ToolUseMeta, k as composeHooks, l as estimateTokens, p as planMode, r as reasoningToChatFragment } from './Agent-
|
|
1
|
+
import { a as AgentOptions, H as Hooks, i as RunResult, A as Agent } from './Agent-OcAg2yxA.js';
|
|
2
|
+
export { C as ChatFragment, b as CompactResult, D as DEFAULT_MUTATING, c as Decision, P as PermissionOptions, d as PermissionPolicy, e as PermissionRule, f as PreToolUseDecision, R as ReasoningEffort, g as RecordingHooks, h as RecordingLifecycle, T as ToolUse, j as ToolUseMeta, k as composeHooks, l as estimateTokens, p as planMode, r as reasoningToChatFragment } from './Agent-OcAg2yxA.js';
|
|
3
3
|
export { MODEL_ALIASES, ModelSwitch, ResolveModelSwitchOpts, modelShortLabel, resolveModelAlias, resolveModelSwitch } from './models.js';
|
|
4
4
|
import { IFilesystem, FileMetadata } from '@livx.cc/wcli/core';
|
|
5
5
|
export { CommandExecutor, FileMetadata, IFilesystem, IndexedDbFilesystem, MemFilesystem, registerHeadlessCommands } from '@livx.cc/wcli/core';
|
|
6
6
|
import { BodDB } from '@bod.ee/db';
|
|
7
|
-
import { A as AgentTool, C as ChatLike, a as ChatOptions, b as ChatResponse, j as ToolCall, H as HostBridge, U as UserQuestion, g as MessageContent } from './tools-
|
|
8
|
-
export { c as ContentPart, d as HostEvent, I as IMAGE_ELIDE_STUB, e as IMAGE_REF_SCHEME, f as ImageRef, M as Message, R as Role, S as SandboxJobRegistry, h as StreamChunk, T as TodoItem, i as Tool, k as ToolContext, l as bashTool, m as clearImageRefs, n as contentBytes, o as contentText, p as defaultTools, q as editTool, r as elideImages, s as elideInlineImages, t as elideStaleImages, u as exitSessionTool, v as expandImagesForSend, w as imagePart, x as imageRefPart, y as imageRefResult, z as makeContext, B as makeJobTools, D as messageHasImage, E as messageHasImageRef, F as messageHasInlineImage, G as mintImageRef, J as parseImageRef, K as readTool, L as remintImageRefs, N as sendBytes, O as sendBytesPer, P as toWireTools, Q as todoWriteTool, V as toolImageRef, W as toolRegistry, X as toolsByName } from './tools-
|
|
7
|
+
import { A as AgentTool, C as ChatLike, a as ChatOptions, b as ChatResponse, j as ToolCall, H as HostBridge, U as UserQuestion, g as MessageContent } from './tools-BqL8Lk4J.js';
|
|
8
|
+
export { c as ContentPart, d as HostEvent, I as IMAGE_ELIDE_STUB, e as IMAGE_REF_SCHEME, f as ImageRef, M as Message, R as Role, S as SandboxJobRegistry, h as StreamChunk, T as TodoItem, i as Tool, k as ToolContext, l as bashTool, m as clearImageRefs, n as contentBytes, o as contentText, p as defaultTools, q as editTool, r as elideImages, s as elideInlineImages, t as elideStaleImages, u as exitSessionTool, v as expandImagesForSend, w as imagePart, x as imageRefPart, y as imageRefResult, z as makeContext, B as makeJobTools, D as messageHasImage, E as messageHasImageRef, F as messageHasInlineImage, G as mintImageRef, J as parseImageRef, K as readTool, L as remintImageRefs, N as sendBytes, O as sendBytesPer, P as toWireTools, Q as todoWriteTool, V as toolImageRef, W as toolRegistry, X as toolsByName } from './tools-BqL8Lk4J.js';
|
|
9
9
|
import { ShellJobRegistry } from './tools.shell.js';
|
|
10
10
|
import { SpokenSplitter } from '@bod.ee/voice';
|
|
11
11
|
export { AudioSink, AudioSource, AuthProvider, CartesiaTTS, CartesiaTTSOptions, STT_SAMPLE_RATE, SonioxSTT, SonioxSTTOptions, SttLike, TTS_SAMPLE_RATE, TtsLike, VoiceEngine, VoiceEngineOptions, VoiceState, resolveAuth } from '@bod.ee/voice';
|
|
12
|
-
export { M as McpCall, a as McpImage, b as McpRoute, c as McpRouteResolver, d as McpToolResult, e as McpToolSearchOptions, f as McpToolSpec, g as MountedMcpLike, h as buildMcpCatalog, m as makeLazyMcpToolSearch, i as makeMcpToolSearch, j as makeMcpToolSearchFromMounted, k as mcpToolToAgentTool, l as mcpToolsToAgentTools } from './mcp-
|
|
12
|
+
export { M as McpCall, a as McpImage, b as McpRoute, c as McpRouteResolver, d as McpToolResult, e as McpToolSearchOptions, f as McpToolSpec, g as MountedMcpLike, h as buildMcpCatalog, m as makeLazyMcpToolSearch, i as makeMcpToolSearch, j as makeMcpToolSearchFromMounted, k as mcpToolToAgentTool, l as mcpToolsToAgentTools } from './mcp-kgG6rtsd.js';
|
|
13
13
|
import * as libx_js_src_modules_log from 'libx.js/src/modules/log';
|
|
14
14
|
export { log } from 'libx.js/src/modules/log';
|
|
15
15
|
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,9 @@ var init_logging = __esm({
|
|
|
22
22
|
import { readFileSync, statSync, realpathSync } from "fs";
|
|
23
23
|
import { resolve, sep } from "path";
|
|
24
24
|
import { randomBytes } from "crypto";
|
|
25
|
+
function passesBackReasoning(model) {
|
|
26
|
+
return !!model && /^(deepseek|moonshot)\//.test(model);
|
|
27
|
+
}
|
|
25
28
|
function contentText(content) {
|
|
26
29
|
if (content == null) return "";
|
|
27
30
|
if (typeof content === "string") return content;
|
|
@@ -205,7 +208,7 @@ function sendBytes(messages, keepRecent = 1, stub = IMAGE_ELIDE_STUB) {
|
|
|
205
208
|
}
|
|
206
209
|
function sendBytesPer(messages, keepRecent = 1, stub = IMAGE_ELIDE_STUB) {
|
|
207
210
|
const keep = messages.some(bearsImage) ? imageWindows(messages, keepRecent) : null;
|
|
208
|
-
return messages.map((m, i) => wireBytes(m.content, keep == null || keep.has(i), stub) + (m.tool_calls ? JSON.stringify(m.tool_calls).length : 0));
|
|
211
|
+
return messages.map((m, i) => wireBytes(m.content, keep == null || keep.has(i), stub) + (m.tool_calls ? JSON.stringify(m.tool_calls).length : 0) + (m.reasoning_content?.length ?? 0));
|
|
209
212
|
}
|
|
210
213
|
function wireBytes(content, kept, stub) {
|
|
211
214
|
const tool = toolImageRef(content);
|
|
@@ -3944,6 +3947,10 @@ var AgentOptions = class {
|
|
|
3944
3947
|
* not prose: a named tool with zero invocations is provable. On detection the loop nudges ONCE for a real
|
|
3945
3948
|
* call, and records the names in `RunResult.fabricatedToolClaims` either way. */
|
|
3946
3949
|
catchFabricatedToolUse = true;
|
|
3950
|
+
/** An empty model turn (no text, no tools) that ends the loop after work is re-prompted ONCE, then finishes
|
|
3951
|
+
* as `finishReason:'empty'` (a failure) instead of a phantom 'stop'. Off for layers that deliberately
|
|
3952
|
+
* allow a silent turn and repair it themselves (the duplex voice reflex). */
|
|
3953
|
+
failEmptyTurns = true;
|
|
3947
3954
|
/** Fold the dropped middle of an over-long transcript into a synthetic summary (edge-safe, no LLM). Off => drop-oldest. */
|
|
3948
3955
|
compaction;
|
|
3949
3956
|
/** Add `Checkpoint`/`Rollback` tools (requires the fs to be an OverlayFilesystem). */
|
|
@@ -4017,6 +4024,14 @@ var Agent = class _Agent {
|
|
|
4017
4024
|
/** Per-run ground truth: every tool name actually INVOKED this run (native dispatch + delegated
|
|
4018
4025
|
* runtime activity). The ledger that makes a fabricated tool claim provable. Reset per run. */
|
|
4019
4026
|
invokedTools = /* @__PURE__ */ new Set();
|
|
4027
|
+
/** Delegated-runtime HOST tools currently executing (cursor toolExecutor). While > 0 the provider is
|
|
4028
|
+
* not silent — it is waiting on US — so the idle-stall watchdog must not fire (see armStallWatchdog). */
|
|
4029
|
+
hostToolsInFlight = 0;
|
|
4030
|
+
/** The hold is bounded by the transport tool deadline: a host tool that never settles (the helper already gave
|
|
4031
|
+
* up on it) must not disable stall protection for the rest of the request — or this instance. */
|
|
4032
|
+
hostToolHoldUntil = 0;
|
|
4033
|
+
/** Resets the active request's stall timer; a host tool's completion restarts the model's idle window. */
|
|
4034
|
+
stallPoke;
|
|
4020
4035
|
/** Advertised tools this run's text claimed to have used while the ledger showed zero calls. */
|
|
4021
4036
|
fabricatedClaims = /* @__PURE__ */ new Set();
|
|
4022
4037
|
activeHooks;
|
|
@@ -4319,6 +4334,7 @@ var Agent = class _Agent {
|
|
|
4319
4334
|
let toolCallsTotal = 0;
|
|
4320
4335
|
let lastFp = "";
|
|
4321
4336
|
let repeats = 0;
|
|
4337
|
+
let emptyNudged = false;
|
|
4322
4338
|
let closureNudged = false;
|
|
4323
4339
|
let lastProgressAt = start;
|
|
4324
4340
|
const kill = (finishReason) => {
|
|
@@ -4327,12 +4343,13 @@ var Agent = class _Agent {
|
|
|
4327
4343
|
const claims = this.survivingClaims();
|
|
4328
4344
|
return { text: lastAssistantText(this.transcript), steps, finishReason, messages: this.transcript, usage, usageEstimated, ...claims.length ? { fabricatedToolClaims: claims } : {} };
|
|
4329
4345
|
};
|
|
4346
|
+
const overBudget = () => !!o.maxTokens && usage.totalTokens - 0.9 * usage.cacheReadTokens >= o.maxTokens;
|
|
4347
|
+
const canNudge = () => steps < o.maxSteps && !overBudget();
|
|
4330
4348
|
while (true) {
|
|
4331
4349
|
if (o.signal?.aborted) return kill("aborted");
|
|
4332
4350
|
if (steps >= o.maxSteps) return kill("max_steps");
|
|
4333
4351
|
if (o.timeoutMs && Date.now() - lastProgressAt - this.parkedMs >= o.timeoutMs) return kill("timeout");
|
|
4334
|
-
|
|
4335
|
-
if (o.maxTokens && budgetTokens >= o.maxTokens) return kill("budget");
|
|
4352
|
+
if (overBudget()) return kill("budget");
|
|
4336
4353
|
steps++;
|
|
4337
4354
|
this.options.host?.notify?.({ kind: "turn_start", message: `step ${steps}` });
|
|
4338
4355
|
let res;
|
|
@@ -4353,7 +4370,14 @@ var Agent = class _Agent {
|
|
|
4353
4370
|
toolTimeoutMs: effectiveToolTimeoutMs,
|
|
4354
4371
|
toolExecutor: async (name, args) => {
|
|
4355
4372
|
const tc = { id: `cursor-${Date.now()}`, type: "function", function: { name, arguments: JSON.stringify(args) } };
|
|
4356
|
-
|
|
4373
|
+
this.hostToolsInFlight++;
|
|
4374
|
+
this.hostToolHoldUntil = Math.max(this.hostToolHoldUntil, Date.now() + effectiveToolTimeoutMs);
|
|
4375
|
+
try {
|
|
4376
|
+
return await this.dispatch(tc);
|
|
4377
|
+
} finally {
|
|
4378
|
+
this.hostToolsInFlight--;
|
|
4379
|
+
this.stallPoke?.();
|
|
4380
|
+
}
|
|
4357
4381
|
}
|
|
4358
4382
|
} : void 0;
|
|
4359
4383
|
const claudeSteerPo = o.model.startsWith("claude-code/") ? { steer: () => this.drainPendingSteers() } : void 0;
|
|
@@ -4468,14 +4492,28 @@ var Agent = class _Agent {
|
|
|
4468
4492
|
this.transcript.push({
|
|
4469
4493
|
role: "assistant",
|
|
4470
4494
|
content: res.content ?? "",
|
|
4471
|
-
...toolCalls.length ? { tool_calls: toolCalls } : {}
|
|
4495
|
+
...toolCalls.length ? { tool_calls: toolCalls } : {},
|
|
4496
|
+
...res.reasoningContent && passesBackReasoning(o.model) ? { reasoning_content: res.reasoningContent } : {}
|
|
4472
4497
|
});
|
|
4473
4498
|
}
|
|
4474
4499
|
if (toolCalls.length === 0) {
|
|
4475
4500
|
if (this.drainInjections()) continue;
|
|
4501
|
+
if (emptyTurn && o.failEmptyTurns) {
|
|
4502
|
+
if (!emptyNudged && canNudge()) {
|
|
4503
|
+
emptyNudged = true;
|
|
4504
|
+
log6.warn(`empty model turn after tool work (step ${steps}, provider finishReason=${res.finishReason ?? "n/a"}) \u2014 nudging once`);
|
|
4505
|
+
this.transcript.push({ role: "user", content: "Continue \u2014 you returned nothing. Answer the user now using the tool results above, or keep working if you are not done." });
|
|
4506
|
+
continue;
|
|
4507
|
+
}
|
|
4508
|
+
log6.warn(`model returned empty turns after tool work \u2014 surfacing as failed`);
|
|
4509
|
+
const e = new Error("empty_response: model returned an empty turn (no text, no tools) after a re-prompt");
|
|
4510
|
+
e.code = "empty";
|
|
4511
|
+
return { text: "", steps, finishReason: "empty", ...res.finishReason ? { providerFinishReason: res.finishReason } : {}, messages: this.transcript, usage, usageEstimated, error: e };
|
|
4512
|
+
}
|
|
4476
4513
|
const fabricated = o.catchFabricatedToolUse && !closureNudged ? this.fabricatedToolNames(contentText(res.content ?? "")) : [];
|
|
4477
4514
|
for (const n of fabricated) this.fabricatedClaims.add(n);
|
|
4478
|
-
if (fabricated.length &&
|
|
4515
|
+
if (fabricated.length && !canNudge()) log6.warn(`fabricated tool use: text names ${fabricated.join(", ")} but no step/budget left to demand the call \u2014 finishing with the existing answer`);
|
|
4516
|
+
if (fabricated.length && canNudge()) {
|
|
4479
4517
|
closureNudged = true;
|
|
4480
4518
|
log6.warn(`fabricated tool use: text names ${fabricated.join(", ")} but the run invoked ${this.invokedTools.size ? [...this.invokedTools].join(", ") : "nothing"} \u2014 demanding the real call`);
|
|
4481
4519
|
this.transcript.push({
|
|
@@ -4484,7 +4522,7 @@ var Agent = class _Agent {
|
|
|
4484
4522
|
});
|
|
4485
4523
|
continue;
|
|
4486
4524
|
}
|
|
4487
|
-
if (o.closeDelegatedTurns && res.endedWithoutClosing && !closureNudged &&
|
|
4525
|
+
if (o.closeDelegatedTurns && res.endedWithoutClosing && !closureNudged && canNudge()) {
|
|
4488
4526
|
closureNudged = true;
|
|
4489
4527
|
log6.verbose("delegated turn went silent after a tool \u2014 nudging a closing answer");
|
|
4490
4528
|
this.transcript.push({
|
|
@@ -4498,7 +4536,7 @@ var Agent = class _Agent {
|
|
|
4498
4536
|
this.releaseStaleImages();
|
|
4499
4537
|
await this.activeHooks?.onStop?.(res.content ?? "");
|
|
4500
4538
|
const claims = this.survivingClaims();
|
|
4501
|
-
return { text: res.content ?? "", steps, finishReason: "stop", messages: this.transcript, usage, usageEstimated, ...claims.length ? { fabricatedToolClaims: claims } : {} };
|
|
4539
|
+
return { text: res.content ?? "", steps, finishReason: "stop", ...res.finishReason ? { providerFinishReason: res.finishReason } : {}, messages: this.transcript, usage, usageEstimated, ...claims.length ? { fabricatedToolClaims: claims } : {} };
|
|
4502
4540
|
}
|
|
4503
4541
|
const fp = toolCalls.map((tc) => tc.function.name + ":" + (tc.function.arguments ?? "")).join("|");
|
|
4504
4542
|
repeats = fp === lastFp ? repeats + 1 : 1;
|
|
@@ -4562,10 +4600,12 @@ var Agent = class _Agent {
|
|
|
4562
4600
|
if (!stallMs) return;
|
|
4563
4601
|
if (timer) clearTimeout(timer);
|
|
4564
4602
|
timer = setTimeout(() => {
|
|
4603
|
+
if (this.hostToolsInFlight > 0 && Date.now() < this.hostToolHoldUntil) return poke();
|
|
4565
4604
|
fired = true;
|
|
4566
4605
|
ac.abort(stallError());
|
|
4567
4606
|
}, stallMs);
|
|
4568
4607
|
};
|
|
4608
|
+
this.stallPoke = poke;
|
|
4569
4609
|
poke();
|
|
4570
4610
|
const stalled = () => fired && !parent?.aborted;
|
|
4571
4611
|
return {
|
|
@@ -4574,6 +4614,7 @@ var Agent = class _Agent {
|
|
|
4574
4614
|
stalled,
|
|
4575
4615
|
clear: () => {
|
|
4576
4616
|
if (timer) clearTimeout(timer);
|
|
4617
|
+
if (this.stallPoke === poke) this.stallPoke = void 0;
|
|
4577
4618
|
parent?.removeEventListener("abort", onParentAbort);
|
|
4578
4619
|
},
|
|
4579
4620
|
reclassify: (err) => stalled() ? stallError() : err
|
|
@@ -4594,10 +4635,14 @@ var Agent = class _Agent {
|
|
|
4594
4635
|
const delegatedOrder = [];
|
|
4595
4636
|
let sawTool = false;
|
|
4596
4637
|
let trailing = "";
|
|
4638
|
+
let reasoning = "";
|
|
4597
4639
|
for await (const chunk of stream) {
|
|
4598
4640
|
poke?.();
|
|
4599
4641
|
if (this.options.signal?.aborted) break;
|
|
4600
|
-
if (chunk.reasoningContent)
|
|
4642
|
+
if (chunk.reasoningContent) {
|
|
4643
|
+
reasoning += chunk.reasoningContent;
|
|
4644
|
+
this.options.host.notify({ kind: "thinking_delta", message: chunk.reasoningContent });
|
|
4645
|
+
}
|
|
4601
4646
|
if (chunk.toolActivity) {
|
|
4602
4647
|
const a = chunk.toolActivity;
|
|
4603
4648
|
const verb = a.status === "error" ? "\u2717" : a.status === "completed" ? "\u2713" : "\u2192";
|
|
@@ -4628,7 +4673,7 @@ var Agent = class _Agent {
|
|
|
4628
4673
|
}
|
|
4629
4674
|
const delegatedTools = delegatedOrder.map((k) => delegated.get(k));
|
|
4630
4675
|
const endedWithoutClosing = sawTool && (!trailing.trim() || endsOnAnnouncedAction(trailing));
|
|
4631
|
-
return { content, ...finishReason ? { finishReason } : {}, ...toolCalls ? { toolCalls } : {}, ...usage ? { usage } : {}, ...delegatedTools.length ? { delegatedTools } : {}, ...endedWithoutClosing ? { endedWithoutClosing: true } : {} };
|
|
4676
|
+
return { content, ...reasoning ? { reasoningContent: reasoning } : {}, ...finishReason ? { finishReason } : {}, ...toolCalls ? { toolCalls } : {}, ...usage ? { usage } : {}, ...delegatedTools.length ? { delegatedTools } : {}, ...endedWithoutClosing ? { endedWithoutClosing: true } : {} };
|
|
4632
4677
|
}
|
|
4633
4678
|
/**
|
|
4634
4679
|
* FABRICATED TOOL USE — the failure no call-reactive layer can see, because there IS no call.
|
|
@@ -4665,8 +4710,9 @@ var Agent = class _Agent {
|
|
|
4665
4710
|
*/
|
|
4666
4711
|
fabricatedToolNames(text) {
|
|
4667
4712
|
const out = [];
|
|
4713
|
+
const invoked = new Set([...this.invokedTools].map(toolKey));
|
|
4668
4714
|
for (const t of this.activeTools) {
|
|
4669
|
-
if (
|
|
4715
|
+
if (invoked.has(toolKey(t.name))) continue;
|
|
4670
4716
|
if (t.name.length < 4 || !/[_\-A-Z]/.test(t.name)) continue;
|
|
4671
4717
|
const re = new RegExp(`(?:^|[^\\w])${t.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[^\\w]|$)`);
|
|
4672
4718
|
if (re.test(text)) out.push(t.name);
|
|
@@ -4679,7 +4725,8 @@ var Agent = class _Agent {
|
|
|
4679
4725
|
* call that demonstrably ran. See `RunResult.fabricatedToolClaims`. */
|
|
4680
4726
|
survivingClaims() {
|
|
4681
4727
|
if (!this.fabricatedClaims.size) return [];
|
|
4682
|
-
|
|
4728
|
+
const invoked = new Set([...this.invokedTools].map(toolKey));
|
|
4729
|
+
return [...this.fabricatedClaims].filter((n) => !invoked.has(toolKey(n)));
|
|
4683
4730
|
}
|
|
4684
4731
|
async dispatch(tc) {
|
|
4685
4732
|
this.invokedTools.add(tc.function.name);
|
|
@@ -5067,6 +5114,11 @@ function lastAssistantText(messages) {
|
|
|
5067
5114
|
}
|
|
5068
5115
|
return "";
|
|
5069
5116
|
}
|
|
5117
|
+
var TOOL_ALIASES = { bash: "shell" };
|
|
5118
|
+
function toolKey(name) {
|
|
5119
|
+
const k = name.toLowerCase();
|
|
5120
|
+
return TOOL_ALIASES[k] ?? k;
|
|
5121
|
+
}
|
|
5070
5122
|
|
|
5071
5123
|
// src/index.ts
|
|
5072
5124
|
init_NodeDiskFilesystem();
|
|
@@ -7835,6 +7887,8 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
7835
7887
|
instructionFiles: false,
|
|
7836
7888
|
maxSteps: 8,
|
|
7837
7889
|
timeoutMs: 3e4,
|
|
7890
|
+
failEmptyTurns: false,
|
|
7891
|
+
// a silent reflex turn after a dispatch is legitimate — the voice layer repairs it
|
|
7838
7892
|
...o.reflexOptions,
|
|
7839
7893
|
tools,
|
|
7840
7894
|
// Composed AFTER the spread so the dispatch guard can't be dropped by reflexOptions.
|