@bike4mind/cli 0.5.0 → 0.6.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-DBUmvCfe.mjs → ConfigStore-Dt6utdSA.mjs} +53 -2
- package/dist/commands/doctorCommand.mjs +1 -1
- package/dist/commands/headlessCommand.mjs +2 -2
- package/dist/commands/mcpCommand.mjs +1 -1
- package/dist/commands/updateCommand.mjs +1 -1
- package/dist/index.mjs +288 -8
- package/dist/{tools-V5FJ83BJ.mjs → tools-dIVxi-6-.mjs} +170 -72
- package/dist/{updateChecker-Boy2GLk4.mjs → updateChecker-DZXWZfKF.mjs} +1 -1
- package/package.json +19 -19
|
@@ -1027,6 +1027,8 @@ const SreRepoConfigSchema = z.object({
|
|
|
1027
1027
|
maxFixesPerDay: z.number().min(0).default(5),
|
|
1028
1028
|
/** Max revision attempts before escalating to human */
|
|
1029
1029
|
maxRevisions: z.number().int().min(0).max(10).default(2),
|
|
1030
|
+
/** Max CI retry attempts before permanently failing (typecheck/apply-fix failures) */
|
|
1031
|
+
maxCiRetries: z.number().int().min(0).max(3).default(1),
|
|
1030
1032
|
/** Log actions without dispatching */
|
|
1031
1033
|
dryRun: z.boolean().default(false),
|
|
1032
1034
|
/** Comma-separated GitHub usernames to request as PR reviewers */
|
|
@@ -1035,6 +1037,12 @@ const SreRepoConfigSchema = z.object({
|
|
|
1035
1037
|
defaultBranch: z.string().default(""),
|
|
1036
1038
|
/** Build command for the workflow */
|
|
1037
1039
|
buildCommand: z.string().default(""),
|
|
1040
|
+
/**
|
|
1041
|
+
* Repository-specific instructions for the Diagnostician (max 2,000 chars).
|
|
1042
|
+
* For critical constraints that would cause broken fixes if unknown — e.g., "never modify X",
|
|
1043
|
+
* "all DB calls must go through Y wrapper". Not for general coding conventions (use CLAUDE.md).
|
|
1044
|
+
*/
|
|
1045
|
+
sreInstructions: z.string().max(2e3).default(""),
|
|
1038
1046
|
/** Files the Surgeon can modify (glob patterns). Base patterns always merged in. */
|
|
1039
1047
|
allowedFilePatterns: z.array(z.string()).default([]),
|
|
1040
1048
|
/** Files never auto-fixed (glob patterns) */
|
|
@@ -1062,11 +1070,11 @@ const SreRepoConfigSchema = z.object({
|
|
|
1062
1070
|
/** Token budget per analysis */
|
|
1063
1071
|
tokenBudget: z.object({
|
|
1064
1072
|
maxInputTokens: z.number().default(5e4),
|
|
1065
|
-
maxOutputTokens: z.number().default(
|
|
1073
|
+
maxOutputTokens: z.number().default(16e3),
|
|
1066
1074
|
maxGithubApiCalls: z.number().default(20)
|
|
1067
1075
|
}).default({
|
|
1068
1076
|
maxInputTokens: 5e4,
|
|
1069
|
-
maxOutputTokens:
|
|
1077
|
+
maxOutputTokens: 16e3,
|
|
1070
1078
|
maxGithubApiCalls: 20
|
|
1071
1079
|
}),
|
|
1072
1080
|
/** Error sources */
|
|
@@ -6280,6 +6288,49 @@ let FriendshipEvents = /* @__PURE__ */ function(FriendshipEvents) {
|
|
|
6280
6288
|
FriendshipEvents["FRIENDSHIP_CANCEL"] = "Friendship Cancelled";
|
|
6281
6289
|
return FriendshipEvents;
|
|
6282
6290
|
}({});
|
|
6291
|
+
/**
|
|
6292
|
+
* Overwatch Analytics Event Schema
|
|
6293
|
+
*
|
|
6294
|
+
* Cross-product event schema for the Overwatch marketing command center.
|
|
6295
|
+
* Products (VibesWire, B4M, StocksAndVibes, K2Kanji) emit these events
|
|
6296
|
+
* to a shared SQS queue. Overwatch consumes them and rolls up DAU/WAU/MAU.
|
|
6297
|
+
*
|
|
6298
|
+
* Unlike the B4M-internal IBaseEvent analytics events, this schema is
|
|
6299
|
+
* designed for cross-product use with Zod validation at both emission
|
|
6300
|
+
* and consumption boundaries.
|
|
6301
|
+
*/
|
|
6302
|
+
const OverwatchUtmSchema = z.object({
|
|
6303
|
+
source: z.string().max(128).optional(),
|
|
6304
|
+
medium: z.string().max(128).optional(),
|
|
6305
|
+
campaign: z.string().max(128).optional(),
|
|
6306
|
+
content: z.string().max(128).optional()
|
|
6307
|
+
});
|
|
6308
|
+
z.object({
|
|
6309
|
+
/** UUID for deduplication (SQS is at-least-once) */
|
|
6310
|
+
eventId: z.string().uuid(),
|
|
6311
|
+
/** Schema version for forward compatibility */
|
|
6312
|
+
schemaVersion: z.number().int().positive(),
|
|
6313
|
+
/** Product identifier: 'vibeswire', 'bike4mind', 'stocksandvibes', 'k2kanji', etc. */
|
|
6314
|
+
productId: z.string().min(1).max(64),
|
|
6315
|
+
/** Product's internal user ID */
|
|
6316
|
+
userId: z.string().min(1).max(256),
|
|
6317
|
+
/** Session identifier for retention/funnel analysis */
|
|
6318
|
+
sessionId: z.string().min(1).max(256),
|
|
6319
|
+
/** Event type: 'session_start', 'signup', 'feature_used', etc. */
|
|
6320
|
+
event: z.string().min(1).max(128),
|
|
6321
|
+
/** ISO 8601 timestamp */
|
|
6322
|
+
timestamp: z.string().datetime(),
|
|
6323
|
+
/** Where the user came from */
|
|
6324
|
+
referrer: z.string().url().refine((url) => /^https?:\/\//i.test(url), "referrer must be an http or https URL").max(2048).optional(),
|
|
6325
|
+
/** UTM attribution parameters */
|
|
6326
|
+
utm: OverwatchUtmSchema.optional(),
|
|
6327
|
+
/** Event-specific key-value data. Flat values only, max 1KB serialized. */
|
|
6328
|
+
metadata: z.record(z.string(), z.union([
|
|
6329
|
+
z.string(),
|
|
6330
|
+
z.number(),
|
|
6331
|
+
z.boolean()
|
|
6332
|
+
])).refine((v) => JSON.stringify(v).length <= 1024, "metadata must be ≤ 1KB serialized").optional()
|
|
6333
|
+
});
|
|
6283
6334
|
const InternalTeamMemberSchema = z.object({
|
|
6284
6335
|
name: z.string().min(1, "Name is required"),
|
|
6285
6336
|
phone: z.string().min(1, "Phone is required"),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as version, n as fetchLatestVersion, r as forceCheckForUpdate } from "../updateChecker-
|
|
2
|
+
import { i as version, n as fetchLatestVersion, r as forceCheckForUpdate } from "../updateChecker-DZXWZfKF.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 { A as getApiUrl, C as WebSocketLlmBackend, G as isReadOnlyTool, J as CheckpointStore, K as ReActAgent, M as PermissionManager, N as generateCliTools, S as FallbackLlmBackend, T as McpManager, U as buildCoreSystemPrompt, V as setWebSocketToolExecutor, X as SessionStore, _ as createSkillTool, b as WebSocketToolExecutor, c as createFindDefinitionTool, d as createCoordinateTaskTool, f as createBackgroundAgentTools, g as SubagentOrchestrator, h as AgentStore, k as loadContextFiles, l as createTodoStore, m as createAgentDelegateTool, p as BackgroundAgentManager, q as CustomCommandStore, s as createGetFileStructureTool, u as createWriteTodosTool, w as ServerLlmBackend, x as WebSocketConnectionManager, y as ApiClient } from "../tools-
|
|
3
|
-
import { n as logger, t as ConfigStore } from "../ConfigStore-
|
|
2
|
+
import { A as getApiUrl, C as WebSocketLlmBackend, G as isReadOnlyTool, J as CheckpointStore, K as ReActAgent, M as PermissionManager, N as generateCliTools, S as FallbackLlmBackend, T as McpManager, U as buildCoreSystemPrompt, V as setWebSocketToolExecutor, X as SessionStore, _ as createSkillTool, b as WebSocketToolExecutor, c as createFindDefinitionTool, d as createCoordinateTaskTool, f as createBackgroundAgentTools, g as SubagentOrchestrator, h as AgentStore, k as loadContextFiles, l as createTodoStore, m as createAgentDelegateTool, p as BackgroundAgentManager, q as CustomCommandStore, s as createGetFileStructureTool, u as createWriteTodosTool, w as ServerLlmBackend, x as WebSocketConnectionManager, y as ApiClient } from "../tools-dIVxi-6-.mjs";
|
|
3
|
+
import { n as logger, t as ConfigStore } from "../ConfigStore-Dt6utdSA.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";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as version, r as forceCheckForUpdate } from "../updateChecker-
|
|
2
|
+
import { i as version, r as forceCheckForUpdate } from "../updateChecker-DZXWZfKF.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
2
|
import { n as useCliStore, t as selectActiveBackgroundAgents } from "./store-B7-LLvvx.mjs";
|
|
3
|
-
import { $ as processFileReferences, A as getApiUrl, B as registerFeatureModuleTools, C as WebSocketLlmBackend, D as formatStep, E as substituteArguments, F as DEFAULT_AGENT_MODEL, G as isReadOnlyTool, H as OllamaBackend, I as DEFAULT_MAX_ITERATIONS, J as CheckpointStore, K as ReActAgent, L as DEFAULT_RETRY_CONFIG, M as PermissionManager, N as generateCliTools, O as extractCompactInstructions, P as ALWAYS_DENIED_FOR_AGENTS, Q as hasFileReferences, R as DEFAULT_THOROUGHNESS, S as FallbackLlmBackend, T as McpManager, U as buildCoreSystemPrompt, V as setWebSocketToolExecutor, W as buildSkillsPromptSection, X as SessionStore, Y as CommandHistoryStore, Z as OAuthClient, _ as createSkillTool, a as createDecisionStore, b as WebSocketToolExecutor, c as createFindDefinitionTool, d as createCoordinateTaskTool, et as searchCommands, f as createBackgroundAgentTools, g as SubagentOrchestrator, h as AgentStore, i as createDecisionLogTool, it as warmFileCache, j as getEnvironmentName, k as loadContextFiles, l as createTodoStore, m as createAgentDelegateTool, n as createBlockerTools, nt as formatFileSize, o as formatDecisionsOutput, p as BackgroundAgentManager, q as CustomCommandStore, r as formatBlockersOutput, rt as searchFiles, s as createGetFileStructureTool, t as createBlockerStore, tt as mergeCommands, u as createWriteTodosTool, v as parseAgentConfig, w as ServerLlmBackend, x as WebSocketConnectionManager, y as ApiClient, z as clearFeatureModuleTools } from "./tools-
|
|
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 { i as version, t as checkForUpdate } from "./updateChecker-
|
|
3
|
+
import { $ as processFileReferences, A as getApiUrl, B as registerFeatureModuleTools, C as WebSocketLlmBackend, D as formatStep, E as substituteArguments, F as DEFAULT_AGENT_MODEL, G as isReadOnlyTool, H as OllamaBackend, I as DEFAULT_MAX_ITERATIONS, J as CheckpointStore, K as ReActAgent, L as DEFAULT_RETRY_CONFIG, M as PermissionManager, N as generateCliTools, O as extractCompactInstructions, P as ALWAYS_DENIED_FOR_AGENTS, Q as hasFileReferences, R as DEFAULT_THOROUGHNESS, S as FallbackLlmBackend, T as McpManager, U as buildCoreSystemPrompt, V as setWebSocketToolExecutor, W as buildSkillsPromptSection, X as SessionStore, Y as CommandHistoryStore, Z as OAuthClient, _ as createSkillTool, a as createDecisionStore, b as WebSocketToolExecutor, c as createFindDefinitionTool, d as createCoordinateTaskTool, et as searchCommands, f as createBackgroundAgentTools, g as SubagentOrchestrator, h as AgentStore, i as createDecisionLogTool, it as warmFileCache, j as getEnvironmentName, k as loadContextFiles, l as createTodoStore, m as createAgentDelegateTool, n as createBlockerTools, nt as formatFileSize, o as formatDecisionsOutput, p as BackgroundAgentManager, q as CustomCommandStore, r as formatBlockersOutput, rt as searchFiles, s as createGetFileStructureTool, t as createBlockerStore, tt as mergeCommands, u as createWriteTodosTool, v as parseAgentConfig, w as ServerLlmBackend, x as WebSocketConnectionManager, y as ApiClient, z as clearFeatureModuleTools } from "./tools-dIVxi-6-.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-Dt6utdSA.mjs";
|
|
5
|
+
import { i as version, t as checkForUpdate } from "./updateChecker-DZXWZfKF.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";
|
|
@@ -2484,10 +2484,207 @@ function createCompactedSession(originalSession, summary, preservedMessages) {
|
|
|
2484
2484
|
totalTokens: 0,
|
|
2485
2485
|
totalCost: 0,
|
|
2486
2486
|
toolCallCount: 0,
|
|
2487
|
-
compactedFrom: originalSession.id
|
|
2487
|
+
compactedFrom: originalSession.id,
|
|
2488
|
+
...originalSession.metadata.workflow ? { workflow: originalSession.metadata.workflow } : {}
|
|
2488
2489
|
}
|
|
2489
2490
|
};
|
|
2490
2491
|
}
|
|
2492
|
+
/**
|
|
2493
|
+
* Prefix tag used to mark a system message as an injected handoff. Kept as a
|
|
2494
|
+
* single source of truth so the dedup-on-resume check and the system-message
|
|
2495
|
+
* builder cannot drift out of sync.
|
|
2496
|
+
*/
|
|
2497
|
+
const HANDOFF_MARKER = "[Session handoff from previous session]";
|
|
2498
|
+
const MAX_MESSAGE_CHARS = 2e3;
|
|
2499
|
+
/**
|
|
2500
|
+
* Cap on the number of conversation messages included in the handoff prompt.
|
|
2501
|
+
* Prevents unbounded prompt growth on long sessions; the most recent messages
|
|
2502
|
+
* are kept since they best reflect the session's current state. Decisions and
|
|
2503
|
+
* blockers from workflow state are still included in full above the excerpt.
|
|
2504
|
+
*/
|
|
2505
|
+
const MAX_CONVERSATION_MESSAGES = 50;
|
|
2506
|
+
const ROLE_LABELS = {
|
|
2507
|
+
user: "User",
|
|
2508
|
+
assistant: "Assistant",
|
|
2509
|
+
system: "System"
|
|
2510
|
+
};
|
|
2511
|
+
/**
|
|
2512
|
+
* Build a prompt instructing the LLM to produce a structured session handoff
|
|
2513
|
+
* as JSON. Incorporates decisions and blockers from the existing workflow state
|
|
2514
|
+
* so the handoff reflects durable state, not just chat history.
|
|
2515
|
+
*
|
|
2516
|
+
* Any previously-injected handoff message (from a prior /resume) is filtered
|
|
2517
|
+
* out — the LLM should produce a fresh handoff from the actual conversation,
|
|
2518
|
+
* not echo back a prior handoff sitting at the top of the message list.
|
|
2519
|
+
*
|
|
2520
|
+
* Returns an empty string for short sessions — callers should skip generation.
|
|
2521
|
+
*/
|
|
2522
|
+
function buildHandoffPrompt(session) {
|
|
2523
|
+
if (session.messages.length < 4) return "";
|
|
2524
|
+
const filtered = session.messages.filter((m) => !isInjectedHandoff(m));
|
|
2525
|
+
const conversation = filtered.length > MAX_CONVERSATION_MESSAGES ? filtered.slice(-MAX_CONVERSATION_MESSAGES) : filtered;
|
|
2526
|
+
let prompt = `You are generating a structured session handoff so the next session (or another agent) can pick up seamlessly without re-reading the full chat history.
|
|
2527
|
+
|
|
2528
|
+
Output a single JSON object — no prose, no markdown fences — with exactly these fields:
|
|
2529
|
+
|
|
2530
|
+
{
|
|
2531
|
+
"summary": "2-4 sentence overview of what this session accomplished and where it ended",
|
|
2532
|
+
"keyFindings": ["concise factual discoveries — e.g. 'auth bug is in middleware.ts:42, caused by missing token refresh'"],
|
|
2533
|
+
"nextSteps": ["concrete actions the next session should take, in priority order"],
|
|
2534
|
+
"pendingDecisions": ["open questions or trade-offs awaiting a decision"],
|
|
2535
|
+
"blockers": ["anything preventing progress — wait conditions, missing inputs, broken upstream"]
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2538
|
+
Rules:
|
|
2539
|
+
- Each list item is a single line, no nested structure.
|
|
2540
|
+
- Empty lists are fine — use [] when nothing applies.
|
|
2541
|
+
- Be specific: cite filenames, function names, error messages where relevant.
|
|
2542
|
+
- Do not invent context. Only include items grounded in the conversation or workflow state below.
|
|
2543
|
+
|
|
2544
|
+
`;
|
|
2545
|
+
prompt += appendWorkflowContext(session.metadata.workflow);
|
|
2546
|
+
prompt += `CONVERSATION:\n\n`;
|
|
2547
|
+
for (const msg of conversation) {
|
|
2548
|
+
const role = ROLE_LABELS[msg.role] || "System";
|
|
2549
|
+
const content = msg.content.length > MAX_MESSAGE_CHARS ? msg.content.slice(0, MAX_MESSAGE_CHARS) + "...[truncated]" : msg.content;
|
|
2550
|
+
prompt += `**${role}:** ${content}\n\n`;
|
|
2551
|
+
}
|
|
2552
|
+
prompt += `\nReturn only the JSON object.`;
|
|
2553
|
+
return prompt;
|
|
2554
|
+
}
|
|
2555
|
+
function appendWorkflowContext(workflow) {
|
|
2556
|
+
if (!workflow) return "";
|
|
2557
|
+
const sections = [];
|
|
2558
|
+
if (workflow.decisions.length > 0) {
|
|
2559
|
+
const lines = workflow.decisions.map((d) => `- ${d.summary} (rationale: ${d.rationale})`);
|
|
2560
|
+
sections.push(`LOGGED DECISIONS:\n${lines.join("\n")}`);
|
|
2561
|
+
}
|
|
2562
|
+
const openBlockers = workflow.blockers.filter((b) => b.status === "open");
|
|
2563
|
+
if (openBlockers.length > 0) {
|
|
2564
|
+
const lines = openBlockers.map((b) => `- ${b.description}`);
|
|
2565
|
+
sections.push(`OPEN BLOCKERS:\n${lines.join("\n")}`);
|
|
2566
|
+
}
|
|
2567
|
+
return sections.length > 0 ? `${sections.join("\n\n")}\n\n` : "";
|
|
2568
|
+
}
|
|
2569
|
+
/**
|
|
2570
|
+
* Parse a raw LLM response into a SessionHandoff.
|
|
2571
|
+
*
|
|
2572
|
+
* Tolerates fenced code blocks (```json ... ```) and surrounding prose by
|
|
2573
|
+
* extracting the first balanced JSON object. Returns null if no valid handoff
|
|
2574
|
+
* can be parsed — callers decide how to surface that to the user.
|
|
2575
|
+
*/
|
|
2576
|
+
function parseHandoffResponse(response) {
|
|
2577
|
+
const json = extractJsonObject(response);
|
|
2578
|
+
if (!json) return null;
|
|
2579
|
+
let parsed;
|
|
2580
|
+
try {
|
|
2581
|
+
parsed = JSON.parse(json);
|
|
2582
|
+
} catch {
|
|
2583
|
+
return null;
|
|
2584
|
+
}
|
|
2585
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
2586
|
+
const obj = parsed;
|
|
2587
|
+
const summary = typeof obj.summary === "string" ? obj.summary.trim() : "";
|
|
2588
|
+
if (!summary) return null;
|
|
2589
|
+
return {
|
|
2590
|
+
summary,
|
|
2591
|
+
keyFindings: toStringArray(obj.keyFindings),
|
|
2592
|
+
nextSteps: toStringArray(obj.nextSteps),
|
|
2593
|
+
pendingDecisions: toStringArray(obj.pendingDecisions),
|
|
2594
|
+
blockers: toStringArray(obj.blockers),
|
|
2595
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2596
|
+
};
|
|
2597
|
+
}
|
|
2598
|
+
function toStringArray(value) {
|
|
2599
|
+
if (!Array.isArray(value)) return [];
|
|
2600
|
+
return value.filter((v) => typeof v === "string" && v.trim().length > 0).map((v) => v.trim());
|
|
2601
|
+
}
|
|
2602
|
+
function extractJsonObject(text) {
|
|
2603
|
+
const trimmed = text.trim();
|
|
2604
|
+
if (!trimmed) return null;
|
|
2605
|
+
const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
2606
|
+
const candidate = fenced ? fenced[1].trim() : trimmed;
|
|
2607
|
+
const start = candidate.indexOf("{");
|
|
2608
|
+
if (start === -1) return null;
|
|
2609
|
+
let depth = 0;
|
|
2610
|
+
let inString = false;
|
|
2611
|
+
let escaped = false;
|
|
2612
|
+
for (let i = start; i < candidate.length; i++) {
|
|
2613
|
+
const ch = candidate[i];
|
|
2614
|
+
if (escaped) {
|
|
2615
|
+
escaped = false;
|
|
2616
|
+
continue;
|
|
2617
|
+
}
|
|
2618
|
+
if (ch === "\\" && inString) {
|
|
2619
|
+
escaped = true;
|
|
2620
|
+
continue;
|
|
2621
|
+
}
|
|
2622
|
+
if (ch === "\"") {
|
|
2623
|
+
inString = !inString;
|
|
2624
|
+
continue;
|
|
2625
|
+
}
|
|
2626
|
+
if (inString) continue;
|
|
2627
|
+
if (ch === "{") depth++;
|
|
2628
|
+
else if (ch === "}") {
|
|
2629
|
+
depth--;
|
|
2630
|
+
if (depth === 0) return candidate.slice(start, i + 1);
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
return null;
|
|
2634
|
+
}
|
|
2635
|
+
/**
|
|
2636
|
+
* Render a SessionHandoff for terminal display, including the generation
|
|
2637
|
+
* timestamp so the user knows how fresh the context is.
|
|
2638
|
+
*/
|
|
2639
|
+
function formatHandoffOutput(handoff) {
|
|
2640
|
+
return formatHandoff(handoff, { includeTimestamp: true });
|
|
2641
|
+
}
|
|
2642
|
+
/**
|
|
2643
|
+
* Build the system message that injects handoff context into a resumed
|
|
2644
|
+
* session. Excludes the timestamp so the message text is stable across
|
|
2645
|
+
* regenerations — important for keeping LLM prompt caches warm.
|
|
2646
|
+
*/
|
|
2647
|
+
function buildHandoffSystemMessage(handoff) {
|
|
2648
|
+
return `${HANDOFF_MARKER}\n\n${formatHandoff(handoff, { includeTimestamp: false })}`;
|
|
2649
|
+
}
|
|
2650
|
+
function formatHandoff(handoff, options) {
|
|
2651
|
+
const lines = [handoff.summary, ""];
|
|
2652
|
+
appendSection(lines, "Key findings", handoff.keyFindings);
|
|
2653
|
+
appendSection(lines, "Next steps", handoff.nextSteps);
|
|
2654
|
+
appendSection(lines, "Pending decisions", handoff.pendingDecisions);
|
|
2655
|
+
appendSection(lines, "Blockers", handoff.blockers);
|
|
2656
|
+
if (options.includeTimestamp) lines.push(`Generated: ${handoff.generatedAt}`);
|
|
2657
|
+
else if (lines[lines.length - 1] === "") lines.pop();
|
|
2658
|
+
return lines.join("\n");
|
|
2659
|
+
}
|
|
2660
|
+
function appendSection(lines, heading, items) {
|
|
2661
|
+
if (items.length === 0) return;
|
|
2662
|
+
lines.push(`${heading}:`);
|
|
2663
|
+
for (const item of items) lines.push(` - ${item}`);
|
|
2664
|
+
lines.push("");
|
|
2665
|
+
}
|
|
2666
|
+
/**
|
|
2667
|
+
* True when a message is a previously-injected handoff system message.
|
|
2668
|
+
* Used to deduplicate handoff injections across save/resume cycles.
|
|
2669
|
+
*/
|
|
2670
|
+
function isInjectedHandoff(message) {
|
|
2671
|
+
return message.role === "system" && message.content.startsWith("[Session handoff from previous session]");
|
|
2672
|
+
}
|
|
2673
|
+
/**
|
|
2674
|
+
* Return a new message list with the handoff prepended as a system message.
|
|
2675
|
+
* Any previously-injected handoff anywhere in the list is removed so the
|
|
2676
|
+
* message list stays stable across repeated save/resume cycles. We scan the
|
|
2677
|
+
* whole list rather than just index 0 because compaction can prepend a
|
|
2678
|
+
* summary system message, pushing the prior handoff to a later index.
|
|
2679
|
+
*/
|
|
2680
|
+
function injectHandoffMessage(messages, handoff) {
|
|
2681
|
+
return [{
|
|
2682
|
+
id: v4(),
|
|
2683
|
+
role: "system",
|
|
2684
|
+
content: buildHandoffSystemMessage(handoff),
|
|
2685
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2686
|
+
}, ...messages.filter((m) => !isInjectedHandoff(m))];
|
|
2687
|
+
}
|
|
2491
2688
|
//#endregion
|
|
2492
2689
|
//#region src/utils/imageRenderer.ts
|
|
2493
2690
|
/**
|
|
@@ -6426,6 +6623,46 @@ function CliApp() {
|
|
|
6426
6623
|
console.log("");
|
|
6427
6624
|
}
|
|
6428
6625
|
};
|
|
6626
|
+
/**
|
|
6627
|
+
* Generate a structured session handoff via a single LLM call and persist it
|
|
6628
|
+
* onto the session's workflow state. Returns the handoff on success, or null
|
|
6629
|
+
* if generation was skipped (short session) or failed (parse / agent error).
|
|
6630
|
+
*
|
|
6631
|
+
* Failures are best-effort and surfaced as a warning rather than thrown —
|
|
6632
|
+
* the surrounding /save flow must not block on handoff generation.
|
|
6633
|
+
*
|
|
6634
|
+
* Callers are responsible for saving the session afterwards.
|
|
6635
|
+
*/
|
|
6636
|
+
const generateHandoff = async (session) => {
|
|
6637
|
+
if (!state.agent) return null;
|
|
6638
|
+
const prompt = buildHandoffPrompt(session);
|
|
6639
|
+
if (!prompt) return null;
|
|
6640
|
+
console.log("📝 Generating session handoff...");
|
|
6641
|
+
useCliStore.getState().setIsThinking(true);
|
|
6642
|
+
try {
|
|
6643
|
+
const result = await state.agent.run(prompt, { maxIterations: 1 });
|
|
6644
|
+
const handoff = parseHandoffResponse(result.finalAnswer);
|
|
6645
|
+
if (!handoff) {
|
|
6646
|
+
console.warn("⚠️ Handoff generation returned no parseable JSON; skipping.");
|
|
6647
|
+
logger.debug(`Handoff response: ${result.finalAnswer.slice(0, 500)}`);
|
|
6648
|
+
return null;
|
|
6649
|
+
}
|
|
6650
|
+
session.metadata.workflow = {
|
|
6651
|
+
decisions: decisionStoreRef.current.decisions,
|
|
6652
|
+
blockers: blockerStoreRef.current.blockers,
|
|
6653
|
+
handoff,
|
|
6654
|
+
reviewGates: session.metadata.workflow?.reviewGates
|
|
6655
|
+
};
|
|
6656
|
+
return handoff;
|
|
6657
|
+
} catch (err) {
|
|
6658
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
6659
|
+
console.warn(`⚠️ Handoff generation failed: ${reason}`);
|
|
6660
|
+
logger.debug(`Handoff generation error: ${reason}`);
|
|
6661
|
+
return null;
|
|
6662
|
+
} finally {
|
|
6663
|
+
useCliStore.getState().setIsThinking(false);
|
|
6664
|
+
}
|
|
6665
|
+
};
|
|
6429
6666
|
const handleCommand = async (command, args) => {
|
|
6430
6667
|
const customCommand = state.customCommandStore.getCommand(command);
|
|
6431
6668
|
if (customCommand) try {
|
|
@@ -6565,8 +6802,10 @@ Multi-line Input:
|
|
|
6565
6802
|
handoff: state.session.metadata.workflow?.handoff,
|
|
6566
6803
|
reviewGates: state.session.metadata.workflow?.reviewGates
|
|
6567
6804
|
};
|
|
6805
|
+
const handoff = await generateHandoff(state.session);
|
|
6568
6806
|
await state.sessionStore.save(state.session);
|
|
6569
6807
|
console.log(`✅ Session saved as "${sessionName}"`);
|
|
6808
|
+
if (handoff) console.log("🤝 Session handoff generated");
|
|
6570
6809
|
break;
|
|
6571
6810
|
}
|
|
6572
6811
|
case "resume":
|
|
@@ -6592,15 +6831,24 @@ Multi-line Input:
|
|
|
6592
6831
|
await logger.initialize(loadedSession.id);
|
|
6593
6832
|
logger.debug("=== Session Resumed ===");
|
|
6594
6833
|
if (state.checkpointStore) state.checkpointStore.setSessionId(loadedSession.id);
|
|
6834
|
+
const handoff = loadedSession.metadata.workflow?.handoff;
|
|
6835
|
+
const sessionForState = handoff ? {
|
|
6836
|
+
...loadedSession,
|
|
6837
|
+
messages: injectHandoffMessage(loadedSession.messages, handoff)
|
|
6838
|
+
} : loadedSession;
|
|
6595
6839
|
setState((prev) => ({
|
|
6596
6840
|
...prev,
|
|
6597
|
-
session:
|
|
6841
|
+
session: sessionForState
|
|
6598
6842
|
}));
|
|
6599
|
-
setStoreSession(
|
|
6843
|
+
setStoreSession(sessionForState);
|
|
6600
6844
|
useCliStore.getState().clearPendingMessages();
|
|
6601
6845
|
usageCache = null;
|
|
6602
|
-
console.log(`\n✅ Session resumed: "${
|
|
6603
|
-
console.log(`📝 ${
|
|
6846
|
+
console.log(`\n✅ Session resumed: "${sessionForState.name}"`);
|
|
6847
|
+
console.log(`📝 ${sessionForState.messages.length} messages | 🤖 ${sessionForState.model} | 📊 ${sessionForState.metadata.totalTokens.toLocaleString()} tokens\n`);
|
|
6848
|
+
if (handoff) {
|
|
6849
|
+
console.log("🤝 Session handoff:\n");
|
|
6850
|
+
console.log(formatHandoffOutput(handoff));
|
|
6851
|
+
}
|
|
6604
6852
|
};
|
|
6605
6853
|
setState((prev) => ({
|
|
6606
6854
|
...prev,
|
|
@@ -7528,6 +7776,38 @@ Multi-line Input:
|
|
|
7528
7776
|
console.log(formatBlockersOutput(blockerStoreRef.current.blockers));
|
|
7529
7777
|
console.log("");
|
|
7530
7778
|
break;
|
|
7779
|
+
case "handoff": {
|
|
7780
|
+
if (!state.session) {
|
|
7781
|
+
console.log("No active session");
|
|
7782
|
+
break;
|
|
7783
|
+
}
|
|
7784
|
+
const existing = state.session.metadata.workflow?.handoff;
|
|
7785
|
+
const wantsRegen = args[0] === "generate" || args[0] === "regen";
|
|
7786
|
+
if (existing && !wantsRegen) {
|
|
7787
|
+
console.log("\n🤝 Session handoff\n");
|
|
7788
|
+
console.log(formatHandoffOutput(existing));
|
|
7789
|
+
console.log("Run /handoff generate to refresh.\n");
|
|
7790
|
+
break;
|
|
7791
|
+
}
|
|
7792
|
+
if (state.session.messages.length < 4) {
|
|
7793
|
+
console.log(`Not enough messages to generate a handoff (need at least 4)`);
|
|
7794
|
+
break;
|
|
7795
|
+
}
|
|
7796
|
+
if (!state.agent) {
|
|
7797
|
+
console.log("Cannot generate handoff: no active agent");
|
|
7798
|
+
break;
|
|
7799
|
+
}
|
|
7800
|
+
const handoff = await generateHandoff(state.session);
|
|
7801
|
+
if (!handoff) {
|
|
7802
|
+
console.log("❌ Failed to generate handoff");
|
|
7803
|
+
break;
|
|
7804
|
+
}
|
|
7805
|
+
await state.sessionStore.save(state.session);
|
|
7806
|
+
console.log("\n🤝 Session handoff\n");
|
|
7807
|
+
console.log(formatHandoffOutput(handoff));
|
|
7808
|
+
console.log("\n✅ Session saved with refreshed handoff");
|
|
7809
|
+
break;
|
|
7810
|
+
}
|
|
7531
7811
|
case "dirs": {
|
|
7532
7812
|
const additionalDirs = await state.configStore.getAdditionalDirectories();
|
|
7533
7813
|
const cwd = process.cwd();
|
|
@@ -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-Dt6utdSA.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";
|
|
@@ -530,6 +530,10 @@ const COMMANDS = [
|
|
|
530
530
|
{
|
|
531
531
|
name: "blockers",
|
|
532
532
|
description: "Show tracked blockers for current session"
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
name: "handoff",
|
|
536
|
+
description: "Show or generate the session handoff for cross-session continuity"
|
|
533
537
|
}
|
|
534
538
|
];
|
|
535
539
|
/**
|
|
@@ -3099,8 +3103,7 @@ var AIImageService = class {
|
|
|
3099
3103
|
}
|
|
3100
3104
|
};
|
|
3101
3105
|
//#endregion
|
|
3102
|
-
//#region ../../b4m-core/
|
|
3103
|
-
var BaseStorage = class {};
|
|
3106
|
+
//#region ../../b4m-core/observability/dist/index.mjs
|
|
3104
3107
|
var Logger = class Logger {
|
|
3105
3108
|
static globalInstance = new Logger();
|
|
3106
3109
|
metadata = {};
|
|
@@ -3304,6 +3307,9 @@ var Logger = class Logger {
|
|
|
3304
3307
|
blue: `\x1b[34m${args.join(" ")}\x1b[0m`
|
|
3305
3308
|
});
|
|
3306
3309
|
};
|
|
3310
|
+
//#endregion
|
|
3311
|
+
//#region ../../b4m-core/utils/dist/index.mjs
|
|
3312
|
+
var BaseStorage = class {};
|
|
3307
3313
|
dotenv.config();
|
|
3308
3314
|
/**
|
|
3309
3315
|
* Execute an array of async tasks either in parallel (with concurrency limiting)
|
|
@@ -4760,6 +4766,8 @@ var OpenAIBackend = class {
|
|
|
4760
4766
|
...options
|
|
4761
4767
|
};
|
|
4762
4768
|
const toolCallCount = options._internal?.toolCallCount ?? 0;
|
|
4769
|
+
const accumInputTokens = options._internal?.accumInputTokens ?? 0;
|
|
4770
|
+
const accumOutputTokens = options._internal?.accumOutputTokens ?? 0;
|
|
4763
4771
|
if (toolCallCount >= 10 && options.tools?.length) {
|
|
4764
4772
|
this.logger.warn(`⚠️ Max tool calls limit (10) reached. Disabling tools to prevent infinite loops.`);
|
|
4765
4773
|
await this.complete(model, messages, {
|
|
@@ -4942,7 +4950,9 @@ var OpenAIBackend = class {
|
|
|
4942
4950
|
tools: anyMcpTool ? options.tools : void 0,
|
|
4943
4951
|
_internal: {
|
|
4944
4952
|
...options._internal,
|
|
4945
|
-
toolCallCount: toolCallCount + 1
|
|
4953
|
+
toolCallCount: toolCallCount + 1,
|
|
4954
|
+
accumInputTokens: accumInputTokens + (response.usage?.prompt_tokens || 0),
|
|
4955
|
+
accumOutputTokens: accumOutputTokens + (response.usage?.completion_tokens || 0)
|
|
4946
4956
|
}
|
|
4947
4957
|
}, recursiveCallback, toolsUsed);
|
|
4948
4958
|
if (anyArtifactWasStreamed && recursiveBuffer) {
|
|
@@ -4953,8 +4963,8 @@ var OpenAIBackend = class {
|
|
|
4953
4963
|
} else {
|
|
4954
4964
|
this.logger.debug(`[Tool Execution] executeTools=false, passing tool calls to callback`);
|
|
4955
4965
|
await callback([null], {
|
|
4956
|
-
inputTokens: response.usage?.prompt_tokens || 0,
|
|
4957
|
-
outputTokens: response.usage?.completion_tokens || 0,
|
|
4966
|
+
inputTokens: accumInputTokens + (response.usage?.prompt_tokens || 0),
|
|
4967
|
+
outputTokens: accumOutputTokens + (response.usage?.completion_tokens || 0),
|
|
4958
4968
|
toolsUsed: toolsUsed.length > 0 ? toolsUsed : void 0
|
|
4959
4969
|
});
|
|
4960
4970
|
return;
|
|
@@ -4968,8 +4978,8 @@ var OpenAIBackend = class {
|
|
|
4968
4978
|
if (cacheStats) logCacheStats(this.logger, cacheStats, { streaming: false });
|
|
4969
4979
|
}
|
|
4970
4980
|
await callback(streamedText, {
|
|
4971
|
-
inputTokens: response.usage?.prompt_tokens || 0,
|
|
4972
|
-
outputTokens: response.usage?.completion_tokens || 0,
|
|
4981
|
+
inputTokens: accumInputTokens + (response.usage?.prompt_tokens || 0),
|
|
4982
|
+
outputTokens: accumOutputTokens + (response.usage?.completion_tokens || 0),
|
|
4973
4983
|
toolsUsed: toolsUsed.length > 0 ? toolsUsed : void 0,
|
|
4974
4984
|
cacheStats
|
|
4975
4985
|
});
|
|
@@ -5008,8 +5018,8 @@ var OpenAIBackend = class {
|
|
|
5008
5018
|
if (c.delta.content) streamedText[c.index] = (streamedText[c.index] || "") + c.delta.content;
|
|
5009
5019
|
});
|
|
5010
5020
|
await callback(streamedText, {
|
|
5011
|
-
inputTokens,
|
|
5012
|
-
outputTokens,
|
|
5021
|
+
inputTokens: accumInputTokens + inputTokens,
|
|
5022
|
+
outputTokens: accumOutputTokens + outputTokens,
|
|
5013
5023
|
toolsUsed: toolsUsed.length > 0 ? toolsUsed : void 0
|
|
5014
5024
|
});
|
|
5015
5025
|
}
|
|
@@ -5114,8 +5124,8 @@ var OpenAIBackend = class {
|
|
|
5114
5124
|
thisToolHadArtifact = true;
|
|
5115
5125
|
anyArtifactWasStreamed = true;
|
|
5116
5126
|
await callback(results, {
|
|
5117
|
-
inputTokens,
|
|
5118
|
-
outputTokens,
|
|
5127
|
+
inputTokens: accumInputTokens + inputTokens,
|
|
5128
|
+
outputTokens: accumOutputTokens + outputTokens,
|
|
5119
5129
|
toolsUsed: toolsUsed.length > 0 ? toolsUsed : void 0,
|
|
5120
5130
|
cacheStats
|
|
5121
5131
|
});
|
|
@@ -5138,7 +5148,9 @@ var OpenAIBackend = class {
|
|
|
5138
5148
|
tools: anyMcpTool ? options.tools : void 0,
|
|
5139
5149
|
_internal: {
|
|
5140
5150
|
...options._internal,
|
|
5141
|
-
toolCallCount: toolCallCount + 1
|
|
5151
|
+
toolCallCount: toolCallCount + 1,
|
|
5152
|
+
accumInputTokens: accumInputTokens + inputTokens,
|
|
5153
|
+
accumOutputTokens: accumOutputTokens + outputTokens
|
|
5142
5154
|
}
|
|
5143
5155
|
}, async (results, meta) => {
|
|
5144
5156
|
for (const r of results) if (r != null) recursiveBuffer += r;
|
|
@@ -5151,14 +5163,16 @@ var OpenAIBackend = class {
|
|
|
5151
5163
|
tools: anyMcpTool ? options.tools : void 0,
|
|
5152
5164
|
_internal: {
|
|
5153
5165
|
...options._internal,
|
|
5154
|
-
toolCallCount: toolCallCount + 1
|
|
5166
|
+
toolCallCount: toolCallCount + 1,
|
|
5167
|
+
accumInputTokens: accumInputTokens + inputTokens,
|
|
5168
|
+
accumOutputTokens: accumOutputTokens + outputTokens
|
|
5155
5169
|
}
|
|
5156
5170
|
}, callback, toolsUsed);
|
|
5157
5171
|
} else {
|
|
5158
5172
|
this.logger.debug(`[Tool Execution] executeTools=false, passing tool calls to callback`);
|
|
5159
5173
|
await callback([null], {
|
|
5160
|
-
inputTokens,
|
|
5161
|
-
outputTokens,
|
|
5174
|
+
inputTokens: accumInputTokens + inputTokens,
|
|
5175
|
+
outputTokens: accumOutputTokens + outputTokens,
|
|
5162
5176
|
toolsUsed: toolsUsed.length > 0 ? toolsUsed : void 0,
|
|
5163
5177
|
cacheStats
|
|
5164
5178
|
});
|
|
@@ -6859,17 +6873,49 @@ const webFetchTool = {
|
|
|
6859
6873
|
})
|
|
6860
6874
|
};
|
|
6861
6875
|
//#endregion
|
|
6862
|
-
//#region ../../b4m-core/services/dist/jsonSanitize-
|
|
6876
|
+
//#region ../../b4m-core/services/dist/jsonSanitize-DPYPbara.mjs
|
|
6863
6877
|
/**
|
|
6864
6878
|
* Sanitize a JSON string by escaping control characters inside string literals.
|
|
6865
6879
|
* LLMs sometimes return JSON with unescaped newlines/tabs inside string values,
|
|
6866
6880
|
* which causes JSON.parse to fail with "Bad control character in string literal".
|
|
6867
6881
|
*
|
|
6868
6882
|
* Covers all U+0000–U+001F control characters per JSON RFC 8259.
|
|
6883
|
+
*
|
|
6884
|
+
* Also applies a lookahead heuristic to escape unescaped double-quotes inside
|
|
6885
|
+
* string values — the most common LLM JSON failure mode (e.g., code snippets
|
|
6886
|
+
* containing logger.error("msg") inside a JSON string).
|
|
6887
|
+
*
|
|
6888
|
+
* Returns the sanitized string and the number of quotes repaired.
|
|
6889
|
+
* If the JSON is already valid, it is returned unchanged (zero repairs).
|
|
6869
6890
|
*/
|
|
6870
6891
|
function sanitizeJsonString(jsonStr) {
|
|
6892
|
+
const { result } = sanitizeJsonStringWithMeta(jsonStr);
|
|
6893
|
+
return result;
|
|
6894
|
+
}
|
|
6895
|
+
function sanitizeJsonStringWithMeta(jsonStr, options) {
|
|
6896
|
+
try {
|
|
6897
|
+
JSON.parse(jsonStr);
|
|
6898
|
+
return {
|
|
6899
|
+
result: jsonStr,
|
|
6900
|
+
repairedQuotes: 0
|
|
6901
|
+
};
|
|
6902
|
+
} catch {}
|
|
6871
6903
|
let result = "";
|
|
6872
6904
|
let inString = false;
|
|
6905
|
+
/**
|
|
6906
|
+
* Tracks whether we are in a key position (true) or value position (false).
|
|
6907
|
+
* Starts as true — the first string in valid JSON must be a key.
|
|
6908
|
+
* Flips to false after a key-closing quote + colon pair.
|
|
6909
|
+
* Flips back to true after a value-closing quote (or structural char) + comma/`{`/`[`.
|
|
6910
|
+
*
|
|
6911
|
+
* This is used to disambiguate `:` after a `"` inside a string:
|
|
6912
|
+
* - In key position: `"` + `:` → structural (closes the key, colon follows)
|
|
6913
|
+
* - In value position: `"` + `:` → embedded (e.g., `"status": it was null"`)
|
|
6914
|
+
*/
|
|
6915
|
+
let inKey = true;
|
|
6916
|
+
/** Tracks nested structure context for truncation repair. */
|
|
6917
|
+
const structureStack = [];
|
|
6918
|
+
let repairedQuotes = 0;
|
|
6873
6919
|
let i = 0;
|
|
6874
6920
|
while (i < jsonStr.length) {
|
|
6875
6921
|
const char = jsonStr[i];
|
|
@@ -6879,8 +6925,25 @@ function sanitizeJsonString(jsonStr) {
|
|
|
6879
6925
|
continue;
|
|
6880
6926
|
}
|
|
6881
6927
|
if (char === "\"") {
|
|
6882
|
-
|
|
6883
|
-
|
|
6928
|
+
if (!inString) {
|
|
6929
|
+
inString = true;
|
|
6930
|
+
result += char;
|
|
6931
|
+
i++;
|
|
6932
|
+
continue;
|
|
6933
|
+
}
|
|
6934
|
+
let j = i + 1;
|
|
6935
|
+
while (j < jsonStr.length && (jsonStr[j] === " " || jsonStr[j] === " " || jsonStr[j] === "\r" || jsonStr[j] === "\n")) j++;
|
|
6936
|
+
const nextNonWs = j < jsonStr.length ? jsonStr[j] : "";
|
|
6937
|
+
if (nextNonWs === "," || nextNonWs === "}" || nextNonWs === "]" || nextNonWs === "" || nextNonWs === ":" && inKey) {
|
|
6938
|
+
inString = false;
|
|
6939
|
+
result += char;
|
|
6940
|
+
i++;
|
|
6941
|
+
if (nextNonWs === ":") inKey = false;
|
|
6942
|
+
else if (nextNonWs === "," || nextNonWs === "}" || nextNonWs === "]") inKey = true;
|
|
6943
|
+
continue;
|
|
6944
|
+
}
|
|
6945
|
+
result += "\\\"";
|
|
6946
|
+
repairedQuotes++;
|
|
6884
6947
|
i++;
|
|
6885
6948
|
continue;
|
|
6886
6949
|
}
|
|
@@ -6907,10 +6970,45 @@ function sanitizeJsonString(jsonStr) {
|
|
|
6907
6970
|
break;
|
|
6908
6971
|
}
|
|
6909
6972
|
else result += char;
|
|
6910
|
-
} else
|
|
6973
|
+
} else {
|
|
6974
|
+
result += char;
|
|
6975
|
+
if (char === "{") {
|
|
6976
|
+
structureStack.push("{");
|
|
6977
|
+
inKey = true;
|
|
6978
|
+
} else if (char === "[") {
|
|
6979
|
+
structureStack.push("[");
|
|
6980
|
+
inKey = false;
|
|
6981
|
+
} else if (char === "}" || char === "]") {
|
|
6982
|
+
if (structureStack.length > 0) structureStack.pop();
|
|
6983
|
+
inKey = (structureStack.length > 0 ? structureStack[structureStack.length - 1] : null) !== "[";
|
|
6984
|
+
} else if (char === ",") {
|
|
6985
|
+
if ((structureStack.length > 0 ? structureStack[structureStack.length - 1] : null) === "{") inKey = true;
|
|
6986
|
+
}
|
|
6987
|
+
}
|
|
6911
6988
|
i++;
|
|
6912
6989
|
}
|
|
6913
|
-
|
|
6990
|
+
if (inString) {
|
|
6991
|
+
if (options?.attemptTruncationRepair && structureStack.length > 0) {
|
|
6992
|
+
const closing = [...structureStack].reverse().map((c) => c === "{" ? "}" : "]").join("");
|
|
6993
|
+
const salvage = result + "\"" + closing;
|
|
6994
|
+
try {
|
|
6995
|
+
JSON.parse(salvage);
|
|
6996
|
+
return {
|
|
6997
|
+
result: salvage,
|
|
6998
|
+
repairedQuotes,
|
|
6999
|
+
truncationRepaired: true
|
|
7000
|
+
};
|
|
7001
|
+
} catch {}
|
|
7002
|
+
}
|
|
7003
|
+
return {
|
|
7004
|
+
result: jsonStr,
|
|
7005
|
+
repairedQuotes: 0
|
|
7006
|
+
};
|
|
7007
|
+
}
|
|
7008
|
+
return {
|
|
7009
|
+
result,
|
|
7010
|
+
repairedQuotes
|
|
7011
|
+
};
|
|
6914
7012
|
}
|
|
6915
7013
|
//#endregion
|
|
6916
7014
|
//#region ../../b4m-core/quantum/dist/index.mjs
|
|
@@ -8758,7 +8856,7 @@ No markdown, no explanation, no code blocks — just the raw JSON object.`;
|
|
|
8758
8856
|
"Minimize: sum(error^2)"
|
|
8759
8857
|
].join("\n");
|
|
8760
8858
|
//#endregion
|
|
8761
|
-
//#region ../../b4m-core/services/dist/tools-
|
|
8859
|
+
//#region ../../b4m-core/services/dist/tools-B55E_Q3K.mjs
|
|
8762
8860
|
async function performDeepResearch(context, params, config) {
|
|
8763
8861
|
const maxDepth = config.maxDepth || 7;
|
|
8764
8862
|
const duration = config.duration || 4.5;
|
|
@@ -15798,56 +15896,6 @@ z.object({
|
|
|
15798
15896
|
embargoDetected: z.boolean().prefault(false),
|
|
15799
15897
|
suggestedTags: z.array(z.string()).prefault([])
|
|
15800
15898
|
});
|
|
15801
|
-
z.object({
|
|
15802
|
-
userId: z.string(),
|
|
15803
|
-
sessionId: z.string(),
|
|
15804
|
-
questId: z.string(),
|
|
15805
|
-
message: z.string().min(1, "Message cannot be empty"),
|
|
15806
|
-
messageFileIds: z.array(z.string()),
|
|
15807
|
-
historyCount: z.number(),
|
|
15808
|
-
fabFileIds: z.array(z.string()),
|
|
15809
|
-
params: ChatCompletionCreateInputSchema,
|
|
15810
|
-
dashboardParams: DashboardParamsSchema.optional(),
|
|
15811
|
-
enableQuestMaster: z.boolean().optional(),
|
|
15812
|
-
enableMementos: z.boolean().optional(),
|
|
15813
|
-
enableArtifacts: z.boolean().optional(),
|
|
15814
|
-
enableAgents: z.boolean().optional(),
|
|
15815
|
-
enableLattice: z.boolean().optional(),
|
|
15816
|
-
promptMeta: PromptMetaZodSchema,
|
|
15817
|
-
tools: z.array(z.union([b4mLLMTools, z.string()])).optional(),
|
|
15818
|
-
mcpServers: z.array(z.string()).optional(),
|
|
15819
|
-
projectId: z.string().optional(),
|
|
15820
|
-
organizationId: z.string().nullable().optional(),
|
|
15821
|
-
questMaster: QuestMasterParamsSchema.optional(),
|
|
15822
|
-
toolPromptId: z.string().optional(),
|
|
15823
|
-
researchMode: ResearchModeParamsSchema.optional(),
|
|
15824
|
-
fallbackModel: z.string().optional(),
|
|
15825
|
-
embeddingModel: z.string().optional(),
|
|
15826
|
-
queryComplexity: z.string(),
|
|
15827
|
-
imageConfig: GenerateImageToolCallSchema.optional(),
|
|
15828
|
-
deepResearchConfig: z.object({
|
|
15829
|
-
maxDepth: z.number().optional(),
|
|
15830
|
-
duration: z.number().optional(),
|
|
15831
|
-
searchers: z.array(z.any()).optional()
|
|
15832
|
-
}).optional(),
|
|
15833
|
-
extraContextMessages: z.array(z.object({
|
|
15834
|
-
role: z.enum([
|
|
15835
|
-
"user",
|
|
15836
|
-
"assistant",
|
|
15837
|
-
"system",
|
|
15838
|
-
"function",
|
|
15839
|
-
"tool"
|
|
15840
|
-
]),
|
|
15841
|
-
content: z.union([z.string(), z.array(z.any())]),
|
|
15842
|
-
fabFileIds: z.array(z.string()).optional()
|
|
15843
|
-
})).optional(),
|
|
15844
|
-
/** User's timezone (IANA format, e.g., "America/New_York") */
|
|
15845
|
-
timezone: z.string().optional(),
|
|
15846
|
-
/** Persona-based sub-agent filter — only these agent names are available for delegation */
|
|
15847
|
-
allowedAgents: z.array(z.string()).optional(),
|
|
15848
|
-
/** When true, Quest Processor injects Slack-specific tool configs (help, notebooks, curated files) */
|
|
15849
|
-
enableSlackTools: z.boolean().optional()
|
|
15850
|
-
});
|
|
15851
15899
|
/**
|
|
15852
15900
|
* Regex patterns for extracting GitHub entities from text
|
|
15853
15901
|
*/
|
|
@@ -16345,6 +16393,56 @@ var CombinedExtractor = class {
|
|
|
16345
16393
|
}
|
|
16346
16394
|
};
|
|
16347
16395
|
new CombinedExtractor();
|
|
16396
|
+
z.object({
|
|
16397
|
+
userId: z.string(),
|
|
16398
|
+
sessionId: z.string(),
|
|
16399
|
+
questId: z.string(),
|
|
16400
|
+
message: z.string().min(1, "Message cannot be empty"),
|
|
16401
|
+
messageFileIds: z.array(z.string()),
|
|
16402
|
+
historyCount: z.number(),
|
|
16403
|
+
fabFileIds: z.array(z.string()),
|
|
16404
|
+
params: ChatCompletionCreateInputSchema,
|
|
16405
|
+
dashboardParams: DashboardParamsSchema.optional(),
|
|
16406
|
+
enableQuestMaster: z.boolean().optional(),
|
|
16407
|
+
enableMementos: z.boolean().optional(),
|
|
16408
|
+
enableArtifacts: z.boolean().optional(),
|
|
16409
|
+
enableAgents: z.boolean().optional(),
|
|
16410
|
+
enableLattice: z.boolean().optional(),
|
|
16411
|
+
promptMeta: PromptMetaZodSchema,
|
|
16412
|
+
tools: z.array(z.union([b4mLLMTools, z.string()])).optional(),
|
|
16413
|
+
mcpServers: z.array(z.string()).optional(),
|
|
16414
|
+
projectId: z.string().optional(),
|
|
16415
|
+
organizationId: z.string().nullable().optional(),
|
|
16416
|
+
questMaster: QuestMasterParamsSchema.optional(),
|
|
16417
|
+
toolPromptId: z.string().optional(),
|
|
16418
|
+
researchMode: ResearchModeParamsSchema.optional(),
|
|
16419
|
+
fallbackModel: z.string().optional(),
|
|
16420
|
+
embeddingModel: z.string().optional(),
|
|
16421
|
+
queryComplexity: z.string(),
|
|
16422
|
+
imageConfig: GenerateImageToolCallSchema.optional(),
|
|
16423
|
+
deepResearchConfig: z.object({
|
|
16424
|
+
maxDepth: z.number().optional(),
|
|
16425
|
+
duration: z.number().optional(),
|
|
16426
|
+
searchers: z.array(z.any()).optional()
|
|
16427
|
+
}).optional(),
|
|
16428
|
+
extraContextMessages: z.array(z.object({
|
|
16429
|
+
role: z.enum([
|
|
16430
|
+
"user",
|
|
16431
|
+
"assistant",
|
|
16432
|
+
"system",
|
|
16433
|
+
"function",
|
|
16434
|
+
"tool"
|
|
16435
|
+
]),
|
|
16436
|
+
content: z.union([z.string(), z.array(z.any())]),
|
|
16437
|
+
fabFileIds: z.array(z.string()).optional()
|
|
16438
|
+
})).optional(),
|
|
16439
|
+
/** User's timezone (IANA format, e.g., "America/New_York") */
|
|
16440
|
+
timezone: z.string().optional(),
|
|
16441
|
+
/** Persona-based sub-agent filter — only these agent names are available for delegation */
|
|
16442
|
+
allowedAgents: z.array(z.string()).optional(),
|
|
16443
|
+
/** When true, Quest Processor injects Slack-specific tool configs (help, notebooks, curated files) */
|
|
16444
|
+
enableSlackTools: z.boolean().optional()
|
|
16445
|
+
});
|
|
16348
16446
|
(class AnomalyAlertService {
|
|
16349
16447
|
static {
|
|
16350
16448
|
this.MAX_CACHE_ENTRIES = 1e3;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bike4mind/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -32,15 +32,15 @@
|
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@anthropic-ai/sdk": "^0.79.0",
|
|
35
|
-
"@aws-sdk/client-apigatewaymanagementapi": "^3.
|
|
36
|
-
"@aws-sdk/client-bedrock-runtime": "^3.
|
|
37
|
-
"@aws-sdk/client-cloudwatch": "^3.
|
|
38
|
-
"@aws-sdk/client-lambda": "^3.
|
|
39
|
-
"@aws-sdk/client-s3": "^3.
|
|
40
|
-
"@aws-sdk/client-sqs": "^3.
|
|
41
|
-
"@aws-sdk/client-transcribe": "^3.
|
|
42
|
-
"@aws-sdk/credential-provider-node": "^3.972.
|
|
43
|
-
"@aws-sdk/s3-request-presigner": "^3.
|
|
35
|
+
"@aws-sdk/client-apigatewaymanagementapi": "^3.1041.0",
|
|
36
|
+
"@aws-sdk/client-bedrock-runtime": "^3.1041.0",
|
|
37
|
+
"@aws-sdk/client-cloudwatch": "^3.1041.0",
|
|
38
|
+
"@aws-sdk/client-lambda": "^3.1041.0",
|
|
39
|
+
"@aws-sdk/client-s3": "^3.1041.0",
|
|
40
|
+
"@aws-sdk/client-sqs": "^3.1041.0",
|
|
41
|
+
"@aws-sdk/client-transcribe": "^3.1041.0",
|
|
42
|
+
"@aws-sdk/credential-provider-node": "^3.972.39",
|
|
43
|
+
"@aws-sdk/s3-request-presigner": "^3.1041.0",
|
|
44
44
|
"@casl/ability": "^6.8.1",
|
|
45
45
|
"@google/genai": "^1.46.0",
|
|
46
46
|
"@joplin/turndown-plugin-gfm": "^1.0.64",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"@modelcontextprotocol/sdk": "1.27.1",
|
|
49
49
|
"@octokit/rest": "^22.0.1",
|
|
50
50
|
"@opensearch-project/opensearch": "2.11.0",
|
|
51
|
-
"@smithy/node-http-handler": "^4.
|
|
51
|
+
"@smithy/node-http-handler": "^4.6.1",
|
|
52
52
|
"async-mutex": "^0.5.0",
|
|
53
53
|
"axios": "1.15.1",
|
|
54
54
|
"bcryptjs": "^3.0.2",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"eventsource-parser": "^3.0.8",
|
|
64
64
|
"exceljs": "^4.4.0",
|
|
65
65
|
"fdir": "^6.5.0",
|
|
66
|
-
"file-type": "^
|
|
66
|
+
"file-type": "^22.0.1",
|
|
67
67
|
"fuse.js": "^7.3.0",
|
|
68
68
|
"fzf": "^0.5.2",
|
|
69
69
|
"glob": "^13.0.6",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"mammoth": "^1.12.0",
|
|
79
79
|
"marked": "^15.0.11",
|
|
80
80
|
"mathjs": "^15.2.0",
|
|
81
|
-
"mime-types": "^
|
|
81
|
+
"mime-types": "^3.0.2",
|
|
82
82
|
"mongoose": "^8.8.3",
|
|
83
83
|
"ollama": "^0.6.3",
|
|
84
84
|
"open": "^11.0.0",
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"yauzl": "^3.3.0",
|
|
104
104
|
"zod": "^4.3.6",
|
|
105
105
|
"zod-validation-error": "^5.0.0",
|
|
106
|
-
"zustand": "^
|
|
106
|
+
"zustand": "^5.0.13"
|
|
107
107
|
},
|
|
108
108
|
"devDependencies": {
|
|
109
109
|
"@types/better-sqlite3": "^7.6.13",
|
|
@@ -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.
|
|
122
|
-
"@bike4mind/common": "
|
|
123
|
-
"@bike4mind/mcp": "
|
|
124
|
-
"@bike4mind/services": "
|
|
125
|
-
"@bike4mind/utils": "
|
|
121
|
+
"@bike4mind/agents": "0.0.0-changeset-release-main-20260505221459",
|
|
122
|
+
"@bike4mind/common": "0.0.0-changeset-release-main-20260505221459",
|
|
123
|
+
"@bike4mind/mcp": "0.0.0-changeset-release-main-20260505221459",
|
|
124
|
+
"@bike4mind/services": "0.0.0-changeset-release-main-20260505221459",
|
|
125
|
+
"@bike4mind/utils": "0.0.0-changeset-release-main-20260505221459"
|
|
126
126
|
},
|
|
127
127
|
"optionalDependencies": {
|
|
128
128
|
"@vscode/ripgrep": "^1.17.1"
|