@bike4mind/cli 0.6.1 → 0.8.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-Bj1IOvWn.mjs} +32 -5
- 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 +243 -27
- package/dist/store-44C_Fvdb.mjs +3 -0
- package/dist/{store-B7-LLvvx.mjs → store-B0ImnWR4.mjs} +24 -2
- package/dist/{tools-BUs_OkJc.mjs → tools-Dg1HL5PO.mjs} +443 -30
- package/dist/{updateChecker-D-xoVcN3.mjs → updateChecker-Bbkc_8IL.mjs} +1 -1
- package/package.json +7 -6
- package/dist/store-B_ILRSdP.mjs +0 -3
|
@@ -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-Bj1IOvWn.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
|
|
@@ -2911,6 +3023,84 @@ const TOOL_WRITE_TODOS = "write_todos";
|
|
|
2911
3023
|
const TOOL_CREATE_DYNAMIC_AGENT = "create_dynamic_agent";
|
|
2912
3024
|
const EXPLORE_SUBAGENT_TYPE = "explore";
|
|
2913
3025
|
/**
|
|
3026
|
+
* Plan-mode section: tells the model that write tools are blocked and where to put the plan.
|
|
3027
|
+
* Appended dynamically when the user cycles into plan mode via Shift+Tab.
|
|
3028
|
+
*
|
|
3029
|
+
* The phased workflow (understand → clarify → design → present) mirrors Claude Code's
|
|
3030
|
+
* plan-mode prompting: research before designing, ask the user before assuming, and only
|
|
3031
|
+
* write the plan after ambiguities are resolved.
|
|
3032
|
+
*/
|
|
3033
|
+
function buildPlanModePromptSection(planModeFilePath) {
|
|
3034
|
+
return `
|
|
3035
|
+
|
|
3036
|
+
## PLAN MODE ACTIVE
|
|
3037
|
+
|
|
3038
|
+
The user has cycled into plan mode (Shift+Tab). Plan mode restricts WRITING, not READING. You still have a complete read toolkit — use it.
|
|
3039
|
+
|
|
3040
|
+
**Tools available in plan mode (use these aggressively):**
|
|
3041
|
+
- \`grep_search\` — find symbols, strings, patterns across files
|
|
3042
|
+
- \`glob_files\` — list files by pattern (use this instead of \`ls\` / \`find\`)
|
|
3043
|
+
- \`file_read\` — read file contents
|
|
3044
|
+
- \`find_definition\` — locate where a symbol is defined
|
|
3045
|
+
- \`get_file_structure\` — AST overview of a file
|
|
3046
|
+
- \`agent_delegate\` — delegate to read-only subagents (e.g. 'explore', 'plan')
|
|
3047
|
+
- \`ask_user_question\` — ask the user clarifying questions
|
|
3048
|
+
- \`current_datetime\`, \`math_evaluate\`, \`web_search\`, \`web_fetch\` — also fine
|
|
3049
|
+
|
|
3050
|
+
**Tools blocked in plan mode:**
|
|
3051
|
+
- \`bash_execute\`, \`edit_local_file\`, \`create_file\`, \`delete_file\` — and any other tool that mutates state.
|
|
3052
|
+
- Exception: \`create_file\` / \`edit_local_file\` targeting paths under \`${planModeFilePath.replace(/\/plan-[^/]+\.md$/, "/")}\` are allowed (that's where you write the plan).
|
|
3053
|
+
|
|
3054
|
+
**Forbidden responses:**
|
|
3055
|
+
- ❌ "I can't explore the directory because plan mode blocks shell commands."
|
|
3056
|
+
- ❌ "I'd need to run bash to check this."
|
|
3057
|
+
- ❌ Any variant of "plan mode prevents me from researching."
|
|
3058
|
+
- ✅ Instead: use \`glob_files\`, \`grep_search\`, \`file_read\`, or delegate to the \`explore\` subagent. Bash is not the only way to investigate code.
|
|
3059
|
+
|
|
3060
|
+
Ground every claim in files you have actually read. Do not write pseudocode in chat as a substitute for reading the real code.
|
|
3061
|
+
|
|
3062
|
+
Follow this phased workflow. Do not skip phases.
|
|
3063
|
+
|
|
3064
|
+
### Phase 1 — Understand
|
|
3065
|
+
- Read the relevant code before forming opinions. Use grep_search / glob_files / file_read directly for narrow lookups.
|
|
3066
|
+
- For broad or uncertain scope, delegate to the 'explore' subagent via agent_delegate so the main context stays focused.
|
|
3067
|
+
- Identify what already exists vs. what needs to be built. Reuse existing functions and patterns rather than proposing new ones.
|
|
3068
|
+
|
|
3069
|
+
### Phase 2 — Clarify (REQUIRED if anything is ambiguous)
|
|
3070
|
+
- If requirements are unclear, if there are multiple reasonable approaches, or if you would otherwise be guessing about user intent, **call the ask_user_question tool BEFORE writing the plan**.
|
|
3071
|
+
- Ask about: trade-offs the user should pick between, scope (which files / how broad), behavior on edge cases, naming, dependencies you would add.
|
|
3072
|
+
- Do NOT ask "is the plan ready?" or "should I proceed?" — those are decided by the user pressing Shift+Tab. Only ask substantive design questions.
|
|
3073
|
+
- If the request is fully unambiguous, skip this phase. Do not invent questions.
|
|
3074
|
+
|
|
3075
|
+
### Phase 3 — Design
|
|
3076
|
+
- For complex tasks, delegate to the 'plan' subagent via agent_delegate to get a structured implementation plan, then critique its output.
|
|
3077
|
+
- For simple tasks, design directly.
|
|
3078
|
+
|
|
3079
|
+
### Phase 4 — Write the plan
|
|
3080
|
+
Write the plan to \`${planModeFilePath}\` (writes to this path are permitted in plan mode). Build it incrementally — append sections as you research, do not wait until the end. Structure it as:
|
|
3081
|
+
|
|
3082
|
+
\`\`\`markdown
|
|
3083
|
+
## Context
|
|
3084
|
+
Why this change is being made — the problem, what prompted it, the intended outcome.
|
|
3085
|
+
|
|
3086
|
+
## Approach
|
|
3087
|
+
The chosen approach in 1-3 sentences. Mention rejected alternatives only if non-obvious.
|
|
3088
|
+
|
|
3089
|
+
## Files to change
|
|
3090
|
+
- path/to/file.ts — what changes and why
|
|
3091
|
+
- path/to/other.ts — what changes and why
|
|
3092
|
+
|
|
3093
|
+
## Reused existing code
|
|
3094
|
+
- function/module path — how it will be reused
|
|
3095
|
+
|
|
3096
|
+
## Verification
|
|
3097
|
+
How to confirm the change works end-to-end (commands, tests, manual steps).
|
|
3098
|
+
\`\`\`
|
|
3099
|
+
|
|
3100
|
+
### Phase 5 — Hand off
|
|
3101
|
+
Tell the user the plan is ready and where it lives (\`${planModeFilePath}\`). Summarize in 1-2 sentences. Do not ask for approval — the user will press Shift+Tab to exit plan mode and authorize execution.`;
|
|
3102
|
+
}
|
|
3103
|
+
/**
|
|
2914
3104
|
* Build the CLI system prompt with optional project context
|
|
2915
3105
|
* @param contextSection - Optional project-specific context to append (legacy string) or config object
|
|
2916
3106
|
* @param config - Configuration object for building context sections (when first param is string)
|
|
@@ -2922,6 +3112,7 @@ function buildCoreSystemPrompt(contextSection, config) {
|
|
|
2922
3112
|
let dynamicAgentSection = "";
|
|
2923
3113
|
let directoriesSection = "";
|
|
2924
3114
|
let featureModulesSection = "";
|
|
3115
|
+
let planModeSection = "";
|
|
2925
3116
|
if (typeof contextSection === "string") {
|
|
2926
3117
|
projectContextSection = contextSection;
|
|
2927
3118
|
if (config) {
|
|
@@ -2929,6 +3120,7 @@ function buildCoreSystemPrompt(contextSection, config) {
|
|
|
2929
3120
|
if (config.agentStore) agentDirectoryContext = config.agentStore.getDirectoryContext();
|
|
2930
3121
|
if (config.enableDynamicAgentCreation) dynamicAgentSection = buildDynamicAgentPromptSection();
|
|
2931
3122
|
if (config.featureModulePrompts) featureModulesSection = config.featureModulePrompts;
|
|
3123
|
+
if (config.planModeFilePath) planModeSection = buildPlanModePromptSection(config.planModeFilePath);
|
|
2932
3124
|
}
|
|
2933
3125
|
} else if (contextSection && typeof contextSection === "object") {
|
|
2934
3126
|
config = contextSection;
|
|
@@ -2938,6 +3130,7 @@ function buildCoreSystemPrompt(contextSection, config) {
|
|
|
2938
3130
|
if (config.enableDynamicAgentCreation) dynamicAgentSection = buildDynamicAgentPromptSection();
|
|
2939
3131
|
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\nTo access files in additional directories, pass the full path to the 'dir_path' parameter of file tools:\n- ${TOOL_GREP_SEARCH}(pattern="...", dir_path="/path/to/additional/dir")\n- ${TOOL_GLOB_FILES}(pattern="**/*.ts", dir_path="/path/to/additional/dir")\n- ${TOOL_FILE_READ}(path="/path/to/additional/dir/file.ts")\n\nWhen the user asks about content in an additional directory, search there first using the dir_path parameter.`;
|
|
2940
3132
|
if (config.featureModulePrompts) featureModulesSection = config.featureModulePrompts;
|
|
3133
|
+
if (config.planModeFilePath) planModeSection = buildPlanModePromptSection(config.planModeFilePath);
|
|
2941
3134
|
}
|
|
2942
3135
|
return `You are an autonomous AI assistant with access to tools. Your job is to help users by taking action and solving problems proactively.
|
|
2943
3136
|
|
|
@@ -3030,7 +3223,7 @@ EXAMPLES:
|
|
|
3030
3223
|
Remember: Use context from previous messages to understand follow-up questions.
|
|
3031
3224
|
|
|
3032
3225
|
DURABLE WORKFLOW TRACKING:
|
|
3033
|
-
You have tools for tracking decisions and
|
|
3226
|
+
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
3227
|
|
|
3035
3228
|
- 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
3229
|
|
|
@@ -3038,7 +3231,40 @@ You have tools for tracking decisions and blockers during your work. These creat
|
|
|
3038
3231
|
|
|
3039
3232
|
- resolve_blocker: When a blocker is cleared, record how it was resolved. Use the blocker ID from the track_blocker output.
|
|
3040
3233
|
|
|
3041
|
-
|
|
3234
|
+
- 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.
|
|
3235
|
+
|
|
3236
|
+
These tools are lightweight — use them naturally as part of your work, not as a ceremony.${directoriesSection}${projectContextSection}${skillsSection}${featureModulesSection}${planModeSection}`;
|
|
3237
|
+
}
|
|
3238
|
+
/**
|
|
3239
|
+
* Build the minimal-variant system prompt. Reuses the project context,
|
|
3240
|
+
* skills, additional-directories, and feature-module sections from
|
|
3241
|
+
* `buildCoreSystemPrompt` — those carry user-specific information the
|
|
3242
|
+
* model needs and are independent of the behavioral scaffolding.
|
|
3243
|
+
*/
|
|
3244
|
+
function buildMinimalSystemPrompt(config = {}) {
|
|
3245
|
+
let projectContextSection = "";
|
|
3246
|
+
let skillsSection = "";
|
|
3247
|
+
let directoriesSection = "";
|
|
3248
|
+
let featureModulesSection = "";
|
|
3249
|
+
let planModeSection = "";
|
|
3250
|
+
if (config.contextContent) projectContextSection = `\n\n## Project Context\n\nFollow these project-specific instructions:\n\n${config.contextContent}`;
|
|
3251
|
+
if (config.enableSkillTool !== false && config.customCommands && config.customCommands.length > 0) skillsSection = buildSkillsPromptSection(config.customCommands);
|
|
3252
|
+
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.`;
|
|
3253
|
+
if (config.featureModulePrompts) featureModulesSection = config.featureModulePrompts;
|
|
3254
|
+
if (config.planModeFilePath) planModeSection = buildPlanModePromptSection(config.planModeFilePath);
|
|
3255
|
+
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.
|
|
3256
|
+
|
|
3257
|
+
Guidelines:
|
|
3258
|
+
- Be concise in your responses.
|
|
3259
|
+
- Show file paths clearly when working with files.
|
|
3260
|
+
- 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}${planModeSection}`;
|
|
3261
|
+
}
|
|
3262
|
+
/**
|
|
3263
|
+
* Pick a system prompt by variant. The dispatch point for the
|
|
3264
|
+
* production CLI's `promptVariant` preference flag.
|
|
3265
|
+
*/
|
|
3266
|
+
function buildSystemPrompt(variant, config = {}) {
|
|
3267
|
+
return variant === "minimal" ? buildMinimalSystemPrompt(config) : buildCoreSystemPrompt(config);
|
|
3042
3268
|
}
|
|
3043
3269
|
/**
|
|
3044
3270
|
* Build the dynamic agent creation prompt section
|
|
@@ -3091,6 +3317,41 @@ You have access to the '${TOOL_CREATE_DYNAMIC_AGENT}' tool, which lets you creat
|
|
|
3091
3317
|
thoroughness: "quick"`;
|
|
3092
3318
|
}
|
|
3093
3319
|
//#endregion
|
|
3320
|
+
//#region src/utils/planMode.ts
|
|
3321
|
+
const PLAN_MODE_DIR = ".b4m-cli/plans";
|
|
3322
|
+
const PATH_TOOLS = new Set([
|
|
3323
|
+
"create_file",
|
|
3324
|
+
"edit_local_file",
|
|
3325
|
+
"delete_file"
|
|
3326
|
+
]);
|
|
3327
|
+
/**
|
|
3328
|
+
* Directory plan-mode artifacts are written to (under the working directory).
|
|
3329
|
+
* Writes to paths under this directory are permitted while plan mode is active.
|
|
3330
|
+
*/
|
|
3331
|
+
function getPlanModeFileDir(cwd = process.cwd()) {
|
|
3332
|
+
return path.resolve(cwd, PLAN_MODE_DIR);
|
|
3333
|
+
}
|
|
3334
|
+
/**
|
|
3335
|
+
* Default plan file for the current session. Sessions can override this if needed.
|
|
3336
|
+
*/
|
|
3337
|
+
function getPlanModeFilePath(sessionId, cwd = process.cwd()) {
|
|
3338
|
+
const safeId = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
3339
|
+
return path.join(getPlanModeFileDir(cwd), `plan-${safeId}.md`);
|
|
3340
|
+
}
|
|
3341
|
+
/**
|
|
3342
|
+
* Whether a write/edit/delete tool call targets a path inside the plan-mode directory.
|
|
3343
|
+
* Used to allow incremental plan-file writes while plan mode blocks other writes.
|
|
3344
|
+
*/
|
|
3345
|
+
function isWriteTargetingPlanFile(toolName, args, cwd = process.cwd()) {
|
|
3346
|
+
if (!PATH_TOOLS.has(toolName)) return false;
|
|
3347
|
+
const argPath = args?.path;
|
|
3348
|
+
if (typeof argPath !== "string" || argPath.length === 0) return false;
|
|
3349
|
+
const resolved = path.resolve(cwd, argPath);
|
|
3350
|
+
const planDir = getPlanModeFileDir(cwd);
|
|
3351
|
+
const rel = path.relative(planDir, resolved);
|
|
3352
|
+
return rel === "" || !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
3353
|
+
}
|
|
3354
|
+
//#endregion
|
|
3094
3355
|
//#region ../../b4m-core/utils/dist/imageGeneration/AIImageService.mjs
|
|
3095
3356
|
/**
|
|
3096
3357
|
* Abstract class for AI Image Service
|
|
@@ -5580,7 +5841,7 @@ z.object({
|
|
|
5580
5841
|
* @see https://platform.openai.com/docs/guides/function-calling
|
|
5581
5842
|
*/
|
|
5582
5843
|
const MAX_GOAL_LENGTH = 1e3;
|
|
5583
|
-
const MAX_DESCRIPTION_LENGTH = 2e3;
|
|
5844
|
+
const MAX_DESCRIPTION_LENGTH$1 = 2e3;
|
|
5584
5845
|
ChatModels.GPT5, ChatModels.GPT5_MINI, ChatModels.GPT5_NANO, ChatModels.GPT5_1, ChatModels.GPT5_2, ChatModels.GPT5_4, ChatModels.GPT5_4_MINI, ChatModels.GPT5_4_NANO, ChatModels.GPT5_5;
|
|
5585
5846
|
/**
|
|
5586
5847
|
* Invokes the image processor Lambda to convert and resize images
|
|
@@ -15061,7 +15322,8 @@ updateUserSchema.extend({
|
|
|
15061
15322
|
note: z.string(),
|
|
15062
15323
|
userName: z.string()
|
|
15063
15324
|
})).optional(),
|
|
15064
|
-
numReferralsAvailable: z.number().optional()
|
|
15325
|
+
numReferralsAvailable: z.number().optional(),
|
|
15326
|
+
disputePending: z.boolean().optional()
|
|
15065
15327
|
});
|
|
15066
15328
|
z.object({
|
|
15067
15329
|
username: z.string(),
|
|
@@ -15764,7 +16026,7 @@ z.object({
|
|
|
15764
16026
|
const questSchema = z.object({
|
|
15765
16027
|
id: z.string(),
|
|
15766
16028
|
title: z.string().min(1).max(255),
|
|
15767
|
-
description: z.string().max(MAX_DESCRIPTION_LENGTH),
|
|
16029
|
+
description: z.string().max(MAX_DESCRIPTION_LENGTH$1),
|
|
15768
16030
|
status: z.enum([
|
|
15769
16031
|
"not_started",
|
|
15770
16032
|
"in_progress",
|
|
@@ -15787,7 +16049,7 @@ const questResourceSchema = z.object({
|
|
|
15787
16049
|
});
|
|
15788
16050
|
z.object({
|
|
15789
16051
|
title: z.string().min(1).max(255),
|
|
15790
|
-
description: z.string().max(MAX_DESCRIPTION_LENGTH).optional(),
|
|
16052
|
+
description: z.string().max(MAX_DESCRIPTION_LENGTH$1).optional(),
|
|
15791
16053
|
goal: z.string().min(1).max(MAX_GOAL_LENGTH),
|
|
15792
16054
|
complexity: z.enum([
|
|
15793
16055
|
"beginner",
|
|
@@ -18827,9 +19089,18 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
|
|
|
18827
19089
|
});
|
|
18828
19090
|
return result;
|
|
18829
19091
|
}
|
|
19092
|
+
const { useCliStore } = await import("./store-44C_Fvdb.mjs");
|
|
19093
|
+
const interactionMode = useCliStore.getState().interactionMode;
|
|
19094
|
+
if (interactionMode === "plan" && !isReadOnlyTool(toolName) && !isWriteTargetingPlanFile(toolName, args)) {
|
|
19095
|
+
const result = `Tool "${toolName}" is blocked while plan mode is active. Plan mode is read-only — research the codebase, then write your plan to a file under ${getPlanModeFileDir()}/. The user will press Shift+Tab to exit plan mode and authorize execution.`;
|
|
19096
|
+
agentContext.observationQueue.push({
|
|
19097
|
+
toolName,
|
|
19098
|
+
result
|
|
19099
|
+
});
|
|
19100
|
+
return result;
|
|
19101
|
+
}
|
|
18830
19102
|
if (!permissionManager.needsPermission(toolName, { isSandboxed })) return executeAndRecord();
|
|
18831
|
-
|
|
18832
|
-
if (useCliStore.getState().autoAcceptEdits) return executeAndRecord();
|
|
19103
|
+
if (interactionMode === "auto-accept") return executeAndRecord();
|
|
18833
19104
|
const response = await showPermissionPrompt(toolName, effectiveArgs, await generateToolPreview(toolName, args, isSandboxed));
|
|
18834
19105
|
if (response.action === "deny") throw new PermissionDeniedError(toolName, args);
|
|
18835
19106
|
if (response.action === "allow-session") permissionManager.trustToolForSession(toolName);
|
|
@@ -23620,7 +23891,7 @@ function createGetFileStructureTool() {
|
|
|
23620
23891
|
* Validate log_decision parameters
|
|
23621
23892
|
* @throws Error if validation fails
|
|
23622
23893
|
*/
|
|
23623
|
-
function validateParams(args) {
|
|
23894
|
+
function validateParams$1(args) {
|
|
23624
23895
|
const params = args;
|
|
23625
23896
|
if (typeof params.summary !== "string" || params.summary.trim() === "") throw new Error("log_decision: summary must be a non-empty string");
|
|
23626
23897
|
if (typeof params.rationale !== "string" || params.rationale.trim() === "") throw new Error("log_decision: rationale must be a non-empty string");
|
|
@@ -23660,7 +23931,7 @@ function formatDecisionsOutput(decisions) {
|
|
|
23660
23931
|
function createDecisionLogTool(store) {
|
|
23661
23932
|
return {
|
|
23662
23933
|
toolFn: async (args) => {
|
|
23663
|
-
const params = validateParams(args);
|
|
23934
|
+
const params = validateParams$1(args);
|
|
23664
23935
|
const decision = {
|
|
23665
23936
|
id: v4(),
|
|
23666
23937
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -23845,4 +24116,146 @@ function createBlockerStore(onUpdate) {
|
|
|
23845
24116
|
};
|
|
23846
24117
|
}
|
|
23847
24118
|
//#endregion
|
|
23848
|
-
|
|
24119
|
+
//#region src/tools/reviewGateTool.ts
|
|
24120
|
+
const MAX_DESCRIPTION_LENGTH = 2e3;
|
|
24121
|
+
const MAX_RECOMMENDATION_LENGTH = 1e3;
|
|
24122
|
+
const MAX_OPTION_LENGTH = 500;
|
|
24123
|
+
const MAX_OPTIONS_COUNT = 10;
|
|
24124
|
+
/**
|
|
24125
|
+
* Validate request_review_gate parameters
|
|
24126
|
+
* @throws Error if validation fails
|
|
24127
|
+
*/
|
|
24128
|
+
function validateParams(args) {
|
|
24129
|
+
const params = args;
|
|
24130
|
+
if (typeof params.description !== "string" || params.description.trim() === "") throw new Error("request_review_gate: description must be a non-empty string");
|
|
24131
|
+
if (params.description.length > MAX_DESCRIPTION_LENGTH) throw new Error(`request_review_gate: description must be ${MAX_DESCRIPTION_LENGTH} characters or fewer`);
|
|
24132
|
+
if (params.options !== void 0) {
|
|
24133
|
+
if (!Array.isArray(params.options)) throw new Error("request_review_gate: options must be an array of strings");
|
|
24134
|
+
if (params.options.length > MAX_OPTIONS_COUNT) throw new Error(`request_review_gate: options must contain ${MAX_OPTIONS_COUNT} entries or fewer`);
|
|
24135
|
+
for (const opt of params.options) {
|
|
24136
|
+
if (typeof opt !== "string") throw new Error("request_review_gate: each option must be a string");
|
|
24137
|
+
if (opt.length > MAX_OPTION_LENGTH) throw new Error(`request_review_gate: each option must be ${MAX_OPTION_LENGTH} characters or fewer`);
|
|
24138
|
+
}
|
|
24139
|
+
}
|
|
24140
|
+
if (params.recommendation !== void 0) {
|
|
24141
|
+
if (typeof params.recommendation !== "string") throw new Error("request_review_gate: recommendation must be a string");
|
|
24142
|
+
if (params.recommendation.length > MAX_RECOMMENDATION_LENGTH) throw new Error(`request_review_gate: recommendation must be ${MAX_RECOMMENDATION_LENGTH} characters or fewer`);
|
|
24143
|
+
}
|
|
24144
|
+
const options = params.options?.map((o) => o.trim()).filter((o) => o.length > 0);
|
|
24145
|
+
return {
|
|
24146
|
+
description: params.description.trim(),
|
|
24147
|
+
options: options && options.length > 0 ? options : void 0,
|
|
24148
|
+
recommendation: typeof params.recommendation === "string" ? params.recommendation.trim() : void 0
|
|
24149
|
+
};
|
|
24150
|
+
}
|
|
24151
|
+
/**
|
|
24152
|
+
* Format review gates for display output
|
|
24153
|
+
*/
|
|
24154
|
+
function formatReviewGatesOutput(gates) {
|
|
24155
|
+
if (gates.length === 0) return "No review gates recorded in this session.";
|
|
24156
|
+
return gates.map((gate, index) => {
|
|
24157
|
+
const lines = [`${index + 1}. **${gate.description}**`, ` Status: ${gate.status}`];
|
|
24158
|
+
if (gate.recommendation) lines.push(` Recommendation: ${gate.recommendation}`);
|
|
24159
|
+
if (gate.options && gate.options.length > 0) {
|
|
24160
|
+
lines.push(" Options:");
|
|
24161
|
+
for (const opt of gate.options) lines.push(` • ${opt}`);
|
|
24162
|
+
}
|
|
24163
|
+
if (gate.userNote) lines.push(` Note: ${gate.userNote}`);
|
|
24164
|
+
const requested = new Date(gate.timestamp).toLocaleTimeString();
|
|
24165
|
+
lines.push(` Requested at: ${requested}`);
|
|
24166
|
+
if (gate.resolvedAt) {
|
|
24167
|
+
const resolved = new Date(gate.resolvedAt).toLocaleTimeString();
|
|
24168
|
+
lines.push(` Resolved at: ${resolved}`);
|
|
24169
|
+
}
|
|
24170
|
+
return lines.join("\n");
|
|
24171
|
+
}).join("\n\n");
|
|
24172
|
+
}
|
|
24173
|
+
/**
|
|
24174
|
+
* Create the request_review_gate tool.
|
|
24175
|
+
*
|
|
24176
|
+
* Pauses agent execution and prompts the user for explicit approval at a
|
|
24177
|
+
* significant decision point. The agent halts until the user responds.
|
|
24178
|
+
*
|
|
24179
|
+
* Decisions are persisted in the session's workflow state (`reviewGates`)
|
|
24180
|
+
* for audit trail and cross-session continuity.
|
|
24181
|
+
*/
|
|
24182
|
+
function createReviewGateTool(store, requestReviewFn) {
|
|
24183
|
+
return {
|
|
24184
|
+
toolFn: async (args) => {
|
|
24185
|
+
const params = validateParams(args);
|
|
24186
|
+
const id = v4();
|
|
24187
|
+
const requestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
24188
|
+
const response = await requestReviewFn({
|
|
24189
|
+
id,
|
|
24190
|
+
description: params.description,
|
|
24191
|
+
options: params.options,
|
|
24192
|
+
recommendation: params.recommendation
|
|
24193
|
+
});
|
|
24194
|
+
const trimmedNote = response.note?.trim();
|
|
24195
|
+
const entry = {
|
|
24196
|
+
id,
|
|
24197
|
+
timestamp: requestedAt,
|
|
24198
|
+
description: params.description,
|
|
24199
|
+
status: response.decision,
|
|
24200
|
+
resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24201
|
+
userNote: trimmedNote && trimmedNote.length > 0 ? trimmedNote : void 0,
|
|
24202
|
+
options: params.options,
|
|
24203
|
+
recommendation: params.recommendation
|
|
24204
|
+
};
|
|
24205
|
+
store.reviewGates.push(entry);
|
|
24206
|
+
if (store.onUpdate) store.onUpdate(store.reviewGates);
|
|
24207
|
+
const verdict = response.decision === "approved" ? "APPROVED" : "REJECTED";
|
|
24208
|
+
const noteText = entry.userNote ? `\nUser note: ${entry.userNote}` : "";
|
|
24209
|
+
return `Review gate ${verdict} [${id.slice(0, 8)}]: ${params.description}${noteText}`;
|
|
24210
|
+
},
|
|
24211
|
+
toolSchema: {
|
|
24212
|
+
name: "request_review_gate",
|
|
24213
|
+
description: `Pause execution and request explicit human approval at a significant decision point.
|
|
24214
|
+
|
|
24215
|
+
Review gates protect meaning. Stop before crossing decisions that affect interpretation, evidence, cost, credentials, platform, or public commitment.
|
|
24216
|
+
|
|
24217
|
+
**When to use:**
|
|
24218
|
+
- Synthesizing findings before narrowing research scope
|
|
24219
|
+
- Hard-to-reverse decisions (refactors, architectural pivots, dependency swaps)
|
|
24220
|
+
- Decisions affecting cost, credentials, or external commitments
|
|
24221
|
+
- Major direction changes after exploration
|
|
24222
|
+
|
|
24223
|
+
**When NOT to use:**
|
|
24224
|
+
- Routine operations (reading files, running tests, listing directories)
|
|
24225
|
+
- Operations already covered by the standard permission system (file edits, bash commands)
|
|
24226
|
+
- Trivial choices that wouldn't matter to someone resuming this work
|
|
24227
|
+
|
|
24228
|
+
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.`,
|
|
24229
|
+
parameters: {
|
|
24230
|
+
type: "object",
|
|
24231
|
+
properties: {
|
|
24232
|
+
description: {
|
|
24233
|
+
type: "string",
|
|
24234
|
+
description: "Clear explanation of what the user is being asked to approve, including relevant context"
|
|
24235
|
+
},
|
|
24236
|
+
options: {
|
|
24237
|
+
type: "array",
|
|
24238
|
+
items: { type: "string" },
|
|
24239
|
+
description: "Optional list of alternatives the user can choose between"
|
|
24240
|
+
},
|
|
24241
|
+
recommendation: {
|
|
24242
|
+
type: "string",
|
|
24243
|
+
description: "Optional recommendation from the AI on the preferred path and why"
|
|
24244
|
+
}
|
|
24245
|
+
},
|
|
24246
|
+
required: ["description"]
|
|
24247
|
+
}
|
|
24248
|
+
}
|
|
24249
|
+
};
|
|
24250
|
+
}
|
|
24251
|
+
/**
|
|
24252
|
+
* Create a new empty ReviewGateStore
|
|
24253
|
+
*/
|
|
24254
|
+
function createReviewGateStore(onUpdate) {
|
|
24255
|
+
return {
|
|
24256
|
+
reviewGates: [],
|
|
24257
|
+
onUpdate
|
|
24258
|
+
};
|
|
24259
|
+
}
|
|
24260
|
+
//#endregion
|
|
24261
|
+
export { CommandHistoryStore 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, buildSkillsPromptSection as J, getPlanModeFilePath as K, ALWAYS_DENIED_FOR_AGENTS as L, loadContextFiles as M, getApiUrl as N, McpManager as O, getEnvironmentName as P, CheckpointStore as Q, DEFAULT_AGENT_MODEL as R, ApiClient as S, FallbackLlmBackend as T, registerFeatureModuleTools as U, DEFAULT_THOROUGHNESS as V, setWebSocketToolExecutor as W, ReActAgent as X, isReadOnlyTool as Y, CustomCommandStore as Z, createAgentDelegateTool as _, createBlockerTools as a, mergeCommands as at, createSkillTool as b, createDecisionStore as c, warmFileCache as ct, createFindDefinitionTool as d, SessionStore as et, createTodoStore as f, BackgroundAgentManager as g, createBackgroundAgentTools as h, createBlockerStore as i, searchCommands as it, extractCompactInstructions as j, substituteArguments as k, formatDecisionsOutput as l, createCoordinateTaskTool as m, createReviewGateTool as n, hasFileReferences as nt, formatBlockersOutput as o, formatFileSize$1 as ot, createWriteTodosTool as p, buildSystemPrompt as q, formatReviewGatesOutput as r, processFileReferences as rt, createDecisionLogTool as s, searchFiles as st, createReviewGateStore as t, OAuthClient as tt, createGetFileStructureTool as u, AgentStore as v, WebSocketConnectionManager as w, parseAgentConfig as x, SubagentOrchestrator as y, DEFAULT_MAX_ITERATIONS as z };
|