@bike4mind/cli 3.3.0 → 4.0.0
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 +139 -37
- package/bin/bike4mind-cli.mjs +55 -1
- package/bin/hearth-hook.mjs +53 -7
- package/dist/{AgentHistoryStore-CLidbOHG.mjs → AgentHistoryStore-DgqmcVLN.mjs} +5597 -5568
- package/dist/ApiClient-_JrM_xWw.mjs +133 -0
- package/dist/{ConfigStore-DUATzLxB.mjs → ConfigStore-BwqsQ0U9.mjs} +1409 -189
- package/dist/RemoteSkillSource-qTvRkaNA.mjs +455 -0
- package/dist/{WorkItemsClient-BSoOblGp.mjs → WorkItemsClient-CERTDrhT.mjs} +1 -1
- package/dist/{buildAgent-DyZWK0LW.mjs → buildToolSearch-D9GEK91u.mjs} +406 -47
- package/dist/commands/acpCommand.mjs +13 -13
- package/dist/commands/apiCommand.mjs +1 -1
- package/dist/commands/doctorCommand.mjs +1 -1
- package/dist/commands/envCommand.mjs +1 -1
- package/dist/commands/headlessCommand.mjs +5 -4
- package/dist/commands/mcpCommand.mjs +4 -3
- package/dist/commands/pluginCommand.mjs +1 -1
- package/dist/commands/skillsCommand.mjs +259 -0
- package/dist/commands/updateCommand.mjs +1 -1
- package/dist/{dist-DPP7UmK9.mjs → dist-Ul2qwn5x.mjs} +17 -132
- package/dist/index.mjs +171 -48
- package/dist/{package-DYPVrada.mjs → package-CQaam4Dq.mjs} +1 -1
- package/dist/{serve-w6v3rZiR.mjs → serve-CFxUhjml.mjs} +657 -27
- package/dist/store-BYeE0shc.mjs +2 -0
- package/dist/{store-BxmPEI3v.mjs → store-BbvSi3KK.mjs} +26 -2
- package/package.json +11 -10
- package/dist/store-BkMur6uk.mjs +0 -2
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as
|
|
3
|
-
import { a as selectLiveSubagentTokens, i as selectLiveSubagentCredits, n as selectActiveBackgroundShells, o as useCliStore, r as selectCompletedBackgroundShells, t as selectActiveBackgroundAgents } from "./store-
|
|
4
|
-
import {
|
|
2
|
+
import { $ as processFileReferences, A as createAgentDelegateTool, B as DEFAULT_THOROUGHNESS, C as createTodoStore, D as createCoordinateTaskTool, E as parseAgentConfig, H as buildSkillsPromptSection, I as ALWAYS_DENIED_FOR_AGENTS, J as getProcessHooks, K as findIterationBoundary, L as DEFAULT_AGENT_MODEL, M as substituteArguments, N as PermissionManager, O as createResumeAgentTool, P as buildCliToolList, Q as CustomCommandStore, R as DEFAULT_MAX_ITERATIONS, S as createFindDefinitionTool, T as createSkillTool, V as buildSystemPrompt, Y as setWebSocketToolExecutor, Z as getPlanModeFilePath, _ as createDecisionLogTool, at as searchFiles, b as createGetFileStructureTool, ct as COMPACTION_SUMMARY_MARKER, d as createReviewGateStore, et as hasFileReferences, f as createReviewGateTool, g as formatBlockersOutput, h as createBlockerTools, it as formatFileSize, j as FallbackLlmBackend, k as createBackgroundAgentTools, m as createBlockerStore, nt as SessionStore, ot as warmFileCache, p as formatReviewGatesOutput, rt as searchCommands, s as buildSandbox, tt as CheckpointStore, u as isConnectionDrop, ut as MAX_PASTE_SIZE, v as createDecisionStore, w as createWriteTodosTool, x as createWorkItemTools, y as formatDecisionsOutput, z as DEFAULT_RETRY_CONFIG } from "./AgentHistoryStore-DgqmcVLN.mjs";
|
|
3
|
+
import { a as selectLiveSubagentTokens, i as selectLiveSubagentCredits, n as selectActiveBackgroundShells, o as useCliStore, r as selectCompletedBackgroundShells, t as selectActiveBackgroundAgents } from "./store-BbvSi3KK.mjs";
|
|
4
|
+
import { $t as selfClaimedActorKindSchema, B as ChatModels, Dt as isEarlyStop, Jt as DELEGATION_PAYLOAD_SCHEMA_NAME, Qt as delegationPayloadSchema, S as isValidSessionId, Vt as tokenEstimateMultiplier, Xt as actorKindMarker, Yt as actorColorIndex, Zt as actorKindSchema, _ as LOCAL_DEV_URL, at as REVIEW_GATE_STATUS_VALUES, b as logger, ct as SUBQUEST_STATUS_VALUES, d as getCreditsUrl, f as getEnvironmentName, i as ToolCatalog, m as resolveApiEndpoint, n as extractCompactInstructions, p as requireApiUrl, qt as ACTOR_COLOR_SLOTS, t as ConfigStore, u as ApiEndpointUnconfiguredError, x as SESSION_ID_PATTERN, y as parseApiUrl, z as CREDIT_DEDUCT_TRANSACTION_TYPES } from "./ConfigStore-BwqsQ0U9.mjs";
|
|
5
|
+
import { a as rewireReservedNames, i as mergeCommands, t as RemoteSkillSource } from "./RemoteSkillSource-qTvRkaNA.mjs";
|
|
5
6
|
import { r as isFeatureEnabled, t as PluginStore } from "./PluginStore-DwvOJ-G3.mjs";
|
|
6
|
-
import { i as getTokenCounter, n as ConversationContext, r as reconstructTurnBlocks, t as WorkItemsClient } from "./WorkItemsClient-
|
|
7
|
-
import { a as
|
|
7
|
+
import { i as getTokenCounter, n as ConversationContext, r as reconstructTurnBlocks, t as WorkItemsClient } from "./WorkItemsClient-CERTDrhT.mjs";
|
|
8
|
+
import { a as deferredToolRegistry, i as buildLlmBackend, n as buildAgent, o as NotifyingLlmBackend, r as buildSupportingStores, t as buildToolSearch } from "./buildToolSearch-D9GEK91u.mjs";
|
|
8
9
|
import { t as getShellSessionManager } from "./ShellSessionManager-6o8KZzl1-vrbPAUTq.mjs";
|
|
9
|
-
import {
|
|
10
|
-
import { t as version } from "./package-
|
|
10
|
+
import { r as OAuthClient, t as ApiClient } from "./ApiClient-_JrM_xWw.mjs";
|
|
11
|
+
import { t as version } from "./package-CQaam4Dq.mjs";
|
|
11
12
|
import { s as getStartupUpdateStatus } from "./updateChecker-CCLVVdH0.mjs";
|
|
12
13
|
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
|
|
13
14
|
import { Box, Static, Text, measureElement, render, useApp, useInput, usePaste, useStdout } from "ink";
|
|
@@ -627,6 +628,7 @@ function InputPrompt({ onSubmit, onBashCommand, onImageDetected, disabled = fals
|
|
|
627
628
|
const pastedLineCount = useCliStore((state) => state.pastedLineCount);
|
|
628
629
|
const setPastedContent = useCliStore((state) => state.setPastedContent);
|
|
629
630
|
const clearPaste = useCliStore((state) => state.clearPaste);
|
|
631
|
+
const turnBusy = useCliStore((state) => state.isThinking || state.turnInFlight);
|
|
630
632
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
631
633
|
const [historyIndex, setHistoryIndex] = useState(-1);
|
|
632
634
|
const [tempInput, setTempInput] = useState("");
|
|
@@ -705,6 +707,15 @@ function InputPrompt({ onSubmit, onBashCommand, onImageDetected, disabled = fals
|
|
|
705
707
|
return;
|
|
706
708
|
}
|
|
707
709
|
}
|
|
710
|
+
if (key.tab && !key.shift && turnBusy && !isBashMode && !shouldShowCommandAutocomplete && !fileAutocomplete?.active) {
|
|
711
|
+
const content = pastedContent ?? value;
|
|
712
|
+
if (!content.trim()) return;
|
|
713
|
+
if (pastedContent) clearPaste();
|
|
714
|
+
onSubmit(content, { followUp: true });
|
|
715
|
+
setValue("");
|
|
716
|
+
setHistoryIndex(-1);
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
708
719
|
if (!shouldShowCommandAutocomplete && !fileAutocomplete?.active && history.length > 0) {
|
|
709
720
|
if (key.upArrow) {
|
|
710
721
|
if (historyIndex === -1) {
|
|
@@ -2706,6 +2717,8 @@ const MessageItem = React.memo(function MessageItem({ message, showThoughts = tr
|
|
|
2706
2717
|
* agent/shell status blocks render nothing when idle.
|
|
2707
2718
|
*/
|
|
2708
2719
|
const LIVE_CHROME_ROWS = 10;
|
|
2720
|
+
/** Shown under the queued-message rows; mirrors the routing in handleSubmit and the ESC handler in index.tsx. */
|
|
2721
|
+
const QUEUE_HINT = "Enter steers this turn · Tab queues for next turn · Esc cancels and restores to input";
|
|
2709
2722
|
function App({ onMessage, onBackgroundCompletion, onCommand, onBashCommand, onPermissionResponse, onUserQuestionResponse, onReviewGateResponse, onImageDetected, commandHistory = [], commands = [], config, availableModels = [], pluginDescriptors = [], onSaveConfig, prefillInput, onPrefillConsumed, mcpManager }) {
|
|
2710
2723
|
const messages = useCliStore(useShallow((state) => state.session?.messages || []));
|
|
2711
2724
|
const pendingMessages = useCliStore((state) => state.pendingMessages);
|
|
@@ -2729,15 +2742,17 @@ function App({ onMessage, onBackgroundCompletion, onCommand, onBashCommand, onPe
|
|
|
2729
2742
|
const exitRequested = useCliStore((state) => state.exitRequested);
|
|
2730
2743
|
const setIsThinking = useCliStore((state) => state.setIsThinking);
|
|
2731
2744
|
const pendingBackgroundTrigger = useCliStore((state) => state.pendingBackgroundTrigger);
|
|
2745
|
+
const turnInFlight = useCliStore((state) => state.turnInFlight);
|
|
2732
2746
|
const setPendingBackgroundTrigger = useCliStore((state) => state.setPendingBackgroundTrigger);
|
|
2733
2747
|
useEffect(() => {
|
|
2734
|
-
if (pendingBackgroundTrigger && !isThinking && onBackgroundCompletion) {
|
|
2748
|
+
if (pendingBackgroundTrigger && !isThinking && !turnInFlight && onBackgroundCompletion) {
|
|
2735
2749
|
setPendingBackgroundTrigger(false);
|
|
2736
2750
|
onBackgroundCompletion();
|
|
2737
2751
|
}
|
|
2738
2752
|
}, [
|
|
2739
2753
|
pendingBackgroundTrigger,
|
|
2740
2754
|
isThinking,
|
|
2755
|
+
turnInFlight,
|
|
2741
2756
|
setPendingBackgroundTrigger,
|
|
2742
2757
|
onBackgroundCompletion
|
|
2743
2758
|
]);
|
|
@@ -2767,8 +2782,9 @@ function App({ onMessage, onBackgroundCompletion, onCommand, onBashCommand, onPe
|
|
|
2767
2782
|
useInput(() => setPromptInputTick((tick) => tick + 1), { isActive: promptKey !== "" });
|
|
2768
2783
|
const promptRows = promptKey === "" ? 0 : measuredPrompt?.key === promptKey ? measuredPrompt.rows : Infinity;
|
|
2769
2784
|
const [terminalCols, terminalRows] = useStdoutDimensions();
|
|
2770
|
-
const
|
|
2771
|
-
const
|
|
2785
|
+
const queueRows = messageQueue.length > 0 ? messageQueue.length + 1 : 0;
|
|
2786
|
+
const liveRows = Math.max(0, terminalRows - LIVE_CHROME_ROWS - queueRows - promptRows);
|
|
2787
|
+
const handleSubmit = React.useCallback(async (input, options) => {
|
|
2772
2788
|
const trimmed = input.trim();
|
|
2773
2789
|
if (!trimmed) return;
|
|
2774
2790
|
if (trimmed.startsWith("/") && !trimmed.match(/^\/[A-Z]/)) {
|
|
@@ -2789,8 +2805,10 @@ function App({ onMessage, onBackgroundCompletion, onCommand, onBashCommand, onPe
|
|
|
2789
2805
|
messageToSend = `${messageToSend}\n\n${errorBlock}`;
|
|
2790
2806
|
}
|
|
2791
2807
|
}
|
|
2792
|
-
|
|
2793
|
-
|
|
2808
|
+
const store = useCliStore.getState();
|
|
2809
|
+
if (store.isThinking || store.turnInFlight) {
|
|
2810
|
+
const kind = options?.followUp || !store.turnInFlight ? "followUp" : "steer";
|
|
2811
|
+
store.enqueueMessage(messageToSend, kind, trimmed);
|
|
2794
2812
|
return;
|
|
2795
2813
|
}
|
|
2796
2814
|
setIsThinking(true);
|
|
@@ -2888,7 +2906,9 @@ function App({ onMessage, onBackgroundCompletion, onCommand, onBashCommand, onPe
|
|
|
2888
2906
|
paddingX: 1,
|
|
2889
2907
|
marginBottom: 1
|
|
2890
2908
|
}, messageQueue.map((queuedMessage, idx) => {
|
|
2891
|
-
const
|
|
2909
|
+
const label = queuedMessage.kind === "followUp" ? "[next turn] " : "";
|
|
2910
|
+
const [firstLine, ...moreLines] = (queuedMessage.typed ?? queuedMessage.text).split("\n");
|
|
2911
|
+
const rowText = `\u276F ${label}${firstLine}${moreLines.length > 0 ? " ..." : ""}`;
|
|
2892
2912
|
const padded = rowText.length >= terminalCols - 2 ? rowText : rowText.padEnd(terminalCols - 2);
|
|
2893
2913
|
return /* @__PURE__ */ React.createElement(Text, {
|
|
2894
2914
|
key: idx,
|
|
@@ -2896,7 +2916,10 @@ function App({ onMessage, onBackgroundCompletion, onCommand, onBashCommand, onPe
|
|
|
2896
2916
|
color: "white",
|
|
2897
2917
|
wrap: "truncate-end"
|
|
2898
2918
|
}, padded);
|
|
2899
|
-
})
|
|
2919
|
+
}), /* @__PURE__ */ React.createElement(Text, {
|
|
2920
|
+
dimColor: true,
|
|
2921
|
+
wrap: "truncate-end"
|
|
2922
|
+
}, QUEUE_HINT)), /* @__PURE__ */ React.createElement(Box, {
|
|
2900
2923
|
borderStyle: "single",
|
|
2901
2924
|
borderColor: isBashMode ? "yellow" : "cyan",
|
|
2902
2925
|
borderTop: true,
|
|
@@ -3049,7 +3072,7 @@ function RewindSelector({ messages, onSelect, onCancel }) {
|
|
|
3049
3072
|
const items = messages.map((msg, idx) => ({
|
|
3050
3073
|
msg,
|
|
3051
3074
|
originalIndex: idx
|
|
3052
|
-
})).filter(({ msg }) => msg.role === "user").map(({ msg, originalIndex }, index) => {
|
|
3075
|
+
})).filter(({ msg }) => msg.role === "user" && !msg.metadata?.steered).map(({ msg, originalIndex }, index) => {
|
|
3053
3076
|
const preview = msg.content.length > 50 ? msg.content.substring(0, 50) + "..." : msg.content;
|
|
3054
3077
|
const timestamp = new Date(msg.timestamp).toLocaleString();
|
|
3055
3078
|
return {
|
|
@@ -8207,7 +8230,9 @@ Terminal Setup:
|
|
|
8207
8230
|
|
|
8208
8231
|
Keyboard Shortcuts:
|
|
8209
8232
|
Ctrl+C - Press twice to exit
|
|
8210
|
-
Esc - Abort current operation
|
|
8233
|
+
Esc - Abort current operation (queued messages return to the input)
|
|
8234
|
+
Enter (busy) - Steer the running turn with the message
|
|
8235
|
+
Tab (busy) - Queue the message to run as the next turn
|
|
8211
8236
|
Shift+Tab - Toggle auto-accept edits
|
|
8212
8237
|
Ctrl+U - Clear current line
|
|
8213
8238
|
Ctrl+K - Clear from cursor to end of line
|
|
@@ -8394,6 +8419,57 @@ async function handleSandboxTrustDomainCommand(deps, domains) {
|
|
|
8394
8419
|
return lines.join("\n");
|
|
8395
8420
|
}
|
|
8396
8421
|
//#endregion
|
|
8422
|
+
//#region src/session/steering.ts
|
|
8423
|
+
/**
|
|
8424
|
+
* Steering: user input submitted while a turn runs joins that turn at its next
|
|
8425
|
+
* model request instead of waiting for the turn to end.
|
|
8426
|
+
*
|
|
8427
|
+
* Delivery rides the agent's `drainFeedback` hook (see AgentRunOptions), which
|
|
8428
|
+
* fires after a tool batch resolves or when the model answers with iterations
|
|
8429
|
+
* left, and is never called once the run is aborted. A drain call is therefore
|
|
8430
|
+
* the delivery point: the steers leave the queue and enter the session in one
|
|
8431
|
+
* synchronous step, so each one is delivered exactly once - to the model and
|
|
8432
|
+
* the transcript together - or stays queued for the host to run as a follow-up
|
|
8433
|
+
* turn (turnController.ts) or restore to the input on cancel (index.tsx ESC).
|
|
8434
|
+
*/
|
|
8435
|
+
const STEER_PREAMBLE = "The user sent the following while you were working. Take it into account before you continue, and change course if it asks you to.";
|
|
8436
|
+
/** The feedback text the model sees for `steers`, or null when there are none. */
|
|
8437
|
+
function formatSteerFeedback(steers) {
|
|
8438
|
+
if (steers.length === 0) return null;
|
|
8439
|
+
return [STEER_PREAMBLE, ...steers].join("\n\n");
|
|
8440
|
+
}
|
|
8441
|
+
/**
|
|
8442
|
+
* Take every queued steer and record each as a user message on the session.
|
|
8443
|
+
* Returns the feedback for the agent, or null when nothing is queued or the
|
|
8444
|
+
* turn was cancelled (the steers then stay queued).
|
|
8445
|
+
*/
|
|
8446
|
+
function deliverSteers(signal) {
|
|
8447
|
+
if (signal.aborted) return null;
|
|
8448
|
+
const store = useCliStore.getState();
|
|
8449
|
+
const session = store.session;
|
|
8450
|
+
if (!session) return null;
|
|
8451
|
+
const steers = store.takeSteerMessages();
|
|
8452
|
+
const feedback = formatSteerFeedback(steers);
|
|
8453
|
+
if (!feedback) return null;
|
|
8454
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
8455
|
+
const steerMessages = steers.map((text) => ({
|
|
8456
|
+
id: v4(),
|
|
8457
|
+
role: "user",
|
|
8458
|
+
content: text,
|
|
8459
|
+
timestamp,
|
|
8460
|
+
metadata: { steered: true }
|
|
8461
|
+
}));
|
|
8462
|
+
store.setSession({
|
|
8463
|
+
...session,
|
|
8464
|
+
messages: [...session.messages, ...steerMessages],
|
|
8465
|
+
updatedAt: timestamp
|
|
8466
|
+
});
|
|
8467
|
+
return {
|
|
8468
|
+
feedback,
|
|
8469
|
+
steers
|
|
8470
|
+
};
|
|
8471
|
+
}
|
|
8472
|
+
//#endregion
|
|
8397
8473
|
//#region src/session/turnController.ts
|
|
8398
8474
|
/**
|
|
8399
8475
|
* Turn lifecycle (issue #228, phase 2).
|
|
@@ -8407,16 +8483,53 @@ async function handleSandboxTrustDomainCommand(deps, domains) {
|
|
|
8407
8483
|
* keeps only a thin wrapper that builds the context from its render state.
|
|
8408
8484
|
*/
|
|
8409
8485
|
/**
|
|
8410
|
-
* Run one user turn to completion
|
|
8411
|
-
* errors are handled and surfaced to the
|
|
8412
|
-
*
|
|
8486
|
+
* Run one user turn to completion, then each follow-up queued behind it. Never
|
|
8487
|
+
* throws: transport, auth, and abort errors are handled and surfaced to the
|
|
8488
|
+
* console.
|
|
8489
|
+
*
|
|
8490
|
+
* Only one runTurn turn is in flight at a time. A call that arrives while one
|
|
8491
|
+
* is (a tavern prompt, a bash-command drain, a submit during an ESC unwind) is
|
|
8492
|
+
* queued as a follow-up rather than run as a second agent loop over the same
|
|
8493
|
+
* agent. Other agent runs (custom commands, background results) gate on
|
|
8494
|
+
* `turnInFlight` themselves. After
|
|
8495
|
+
* each turn settles - completed, failed, or cancelled - everything still queued
|
|
8496
|
+
* runs as one combined next turn: follow-ups, steers that never reached a
|
|
8497
|
+
* request boundary, and input submitted after a cancel (ESC moves what was
|
|
8498
|
+
* queued before it back to the input, see index.tsx).
|
|
8413
8499
|
*/
|
|
8414
8500
|
async function runTurn(message, ctx) {
|
|
8415
|
-
const
|
|
8501
|
+
const store = useCliStore.getState();
|
|
8502
|
+
if (store.turnInFlight) {
|
|
8503
|
+
store.enqueueMessage(message, "followUp");
|
|
8504
|
+
return;
|
|
8505
|
+
}
|
|
8506
|
+
store.setTurnInFlight(true);
|
|
8416
8507
|
try {
|
|
8417
|
-
|
|
8508
|
+
let next = message;
|
|
8509
|
+
while (next !== null) {
|
|
8510
|
+
const releaseSession = useCliStore.getState().holdSession();
|
|
8511
|
+
try {
|
|
8512
|
+
await runHeldTurn(next, ctx);
|
|
8513
|
+
} catch (error) {
|
|
8514
|
+
console.error(`\n\u274C ${error instanceof Error ? error.message : String(error)}\n`);
|
|
8515
|
+
logger.debug(`Turn failed outside its error handling: ${error instanceof Error ? error.stack : String(error)}`);
|
|
8516
|
+
} finally {
|
|
8517
|
+
releaseSession();
|
|
8518
|
+
}
|
|
8519
|
+
const queued = useCliStore.getState().dequeueAllMessages();
|
|
8520
|
+
next = queued.length > 0 ? queued.join("\n\n") : null;
|
|
8521
|
+
}
|
|
8418
8522
|
} finally {
|
|
8419
|
-
|
|
8523
|
+
useCliStore.getState().setTurnInFlight(false);
|
|
8524
|
+
}
|
|
8525
|
+
}
|
|
8526
|
+
/** Not awaited by the steer drain, so a failed history write is logged rather than left as an unhandled rejection. */
|
|
8527
|
+
async function recordSteerHistory(steers, commandHistoryStore, setCommandHistory) {
|
|
8528
|
+
try {
|
|
8529
|
+
for (const text of steers) await commandHistoryStore.add(text);
|
|
8530
|
+
setCommandHistory(await commandHistoryStore.list());
|
|
8531
|
+
} catch (error) {
|
|
8532
|
+
logger.debug(`[STEER] History write failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
8420
8533
|
}
|
|
8421
8534
|
}
|
|
8422
8535
|
async function runHeldTurn(message, ctx) {
|
|
@@ -8540,7 +8653,19 @@ async function runHeldTurn(message, ctx) {
|
|
|
8540
8653
|
parallelExecution: cliConfig.preferences.enableParallelToolExecution === true,
|
|
8541
8654
|
isReadOnlyTool: (toolName) => toolCatalog.isReadOnly(toolName),
|
|
8542
8655
|
workflowReminder,
|
|
8543
|
-
drainFeedback:
|
|
8656
|
+
drainFeedback: async (phase) => {
|
|
8657
|
+
const diagnosticsFeedback = diagnostics ? await diagnostics.drain(DRAIN_BUDGET_MS[phase], abortController.signal) : null;
|
|
8658
|
+
const steered = deliverSteers(abortController.signal);
|
|
8659
|
+
if (steered) {
|
|
8660
|
+
for (const text of steered.steers) bridgePresence.emitEvent({
|
|
8661
|
+
type: "message",
|
|
8662
|
+
role: "user",
|
|
8663
|
+
text: text.slice(0, 4e3)
|
|
8664
|
+
});
|
|
8665
|
+
recordSteerHistory(steered.steers, commandHistoryStore, setCommandHistory);
|
|
8666
|
+
}
|
|
8667
|
+
return [steered?.feedback, diagnosticsFeedback].filter(Boolean).join("\n\n") || null;
|
|
8668
|
+
},
|
|
8544
8669
|
onContextLimit: createReactiveCompactionHandler(agent, activeSession)
|
|
8545
8670
|
});
|
|
8546
8671
|
} finally {
|
|
@@ -8635,15 +8760,6 @@ async function runHeldTurn(message, ctx) {
|
|
|
8635
8760
|
type: "status",
|
|
8636
8761
|
status: wasAborted ? "idle" : "awaiting_input"
|
|
8637
8762
|
});
|
|
8638
|
-
if (!wasAborted) {
|
|
8639
|
-
const queued = useCliStore.getState().dequeueAllMessages();
|
|
8640
|
-
if (queued.length > 0) {
|
|
8641
|
-
const combined = queued.join("\n\n");
|
|
8642
|
-
setImmediate(() => {
|
|
8643
|
-
runTurn(combined, ctx);
|
|
8644
|
-
});
|
|
8645
|
-
}
|
|
8646
|
-
}
|
|
8647
8763
|
}
|
|
8648
8764
|
}
|
|
8649
8765
|
//#endregion
|
|
@@ -9064,6 +9180,7 @@ function CliApp() {
|
|
|
9064
9180
|
store.clearPaste();
|
|
9065
9181
|
return;
|
|
9066
9182
|
}
|
|
9183
|
+
store.restoreQueueToInput();
|
|
9067
9184
|
if (state.abortController) {
|
|
9068
9185
|
logger.debug("[ABORT] ESC pressed - aborting current operation...");
|
|
9069
9186
|
state.abortController.abort();
|
|
@@ -9071,9 +9188,8 @@ function CliApp() {
|
|
|
9071
9188
|
...prev,
|
|
9072
9189
|
abortController: null
|
|
9073
9190
|
}));
|
|
9074
|
-
|
|
9075
|
-
|
|
9076
|
-
} else useCliStore.getState().clearMessageQueue();
|
|
9191
|
+
store.setIsThinking(false);
|
|
9192
|
+
}
|
|
9077
9193
|
return;
|
|
9078
9194
|
}
|
|
9079
9195
|
if (key.ctrl && input === "c") {
|
|
@@ -9350,9 +9466,17 @@ function CliApp() {
|
|
|
9350
9466
|
const dynamicAgentTool = config.preferences.enableDynamicAgentCreation === true ? createDynamicAgentTool(orchestrator, newSession.id, backgroundManager) : null;
|
|
9351
9467
|
const backgroundTools = createBackgroundAgentTools(backgroundManager);
|
|
9352
9468
|
const resumeAgentTool = createResumeAgentTool(orchestrator, historyStore, backgroundManager);
|
|
9353
|
-
const
|
|
9469
|
+
const agentToolsRef = { current: null };
|
|
9470
|
+
const { llm: toolSearchLlm, toolSearchTool } = buildToolSearch({
|
|
9471
|
+
llm,
|
|
9472
|
+
models,
|
|
9473
|
+
registry: deferredToolRegistry,
|
|
9474
|
+
logger,
|
|
9475
|
+
agentToolsRef
|
|
9476
|
+
});
|
|
9477
|
+
const llmWithFallback = config.fallbackModels && config.fallbackModels.length > 0 ? new FallbackLlmBackend(toolSearchLlm, config.fallbackModels, (fromModel, toModel, error) => {
|
|
9354
9478
|
logger.warn(`⚠️ Model "${fromModel}" failed (${error.message}) — falling back to "${toModel}"`);
|
|
9355
|
-
}) :
|
|
9479
|
+
}) : toolSearchLlm;
|
|
9356
9480
|
const notifyingLlm = new NotifyingLlmBackend(llmWithFallback, backgroundManager);
|
|
9357
9481
|
const todoStore = todoStoreRef.current;
|
|
9358
9482
|
todoStore.todos = [];
|
|
@@ -9390,15 +9514,6 @@ function CliApp() {
|
|
|
9390
9514
|
allowedDirectories: additionalDirectories
|
|
9391
9515
|
}) : null;
|
|
9392
9516
|
const workItemTools = config.preferences.enableWorkItemTools ? createWorkItemTools(new WorkItemsClient(apiClient)) : [];
|
|
9393
|
-
const agentToolsRef = { current: null };
|
|
9394
|
-
const toolSearchTool = deferredToolRegistry.size() > 0 ? createToolSearchTool({
|
|
9395
|
-
registry: deferredToolRegistry,
|
|
9396
|
-
logger,
|
|
9397
|
-
toolListAccessor: () => {
|
|
9398
|
-
if (!agentToolsRef.current) throw new Error("tool_search invoked before agent context was wired");
|
|
9399
|
-
return agentToolsRef.current;
|
|
9400
|
-
}
|
|
9401
|
-
}) : null;
|
|
9402
9517
|
const cliTools = buildCliToolList({
|
|
9403
9518
|
toolSearchTool,
|
|
9404
9519
|
agentDelegateTool,
|
|
@@ -9421,7 +9536,11 @@ function CliApp() {
|
|
|
9421
9536
|
descriptors: pluginDescriptors,
|
|
9422
9537
|
config,
|
|
9423
9538
|
logger,
|
|
9424
|
-
reservedToolNames: [
|
|
9539
|
+
reservedToolNames: [
|
|
9540
|
+
...[...loadedB4mTools, ...cliTools].map((t) => t.toolSchema.name),
|
|
9541
|
+
...deferredToolRegistry.getDirectoryNames(),
|
|
9542
|
+
"tool_search"
|
|
9543
|
+
]
|
|
9425
9544
|
});
|
|
9426
9545
|
rewireReservedNames(state, featureRegistry);
|
|
9427
9546
|
if (wsManager && featureRegistry.hasModules) featureRegistry.registerAllWsHandlers(wsManager);
|
|
@@ -11351,7 +11470,11 @@ function CliApp() {
|
|
|
11351
11470
|
descriptors: state.pluginDescriptors,
|
|
11352
11471
|
config: updatedConfig,
|
|
11353
11472
|
logger,
|
|
11354
|
-
reservedToolNames:
|
|
11473
|
+
reservedToolNames: [
|
|
11474
|
+
...baseTools.map((t) => t.toolSchema.name),
|
|
11475
|
+
...deferredToolRegistry.getDirectoryNames(),
|
|
11476
|
+
"tool_search"
|
|
11477
|
+
]
|
|
11355
11478
|
});
|
|
11356
11479
|
const rebuiltRegistry = rebuilt.registry;
|
|
11357
11480
|
newFeatureRegistry = rebuiltRegistry;
|