@bike4mind/cli 0.6.1 → 0.7.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/dist/{ConfigStore-Dt6utdSA.mjs → ConfigStore-Bu_plzvP.mjs} +12 -3
- package/dist/commands/doctorCommand.mjs +1 -1
- package/dist/commands/headlessCommand.mjs +3 -3
- package/dist/commands/mcpCommand.mjs +1 -1
- package/dist/commands/updateCommand.mjs +1 -1
- package/dist/index.mjs +205 -18
- package/dist/store-D2zh6DFz.mjs +3 -0
- package/dist/{store-B7-LLvvx.mjs → store-FkY4K-0M.mjs} +13 -0
- package/dist/{tools-BUs_OkJc.mjs → tools-DY-JzNoY.mjs} +290 -24
- package/dist/{updateChecker-D-xoVcN3.mjs → updateChecker-Dn-Ri8zw.mjs} +1 -1
- package/package.json +6 -5
- package/dist/store-B_ILRSdP.mjs +0 -3
|
@@ -8829,7 +8829,14 @@ const CliConfigSchema = z.object({
|
|
|
8829
8829
|
maxIterations: z.number().nullable().prefault(10),
|
|
8830
8830
|
enableSkillTool: z.boolean().optional().prefault(true),
|
|
8831
8831
|
enableDynamicAgentCreation: z.boolean().optional().prefault(false),
|
|
8832
|
-
enableCoordinatorMode: z.boolean().optional().prefault(false)
|
|
8832
|
+
enableCoordinatorMode: z.boolean().optional().prefault(false),
|
|
8833
|
+
/**
|
|
8834
|
+
* System-prompt variant. 'current' uses the elaborate behavioral-scaffolding
|
|
8835
|
+
* prompt; 'minimal' uses a pi-style short prompt. See apps/cli/src/core/prompts.ts.
|
|
8836
|
+
* Defaults to 'current' for backward compatibility; switch via /config or by
|
|
8837
|
+
* editing the config file directly.
|
|
8838
|
+
*/
|
|
8839
|
+
promptVariant: z.enum(["current", "minimal"]).optional().prefault("current")
|
|
8833
8840
|
}),
|
|
8834
8841
|
tools: z.object({
|
|
8835
8842
|
enabled: z.array(z.string()),
|
|
@@ -8862,7 +8869,8 @@ const ProjectConfigSchema = z.object({
|
|
|
8862
8869
|
exportFormat: z.enum(["markdown", "json"]).optional(),
|
|
8863
8870
|
enableSkillTool: z.boolean().optional(),
|
|
8864
8871
|
enableDynamicAgentCreation: z.boolean().optional(),
|
|
8865
|
-
enableCoordinatorMode: z.boolean().optional()
|
|
8872
|
+
enableCoordinatorMode: z.boolean().optional(),
|
|
8873
|
+
promptVariant: z.enum(["current", "minimal"]).optional()
|
|
8866
8874
|
}).optional(),
|
|
8867
8875
|
sandbox: PartialSandboxConfigSchema,
|
|
8868
8876
|
additionalDirectories: z.array(z.string()).optional()
|
|
@@ -8912,7 +8920,8 @@ const DEFAULT_CONFIG = {
|
|
|
8912
8920
|
maxIterations: 10,
|
|
8913
8921
|
enableSkillTool: true,
|
|
8914
8922
|
enableDynamicAgentCreation: false,
|
|
8915
|
-
enableCoordinatorMode: false
|
|
8923
|
+
enableCoordinatorMode: false,
|
|
8924
|
+
promptVariant: "current"
|
|
8916
8925
|
},
|
|
8917
8926
|
tools: {
|
|
8918
8927
|
enabled: [],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as version, n as compareSemver, r as fetchLatestVersion } from "../updateChecker-
|
|
2
|
+
import { a as version, n as compareSemver, r as fetchLatestVersion } from "../updateChecker-Dn-Ri8zw.mjs";
|
|
3
3
|
import { execSync } from "child_process";
|
|
4
4
|
import { constants, existsSync, promises } from "fs";
|
|
5
5
|
import { homedir } from "os";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { n as logger, t as ConfigStore } from "../ConfigStore-
|
|
2
|
+
import { $ as SessionStore, C as WebSocketToolExecutor, D as ServerLlmBackend, E as WebSocketLlmBackend, F as PermissionManager, I as generateCliTools, J as isReadOnlyTool, K as buildSystemPrompt, M as loadContextFiles, N as getApiUrl, O as McpManager, S as ApiClient, T as FallbackLlmBackend, W as setWebSocketToolExecutor, X as CustomCommandStore, Y as ReActAgent, Z as CheckpointStore, _ as createAgentDelegateTool, b as createSkillTool, d as createFindDefinitionTool, f as createTodoStore, g as BackgroundAgentManager, h as createBackgroundAgentTools, m as createCoordinateTaskTool, p as createWriteTodosTool, u as createGetFileStructureTool, v as AgentStore, w as WebSocketConnectionManager, y as SubagentOrchestrator } from "../tools-DY-JzNoY.mjs";
|
|
3
|
+
import { n as logger, t as ConfigStore } from "../ConfigStore-Bu_plzvP.mjs";
|
|
4
4
|
import { t as DEFAULT_SANDBOX_CONFIG } from "../types-DBEjF9YS.mjs";
|
|
5
5
|
import { t as createSandboxRuntime } from "../SandboxRuntimeAdapter-C1B4t20N.mjs";
|
|
6
6
|
import { t as SandboxOrchestrator } from "../SandboxOrchestrator-BEW3rqYi.mjs";
|
|
@@ -193,7 +193,7 @@ async function handleHeadlessCommand(options) {
|
|
|
193
193
|
...mcpTools,
|
|
194
194
|
...cliTools
|
|
195
195
|
];
|
|
196
|
-
const systemPrompt =
|
|
196
|
+
const systemPrompt = buildSystemPrompt(config.preferences.promptVariant ?? "current", {
|
|
197
197
|
contextContent: contextResult.mergedContent,
|
|
198
198
|
agentStore,
|
|
199
199
|
customCommands: customCommandStore.getAllCommands(),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as version, i as forceCheckForUpdate } from "../updateChecker-
|
|
2
|
+
import { a as version, i as forceCheckForUpdate } from "../updateChecker-Dn-Ri8zw.mjs";
|
|
3
3
|
import { execSync } from "child_process";
|
|
4
4
|
//#region src/commands/updateCommand.ts
|
|
5
5
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as useCliStore, t as selectActiveBackgroundAgents } from "./store-
|
|
3
|
-
import { $ as
|
|
4
|
-
import { Mt as validateNotebookPath$1, g as ChatModels, jt as validateJupyterKernelName, m as CREDIT_DEDUCT_TRANSACTION_TYPES, n as logger, t as ConfigStore } from "./ConfigStore-
|
|
5
|
-
import { a as version, t as checkForUpdate } from "./updateChecker-
|
|
2
|
+
import { n as useCliStore, t as selectActiveBackgroundAgents } from "./store-FkY4K-0M.mjs";
|
|
3
|
+
import { $ as SessionStore, A as formatStep, B as DEFAULT_RETRY_CONFIG, C as WebSocketToolExecutor, D as ServerLlmBackend, E as WebSocketLlmBackend, F as PermissionManager, G as OllamaBackend, H as clearFeatureModuleTools, I as generateCliTools, J as isReadOnlyTool, K as buildSystemPrompt, L as ALWAYS_DENIED_FOR_AGENTS, M as loadContextFiles, N as getApiUrl, O as McpManager, P as getEnvironmentName, Q as CommandHistoryStore, R as DEFAULT_AGENT_MODEL, S as ApiClient, T as FallbackLlmBackend, U as registerFeatureModuleTools, V as DEFAULT_THOROUGHNESS, W as setWebSocketToolExecutor, X as CustomCommandStore, Y as ReActAgent, Z as CheckpointStore, _ as createAgentDelegateTool, a as createBlockerTools, at as formatFileSize, b as createSkillTool, c as createDecisionStore, d as createFindDefinitionTool, et as OAuthClient, f as createTodoStore, g as BackgroundAgentManager, h as createBackgroundAgentTools, i as createBlockerStore, it as mergeCommands, j as extractCompactInstructions, k as substituteArguments, l as formatDecisionsOutput, m as createCoordinateTaskTool, n as createReviewGateTool, nt as processFileReferences, o as formatBlockersOutput, ot as searchFiles, p as createWriteTodosTool, q as buildSkillsPromptSection, r as formatReviewGatesOutput, rt as searchCommands, s as createDecisionLogTool, st as warmFileCache, t as createReviewGateStore, tt as hasFileReferences, u as createGetFileStructureTool, v as AgentStore, w as WebSocketConnectionManager, x as parseAgentConfig, y as SubagentOrchestrator, z as DEFAULT_MAX_ITERATIONS } from "./tools-DY-JzNoY.mjs";
|
|
4
|
+
import { Mt as validateNotebookPath$1, g as ChatModels, jt as validateJupyterKernelName, m as CREDIT_DEDUCT_TRANSACTION_TYPES, n as logger, t as ConfigStore } from "./ConfigStore-Bu_plzvP.mjs";
|
|
5
|
+
import { a as version, t as checkForUpdate } from "./updateChecker-Dn-Ri8zw.mjs";
|
|
6
6
|
import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react";
|
|
7
7
|
import { Box, Static, Text, render, useApp, useInput } from "ink";
|
|
8
8
|
import { execSync } from "child_process";
|
|
@@ -1274,6 +1274,138 @@ function UserQuestionPrompt({ payload, onResponse }) {
|
|
|
1274
1274
|
})), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, isOnOther ? "Type your answer, Enter to submit, or arrow keys to go back" : isMulti ? "Press 1-" + options.length + ", Space to toggle, Enter to confirm" : "Press 1-" + options.length + ", or arrow keys + Enter")));
|
|
1275
1275
|
}
|
|
1276
1276
|
//#endregion
|
|
1277
|
+
//#region src/components/ReviewGatePrompt.tsx
|
|
1278
|
+
const ITEMS = [
|
|
1279
|
+
{
|
|
1280
|
+
label: "✓ Approve",
|
|
1281
|
+
decision: "approved",
|
|
1282
|
+
withNote: false
|
|
1283
|
+
},
|
|
1284
|
+
{
|
|
1285
|
+
label: "✓ Approve with note...",
|
|
1286
|
+
decision: "approved",
|
|
1287
|
+
withNote: true
|
|
1288
|
+
},
|
|
1289
|
+
{
|
|
1290
|
+
label: "✗ Reject",
|
|
1291
|
+
decision: "rejected",
|
|
1292
|
+
withNote: false
|
|
1293
|
+
},
|
|
1294
|
+
{
|
|
1295
|
+
label: "✗ Reject with note...",
|
|
1296
|
+
decision: "rejected",
|
|
1297
|
+
withNote: true
|
|
1298
|
+
}
|
|
1299
|
+
];
|
|
1300
|
+
/**
|
|
1301
|
+
* Review gate prompt component.
|
|
1302
|
+
*
|
|
1303
|
+
* Pauses the agent and asks the user to explicitly approve or reject a
|
|
1304
|
+
* significant decision. An optional free-text note can be attached to either
|
|
1305
|
+
* decision via the dedicated "...with note..." actions.
|
|
1306
|
+
*
|
|
1307
|
+
* Keyboard:
|
|
1308
|
+
* - 1–4: shortcut for each action
|
|
1309
|
+
* - y: Approve (no note); n: Reject (no note)
|
|
1310
|
+
* - ↑/↓: navigate; Enter: confirm selection
|
|
1311
|
+
* - When a "with note..." action is selected, an inline text input appears.
|
|
1312
|
+
* Type the note and press Enter to submit. Use ↑/↓ to switch to a no-note
|
|
1313
|
+
* action without losing the typed note.
|
|
1314
|
+
*/
|
|
1315
|
+
function ReviewGatePrompt({ description, options, recommendation, onResponse }) {
|
|
1316
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
1317
|
+
const [note, setNote] = useState("");
|
|
1318
|
+
const [responded, setResponded] = useState(false);
|
|
1319
|
+
const submit = useCallback((item, noteText) => {
|
|
1320
|
+
if (responded) return;
|
|
1321
|
+
setResponded(true);
|
|
1322
|
+
const trimmed = noteText.trim();
|
|
1323
|
+
onResponse({
|
|
1324
|
+
decision: item.decision,
|
|
1325
|
+
note: trimmed.length > 0 ? trimmed : void 0
|
|
1326
|
+
});
|
|
1327
|
+
}, [responded, onResponse]);
|
|
1328
|
+
const selectedItem = ITEMS[selectedIndex];
|
|
1329
|
+
const noteMode = selectedItem.withNote;
|
|
1330
|
+
const handleConfirm = useCallback(() => {
|
|
1331
|
+
if (selectedItem.withNote && note.trim().length === 0) return;
|
|
1332
|
+
submit(selectedItem, note);
|
|
1333
|
+
}, [
|
|
1334
|
+
selectedItem,
|
|
1335
|
+
note,
|
|
1336
|
+
submit
|
|
1337
|
+
]);
|
|
1338
|
+
useInput((input, key) => {
|
|
1339
|
+
if (responded) return;
|
|
1340
|
+
if (key.upArrow) {
|
|
1341
|
+
setSelectedIndex((i) => i > 0 ? i - 1 : ITEMS.length - 1);
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
if (key.downArrow) {
|
|
1345
|
+
setSelectedIndex((i) => i < ITEMS.length - 1 ? i + 1 : 0);
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
if (noteMode) return;
|
|
1349
|
+
const num = parseInt(input, 10);
|
|
1350
|
+
if (num >= 1 && num <= ITEMS.length) {
|
|
1351
|
+
const item = ITEMS[num - 1];
|
|
1352
|
+
setSelectedIndex(num - 1);
|
|
1353
|
+
if (!item.withNote) submit(item, "");
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
if (input.toLowerCase() === "y") {
|
|
1357
|
+
submit(ITEMS[0], "");
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
if (input.toLowerCase() === "n") {
|
|
1361
|
+
submit(ITEMS[2], "");
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
if (key.return) handleConfirm();
|
|
1365
|
+
}, { isActive: !responded });
|
|
1366
|
+
if (responded) return null;
|
|
1367
|
+
return /* @__PURE__ */ React.createElement(Box, {
|
|
1368
|
+
flexDirection: "column",
|
|
1369
|
+
borderStyle: "bold",
|
|
1370
|
+
borderColor: "magenta",
|
|
1371
|
+
padding: 1,
|
|
1372
|
+
marginY: 1
|
|
1373
|
+
}, /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, {
|
|
1374
|
+
bold: true,
|
|
1375
|
+
color: "magenta"
|
|
1376
|
+
}, "🛑 Review Gate")), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, description)), recommendation && /* @__PURE__ */ React.createElement(Box, {
|
|
1377
|
+
marginTop: 1,
|
|
1378
|
+
flexDirection: "column"
|
|
1379
|
+
}, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Recommendation:"), /* @__PURE__ */ React.createElement(Box, { paddingLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, recommendation))), options && options.length > 0 && /* @__PURE__ */ React.createElement(Box, {
|
|
1380
|
+
marginTop: 1,
|
|
1381
|
+
flexDirection: "column"
|
|
1382
|
+
}, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Options:"), /* @__PURE__ */ React.createElement(Box, {
|
|
1383
|
+
paddingLeft: 2,
|
|
1384
|
+
flexDirection: "column"
|
|
1385
|
+
}, options.map((opt, idx) => /* @__PURE__ */ React.createElement(Text, {
|
|
1386
|
+
key: idx,
|
|
1387
|
+
dimColor: true
|
|
1388
|
+
}, "• ", opt)))), /* @__PURE__ */ React.createElement(Box, {
|
|
1389
|
+
marginTop: 1,
|
|
1390
|
+
flexDirection: "column"
|
|
1391
|
+
}, ITEMS.map((item, index) => {
|
|
1392
|
+
const isHighlighted = index === selectedIndex;
|
|
1393
|
+
const color = item.decision === "approved" ? "green" : "red";
|
|
1394
|
+
return /* @__PURE__ */ React.createElement(Box, { key: item.label }, /* @__PURE__ */ React.createElement(Text, { color: "cyan" }, index + 1, "."), /* @__PURE__ */ React.createElement(Text, {
|
|
1395
|
+
color: isHighlighted ? color : void 0,
|
|
1396
|
+
bold: isHighlighted
|
|
1397
|
+
}, isHighlighted ? " ❯ " : " ", item.label));
|
|
1398
|
+
})), noteMode && /* @__PURE__ */ React.createElement(Box, {
|
|
1399
|
+
marginTop: 1,
|
|
1400
|
+
flexDirection: "column"
|
|
1401
|
+
}, /* @__PURE__ */ React.createElement(Text, { bold: true }, "Note:"), /* @__PURE__ */ React.createElement(Box, { paddingLeft: 2 }, /* @__PURE__ */ React.createElement(TextInput, {
|
|
1402
|
+
value: note,
|
|
1403
|
+
onChange: setNote,
|
|
1404
|
+
onSubmit: handleConfirm,
|
|
1405
|
+
placeholder: "Type a note and press Enter…"
|
|
1406
|
+
}))), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, noteMode ? "Type note + Enter to submit, ↑↓ to switch action" : "Press 1-4, y/n, or ↑↓ + Enter")));
|
|
1407
|
+
}
|
|
1408
|
+
//#endregion
|
|
1277
1409
|
//#region src/components/ConfigEditor.tsx
|
|
1278
1410
|
/**
|
|
1279
1411
|
* Max iterations options: 10, 20, 30, 40, 50, Infinite (null)
|
|
@@ -1886,7 +2018,7 @@ const MessageItem = React.memo(function MessageItem({ message }) {
|
|
|
1886
2018
|
});
|
|
1887
2019
|
//#endregion
|
|
1888
2020
|
//#region src/components/App.tsx
|
|
1889
|
-
function App({ onMessage, onBackgroundCompletion, onCommand, onBashCommand, onPermissionResponse, onUserQuestionResponse, onImageDetected, commandHistory = [], commands = [], config, availableModels = [], onSaveConfig, prefillInput, onPrefillConsumed, mcpManager }) {
|
|
2021
|
+
function App({ onMessage, onBackgroundCompletion, onCommand, onBashCommand, onPermissionResponse, onUserQuestionResponse, onReviewGateResponse, onImageDetected, commandHistory = [], commands = [], config, availableModels = [], onSaveConfig, prefillInput, onPrefillConsumed, mcpManager }) {
|
|
1890
2022
|
const messages = useCliStore((state) => state.session?.messages || []);
|
|
1891
2023
|
const pendingMessages = useCliStore((state) => state.pendingMessages);
|
|
1892
2024
|
const sessionName = useCliStore((state) => state.session?.name || "New Session");
|
|
@@ -1896,6 +2028,7 @@ function App({ onMessage, onBackgroundCompletion, onCommand, onBashCommand, onPe
|
|
|
1896
2028
|
const isThinking = useCliStore((state) => state.isThinking);
|
|
1897
2029
|
const permissionPrompt = useCliStore((state) => state.permissionPrompt);
|
|
1898
2030
|
const userQuestionPrompt = useCliStore((state) => state.userQuestionPrompt);
|
|
2031
|
+
const reviewGatePrompt = useCliStore((state) => state.reviewGatePrompt);
|
|
1899
2032
|
const showConfigEditor = useCliStore((state) => state.showConfigEditor);
|
|
1900
2033
|
const setShowConfigEditor = useCliStore((state) => state.setShowConfigEditor);
|
|
1901
2034
|
const showMcpViewer = useCliStore((state) => state.showMcpViewer);
|
|
@@ -1988,7 +2121,16 @@ function App({ onMessage, onBackgroundCompletion, onCommand, onBashCommand, onPe
|
|
|
1988
2121
|
}, /* @__PURE__ */ React.createElement(UserQuestionPrompt, {
|
|
1989
2122
|
payload: userQuestionPrompt.payload,
|
|
1990
2123
|
onResponse: (response) => onUserQuestionResponse(response, userQuestionPrompt.id)
|
|
1991
|
-
})),
|
|
2124
|
+
})), reviewGatePrompt && /* @__PURE__ */ React.createElement(Box, {
|
|
2125
|
+
key: reviewGatePrompt.id,
|
|
2126
|
+
flexDirection: "column",
|
|
2127
|
+
paddingX: 1
|
|
2128
|
+
}, /* @__PURE__ */ React.createElement(ReviewGatePrompt, {
|
|
2129
|
+
description: reviewGatePrompt.description,
|
|
2130
|
+
options: reviewGatePrompt.options,
|
|
2131
|
+
recommendation: reviewGatePrompt.recommendation,
|
|
2132
|
+
onResponse: (response) => onReviewGateResponse(response, reviewGatePrompt.id)
|
|
2133
|
+
})), !permissionPrompt && !userQuestionPrompt && !reviewGatePrompt && /* @__PURE__ */ React.createElement(AgentThinking, null), /* @__PURE__ */ React.createElement(BackgroundAgentStatus, null), /* @__PURE__ */ React.createElement(CompletedGroupNotification, null), exitRequested && /* @__PURE__ */ React.createElement(Box, {
|
|
1992
2134
|
paddingX: 1,
|
|
1993
2135
|
marginBottom: 1
|
|
1994
2136
|
}, /* @__PURE__ */ React.createElement(Text, {
|
|
@@ -2002,7 +2144,7 @@ function App({ onMessage, onBackgroundCompletion, onCommand, onBashCommand, onPe
|
|
|
2002
2144
|
onSubmit: handleSubmit,
|
|
2003
2145
|
onBashCommand,
|
|
2004
2146
|
onImageDetected,
|
|
2005
|
-
disabled: isThinking || !!permissionPrompt || !!userQuestionPrompt,
|
|
2147
|
+
disabled: isThinking || !!permissionPrompt || !!userQuestionPrompt || !!reviewGatePrompt,
|
|
2006
2148
|
history: commandHistory,
|
|
2007
2149
|
commands,
|
|
2008
2150
|
prefillInput,
|
|
@@ -2469,7 +2611,7 @@ function buildCompactionPrompt(messages, options = {}) {
|
|
|
2469
2611
|
function createCompactedSession(originalSession, summary, preservedMessages) {
|
|
2470
2612
|
const summaryMessage = {
|
|
2471
2613
|
id: v4(),
|
|
2472
|
-
role: "
|
|
2614
|
+
role: "user",
|
|
2473
2615
|
content: `[Previous conversation summary]\n\n${summary}`,
|
|
2474
2616
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2475
2617
|
};
|
|
@@ -5235,10 +5377,13 @@ function CliApp() {
|
|
|
5235
5377
|
const imageStoreInitPromise = useRef(null);
|
|
5236
5378
|
const decisionStoreRef = useRef(createDecisionStore());
|
|
5237
5379
|
const blockerStoreRef = useRef(createBlockerStore());
|
|
5380
|
+
const reviewGateStoreRef = useRef(createReviewGateStore());
|
|
5238
5381
|
const setStoreSession = useCliStore((state) => state.setSession);
|
|
5239
5382
|
const enqueuePermissionPrompt = useCliStore((state) => state.enqueuePermissionPrompt);
|
|
5240
5383
|
const enqueueUserQuestionPrompt = useCliStore((state) => state.enqueueUserQuestionPrompt);
|
|
5241
5384
|
const dequeueUserQuestionPrompt = useCliStore((state) => state.dequeueUserQuestionPrompt);
|
|
5385
|
+
const enqueueReviewGatePrompt = useCliStore((state) => state.enqueueReviewGatePrompt);
|
|
5386
|
+
const dequeueReviewGatePrompt = useCliStore((state) => state.dequeueReviewGatePrompt);
|
|
5242
5387
|
const setShowConfigEditor = useCliStore((state) => state.setShowConfigEditor);
|
|
5243
5388
|
const setShowMcpViewer = useCliStore((state) => state.setShowMcpViewer);
|
|
5244
5389
|
const setExitRequested = useCliStore((state) => state.setExitRequested);
|
|
@@ -5628,6 +5773,22 @@ function CliApp() {
|
|
|
5628
5773
|
});
|
|
5629
5774
|
});
|
|
5630
5775
|
};
|
|
5776
|
+
const reviewGateFn = (params) => {
|
|
5777
|
+
return new Promise((resolve) => {
|
|
5778
|
+
enqueueReviewGatePrompt({
|
|
5779
|
+
id: params.id,
|
|
5780
|
+
description: params.description,
|
|
5781
|
+
options: params.options,
|
|
5782
|
+
recommendation: params.recommendation,
|
|
5783
|
+
resolve
|
|
5784
|
+
});
|
|
5785
|
+
bridgePresence.emitEvent({
|
|
5786
|
+
type: "status",
|
|
5787
|
+
status: "awaiting_input",
|
|
5788
|
+
text: `Review gate: ${params.description}`.slice(0, 240)
|
|
5789
|
+
});
|
|
5790
|
+
});
|
|
5791
|
+
};
|
|
5631
5792
|
const agentContext = {
|
|
5632
5793
|
currentAgent: null,
|
|
5633
5794
|
observationQueue: []
|
|
@@ -5681,12 +5842,15 @@ function CliApp() {
|
|
|
5681
5842
|
const writeTodosTool = createWriteTodosTool(createTodoStore());
|
|
5682
5843
|
const decisionLogTool = createDecisionLogTool(decisionStoreRef.current);
|
|
5683
5844
|
const blockerTools = createBlockerTools(blockerStoreRef.current);
|
|
5845
|
+
const reviewGateTool = createReviewGateTool(reviewGateStoreRef.current, reviewGateFn);
|
|
5684
5846
|
if (newSession.metadata.workflow) {
|
|
5685
5847
|
decisionStoreRef.current.decisions = [...newSession.metadata.workflow.decisions];
|
|
5686
5848
|
blockerStoreRef.current.blockers = [...newSession.metadata.workflow.blockers];
|
|
5849
|
+
reviewGateStoreRef.current.reviewGates = [...newSession.metadata.workflow.reviewGates ?? []];
|
|
5687
5850
|
} else {
|
|
5688
5851
|
decisionStoreRef.current.decisions = [];
|
|
5689
5852
|
blockerStoreRef.current.blockers = [];
|
|
5853
|
+
reviewGateStoreRef.current.reviewGates = [];
|
|
5690
5854
|
}
|
|
5691
5855
|
const enableSkillTool = config.preferences.enableSkillTool !== false;
|
|
5692
5856
|
const skillTool = enableSkillTool ? createSkillTool({
|
|
@@ -5707,6 +5871,7 @@ function CliApp() {
|
|
|
5707
5871
|
writeTodosTool,
|
|
5708
5872
|
decisionLogTool,
|
|
5709
5873
|
...blockerTools,
|
|
5874
|
+
reviewGateTool,
|
|
5710
5875
|
findDefinitionTool,
|
|
5711
5876
|
getFileStructureTool
|
|
5712
5877
|
];
|
|
@@ -5741,7 +5906,7 @@ function CliApp() {
|
|
|
5741
5906
|
if (contextResult.projectContext) startupLog.push(`📄 Project context: ${contextResult.projectContext.filename}`);
|
|
5742
5907
|
for (const error of contextResult.errors) startupLog.push(`⚠️ Context file error: ${error}`);
|
|
5743
5908
|
const featureModulePrompts = featureRegistry.getSystemPromptSections();
|
|
5744
|
-
const cliSystemPrompt =
|
|
5909
|
+
const cliSystemPrompt = buildSystemPrompt(config.preferences.promptVariant ?? "current", {
|
|
5745
5910
|
contextContent: contextResult.mergedContent,
|
|
5746
5911
|
agentStore,
|
|
5747
5912
|
customCommands: state.customCommandStore.getAllCommands(),
|
|
@@ -5980,6 +6145,14 @@ function CliApp() {
|
|
|
5980
6145
|
});
|
|
5981
6146
|
return;
|
|
5982
6147
|
}
|
|
6148
|
+
if (s.reviewGatePrompt) {
|
|
6149
|
+
bridgePresence.emitEvent({
|
|
6150
|
+
type: "status",
|
|
6151
|
+
status: "awaiting_input",
|
|
6152
|
+
text: `Review gate: ${s.reviewGatePrompt.description}`.slice(0, 240)
|
|
6153
|
+
});
|
|
6154
|
+
return;
|
|
6155
|
+
}
|
|
5983
6156
|
const abort = abortControllerRef.current;
|
|
5984
6157
|
if (!abort || abort.signal.aborted) return;
|
|
5985
6158
|
bridgePresence.emitEvent({
|
|
@@ -6137,11 +6310,11 @@ function CliApp() {
|
|
|
6137
6310
|
totalTokens: currentSession.metadata.totalTokens + result.completionInfo.totalTokens,
|
|
6138
6311
|
totalCredits: (currentSession.metadata.totalCredits || 0) + (result.completionInfo.totalCredits || 0),
|
|
6139
6312
|
toolCallCount: currentSession.metadata.toolCallCount + successfulToolCalls,
|
|
6140
|
-
workflow: decisionStoreRef.current.decisions.length > 0 || blockerStoreRef.current.blockers.length > 0 ? {
|
|
6313
|
+
workflow: decisionStoreRef.current.decisions.length > 0 || blockerStoreRef.current.blockers.length > 0 || reviewGateStoreRef.current.reviewGates.length > 0 ? {
|
|
6141
6314
|
decisions: decisionStoreRef.current.decisions,
|
|
6142
6315
|
blockers: blockerStoreRef.current.blockers,
|
|
6143
6316
|
handoff: currentSession.metadata.workflow?.handoff,
|
|
6144
|
-
reviewGates:
|
|
6317
|
+
reviewGates: reviewGateStoreRef.current.reviewGates
|
|
6145
6318
|
} : currentSession.metadata.workflow
|
|
6146
6319
|
}
|
|
6147
6320
|
};
|
|
@@ -6255,7 +6428,7 @@ function CliApp() {
|
|
|
6255
6428
|
if (config?.preferences.autoCompact !== false && activeSession.messages.length >= 6) {
|
|
6256
6429
|
const tokenCounter = getTokenCounter();
|
|
6257
6430
|
const threshold = tokenCounter.getContextWindow(activeSession.model, state.availableModels) * .8;
|
|
6258
|
-
const systemPrompt =
|
|
6431
|
+
const systemPrompt = buildSystemPrompt(config?.preferences.promptVariant ?? "current", {
|
|
6259
6432
|
contextContent: state.contextContent,
|
|
6260
6433
|
agentStore: state.agentStore || void 0,
|
|
6261
6434
|
customCommands: state.customCommandStore.getAllCommands(),
|
|
@@ -6653,7 +6826,7 @@ function CliApp() {
|
|
|
6653
6826
|
decisions: decisionStoreRef.current.decisions,
|
|
6654
6827
|
blockers: blockerStoreRef.current.blockers,
|
|
6655
6828
|
handoff,
|
|
6656
|
-
reviewGates:
|
|
6829
|
+
reviewGates: reviewGateStoreRef.current.reviewGates
|
|
6657
6830
|
};
|
|
6658
6831
|
return handoff;
|
|
6659
6832
|
} catch (err) {
|
|
@@ -6798,11 +6971,11 @@ Multi-line Input:
|
|
|
6798
6971
|
}
|
|
6799
6972
|
const sessionName = args.join(" ") || state.session.name;
|
|
6800
6973
|
state.session.name = sessionName;
|
|
6801
|
-
if (decisionStoreRef.current.decisions.length > 0 || blockerStoreRef.current.blockers.length > 0) state.session.metadata.workflow = {
|
|
6974
|
+
if (decisionStoreRef.current.decisions.length > 0 || blockerStoreRef.current.blockers.length > 0 || reviewGateStoreRef.current.reviewGates.length > 0) state.session.metadata.workflow = {
|
|
6802
6975
|
decisions: decisionStoreRef.current.decisions,
|
|
6803
6976
|
blockers: blockerStoreRef.current.blockers,
|
|
6804
6977
|
handoff: state.session.metadata.workflow?.handoff,
|
|
6805
|
-
reviewGates:
|
|
6978
|
+
reviewGates: reviewGateStoreRef.current.reviewGates
|
|
6806
6979
|
};
|
|
6807
6980
|
const handoff = await generateHandoff(state.session);
|
|
6808
6981
|
await state.sessionStore.save(state.session);
|
|
@@ -7077,6 +7250,7 @@ Multi-line Input:
|
|
|
7077
7250
|
logger.debug("=== New Session Started via /clear ===");
|
|
7078
7251
|
decisionStoreRef.current.decisions = [];
|
|
7079
7252
|
blockerStoreRef.current.blockers = [];
|
|
7253
|
+
reviewGateStoreRef.current.reviewGates = [];
|
|
7080
7254
|
if (state.checkpointStore) state.checkpointStore.setSessionId(newSession.id);
|
|
7081
7255
|
setState((prev) => ({
|
|
7082
7256
|
...prev,
|
|
@@ -7309,7 +7483,8 @@ Multi-line Input:
|
|
|
7309
7483
|
}
|
|
7310
7484
|
const tokenCounter = getTokenCounter();
|
|
7311
7485
|
const contextWindow = tokenCounter.getContextWindow(state.session.model, state.availableModels);
|
|
7312
|
-
const
|
|
7486
|
+
const variantForCount = state.config?.preferences.promptVariant ?? "current";
|
|
7487
|
+
const corePromptTokens = tokenCounter.countTokens(buildSystemPrompt(variantForCount));
|
|
7313
7488
|
const projectContextTokens = state.contextContent ? tokenCounter.countTokens(state.contextContent) : 0;
|
|
7314
7489
|
const commands = state.customCommandStore.getAllCommands();
|
|
7315
7490
|
const skillsSection = buildSkillsPromptSection(commands);
|
|
@@ -7318,7 +7493,7 @@ Multi-line Input:
|
|
|
7318
7493
|
const mcpTools = state.mcpManager?.getTools() || [];
|
|
7319
7494
|
const mcpToolsTokens = tokenCounter.countToolSchemaTokens(mcpTools);
|
|
7320
7495
|
const mcpToolCount = state.mcpManager?.getToolCount() || [];
|
|
7321
|
-
const systemPrompt =
|
|
7496
|
+
const systemPrompt = buildSystemPrompt(variantForCount, {
|
|
7322
7497
|
contextContent: state.contextContent,
|
|
7323
7498
|
agentStore: state.agentStore || void 0,
|
|
7324
7499
|
customCommands: commands,
|
|
@@ -7778,6 +7953,11 @@ Multi-line Input:
|
|
|
7778
7953
|
console.log(formatBlockersOutput(blockerStoreRef.current.blockers));
|
|
7779
7954
|
console.log("");
|
|
7780
7955
|
break;
|
|
7956
|
+
case "review-gates":
|
|
7957
|
+
console.log("\n🛑 Review Gates\n");
|
|
7958
|
+
console.log(formatReviewGatesOutput(reviewGateStoreRef.current.reviewGates));
|
|
7959
|
+
console.log("");
|
|
7960
|
+
break;
|
|
7781
7961
|
case "handoff": {
|
|
7782
7962
|
if (!state.session) {
|
|
7783
7963
|
console.log("No active session");
|
|
@@ -7891,7 +8071,7 @@ Multi-line Input:
|
|
|
7891
8071
|
const newFeatureTools = newFeatureRegistry.getAllTools();
|
|
7892
8072
|
agentContext.tools = [...baseTools, ...newFeatureTools];
|
|
7893
8073
|
const newFeaturePrompts = newFeatureRegistry.getSystemPromptSections();
|
|
7894
|
-
agentContext.systemPrompt =
|
|
8074
|
+
agentContext.systemPrompt = buildSystemPrompt(updatedConfig.preferences.promptVariant ?? "current", {
|
|
7895
8075
|
contextContent: state.contextContent,
|
|
7896
8076
|
agentStore: state.agentStore || void 0,
|
|
7897
8077
|
customCommands: state.customCommandStore.getAllCommands(),
|
|
@@ -8040,6 +8220,13 @@ Multi-line Input:
|
|
|
8040
8220
|
currentPrompt.resolve(response);
|
|
8041
8221
|
dequeueUserQuestionPrompt();
|
|
8042
8222
|
emitNextAwaitingStatus();
|
|
8223
|
+
},
|
|
8224
|
+
onReviewGateResponse: (response, promptId) => {
|
|
8225
|
+
const currentPrompt = useCliStore.getState().reviewGatePrompt;
|
|
8226
|
+
if (currentPrompt?.id !== promptId) return;
|
|
8227
|
+
currentPrompt.resolve(response);
|
|
8228
|
+
dequeueReviewGatePrompt();
|
|
8229
|
+
emitNextAwaitingStatus();
|
|
8043
8230
|
}
|
|
8044
8231
|
});
|
|
8045
8232
|
}
|
|
@@ -105,6 +105,19 @@ const useCliStore = create((set) => ({
|
|
|
105
105
|
userQuestionQueue: rest
|
|
106
106
|
};
|
|
107
107
|
}),
|
|
108
|
+
reviewGatePrompt: null,
|
|
109
|
+
reviewGateQueue: [],
|
|
110
|
+
enqueueReviewGatePrompt: (prompt) => set((state) => {
|
|
111
|
+
if (!state.reviewGatePrompt) return { reviewGatePrompt: prompt };
|
|
112
|
+
return { reviewGateQueue: [...state.reviewGateQueue, prompt] };
|
|
113
|
+
}),
|
|
114
|
+
dequeueReviewGatePrompt: () => set((state) => {
|
|
115
|
+
const [next, ...rest] = state.reviewGateQueue;
|
|
116
|
+
return {
|
|
117
|
+
reviewGatePrompt: next ?? null,
|
|
118
|
+
reviewGateQueue: rest
|
|
119
|
+
};
|
|
120
|
+
}),
|
|
108
121
|
showConfigEditor: false,
|
|
109
122
|
setShowConfigEditor: (show) => set({ showConfigEditor: show }),
|
|
110
123
|
showMcpViewer: false,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as RechartsChartTypeList, A as ImageGenerationUsageTransaction, At as secureParameters, B as NotFoundError, C as FileEvents, Ct as getMcpProviderMetadata, D as GenericCreditAddTransaction, Dt as obfuscateApiKey, E as GenerateImageToolCallSchema, Et as isGPTImageModel, F as KnowledgeType, G as ProfileEvents, H as OpenAIImageGenerationInput, I as LLMEvents, J as PurchaseTransaction, K as ProjectEvents, L as MiscEvents, M as InboxEvents, N as InviteEvents, Nt as CollectionType, O as GenericCreditDeductTransaction, Ot as resolveNavigationIntents, P as InviteType, Q as ReceivedCreditTransaction, R as ModalEvents, S as FeedbackEvents, St as getDataLakeTags, T as GEMINI_IMAGE_MODELS, Tt as isGPTImage2Model, U as Permission, V as OpenAIEmbeddingModel, W as PermissionDeniedError, X as REASONING_SUPPORTED_MODELS, Y as QuestMasterParamsSchema, Z as RealtimeVoiceUsageTransaction, _ as CompletionApiUsageTransaction, _t as VideoModels, a as ApiKeyEvents, at as SessionEvents, b as FIXED_TEMPERATURE_MODELS, bt as b4mLLMTools, c as AppFileEvents, ct as SupportedFabFileMimeTypes, d as BFL_IMAGE_MODELS, dt as TextGenerationUsageTransaction, et as RegInviteEvents, f as BFL_SAFETY_TOLERANCE, ft as ToolUsageTransaction, g as ChatModels, gt as VideoGenerationUsageTransaction, h as ChatCompletionCreateInputSchema, ht as VIDEO_SIZE_CONSTRAINTS, i as AiEvents, it as ResearchTaskType, j as ImageModels, k as ImageEditUsageTransaction, kt as sanitizeTelemetryError, l as ArtifactTypeSchema, lt as TagType, mt as UiNavigationEvents, n as logger, nt as ResearchTaskExecutionType, o as ApiKeyScope, ot as SpeechToTextModels, p as BedrockEmbeddingModel, pt as TransferCreditTransaction, q as PromptMetaZodSchema, r as ALERT_THRESHOLDS, rt as ResearchTaskPeriodicFrequencyType, s as ApiKeyType, st as SubscriptionCreditTransaction, t as ConfigStore, tt as ResearchModeParamsSchema, u as AuthEvents, ut as TaskScheduleHandler, v as DashboardParamsSchema, vt as VoyageAIEmbeddingModel, w as FriendshipEvents, wt as getViewById, x as FavoriteDocumentType, xt as getAccessibleDataLakes, y as ElabsEvents, yt as XAI_IMAGE_MODELS, z as ModelBackend } from "./ConfigStore-
|
|
2
|
+
import { $ as RechartsChartTypeList, A as ImageGenerationUsageTransaction, At as secureParameters, B as NotFoundError, C as FileEvents, Ct as getMcpProviderMetadata, D as GenericCreditAddTransaction, Dt as obfuscateApiKey, E as GenerateImageToolCallSchema, Et as isGPTImageModel, F as KnowledgeType, G as ProfileEvents, H as OpenAIImageGenerationInput, I as LLMEvents, J as PurchaseTransaction, K as ProjectEvents, L as MiscEvents, M as InboxEvents, N as InviteEvents, Nt as CollectionType, O as GenericCreditDeductTransaction, Ot as resolveNavigationIntents, P as InviteType, Q as ReceivedCreditTransaction, R as ModalEvents, S as FeedbackEvents, St as getDataLakeTags, T as GEMINI_IMAGE_MODELS, Tt as isGPTImage2Model, U as Permission, V as OpenAIEmbeddingModel, W as PermissionDeniedError, X as REASONING_SUPPORTED_MODELS, Y as QuestMasterParamsSchema, Z as RealtimeVoiceUsageTransaction, _ as CompletionApiUsageTransaction, _t as VideoModels, a as ApiKeyEvents, at as SessionEvents, b as FIXED_TEMPERATURE_MODELS, bt as b4mLLMTools, c as AppFileEvents, ct as SupportedFabFileMimeTypes, d as BFL_IMAGE_MODELS, dt as TextGenerationUsageTransaction, et as RegInviteEvents, f as BFL_SAFETY_TOLERANCE, ft as ToolUsageTransaction, g as ChatModels, gt as VideoGenerationUsageTransaction, h as ChatCompletionCreateInputSchema, ht as VIDEO_SIZE_CONSTRAINTS, i as AiEvents, it as ResearchTaskType, j as ImageModels, k as ImageEditUsageTransaction, kt as sanitizeTelemetryError, l as ArtifactTypeSchema, lt as TagType, mt as UiNavigationEvents, n as logger, nt as ResearchTaskExecutionType, o as ApiKeyScope, ot as SpeechToTextModels, p as BedrockEmbeddingModel, pt as TransferCreditTransaction, q as PromptMetaZodSchema, r as ALERT_THRESHOLDS, rt as ResearchTaskPeriodicFrequencyType, s as ApiKeyType, st as SubscriptionCreditTransaction, t as ConfigStore, tt as ResearchModeParamsSchema, u as AuthEvents, ut as TaskScheduleHandler, v as DashboardParamsSchema, vt as VoyageAIEmbeddingModel, w as FriendshipEvents, wt as getViewById, x as FavoriteDocumentType, xt as getAccessibleDataLakes, y as ElabsEvents, yt as XAI_IMAGE_MODELS, z as ModelBackend } from "./ConfigStore-Bu_plzvP.mjs";
|
|
3
3
|
import { n as isPathAllowed, t as assertPathAllowed } from "./pathValidation-CIytuhr3-Dt5dntLx.mjs";
|
|
4
4
|
import { execFile, execFileSync, spawn } from "child_process";
|
|
5
5
|
import { createHash, randomBytes } from "crypto";
|
|
@@ -1906,11 +1906,17 @@ var ReActAgent = class extends EventEmitter {
|
|
|
1906
1906
|
iterations = 0;
|
|
1907
1907
|
/** Whether runIteration() has been initialized (messages built, state reset) */
|
|
1908
1908
|
iterationInitialized = false;
|
|
1909
|
+
/**
|
|
1910
|
+
* Length of the initial messages array (system + previousMessages + user query)
|
|
1911
|
+
* before any ReAct iteration messages are appended. Used by trimConversationHistory
|
|
1912
|
+
* to protect the conversation prefix from being mistaken for iteration nudges.
|
|
1913
|
+
*/
|
|
1914
|
+
initialMessageCount = 0;
|
|
1909
1915
|
constructor(context) {
|
|
1910
1916
|
super();
|
|
1911
1917
|
this.context = {
|
|
1912
1918
|
...context,
|
|
1913
|
-
maxIterations: context.maxIterations ??
|
|
1919
|
+
maxIterations: context.maxIterations ?? 50,
|
|
1914
1920
|
maxTokens: context.maxTokens ?? 4096,
|
|
1915
1921
|
temperature: context.temperature ?? .7
|
|
1916
1922
|
};
|
|
@@ -1933,11 +1939,13 @@ var ReActAgent = class extends EventEmitter {
|
|
|
1933
1939
|
this.toolCallCount = 0;
|
|
1934
1940
|
this.confidenceLog = [];
|
|
1935
1941
|
this.iterationConfidences = [];
|
|
1936
|
-
const maxIterations = options.maxIterations ?? this.context.maxIterations ??
|
|
1942
|
+
const maxIterations = options.maxIterations ?? this.context.maxIterations ?? 50;
|
|
1937
1943
|
const temperature = options.temperature ?? this.context.temperature ?? .7;
|
|
1938
1944
|
const maxTokens = options.maxTokens ?? this.context.maxTokens ?? 4096;
|
|
1945
|
+
const maxTotalTokens = options.maxTotalTokens ?? this.context.maxTotalTokens;
|
|
1939
1946
|
const maxHistoryIterations = options.maxHistoryIterations ?? 4;
|
|
1940
1947
|
let iterations = 0;
|
|
1948
|
+
let reachedMaxTotalTokens = false;
|
|
1941
1949
|
try {
|
|
1942
1950
|
const messages = [
|
|
1943
1951
|
{
|
|
@@ -1951,6 +1959,7 @@ var ReActAgent = class extends EventEmitter {
|
|
|
1951
1959
|
}
|
|
1952
1960
|
];
|
|
1953
1961
|
this.messages = messages;
|
|
1962
|
+
this.initialMessageCount = messages.length;
|
|
1954
1963
|
messages.forEach((msg, i) => {
|
|
1955
1964
|
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
1956
1965
|
this.context.logger.debug(` [${i}] ${msg.role}: ${content.substring(0, 150)}...`);
|
|
@@ -1987,7 +1996,7 @@ var ReActAgent = class extends EventEmitter {
|
|
|
1987
1996
|
const processedToolIds = /* @__PURE__ */ new Set();
|
|
1988
1997
|
let hadToolCalls = false;
|
|
1989
1998
|
if (maxHistoryIterations > 0 && iterations > 1) {
|
|
1990
|
-
trimConversationHistory(messages, maxHistoryIterations);
|
|
1999
|
+
trimConversationHistory(messages, maxHistoryIterations, this.initialMessageCount);
|
|
1991
2000
|
trimSteps(this.steps, maxHistoryIterations);
|
|
1992
2001
|
}
|
|
1993
2002
|
const cacheStrategy = options.enableCaching ? {
|
|
@@ -2119,6 +2128,18 @@ var ReActAgent = class extends EventEmitter {
|
|
|
2119
2128
|
role: "user",
|
|
2120
2129
|
content: `Based on the tool results above, please provide a complete answer. If I asked for multiple things, make sure to address all of them.`
|
|
2121
2130
|
});
|
|
2131
|
+
if (maxTotalTokens !== void 0 && this.totalTokens >= maxTotalTokens && !iterationComplete) {
|
|
2132
|
+
reachedMaxTotalTokens = true;
|
|
2133
|
+
finalAnswer = currentText.trim() || `I stopped after reaching the cumulative token ceiling (${this.totalTokens}/${maxTotalTokens}) without arriving at a final answer.`;
|
|
2134
|
+
const finalStep = {
|
|
2135
|
+
type: "final_answer",
|
|
2136
|
+
content: finalAnswer,
|
|
2137
|
+
metadata: { timestamp: Date.now() }
|
|
2138
|
+
};
|
|
2139
|
+
this.steps.push(finalStep);
|
|
2140
|
+
this.emit("final_answer", finalStep);
|
|
2141
|
+
break;
|
|
2142
|
+
}
|
|
2122
2143
|
if (iterations >= maxIterations) {
|
|
2123
2144
|
reachedMaxIterations = true;
|
|
2124
2145
|
finalAnswer = currentText.trim() || `I reached the maximum number of iterations (${iterations}/${maxIterations}) without arriving at a final answer.`;
|
|
@@ -2147,6 +2168,7 @@ var ReActAgent = class extends EventEmitter {
|
|
|
2147
2168
|
iterations,
|
|
2148
2169
|
toolCalls: this.toolCallCount,
|
|
2149
2170
|
reachedMaxIterations,
|
|
2171
|
+
reachedMaxTotalTokens: reachedMaxTotalTokens || void 0,
|
|
2150
2172
|
averageConfidence: avgConfidence,
|
|
2151
2173
|
minConfidence,
|
|
2152
2174
|
confidenceLog: this.confidenceLog.length > 0 ? this.confidenceLog : void 0
|
|
@@ -2265,7 +2287,8 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
|
|
|
2265
2287
|
totalCredits: this.totalCredits,
|
|
2266
2288
|
toolCallCount: this.toolCallCount,
|
|
2267
2289
|
confidenceLog: structuredClone(this.confidenceLog),
|
|
2268
|
-
iterationConfidences: [...this.iterationConfidences]
|
|
2290
|
+
iterationConfidences: [...this.iterationConfidences],
|
|
2291
|
+
initialMessageCount: this.initialMessageCount
|
|
2269
2292
|
};
|
|
2270
2293
|
}
|
|
2271
2294
|
/**
|
|
@@ -2290,6 +2313,7 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
|
|
|
2290
2313
|
this.toolCallCount = checkpoint.toolCallCount;
|
|
2291
2314
|
this.confidenceLog = structuredClone(checkpoint.confidenceLog);
|
|
2292
2315
|
this.iterationConfidences = [...checkpoint.iterationConfidences];
|
|
2316
|
+
this.initialMessageCount = checkpoint.initialMessageCount ?? this.messages.length;
|
|
2293
2317
|
this.observationQueue = [];
|
|
2294
2318
|
this.iterationInitialized = true;
|
|
2295
2319
|
}
|
|
@@ -2325,9 +2349,10 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
|
|
|
2325
2349
|
* regressing the battle-tested run() path in this PR.
|
|
2326
2350
|
*/
|
|
2327
2351
|
async runIteration(query, options = {}) {
|
|
2328
|
-
const maxIterations = options.maxIterations ?? this.context.maxIterations ??
|
|
2352
|
+
const maxIterations = options.maxIterations ?? this.context.maxIterations ?? 50;
|
|
2329
2353
|
const temperature = options.temperature ?? this.context.temperature ?? .7;
|
|
2330
2354
|
const maxTokens = options.maxTokens ?? this.context.maxTokens ?? 4096;
|
|
2355
|
+
const maxTotalTokens = options.maxTotalTokens ?? this.context.maxTotalTokens;
|
|
2331
2356
|
if (!this.iterationInitialized) {
|
|
2332
2357
|
if (!query) throw new Error("query is required on the first call to runIteration(). Pass the user query, or call fromCheckpoint() first to resume.");
|
|
2333
2358
|
this.steps = [];
|
|
@@ -2352,6 +2377,7 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
|
|
|
2352
2377
|
content: query
|
|
2353
2378
|
}
|
|
2354
2379
|
];
|
|
2380
|
+
this.initialMessageCount = this.messages.length;
|
|
2355
2381
|
this.iterationInitialized = true;
|
|
2356
2382
|
}
|
|
2357
2383
|
if (this.iterations >= maxIterations) {
|
|
@@ -2393,7 +2419,7 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
|
|
|
2393
2419
|
this.context.logger.debug(`[ReActAgent] Starting iteration ${this.iterations}/${maxIterations}`);
|
|
2394
2420
|
const maxHistoryIterations = options.maxHistoryIterations ?? 4;
|
|
2395
2421
|
if (maxHistoryIterations > 0 && this.iterations > 1) {
|
|
2396
|
-
trimConversationHistory(this.messages, maxHistoryIterations);
|
|
2422
|
+
trimConversationHistory(this.messages, maxHistoryIterations, this.initialMessageCount);
|
|
2397
2423
|
trimSteps(this.steps, maxHistoryIterations);
|
|
2398
2424
|
}
|
|
2399
2425
|
let iterationComplete = false;
|
|
@@ -2540,6 +2566,25 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
|
|
|
2540
2566
|
role: "user",
|
|
2541
2567
|
content: "Based on the tool results above, please provide a complete answer. If I asked for multiple things, make sure to address all of them."
|
|
2542
2568
|
});
|
|
2569
|
+
if (!iterationComplete && maxTotalTokens !== void 0 && this.totalTokens >= maxTotalTokens) {
|
|
2570
|
+
finalAnswer = currentText.trim() || `I stopped after reaching the cumulative token ceiling (${this.totalTokens}/${maxTotalTokens}) without arriving at a final answer.`;
|
|
2571
|
+
const ceilingStep = {
|
|
2572
|
+
type: "final_answer",
|
|
2573
|
+
content: finalAnswer,
|
|
2574
|
+
metadata: { timestamp: Date.now() }
|
|
2575
|
+
};
|
|
2576
|
+
this.steps.push(ceilingStep);
|
|
2577
|
+
iterationSteps.push(ceilingStep);
|
|
2578
|
+
this.emit("final_answer", ceilingStep);
|
|
2579
|
+
return {
|
|
2580
|
+
step: ceilingStep,
|
|
2581
|
+
allSteps: iterationSteps,
|
|
2582
|
+
isComplete: true,
|
|
2583
|
+
reachedMaxIterations: false,
|
|
2584
|
+
reachedMaxTotalTokens: true,
|
|
2585
|
+
checkpoint: this.toCheckpoint()
|
|
2586
|
+
};
|
|
2587
|
+
}
|
|
2543
2588
|
if (!iterationComplete && this.iterations >= maxIterations) {
|
|
2544
2589
|
finalAnswer = currentText.trim() || `I reached the maximum number of iterations (${this.iterations}/${maxIterations}) without arriving at a final answer.`;
|
|
2545
2590
|
const maxStep = {
|
|
@@ -2671,10 +2716,19 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
|
|
|
2671
2716
|
return .7;
|
|
2672
2717
|
}
|
|
2673
2718
|
/**
|
|
2674
|
-
* Parse tool arguments, handling both string and object forms
|
|
2719
|
+
* Parse tool arguments, handling both string and object forms.
|
|
2720
|
+
*
|
|
2721
|
+
* Recovers from common malformed-JSON patterns models emit before failing:
|
|
2722
|
+
* - Trailing commas before `]` or `}` (frequent across providers)
|
|
2723
|
+
* - Markdown code fences wrapping the JSON (`` ```json ... ``` ``)
|
|
2724
|
+
*
|
|
2725
|
+
* If recovery succeeds, logs at debug level so provider-specific patterns
|
|
2726
|
+
* can be tracked. If recovery fails, re-throws the original parse error
|
|
2727
|
+
* so the failure mode stays visible.
|
|
2675
2728
|
*/
|
|
2676
2729
|
parseToolArguments(args) {
|
|
2677
|
-
|
|
2730
|
+
if (typeof args !== "string") return args;
|
|
2731
|
+
return parseToolArgsLenient(args, this.context.logger);
|
|
2678
2732
|
}
|
|
2679
2733
|
/**
|
|
2680
2734
|
* Execute a tool and return the result as a string.
|
|
@@ -2715,6 +2769,64 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
|
|
|
2715
2769
|
}
|
|
2716
2770
|
};
|
|
2717
2771
|
/**
|
|
2772
|
+
* Parse JSON tool arguments with recovery for common LLM mistakes.
|
|
2773
|
+
*
|
|
2774
|
+
* Strategy: try standard parse first (the happy path, no allocation). On
|
|
2775
|
+
* failure, attempt targeted fixes one at a time and retry. Each recovery
|
|
2776
|
+
* step is conservative — it only handles patterns that have a low risk of
|
|
2777
|
+
* silently corrupting valid input.
|
|
2778
|
+
*
|
|
2779
|
+
* Exported for direct use and unit testing.
|
|
2780
|
+
*/
|
|
2781
|
+
function parseToolArgsLenient(args, logger) {
|
|
2782
|
+
try {
|
|
2783
|
+
return JSON.parse(args);
|
|
2784
|
+
} catch (originalError) {
|
|
2785
|
+
const recoveries = [
|
|
2786
|
+
{
|
|
2787
|
+
name: "strip-code-fence",
|
|
2788
|
+
transform: stripCodeFence
|
|
2789
|
+
},
|
|
2790
|
+
{
|
|
2791
|
+
name: "strip-trailing-commas",
|
|
2792
|
+
transform: stripTrailingCommas
|
|
2793
|
+
},
|
|
2794
|
+
{
|
|
2795
|
+
name: "strip-code-fence+strip-trailing-commas",
|
|
2796
|
+
transform: (s) => stripTrailingCommas(stripCodeFence(s))
|
|
2797
|
+
}
|
|
2798
|
+
];
|
|
2799
|
+
for (const { name, transform } of recoveries) {
|
|
2800
|
+
const candidate = transform(args);
|
|
2801
|
+
if (candidate === args) continue;
|
|
2802
|
+
try {
|
|
2803
|
+
const result = JSON.parse(candidate);
|
|
2804
|
+
logger?.debug?.(`[ReActAgent] Recovered malformed tool args via ${name}`);
|
|
2805
|
+
return result;
|
|
2806
|
+
} catch {}
|
|
2807
|
+
}
|
|
2808
|
+
throw originalError;
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
/**
|
|
2812
|
+
* Remove a wrapping markdown code fence, e.g. `` ```json\n{...}\n``` ``.
|
|
2813
|
+
* Preserves input if no recognizable fence is found.
|
|
2814
|
+
*/
|
|
2815
|
+
function stripCodeFence(input) {
|
|
2816
|
+
const fenceMatch = input.trim().match(/^```(?:json|JSON)?\s*\n?([\s\S]*?)\n?```$/);
|
|
2817
|
+
return fenceMatch ? fenceMatch[1].trim() : input;
|
|
2818
|
+
}
|
|
2819
|
+
/**
|
|
2820
|
+
* Strip commas that immediately precede `}` or `]` (with optional whitespace).
|
|
2821
|
+
* Conservative: doesn't touch commas inside strings because the regex only
|
|
2822
|
+
* matches commas followed by whitespace and a closing bracket.
|
|
2823
|
+
*
|
|
2824
|
+
* Preserves input if no trailing-comma pattern is found.
|
|
2825
|
+
*/
|
|
2826
|
+
function stripTrailingCommas(input) {
|
|
2827
|
+
return input.replace(/,(\s*[}\]])/g, "$1");
|
|
2828
|
+
}
|
|
2829
|
+
/**
|
|
2718
2830
|
* Trim the steps array to keep only the last N iterations worth of steps.
|
|
2719
2831
|
*
|
|
2720
2832
|
* Each iteration produces 1-5 steps (thought, action(s), observation(s), final_answer).
|
|
@@ -2731,27 +2843,27 @@ function trimSteps(steps, maxIterations) {
|
|
|
2731
2843
|
}
|
|
2732
2844
|
}
|
|
2733
2845
|
/**
|
|
2734
|
-
* Trim conversation history to keep
|
|
2735
|
-
* plus the last N iterations of assistant/tool/user messages.
|
|
2846
|
+
* Trim conversation history to keep the protected prefix (system + previousMessages
|
|
2847
|
+
* + current user query) plus the last N iterations of assistant/tool/user messages.
|
|
2736
2848
|
*
|
|
2737
2849
|
* Each ReAct iteration adds ~3-5 messages (assistant with tool calls, tool results,
|
|
2738
2850
|
* user nudge). Keeping all iterations causes input tokens to grow quadratically.
|
|
2739
2851
|
* Trimming to the last N iterations keeps the agent grounded in recent context
|
|
2740
2852
|
* while dramatically reducing token accumulation.
|
|
2853
|
+
*
|
|
2854
|
+
* @param protectedPrefixCount - Length of the initial messages array before any
|
|
2855
|
+
* ReAct iteration messages were appended. Required when previousMessages is
|
|
2856
|
+
* used, otherwise prior-turn user messages get mistaken for iteration nudges
|
|
2857
|
+
* and trimmed away (including, eventually, the current user query itself).
|
|
2741
2858
|
*/
|
|
2742
|
-
function trimConversationHistory(messages, maxIterations) {
|
|
2743
|
-
|
|
2744
|
-
for (let i = 0; i < messages.length; i++) if (messages[i].role === "user") {
|
|
2745
|
-
dynamicStart = i + 1;
|
|
2746
|
-
break;
|
|
2747
|
-
}
|
|
2859
|
+
function trimConversationHistory(messages, maxIterations, protectedPrefixCount) {
|
|
2860
|
+
const dynamicStart = protectedPrefixCount;
|
|
2748
2861
|
const dynamicMessages = messages.slice(dynamicStart);
|
|
2749
2862
|
if (dynamicMessages.length === 0) return;
|
|
2750
2863
|
const nudgeIndices = [];
|
|
2751
2864
|
for (let i = 0; i < dynamicMessages.length; i++) if (dynamicMessages[i].role === "user" && typeof dynamicMessages[i].content === "string") nudgeIndices.push(i);
|
|
2752
2865
|
if (nudgeIndices.length <= maxIterations) return;
|
|
2753
|
-
const
|
|
2754
|
-
const keepFrom = dynamicStart + cutoffNudgeIndex;
|
|
2866
|
+
const keepFrom = dynamicStart + nudgeIndices[nudgeIndices.length - maxIterations];
|
|
2755
2867
|
messages.splice(dynamicStart, keepFrom - dynamicStart);
|
|
2756
2868
|
}
|
|
2757
2869
|
//#endregion
|
|
@@ -3030,7 +3142,7 @@ EXAMPLES:
|
|
|
3030
3142
|
Remember: Use context from previous messages to understand follow-up questions.
|
|
3031
3143
|
|
|
3032
3144
|
DURABLE WORKFLOW TRACKING:
|
|
3033
|
-
You have tools for tracking decisions and
|
|
3145
|
+
You have tools for tracking decisions, blockers, and human review gates during your work. These create an audit trail that persists across sessions, enabling anyone to understand why things were done and what's still outstanding.
|
|
3034
3146
|
|
|
3035
3147
|
- log_decision: When you make a significant decision (architecture choice, scope narrowing, interpretation of ambiguous requirements, trade-off between alternatives), log it with rationale. Do NOT log trivial decisions. Log decisions that would matter if someone needed to understand WHY you did something or if they needed to resume this work.
|
|
3036
3148
|
|
|
@@ -3038,9 +3150,40 @@ You have tools for tracking decisions and blockers during your work. These creat
|
|
|
3038
3150
|
|
|
3039
3151
|
- resolve_blocker: When a blocker is cleared, record how it was resolved. Use the blocker ID from the track_blocker output.
|
|
3040
3152
|
|
|
3153
|
+
- request_review_gate: Pause for explicit human approval before crossing a significant decision point — one that affects interpretation, evidence, cost, credentials, platform, or public commitment (e.g., narrowing research scope after synthesis, hard-to-reverse refactors, architectural pivots, dependency swaps). Do NOT use for routine operations or actions already covered by the standard permission system (file edits, bash commands). Treat a rejection as a hard stop — re-plan, do not retry.
|
|
3154
|
+
|
|
3041
3155
|
These tools are lightweight — use them naturally as part of your work, not as a ceremony.${directoriesSection}${projectContextSection}${skillsSection}${featureModulesSection}`;
|
|
3042
3156
|
}
|
|
3043
3157
|
/**
|
|
3158
|
+
* Build the minimal-variant system prompt. Reuses the project context,
|
|
3159
|
+
* skills, additional-directories, and feature-module sections from
|
|
3160
|
+
* `buildCoreSystemPrompt` — those carry user-specific information the
|
|
3161
|
+
* model needs and are independent of the behavioral scaffolding.
|
|
3162
|
+
*/
|
|
3163
|
+
function buildMinimalSystemPrompt(config = {}) {
|
|
3164
|
+
let projectContextSection = "";
|
|
3165
|
+
let skillsSection = "";
|
|
3166
|
+
let directoriesSection = "";
|
|
3167
|
+
let featureModulesSection = "";
|
|
3168
|
+
if (config.contextContent) projectContextSection = `\n\n## Project Context\n\nFollow these project-specific instructions:\n\n${config.contextContent}`;
|
|
3169
|
+
if (config.enableSkillTool !== false && config.customCommands && config.customCommands.length > 0) skillsSection = buildSkillsPromptSection(config.customCommands);
|
|
3170
|
+
if (config.additionalDirectories && config.additionalDirectories.length > 0) directoriesSection = `\n\n## Additional Allowed Directories\n\nIn addition to the working directory (${process.cwd()}), you have read/write access to these directories:\n${config.additionalDirectories.map((d) => `- ${d}`).join("\n")}\n\nPass full paths to file tools' \`dir_path\` parameter to access these directories.`;
|
|
3171
|
+
if (config.featureModulePrompts) featureModulesSection = config.featureModulePrompts;
|
|
3172
|
+
return `You are an expert coding assistant. You help users by reading files, executing commands, editing code, and writing new files using the tools available to you.
|
|
3173
|
+
|
|
3174
|
+
Guidelines:
|
|
3175
|
+
- Be concise in your responses.
|
|
3176
|
+
- Show file paths clearly when working with files.
|
|
3177
|
+
- When the task is done, give the user a direct answer — no recap of steps already visible in the tool history.${directoriesSection}${projectContextSection}${skillsSection}${featureModulesSection}`;
|
|
3178
|
+
}
|
|
3179
|
+
/**
|
|
3180
|
+
* Pick a system prompt by variant. The dispatch point for the
|
|
3181
|
+
* production CLI's `promptVariant` preference flag.
|
|
3182
|
+
*/
|
|
3183
|
+
function buildSystemPrompt(variant, config = {}) {
|
|
3184
|
+
return variant === "minimal" ? buildMinimalSystemPrompt(config) : buildCoreSystemPrompt(config);
|
|
3185
|
+
}
|
|
3186
|
+
/**
|
|
3044
3187
|
* Build the dynamic agent creation prompt section
|
|
3045
3188
|
* Injected into the system prompt when enableDynamicAgentCreation is true
|
|
3046
3189
|
*/
|
|
@@ -18828,7 +18971,7 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
|
|
|
18828
18971
|
return result;
|
|
18829
18972
|
}
|
|
18830
18973
|
if (!permissionManager.needsPermission(toolName, { isSandboxed })) return executeAndRecord();
|
|
18831
|
-
const { useCliStore } = await import("./store-
|
|
18974
|
+
const { useCliStore } = await import("./store-D2zh6DFz.mjs");
|
|
18832
18975
|
if (useCliStore.getState().autoAcceptEdits) return executeAndRecord();
|
|
18833
18976
|
const response = await showPermissionPrompt(toolName, effectiveArgs, await generateToolPreview(toolName, args, isSandboxed));
|
|
18834
18977
|
if (response.action === "deny") throw new PermissionDeniedError(toolName, args);
|
|
@@ -23620,7 +23763,7 @@ function createGetFileStructureTool() {
|
|
|
23620
23763
|
* Validate log_decision parameters
|
|
23621
23764
|
* @throws Error if validation fails
|
|
23622
23765
|
*/
|
|
23623
|
-
function validateParams(args) {
|
|
23766
|
+
function validateParams$1(args) {
|
|
23624
23767
|
const params = args;
|
|
23625
23768
|
if (typeof params.summary !== "string" || params.summary.trim() === "") throw new Error("log_decision: summary must be a non-empty string");
|
|
23626
23769
|
if (typeof params.rationale !== "string" || params.rationale.trim() === "") throw new Error("log_decision: rationale must be a non-empty string");
|
|
@@ -23660,7 +23803,7 @@ function formatDecisionsOutput(decisions) {
|
|
|
23660
23803
|
function createDecisionLogTool(store) {
|
|
23661
23804
|
return {
|
|
23662
23805
|
toolFn: async (args) => {
|
|
23663
|
-
const params = validateParams(args);
|
|
23806
|
+
const params = validateParams$1(args);
|
|
23664
23807
|
const decision = {
|
|
23665
23808
|
id: v4(),
|
|
23666
23809
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -23845,4 +23988,127 @@ function createBlockerStore(onUpdate) {
|
|
|
23845
23988
|
};
|
|
23846
23989
|
}
|
|
23847
23990
|
//#endregion
|
|
23848
|
-
|
|
23991
|
+
//#region src/tools/reviewGateTool.ts
|
|
23992
|
+
/**
|
|
23993
|
+
* Validate request_review_gate parameters
|
|
23994
|
+
* @throws Error if validation fails
|
|
23995
|
+
*/
|
|
23996
|
+
function validateParams(args) {
|
|
23997
|
+
const params = args;
|
|
23998
|
+
if (typeof params.description !== "string" || params.description.trim() === "") throw new Error("request_review_gate: description must be a non-empty string");
|
|
23999
|
+
if (params.options !== void 0) {
|
|
24000
|
+
if (!Array.isArray(params.options)) throw new Error("request_review_gate: options must be an array of strings");
|
|
24001
|
+
for (const opt of params.options) if (typeof opt !== "string") throw new Error("request_review_gate: each option must be a string");
|
|
24002
|
+
}
|
|
24003
|
+
if (params.recommendation !== void 0 && typeof params.recommendation !== "string") throw new Error("request_review_gate: recommendation must be a string");
|
|
24004
|
+
const options = params.options?.map((o) => o.trim()).filter((o) => o.length > 0);
|
|
24005
|
+
return {
|
|
24006
|
+
description: params.description.trim(),
|
|
24007
|
+
options: options && options.length > 0 ? options : void 0,
|
|
24008
|
+
recommendation: typeof params.recommendation === "string" ? params.recommendation.trim() : void 0
|
|
24009
|
+
};
|
|
24010
|
+
}
|
|
24011
|
+
/**
|
|
24012
|
+
* Format review gates for display output
|
|
24013
|
+
*/
|
|
24014
|
+
function formatReviewGatesOutput(gates) {
|
|
24015
|
+
if (gates.length === 0) return "No review gates recorded in this session.";
|
|
24016
|
+
return gates.map((gate, index) => {
|
|
24017
|
+
const lines = [`${index + 1}. **${gate.description}**`, ` Status: ${gate.status}`];
|
|
24018
|
+
if (gate.userNote) lines.push(` Note: ${gate.userNote}`);
|
|
24019
|
+
const requested = new Date(gate.timestamp).toLocaleTimeString();
|
|
24020
|
+
lines.push(` Requested at: ${requested}`);
|
|
24021
|
+
if (gate.resolvedAt) {
|
|
24022
|
+
const resolved = new Date(gate.resolvedAt).toLocaleTimeString();
|
|
24023
|
+
lines.push(` Resolved at: ${resolved}`);
|
|
24024
|
+
}
|
|
24025
|
+
return lines.join("\n");
|
|
24026
|
+
}).join("\n\n");
|
|
24027
|
+
}
|
|
24028
|
+
/**
|
|
24029
|
+
* Create the request_review_gate tool.
|
|
24030
|
+
*
|
|
24031
|
+
* Pauses agent execution and prompts the user for explicit approval at a
|
|
24032
|
+
* significant decision point. The agent halts until the user responds.
|
|
24033
|
+
*
|
|
24034
|
+
* Decisions are persisted in the session's workflow state (`reviewGates`)
|
|
24035
|
+
* for audit trail and cross-session continuity.
|
|
24036
|
+
*/
|
|
24037
|
+
function createReviewGateTool(store, requestReviewFn) {
|
|
24038
|
+
return {
|
|
24039
|
+
toolFn: async (args) => {
|
|
24040
|
+
const params = validateParams(args);
|
|
24041
|
+
const id = v4();
|
|
24042
|
+
const requestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
24043
|
+
const response = await requestReviewFn({
|
|
24044
|
+
id,
|
|
24045
|
+
description: params.description,
|
|
24046
|
+
options: params.options,
|
|
24047
|
+
recommendation: params.recommendation
|
|
24048
|
+
});
|
|
24049
|
+
const trimmedNote = response.note?.trim();
|
|
24050
|
+
const entry = {
|
|
24051
|
+
id,
|
|
24052
|
+
timestamp: requestedAt,
|
|
24053
|
+
description: params.description,
|
|
24054
|
+
status: response.decision,
|
|
24055
|
+
resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24056
|
+
userNote: trimmedNote && trimmedNote.length > 0 ? trimmedNote : void 0
|
|
24057
|
+
};
|
|
24058
|
+
store.reviewGates.push(entry);
|
|
24059
|
+
if (store.onUpdate) store.onUpdate(store.reviewGates);
|
|
24060
|
+
const verdict = response.decision === "approved" ? "APPROVED" : "REJECTED";
|
|
24061
|
+
const noteText = entry.userNote ? `\nUser note: ${entry.userNote}` : "";
|
|
24062
|
+
return `Review gate ${verdict} [${id.slice(0, 8)}]: ${params.description}${noteText}`;
|
|
24063
|
+
},
|
|
24064
|
+
toolSchema: {
|
|
24065
|
+
name: "request_review_gate",
|
|
24066
|
+
description: `Pause execution and request explicit human approval at a significant decision point.
|
|
24067
|
+
|
|
24068
|
+
Review gates protect meaning. Stop before crossing decisions that affect interpretation, evidence, cost, credentials, platform, or public commitment.
|
|
24069
|
+
|
|
24070
|
+
**When to use:**
|
|
24071
|
+
- Synthesizing findings before narrowing research scope
|
|
24072
|
+
- Hard-to-reverse decisions (refactors, architectural pivots, dependency swaps)
|
|
24073
|
+
- Decisions affecting cost, credentials, or external commitments
|
|
24074
|
+
- Major direction changes after exploration
|
|
24075
|
+
|
|
24076
|
+
**When NOT to use:**
|
|
24077
|
+
- Routine operations (reading files, running tests, listing directories)
|
|
24078
|
+
- Operations already covered by the standard permission system (file edits, bash commands)
|
|
24079
|
+
- Trivial choices that wouldn't matter to someone resuming this work
|
|
24080
|
+
|
|
24081
|
+
The agent will pause until the user explicitly approves or rejects. The user may attach an optional note to their decision (e.g., to redirect or clarify scope). Treat a rejection as a hard stop — re-plan rather than retry.`,
|
|
24082
|
+
parameters: {
|
|
24083
|
+
type: "object",
|
|
24084
|
+
properties: {
|
|
24085
|
+
description: {
|
|
24086
|
+
type: "string",
|
|
24087
|
+
description: "Clear explanation of what the user is being asked to approve, including relevant context"
|
|
24088
|
+
},
|
|
24089
|
+
options: {
|
|
24090
|
+
type: "array",
|
|
24091
|
+
items: { type: "string" },
|
|
24092
|
+
description: "Optional list of alternatives the user can choose between"
|
|
24093
|
+
},
|
|
24094
|
+
recommendation: {
|
|
24095
|
+
type: "string",
|
|
24096
|
+
description: "Optional recommendation from the AI on the preferred path and why"
|
|
24097
|
+
}
|
|
24098
|
+
},
|
|
24099
|
+
required: ["description"]
|
|
24100
|
+
}
|
|
24101
|
+
}
|
|
24102
|
+
};
|
|
24103
|
+
}
|
|
24104
|
+
/**
|
|
24105
|
+
* Create a new empty ReviewGateStore
|
|
24106
|
+
*/
|
|
24107
|
+
function createReviewGateStore(onUpdate) {
|
|
24108
|
+
return {
|
|
24109
|
+
reviewGates: [],
|
|
24110
|
+
onUpdate
|
|
24111
|
+
};
|
|
24112
|
+
}
|
|
24113
|
+
//#endregion
|
|
24114
|
+
export { SessionStore as $, formatStep as A, DEFAULT_RETRY_CONFIG as B, WebSocketToolExecutor as C, ServerLlmBackend as D, WebSocketLlmBackend as E, PermissionManager as F, OllamaBackend as G, clearFeatureModuleTools as H, generateCliTools as I, isReadOnlyTool as J, buildSystemPrompt as K, ALWAYS_DENIED_FOR_AGENTS as L, loadContextFiles as M, getApiUrl as N, McpManager as O, getEnvironmentName as P, CommandHistoryStore as Q, DEFAULT_AGENT_MODEL as R, ApiClient as S, FallbackLlmBackend as T, registerFeatureModuleTools as U, DEFAULT_THOROUGHNESS as V, setWebSocketToolExecutor as W, CustomCommandStore as X, ReActAgent as Y, CheckpointStore as Z, createAgentDelegateTool as _, createBlockerTools as a, formatFileSize$1 as at, createSkillTool as b, createDecisionStore as c, createFindDefinitionTool as d, OAuthClient as et, createTodoStore as f, BackgroundAgentManager as g, createBackgroundAgentTools as h, createBlockerStore as i, mergeCommands as it, extractCompactInstructions as j, substituteArguments as k, formatDecisionsOutput as l, createCoordinateTaskTool as m, createReviewGateTool as n, processFileReferences as nt, formatBlockersOutput as o, searchFiles as ot, createWriteTodosTool as p, buildSkillsPromptSection as q, formatReviewGatesOutput as r, searchCommands as rt, createDecisionLogTool as s, warmFileCache as st, createReviewGateStore as t, hasFileReferences as tt, createGetFileStructureTool as u, AgentStore as v, WebSocketConnectionManager as w, parseAgentConfig as x, SubagentOrchestrator as y, DEFAULT_MAX_ITERATIONS as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bike4mind/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -118,11 +118,11 @@
|
|
|
118
118
|
"tsx": "^4.21.0",
|
|
119
119
|
"typescript": "^5.9.3",
|
|
120
120
|
"vitest": "^4.1.2",
|
|
121
|
-
"@bike4mind/agents": "0.
|
|
121
|
+
"@bike4mind/agents": "0.7.0",
|
|
122
122
|
"@bike4mind/common": "2.92.1",
|
|
123
|
-
"@bike4mind/
|
|
124
|
-
"@bike4mind/
|
|
125
|
-
"@bike4mind/
|
|
123
|
+
"@bike4mind/services": "2.79.4",
|
|
124
|
+
"@bike4mind/utils": "2.19.3",
|
|
125
|
+
"@bike4mind/mcp": "1.37.3"
|
|
126
126
|
},
|
|
127
127
|
"optionalDependencies": {
|
|
128
128
|
"@vscode/ripgrep": "^1.17.1"
|
|
@@ -134,6 +134,7 @@
|
|
|
134
134
|
"test": "vitest run",
|
|
135
135
|
"test:watch": "vitest",
|
|
136
136
|
"start": "node dist/index.mjs",
|
|
137
|
+
"eval:run": "tsx src/evals/cli.ts",
|
|
137
138
|
"postinstall": "node -e \"try { require('better-sqlite3') } catch(e) { if(e.message.includes('bindings')) { console.log('\\n⚠️ Rebuilding better-sqlite3 native bindings...'); require('child_process').execSync('pnpm rebuild better-sqlite3', {stdio:'inherit'}) } }\""
|
|
138
139
|
}
|
|
139
140
|
}
|
package/dist/store-B_ILRSdP.mjs
DELETED