@bike4mind/cli 0.15.3 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -0
- package/bin/bike4mind-cli.mjs +66 -0
- package/dist/{ConfigStore-D4gALtkZ.mjs → ConfigStore-CtGc4fF2.mjs} +98 -7
- package/dist/commands/apiCommand.mjs +1 -1
- package/dist/commands/doctorCommand.mjs +1 -1
- package/dist/commands/envCommand.mjs +1 -1
- package/dist/commands/headlessCommand.mjs +2 -2
- package/dist/commands/mcpCommand.mjs +1 -1
- package/dist/commands/updateCommand.mjs +1 -1
- package/dist/index.mjs +36 -10
- package/dist/{package-CfGv2cC5.mjs → package-3pouvthv.mjs} +1 -1
- package/dist/{tools-GgAy5rmD.mjs → tools-D9eSRR7Q.mjs} +308 -147
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -578,6 +578,55 @@ Coming soon:
|
|
|
578
578
|
⏳ Session search and management
|
|
579
579
|
⏳ Tool execution monitoring
|
|
580
580
|
|
|
581
|
+
## Running as a `claude` engine inside a host app
|
|
582
|
+
|
|
583
|
+
The CLI can be driven by a host app (e.g. a kanban-style agent pipeline) as a
|
|
584
|
+
drop-in replacement for `claude` — running a pipeline **stage** or a **board AI /
|
|
585
|
+
YAML** pane with full parity (board tools over HTTP MCP, the 3-layer brief
|
|
586
|
+
injected, the spec auto-fired on turn 1, and the live-card `processing` /
|
|
587
|
+
`action_required` / `ready` signal).
|
|
588
|
+
|
|
589
|
+
A host that only launches an engine whose executable basename is exactly `claude`
|
|
590
|
+
can be pointed at a **symlink** named `claude`:
|
|
591
|
+
|
|
592
|
+
```bash
|
|
593
|
+
# Create a claude-named symlink to the CLI entry point somewhere on disk:
|
|
594
|
+
ln -s "$(pwd)/apps/cli/bin/bike4mind-cli.mjs" /usr/local/bin/claude-b4m/claude
|
|
595
|
+
|
|
596
|
+
# In the host, set the engine's `terminal_command` to that symlink path:
|
|
597
|
+
# /usr/local/bin/claude-b4m/claude
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
Use a **direct symlink** whose basename is `claude` — not a `package.json` bin
|
|
601
|
+
alias (it would shadow the real `claude`) and not a wrapper shell script (argv
|
|
602
|
+
gets mangled). No other setup is required; the CLI replicates claude's launch-flag
|
|
603
|
+
and lifecycle-hook contract.
|
|
604
|
+
|
|
605
|
+
### Supported claude-compatible flags
|
|
606
|
+
|
|
607
|
+
These are parsed in `bin/bike4mind-cli.mjs` into `B4M_*` env vars consumed by the
|
|
608
|
+
runtime. Unknown flags are tolerated (never hard-error) and the interactive TUI is
|
|
609
|
+
preserved (a positional prompt seeds **and** submits turn 1 without switching to
|
|
610
|
+
headless `-p`):
|
|
611
|
+
|
|
612
|
+
| Flag | Effect |
|
|
613
|
+
| --- | --- |
|
|
614
|
+
| `--mcp-config <file>` | Inject MCP servers from a claude-shape JSON (`{ "mcpServers": {...} }`). Supports HTTP transport (`type: "http"`, `url`, `headers`) with per-launch Bearer auth. |
|
|
615
|
+
| `--strict-mcp-config` | Use ONLY `--mcp-config` servers; ignore file-config and `.mcp.json` (board YAML pane). |
|
|
616
|
+
| `--append-system-prompt <text>` | Append text verbatim to the end of the system prompt (the 3-layer brief). |
|
|
617
|
+
| `--allowedTools <patterns>` | Auto-approve matching tools without a permission prompt (glob `mcp__manifold__*` or a space-separated explicit list). |
|
|
618
|
+
| `--settings <json>` | Inline JSON; the `hooks` subset drives lifecycle hooks (see below). Malformed JSON is ignored, never fatal. |
|
|
619
|
+
| `--session-id <uuid>` / `--resume <uuid>` | Pin / resume a session (board pane). `/clear` and `/compact` keep the pinned uuid; a bad `--resume` prints `No conversation found with session ID <uuid>` and exits non-zero so the host can self-heal the pane. |
|
|
620
|
+
| `<positional prompt>` | Seeds and auto-fires turn 1 while staying interactive. |
|
|
621
|
+
|
|
622
|
+
### Lifecycle hooks (`--settings.hooks`)
|
|
623
|
+
|
|
624
|
+
Mirrors claude's hook contract for the host's `action_required` signal: a
|
|
625
|
+
`Notification`/`permission_prompt` command writes a sentinel file while an
|
|
626
|
+
interactive permission prompt blocks; `PostToolUse` / `Stop` / `UserPromptSubmit`
|
|
627
|
+
commands remove it. Each hook command runs via a real shell with its stdin piped
|
|
628
|
+
then closed (EOF), so a blocking `cat > <file>` hook returns immediately.
|
|
629
|
+
|
|
581
630
|
## License
|
|
582
631
|
|
|
583
632
|
Private - Bike4Mind
|
package/bin/bike4mind-cli.mjs
CHANGED
|
@@ -111,6 +111,39 @@ const argv = await yargs(hideBin(process.argv))
|
|
|
111
111
|
type: 'string',
|
|
112
112
|
description: 'Add local Ollama models to the model picker (e.g. http://localhost:11434)',
|
|
113
113
|
})
|
|
114
|
+
// ─── claude-compatible flags (host drop-in masquerade) ──────────────────────
|
|
115
|
+
// Declared so a host's claude launch flags parse into named options instead of
|
|
116
|
+
// leaking into the positional `argv._` task. See apps/cli README "host app".
|
|
117
|
+
.option('mcp-config', {
|
|
118
|
+
type: 'string',
|
|
119
|
+
description: 'Path to a JSON file of MCP servers to inject ({ "mcpServers": {...} })',
|
|
120
|
+
})
|
|
121
|
+
.option('strict-mcp-config', {
|
|
122
|
+
type: 'boolean',
|
|
123
|
+
description: 'Use ONLY --mcp-config servers; ignore file-config and .mcp.json',
|
|
124
|
+
default: false,
|
|
125
|
+
})
|
|
126
|
+
.option('append-system-prompt', {
|
|
127
|
+
type: 'string',
|
|
128
|
+
description: 'Text appended verbatim to the end of the composed system prompt',
|
|
129
|
+
})
|
|
130
|
+
.option('allowedTools', {
|
|
131
|
+
type: 'array',
|
|
132
|
+
string: true,
|
|
133
|
+
description: 'Tool names/globs auto-approved without a permission prompt (e.g. mcp__manifold__*)',
|
|
134
|
+
})
|
|
135
|
+
.option('settings', {
|
|
136
|
+
type: 'string',
|
|
137
|
+
description: 'Inline JSON settings (currently: lifecycle hooks) merged over user config',
|
|
138
|
+
})
|
|
139
|
+
.option('session-id', {
|
|
140
|
+
type: 'string',
|
|
141
|
+
description: 'Pin this session to a fixed uuid (first launch of a resumable pane)',
|
|
142
|
+
})
|
|
143
|
+
.option('resume', {
|
|
144
|
+
type: 'string',
|
|
145
|
+
description: 'Resume an existing session by uuid',
|
|
146
|
+
})
|
|
114
147
|
.option('api-url', {
|
|
115
148
|
type: 'string',
|
|
116
149
|
description: 'Set a custom API URL (self-hosted instance) and clear auth tokens, then exit',
|
|
@@ -197,6 +230,39 @@ if (argv['ollama-host']) {
|
|
|
197
230
|
process.env.B4M_OLLAMA_HOST = argv['ollama-host'];
|
|
198
231
|
}
|
|
199
232
|
|
|
233
|
+
// ─── claude-compatible flags → B4M_* env (read by src/index.tsx init) ─────────
|
|
234
|
+
// Mirrors the established bin→env→init channel (cf. --add-dir → B4M_ADDITIONAL_DIRS).
|
|
235
|
+
if (argv['mcp-config']) {
|
|
236
|
+
process.env.B4M_MCP_CONFIG_FILE = resolve(argv['mcp-config']);
|
|
237
|
+
}
|
|
238
|
+
if (argv['strict-mcp-config']) {
|
|
239
|
+
process.env.B4M_STRICT_MCP_CONFIG = '1';
|
|
240
|
+
}
|
|
241
|
+
if (argv['append-system-prompt']) {
|
|
242
|
+
process.env.B4M_APPEND_SYSTEM_PROMPT = argv['append-system-prompt'];
|
|
243
|
+
}
|
|
244
|
+
if (argv.allowedTools && argv.allowedTools.length > 0) {
|
|
245
|
+
// Each element may itself be a space-separated list (the host's board pane passes
|
|
246
|
+
// "a b c" as a single argv token); flatten on whitespace into discrete patterns.
|
|
247
|
+
const patterns = argv.allowedTools.flatMap((s) => String(s).split(/\s+/)).filter(Boolean);
|
|
248
|
+
process.env.B4M_ALLOWED_TOOLS = JSON.stringify(patterns);
|
|
249
|
+
}
|
|
250
|
+
if (argv.settings) {
|
|
251
|
+
process.env.B4M_SETTINGS_JSON = argv.settings;
|
|
252
|
+
}
|
|
253
|
+
if (argv['session-id']) {
|
|
254
|
+
process.env.B4M_SESSION_ID = argv['session-id'];
|
|
255
|
+
}
|
|
256
|
+
if (argv.resume) {
|
|
257
|
+
process.env.B4M_RESUME_ID = argv.resume;
|
|
258
|
+
}
|
|
259
|
+
// Positional task (claude `<prompt>` form): seeds AND submits turn 1, stays interactive.
|
|
260
|
+
// Only when it's not a known subcommand and headless -p wasn't used.
|
|
261
|
+
const KNOWN_SUBCOMMANDS = new Set(['mcp', 'update', 'doctor']);
|
|
262
|
+
if (argv.prompt === undefined && argv._.length > 0 && !KNOWN_SUBCOMMANDS.has(String(argv._[0]))) {
|
|
263
|
+
process.env.B4M_INITIAL_PROMPT = String(argv._[0]);
|
|
264
|
+
}
|
|
265
|
+
|
|
200
266
|
// Auto-detect environment: prefer production mode when dist exists
|
|
201
267
|
const distPath = join(__dirname, '../dist/index.mjs');
|
|
202
268
|
const srcPath = join(__dirname, '../src/index.tsx');
|
|
@@ -291,9 +291,6 @@ let ImageModels = /* @__PURE__ */ function(ImageModels) {
|
|
|
291
291
|
ImageModels["FLUX_PRO_1_1"] = "flux-pro-1.1";
|
|
292
292
|
ImageModels["FLUX_PRO_ULTRA"] = "flux-pro-1.1-ultra";
|
|
293
293
|
ImageModels["FLUX_PRO_FILL"] = "flux-pro-1.0-fill";
|
|
294
|
-
ImageModels["FLUX_PRO_CANNY"] = "flux-pro-1.0-canny";
|
|
295
|
-
ImageModels["FLUX_PRO_DEPTH"] = "flux-pro-1.0-depth";
|
|
296
|
-
ImageModels["FLUX_DEV"] = "flux-dev";
|
|
297
294
|
ImageModels["FLUX_KONTEXT_PRO"] = "flux-kontext-pro";
|
|
298
295
|
ImageModels["FLUX_KONTEXT_MAX"] = "flux-kontext-max";
|
|
299
296
|
ImageModels["GROK_IMAGINE_IMAGE_QUALITY"] = "grok-imagine-image-quality";
|
|
@@ -2663,7 +2660,8 @@ const AgentCompletedAction = z.object({
|
|
|
2663
2660
|
executionId: z.string(),
|
|
2664
2661
|
answer: z.string().optional(),
|
|
2665
2662
|
totalIterations: z.number(),
|
|
2666
|
-
totalCreditsUsed: z.number()
|
|
2663
|
+
totalCreditsUsed: z.number(),
|
|
2664
|
+
mementoIds: z.array(z.string()).optional()
|
|
2667
2665
|
});
|
|
2668
2666
|
const AgentFailedAction = z.object({
|
|
2669
2667
|
action: z.literal("failed"),
|
|
@@ -3615,10 +3613,26 @@ const OpenAIImageStyleSchema = z.enum(["vivid", "natural"]);
|
|
|
3615
3613
|
/**
|
|
3616
3614
|
* Maps legacy/removed image model IDs to their current replacements.
|
|
3617
3615
|
* Prevents Zod validation failures when clients send stale persisted model names.
|
|
3616
|
+
*
|
|
3617
|
+
* Values are constrained to `ALL_IMAGE_MODELS` so a remap target can never point at
|
|
3618
|
+
* a model that the schema would itself reject. If a target model is later retired,
|
|
3619
|
+
* re-point its entry to the current replacement rather than deleting it — clients may
|
|
3620
|
+
* still hold the legacy key in persisted state. When adding an entry, bump the
|
|
3621
|
+
* `llm-settings` persist `version` in LLMContext so existing clients re-run the remap.
|
|
3622
|
+
*
|
|
3623
|
+
* Only `flux-dev` is aliased among the recently removed Flux ids. It was a general
|
|
3624
|
+
* text-to-image model with a confirmed stale client (#8853), so remapping to the
|
|
3625
|
+
* current BFL standard is safe and faithful. `flux-pro-1.0-canny` and
|
|
3626
|
+
* `flux-pro-1.0-depth` are intentionally left as hard validation errors: they were
|
|
3627
|
+
* never UI-selectable (nothing persists them, and no alerts show callers sending them)
|
|
3628
|
+
* and they are structural-control models — silently remapping them to a plain
|
|
3629
|
+
* text-to-image model would drop the control image and return the wrong kind of result,
|
|
3630
|
+
* so a clear "unsupported model" error is the more honest response.
|
|
3618
3631
|
*/
|
|
3619
3632
|
const LEGACY_IMAGE_MODEL_MAP = {
|
|
3620
3633
|
"dall-e-3": "gpt-image-1",
|
|
3621
|
-
"dall-e-2": "gpt-image-1"
|
|
3634
|
+
"dall-e-2": "gpt-image-1",
|
|
3635
|
+
"flux-dev": "flux-pro-1.1"
|
|
3622
3636
|
};
|
|
3623
3637
|
const OpenAIImageGenerationInput = z.object({
|
|
3624
3638
|
prompt: z.string(),
|
|
@@ -3829,6 +3843,8 @@ z.enum([
|
|
|
3829
3843
|
"logoSettings",
|
|
3830
3844
|
"enforceCredits",
|
|
3831
3845
|
"enableTeamPlan",
|
|
3846
|
+
"allowOpenRegistration",
|
|
3847
|
+
"defaultFreeCredits",
|
|
3832
3848
|
"enableGoogleCalendar",
|
|
3833
3849
|
"googleCalendarServiceAccountEmail",
|
|
3834
3850
|
"googleCalendarServiceAccountSecret",
|
|
@@ -5765,6 +5781,22 @@ const settingsMap = {
|
|
|
5765
5781
|
group: API_SERVICE_GROUPS.CREDITS.id,
|
|
5766
5782
|
category: "Users"
|
|
5767
5783
|
}),
|
|
5784
|
+
allowOpenRegistration: makeBooleanSetting({
|
|
5785
|
+
key: "allowOpenRegistration",
|
|
5786
|
+
name: "Allow Open Registration",
|
|
5787
|
+
defaultValue: false,
|
|
5788
|
+
description: "Master switch for self-serve signup. When OFF (default), a valid invite code is required to register. When ON, users may register without an invite code; the Default Free Credits grant is then applied after they verify their email (anti-spam — see Default Free Credits). Safe to enable: the pre-request credit reservation caps every free user at the credits they are granted.",
|
|
5789
|
+
group: API_SERVICE_GROUPS.CREDITS.id,
|
|
5790
|
+
category: "Users"
|
|
5791
|
+
}),
|
|
5792
|
+
defaultFreeCredits: makeNumberSetting({
|
|
5793
|
+
key: "defaultFreeCredits",
|
|
5794
|
+
name: "Default Free Credits",
|
|
5795
|
+
defaultValue: 0,
|
|
5796
|
+
description: "Credits granted to a user who registers WITHOUT an invite code (only applies when Allow Open Registration is ON). Granted after the user verifies their email, NOT at signup — an unverified throwaway account gets 0 credits (anti-spam). A free user can never spend more than this — their hard ceiling of real model cost is roughly credits ÷ 1500 USD.",
|
|
5797
|
+
group: API_SERVICE_GROUPS.CREDITS.id,
|
|
5798
|
+
category: "Users"
|
|
5799
|
+
}),
|
|
5768
5800
|
FacebookLink: makeStringSetting({
|
|
5769
5801
|
key: "FacebookLink",
|
|
5770
5802
|
name: "Facebook Link",
|
|
@@ -10238,11 +10270,32 @@ const ApiConfigSchema = z.object({ customUrl: z.url().optional() });
|
|
|
10238
10270
|
*/
|
|
10239
10271
|
const McpServerSchema = z.object({
|
|
10240
10272
|
name: z.string(),
|
|
10273
|
+
type: z.enum(["stdio", "http"]).optional(),
|
|
10241
10274
|
command: z.string().optional(),
|
|
10242
10275
|
args: z.array(z.string()).optional(),
|
|
10276
|
+
url: z.string().optional(),
|
|
10277
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
10243
10278
|
env: z.record(z.string(), z.string()).prefault({}),
|
|
10244
10279
|
enabled: z.boolean().prefault(true)
|
|
10245
|
-
});
|
|
10280
|
+
}).superRefine(mcpServerTransportRefine);
|
|
10281
|
+
/**
|
|
10282
|
+
* A valid MCP server is exactly one of:
|
|
10283
|
+
* - stdio: has `command` (spawned child process)
|
|
10284
|
+
* - http: has `url` (streamable-HTTP endpoint)
|
|
10285
|
+
* Rejecting malformed configs at parse time (not connect time) surfaces errors early.
|
|
10286
|
+
*/
|
|
10287
|
+
function mcpServerTransportRefine(server, ctx) {
|
|
10288
|
+
const hasCommand = typeof server.command === "string" && server.command.trim() !== "";
|
|
10289
|
+
const hasUrl = typeof server.url === "string" && server.url.trim() !== "";
|
|
10290
|
+
if (hasCommand && hasUrl) ctx.addIssue({
|
|
10291
|
+
code: "custom",
|
|
10292
|
+
message: "MCP server cannot set both \"command\" (stdio) and \"url\" (http)"
|
|
10293
|
+
});
|
|
10294
|
+
else if (!hasCommand && !hasUrl) ctx.addIssue({
|
|
10295
|
+
code: "custom",
|
|
10296
|
+
message: "MCP server must set either \"command\" (stdio) or \"url\" (http)"
|
|
10297
|
+
});
|
|
10298
|
+
}
|
|
10246
10299
|
/**
|
|
10247
10300
|
* MCP Servers can be specified in two formats:
|
|
10248
10301
|
* 1. Array format (B4M native): [{ "name": "...", "command": "...", ... }]
|
|
@@ -10251,11 +10304,14 @@ const McpServerSchema = z.object({
|
|
|
10251
10304
|
* Both formats are supported for maximum flexibility and compatibility.
|
|
10252
10305
|
*/
|
|
10253
10306
|
const McpServersSchema = z.union([z.array(McpServerSchema), z.record(z.string(), z.object({
|
|
10307
|
+
type: z.enum(["stdio", "http"]).optional(),
|
|
10254
10308
|
command: z.string().optional(),
|
|
10255
10309
|
args: z.array(z.string()).optional(),
|
|
10310
|
+
url: z.string().optional(),
|
|
10311
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
10256
10312
|
env: z.record(z.string(), z.string()).optional(),
|
|
10257
10313
|
enabled: z.boolean().optional()
|
|
10258
|
-
}))]);
|
|
10314
|
+
}).superRefine(mcpServerTransportRefine))]);
|
|
10259
10315
|
/**
|
|
10260
10316
|
* Normalize MCP servers to internal array format
|
|
10261
10317
|
* Accepts both array and object formats
|
|
@@ -10263,15 +10319,21 @@ const McpServersSchema = z.union([z.array(McpServerSchema), z.record(z.string(),
|
|
|
10263
10319
|
function normalizeMcpServers(servers) {
|
|
10264
10320
|
if (Array.isArray(servers)) return servers.map((server) => ({
|
|
10265
10321
|
name: server.name,
|
|
10322
|
+
type: server.type,
|
|
10266
10323
|
command: server.command,
|
|
10267
10324
|
args: server.args,
|
|
10325
|
+
url: server.url,
|
|
10326
|
+
headers: server.headers,
|
|
10268
10327
|
env: server.env || {},
|
|
10269
10328
|
enabled: server.enabled ?? true
|
|
10270
10329
|
}));
|
|
10271
10330
|
else return Object.entries(servers).map(([name, config]) => ({
|
|
10272
10331
|
name,
|
|
10332
|
+
type: config.type,
|
|
10273
10333
|
command: config.command,
|
|
10274
10334
|
args: config.args,
|
|
10335
|
+
url: config.url,
|
|
10336
|
+
headers: config.headers,
|
|
10275
10337
|
env: config.env || {},
|
|
10276
10338
|
enabled: config.enabled ?? true
|
|
10277
10339
|
}));
|
|
@@ -10503,6 +10565,29 @@ async function loadMcpJsonConfig(projectDir) {
|
|
|
10503
10565
|
}
|
|
10504
10566
|
}
|
|
10505
10567
|
/**
|
|
10568
|
+
* Load an explicit `--mcp-config <file>` (claude shape: `{ "mcpServers": {...} }`).
|
|
10569
|
+
* Unlike `.mcp.json` this is an absolute path passed at launch, not project-relative.
|
|
10570
|
+
* Returns null on missing/malformed file (a bad config must not brick the launch).
|
|
10571
|
+
*/
|
|
10572
|
+
async function loadMcpConfigFile(filePath) {
|
|
10573
|
+
try {
|
|
10574
|
+
const data = await promises.readFile(filePath, "utf-8");
|
|
10575
|
+
const rawConfig = JSON.parse(data);
|
|
10576
|
+
return normalizeMcpServers(McpJsonConfigSchema.parse(rawConfig).mcpServers);
|
|
10577
|
+
} catch (error) {
|
|
10578
|
+
if (error.code === "ENOENT") {
|
|
10579
|
+
console.error(`--mcp-config file not found: ${filePath}`);
|
|
10580
|
+
return null;
|
|
10581
|
+
}
|
|
10582
|
+
if (error instanceof z.ZodError) {
|
|
10583
|
+
console.error("--mcp-config validation error:", error.issues);
|
|
10584
|
+
return null;
|
|
10585
|
+
}
|
|
10586
|
+
console.error("Failed to load --mcp-config file:", error);
|
|
10587
|
+
return null;
|
|
10588
|
+
}
|
|
10589
|
+
}
|
|
10590
|
+
/**
|
|
10506
10591
|
* Merge MCP servers from multiple configs
|
|
10507
10592
|
* Later configs can override earlier ones by name
|
|
10508
10593
|
*/
|
|
@@ -10684,6 +10769,12 @@ var ConfigStore = class {
|
|
|
10684
10769
|
} else this.projectConfigDir = null;
|
|
10685
10770
|
const mergedConfig = mergeConfigs(globalConfig, projectConfig, projectLocalConfig);
|
|
10686
10771
|
if (mcpJsonServers && mcpJsonServers.length > 0) mergedConfig.mcpServers = mergeMcpServers(mcpJsonServers, mergedConfig.mcpServers);
|
|
10772
|
+
const mcpConfigFile = process.env.B4M_MCP_CONFIG_FILE;
|
|
10773
|
+
if (mcpConfigFile) {
|
|
10774
|
+
const injected = await loadMcpConfigFile(mcpConfigFile);
|
|
10775
|
+
if (process.env.B4M_STRICT_MCP_CONFIG === "1") mergedConfig.mcpServers = injected ?? [];
|
|
10776
|
+
else if (injected) mergedConfig.mcpServers = mergeMcpServers(mergedConfig.mcpServers, injected);
|
|
10777
|
+
}
|
|
10687
10778
|
this.config = mergedConfig;
|
|
10688
10779
|
return this.config;
|
|
10689
10780
|
} catch (error) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as version } from "../package-
|
|
2
|
+
import { t as version } from "../package-3pouvthv.mjs";
|
|
3
3
|
import { a as fetchLatestVersion, c as isNpmPrefixWritable, i as compareSemver } from "../updateChecker-C8xsNY2L.mjs";
|
|
4
4
|
import { t as checkRipgrep } from "../ripgrepCheck-BmkyTK2i.mjs";
|
|
5
5
|
import { execSync } from "child_process";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as WebSocketToolExecutor, D as ServerLlmBackend, E as WebSocketLlmBackend, F as generateCliTools,
|
|
3
|
-
import { n as logger, r as getApiUrl, t as ConfigStore } from "../ConfigStore-
|
|
2
|
+
import { $ as SessionStore, C as WebSocketToolExecutor, D as ServerLlmBackend, E as WebSocketLlmBackend, F as generateCliTools, J as isReadOnlyTool, K as buildSystemPrompt, N as loadContextFiles, P as PermissionManager, S as ApiClient, T as FallbackLlmBackend, W as setWebSocketToolExecutor, X as CustomCommandStore, Y as ReActAgent, Z as CheckpointStore, _ as createAgentDelegateTool, b as createSkillTool, d as createFindDefinitionTool, f as createTodoStore, g as BackgroundAgentManager, h as createBackgroundAgentTools, k as McpManager, m as createCoordinateTaskTool, p as createWriteTodosTool, u as createGetFileStructureTool, v as AgentStore, w as WebSocketConnectionManager, y as SubagentOrchestrator } from "../tools-D9eSRR7Q.mjs";
|
|
3
|
+
import { n as logger, r as getApiUrl, t as ConfigStore } from "../ConfigStore-CtGc4fF2.mjs";
|
|
4
4
|
import { t as DEFAULT_SANDBOX_CONFIG } from "../types-LyRNHOiS.mjs";
|
|
5
5
|
import { t as createSandboxRuntime } from "../SandboxRuntimeAdapter-ChGlxSGQ.mjs";
|
|
6
6
|
import { t as SandboxOrchestrator } from "../SandboxOrchestrator-BoINxbX4.mjs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as version } from "../package-
|
|
2
|
+
import { t as version } from "../package-3pouvthv.mjs";
|
|
3
3
|
import { c as isNpmPrefixWritable, l as setAutoUpdatePreference, n as REEXEC_GUARD_ENV, o as forceCheckForUpdate, r as checkForUpdate, s as getAutoUpdatePreference, t as INSTALL_CMD, u as shouldAttemptAutoUpdate } from "../updateChecker-C8xsNY2L.mjs";
|
|
4
4
|
import { t as checkRipgrep } from "../ripgrepCheck-BmkyTK2i.mjs";
|
|
5
5
|
import { execSync, spawnSync } from "child_process";
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as
|
|
2
|
+
import { $ as SessionStore, A as substituteArguments, B as DEFAULT_RETRY_CONFIG, C as WebSocketToolExecutor, D as ServerLlmBackend, E as WebSocketLlmBackend, F as generateCliTools, G as getPlanModeFilePath, H as clearFeatureModuleTools, I as getProcessHooks, J as isReadOnlyTool, K as buildSystemPrompt, L as ALWAYS_DENIED_FOR_AGENTS, M as extractCompactInstructions, N as loadContextFiles, O as isTransientNetworkError, P as PermissionManager, Q as CommandHistoryStore, R as DEFAULT_AGENT_MODEL, S as ApiClient, T as FallbackLlmBackend, U as registerFeatureModuleTools, V as DEFAULT_THOROUGHNESS, W as setWebSocketToolExecutor, X as CustomCommandStore, Y as ReActAgent, Z as CheckpointStore, _ as createAgentDelegateTool, a as createBlockerTools, at as formatFileSize, b as createSkillTool, c as createDecisionStore, d as createFindDefinitionTool, et as OAuthClient, f as createTodoStore, g as BackgroundAgentManager, h as createBackgroundAgentTools, i as createBlockerStore, it as mergeCommands, j as formatStep, k as McpManager, l as formatDecisionsOutput, m as createCoordinateTaskTool, n as createReviewGateTool, nt as processFileReferences, o as formatBlockersOutput, ot as searchFiles, p as createWriteTodosTool, q as buildSkillsPromptSection, r as formatReviewGatesOutput, rt as searchCommands, s as createDecisionLogTool, st as warmFileCache, t as createReviewGateStore, tt as hasFileReferences, u as createGetFileStructureTool, v as AgentStore, w as WebSocketConnectionManager, x as parseAgentConfig, y as SubagentOrchestrator, z as DEFAULT_MAX_ITERATIONS } from "./tools-D9eSRR7Q.mjs";
|
|
3
3
|
import { n as useCliStore, t as selectActiveBackgroundAgents } from "./store-DV5s-qni.mjs";
|
|
4
|
-
import { Gt as validateNotebookPath$1, Wt as validateJupyterKernelName, g as CREDIT_DEDUCT_TRANSACTION_TYPES, i as getEnvironmentName, n as logger, r as getApiUrl, t as ConfigStore, v as ChatModels } from "./ConfigStore-
|
|
5
|
-
import { t as version } from "./package-
|
|
4
|
+
import { Gt as validateNotebookPath$1, Wt as validateJupyterKernelName, g as CREDIT_DEDUCT_TRANSACTION_TYPES, i as getEnvironmentName, n as logger, r as getApiUrl, t as ConfigStore, v as ChatModels } from "./ConfigStore-CtGc4fF2.mjs";
|
|
5
|
+
import { t as version } from "./package-3pouvthv.mjs";
|
|
6
6
|
import { r as checkForUpdate } from "./updateChecker-C8xsNY2L.mjs";
|
|
7
7
|
import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react";
|
|
8
8
|
import { Box, Static, Text, render, useApp, useInput, usePaste, useStdout } from "ink";
|
|
@@ -3083,7 +3083,7 @@ function buildCompactionPrompt(messages, options = {}) {
|
|
|
3083
3083
|
* @param preservedMessages - Recent messages to keep verbatim
|
|
3084
3084
|
* @returns A new session with compacted context
|
|
3085
3085
|
*/
|
|
3086
|
-
function createCompactedSession(originalSession, summary, preservedMessages) {
|
|
3086
|
+
function createCompactedSession(originalSession, summary, preservedMessages, preserveId = false) {
|
|
3087
3087
|
const summaryMessage = {
|
|
3088
3088
|
id: v4(),
|
|
3089
3089
|
role: "user",
|
|
@@ -3103,7 +3103,7 @@ function createCompactedSession(originalSession, summary, preservedMessages) {
|
|
|
3103
3103
|
...preservedMessages
|
|
3104
3104
|
] : [summaryMessage, ...preservedMessages];
|
|
3105
3105
|
return {
|
|
3106
|
-
id: v4(),
|
|
3106
|
+
id: preserveId ? originalSession.id : v4(),
|
|
3107
3107
|
name: `${originalSession.name} (compacted)`,
|
|
3108
3108
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3109
3109
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -6327,8 +6327,18 @@ function CliApp() {
|
|
|
6327
6327
|
logger.warn(`🤖 Using fallback model: ${modelInfo.id}`);
|
|
6328
6328
|
}
|
|
6329
6329
|
llm.currentModel = modelInfo.id;
|
|
6330
|
-
const
|
|
6331
|
-
|
|
6330
|
+
const pinnedSessionId = process.env.B4M_SESSION_ID;
|
|
6331
|
+
const resumeSessionId = process.env.B4M_RESUME_ID;
|
|
6332
|
+
let newSession;
|
|
6333
|
+
if (resumeSessionId) {
|
|
6334
|
+
const resumed = await state.sessionStore.load(resumeSessionId);
|
|
6335
|
+
if (!resumed) {
|
|
6336
|
+
console.error(`No conversation found with session ID ${resumeSessionId}`);
|
|
6337
|
+
process.exit(1);
|
|
6338
|
+
}
|
|
6339
|
+
newSession = resumed;
|
|
6340
|
+
} else newSession = {
|
|
6341
|
+
id: pinnedSessionId || v4(),
|
|
6332
6342
|
name: `Session ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
|
|
6333
6343
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6334
6344
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -6392,6 +6402,7 @@ function CliApp() {
|
|
|
6392
6402
|
let permissionPromptCounter = 0;
|
|
6393
6403
|
const promptFn = (toolName, args, preview) => {
|
|
6394
6404
|
return new Promise((resolve) => {
|
|
6405
|
+
getProcessHooks()?.fireNotificationPermissionPrompt(toolName);
|
|
6395
6406
|
const canBeTrusted = permissionManager.canBeTrusted(toolName);
|
|
6396
6407
|
const id = `perm-${++permissionPromptCounter}`;
|
|
6397
6408
|
const prompt = {
|
|
@@ -6600,6 +6611,7 @@ function CliApp() {
|
|
|
6600
6611
|
additionalDirectories,
|
|
6601
6612
|
featureModulePrompts: featureModulePrompts || void 0,
|
|
6602
6613
|
planModeFilePath: mode === "plan" ? getPlanModeFilePath(newSession.id) : void 0,
|
|
6614
|
+
appendSystemPrompt: process.env.B4M_APPEND_SYSTEM_PROMPT,
|
|
6603
6615
|
deferredToolNames: deferredToolRegistry.getDirectoryNames()
|
|
6604
6616
|
});
|
|
6605
6617
|
const cliSystemPrompt = buildPromptForMode(useCliStore.getState().interactionMode);
|
|
@@ -6778,6 +6790,16 @@ function CliApp() {
|
|
|
6778
6790
|
const handleMessageRef = useRef(null);
|
|
6779
6791
|
const abortControllerRef = useRef(state.abortController);
|
|
6780
6792
|
abortControllerRef.current = state.abortController;
|
|
6793
|
+
const autoFiredRef = useRef(false);
|
|
6794
|
+
useEffect(() => {
|
|
6795
|
+
if (!isInitialized || autoFiredRef.current) return;
|
|
6796
|
+
const initialPrompt = process.env.B4M_INITIAL_PROMPT;
|
|
6797
|
+
if (!initialPrompt) return;
|
|
6798
|
+
autoFiredRef.current = true;
|
|
6799
|
+
setImmediate(() => {
|
|
6800
|
+
handleMessageRef.current?.(initialPrompt);
|
|
6801
|
+
});
|
|
6802
|
+
}, [isInitialized]);
|
|
6781
6803
|
const emitNextAwaitingStatus = () => {
|
|
6782
6804
|
const s = useCliStore.getState();
|
|
6783
6805
|
if (s.permissionPrompt) {
|
|
@@ -7068,6 +7090,7 @@ function CliApp() {
|
|
|
7068
7090
|
console.error("❌ CLI failed to initialize. Try restarting b4m.\n");
|
|
7069
7091
|
return;
|
|
7070
7092
|
}
|
|
7093
|
+
getProcessHooks()?.fireUserPromptSubmit();
|
|
7071
7094
|
bridgePresence.emitEvent({
|
|
7072
7095
|
type: "message",
|
|
7073
7096
|
role: "user",
|
|
@@ -7102,7 +7125,7 @@ function CliApp() {
|
|
|
7102
7125
|
if (compactionPrompt) {
|
|
7103
7126
|
const result = await state.agent.run(compactionPrompt, { maxIterations: 1 });
|
|
7104
7127
|
await state.sessionStore.save(activeSession);
|
|
7105
|
-
const newSession = createCompactedSession(activeSession, result.finalAnswer, preservedMessages);
|
|
7128
|
+
const newSession = createCompactedSession(activeSession, result.finalAnswer, preservedMessages, !!(process.env.B4M_SESSION_ID || process.env.B4M_RESUME_ID));
|
|
7106
7129
|
await logger.initialize(newSession.id);
|
|
7107
7130
|
setState((prev) => ({
|
|
7108
7131
|
...prev,
|
|
@@ -7260,6 +7283,7 @@ function CliApp() {
|
|
|
7260
7283
|
abortController: null
|
|
7261
7284
|
}));
|
|
7262
7285
|
useCliStore.getState().setIsThinking(false);
|
|
7286
|
+
getProcessHooks()?.fireStop();
|
|
7263
7287
|
bridgePresence.emitEvent({
|
|
7264
7288
|
type: "status",
|
|
7265
7289
|
status: wasAborted ? "idle" : "awaiting_input"
|
|
@@ -8118,8 +8142,9 @@ Multi-line Input:
|
|
|
8118
8142
|
console.clear();
|
|
8119
8143
|
renderBanner();
|
|
8120
8144
|
const model = state.session?.model || state.config?.defaultModel || ChatModels.CLAUDE_4_5_SONNET;
|
|
8145
|
+
const clearPinnedId = process.env.B4M_SESSION_ID || process.env.B4M_RESUME_ID;
|
|
8121
8146
|
const newSession = {
|
|
8122
|
-
id: v4(),
|
|
8147
|
+
id: clearPinnedId ? state.session?.id ?? clearPinnedId : v4(),
|
|
8123
8148
|
name: `Session ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
|
|
8124
8149
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8125
8150
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -8458,7 +8483,7 @@ Multi-line Input:
|
|
|
8458
8483
|
const summary = (await state.agent.run(compactionPrompt, { maxIterations: 1 })).finalAnswer;
|
|
8459
8484
|
await state.sessionStore.save(state.session);
|
|
8460
8485
|
const oldSessionName = state.session.name;
|
|
8461
|
-
const newSession = createCompactedSession(state.session, summary, preservedMessages);
|
|
8486
|
+
const newSession = createCompactedSession(state.session, summary, preservedMessages, !!(process.env.B4M_SESSION_ID || process.env.B4M_RESUME_ID));
|
|
8462
8487
|
await logger.initialize(newSession.id);
|
|
8463
8488
|
setState((prev) => ({
|
|
8464
8489
|
...prev,
|
|
@@ -8975,6 +9000,7 @@ Multi-line Input:
|
|
|
8975
9000
|
additionalDirectories: state.additionalDirectories,
|
|
8976
9001
|
featureModulePrompts: newFeaturePrompts || void 0,
|
|
8977
9002
|
planModeFilePath: planFilePathForRebuild,
|
|
9003
|
+
appendSystemPrompt: process.env.B4M_APPEND_SYSTEM_PROMPT,
|
|
8978
9004
|
deferredToolNames: deferredToolRegistry.getDirectoryNames()
|
|
8979
9005
|
}));
|
|
8980
9006
|
const moduleNames = newFeatureRegistry.getModuleNames();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as ProjectEvents, A as GenerateImageToolCallSchema, At as dayjsConfig_default, B as InviteEvents, Bt as resolveNavigationIntents, C as ElabsEvents, Ct as UnauthorizedError, D as ForbiddenError, Dt as VideoModels, E as FileEvents, Et as VideoGenerationUsageTransaction, F as ImageEditUsageTransaction, Ft as isGPTImage2Model, G as ModalEvents, H as KnowledgeType, Ht as secureParameters, I as ImageGenerationUsageTransaction, It as isGPTImageModel, J as OpenAIEmbeddingModel, Jt as isNearLimit, K as ModelBackend, Kt as buildRateLimitLogEntry, L as ImageModels, Lt as isSupportedEmbeddingModel, M as GenericCreditDeductTransaction, Mt as getDataLakeTags, N as HTTPError, Nt as getMcpProviderMetadata, O as FriendshipEvents, Ot as XAI_IMAGE_MODELS, P as HttpStatus, Pt as getViewById, Q as ProfileEvents, R as InboxEvents, Rt as isZodError, S as DashboardParamsSchema, St as UiNavigationEvents, T as FeedbackEvents, Tt as VIDEO_SIZE_CONSTRAINTS, U as LLMEvents, Ut as settingsMap, V as InviteType, Vt as sanitizeTelemetryError, W as MiscEvents, X as Permission, Xt as CollectionType, Y as OpenAIImageGenerationInput, Yt as parseRateLimitHeaders, Z as PermissionDeniedError, _ as ChatCompletionCreateInputSchema, _t as TaskScheduleHandler, a as ALERT_THRESHOLDS, at as ReceivedCreditTransaction, b as CompletionApiUsageTransaction, bt as ToolUsageTransaction, c as ApiKeyScope, ct as ResearchModeParamsSchema, d as ArtifactTypeSchema, dt as ResearchTaskType, et as PromptIntentSchema, f as AuthEvents, ft as SessionEvents, gt as TagType, h as BadRequestError, ht as SupportedFabFileMimeTypes, it as RealtimeVoiceUsageTransaction, j as GenericCreditAddTransaction, jt as getAccessibleDataLakes, k as GEMINI_IMAGE_MODELS, kt as b4mLLMTools, l as ApiKeyType, lt as ResearchTaskExecutionType, m as BFL_SAFETY_TOLERANCE, mt as SubscriptionCreditTransaction, n as logger, nt as PurchaseTransaction, o as AiEvents, ot as RechartsChartTypeList, p as BFL_IMAGE_MODELS, pt as SpeechToTextUsageTransaction, q as NotFoundError, qt as extractSnippetMeta, rt as QuestMasterParamsSchema, s as ApiKeyEvents, st as RegInviteEvents, t as ConfigStore, tt as PromptMetaZodSchema, u as AppFileEvents, ut as ResearchTaskPeriodicFrequencyType, v as ChatModels, vt as TextGenerationUsageTransaction, w as FavoriteDocumentType, wt as UnprocessableEntityError, x as CorruptedFileError, xt as TransferCreditTransaction, y as ClaudeArtifactMimeTypes, yt as TooManyRequestsError, z as InternalServerError, zt as obfuscateApiKey } from "./ConfigStore-
|
|
2
|
+
import { $ as ProjectEvents, A as GenerateImageToolCallSchema, At as dayjsConfig_default, B as InviteEvents, Bt as resolveNavigationIntents, C as ElabsEvents, Ct as UnauthorizedError, D as ForbiddenError, Dt as VideoModels, E as FileEvents, Et as VideoGenerationUsageTransaction, F as ImageEditUsageTransaction, Ft as isGPTImage2Model, G as ModalEvents, H as KnowledgeType, Ht as secureParameters, I as ImageGenerationUsageTransaction, It as isGPTImageModel, J as OpenAIEmbeddingModel, Jt as isNearLimit, K as ModelBackend, Kt as buildRateLimitLogEntry, L as ImageModels, Lt as isSupportedEmbeddingModel, M as GenericCreditDeductTransaction, Mt as getDataLakeTags, N as HTTPError, Nt as getMcpProviderMetadata, O as FriendshipEvents, Ot as XAI_IMAGE_MODELS, P as HttpStatus, Pt as getViewById, Q as ProfileEvents, R as InboxEvents, Rt as isZodError, S as DashboardParamsSchema, St as UiNavigationEvents, T as FeedbackEvents, Tt as VIDEO_SIZE_CONSTRAINTS, U as LLMEvents, Ut as settingsMap, V as InviteType, Vt as sanitizeTelemetryError, W as MiscEvents, X as Permission, Xt as CollectionType, Y as OpenAIImageGenerationInput, Yt as parseRateLimitHeaders, Z as PermissionDeniedError, _ as ChatCompletionCreateInputSchema, _t as TaskScheduleHandler, a as ALERT_THRESHOLDS, at as ReceivedCreditTransaction, b as CompletionApiUsageTransaction, bt as ToolUsageTransaction, c as ApiKeyScope, ct as ResearchModeParamsSchema, d as ArtifactTypeSchema, dt as ResearchTaskType, et as PromptIntentSchema, f as AuthEvents, ft as SessionEvents, gt as TagType, h as BadRequestError, ht as SupportedFabFileMimeTypes, it as RealtimeVoiceUsageTransaction, j as GenericCreditAddTransaction, jt as getAccessibleDataLakes, k as GEMINI_IMAGE_MODELS, kt as b4mLLMTools, l as ApiKeyType, lt as ResearchTaskExecutionType, m as BFL_SAFETY_TOLERANCE, mt as SubscriptionCreditTransaction, n as logger, nt as PurchaseTransaction, o as AiEvents, ot as RechartsChartTypeList, p as BFL_IMAGE_MODELS, pt as SpeechToTextUsageTransaction, q as NotFoundError, qt as extractSnippetMeta, rt as QuestMasterParamsSchema, s as ApiKeyEvents, st as RegInviteEvents, t as ConfigStore, tt as PromptMetaZodSchema, u as AppFileEvents, ut as ResearchTaskPeriodicFrequencyType, v as ChatModels, vt as TextGenerationUsageTransaction, w as FavoriteDocumentType, wt as UnprocessableEntityError, x as CorruptedFileError, xt as TransferCreditTransaction, y as ClaudeArtifactMimeTypes, yt as TooManyRequestsError, z as InternalServerError, zt as obfuscateApiKey } from "./ConfigStore-CtGc4fF2.mjs";
|
|
3
3
|
import { a as isUserLockedOut, c as userCanDisableMFA, d as userRequiresMFA, f as verifyBackupCode, i as getLockoutTimeRemaining, l as userEligibleForMFA, n as generateBackupCodes, o as recordFailedAttempt, p as verifyTOTPToken, r as generateTOTPSetup, s as shouldResetFailedAttempts, t as clearFailedAttempts, u as userHasMFAConfigured } from "./utils-PpNti-tY.mjs";
|
|
4
4
|
import { n as isPathAllowed, t as assertPathAllowed } from "./pathValidation-D8tjkQXE-1HwvsuYT.mjs";
|
|
5
|
-
import { t as version } from "./package-
|
|
5
|
+
import { t as version } from "./package-3pouvthv.mjs";
|
|
6
6
|
import { execFile, execFileSync, spawn } from "child_process";
|
|
7
7
|
import crypto, { createHash, randomBytes, randomUUID } from "crypto";
|
|
8
8
|
import { existsSync, promises, readFileSync, readdirSync, rmSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
@@ -70,6 +70,7 @@ import { Mutex } from "async-mutex";
|
|
|
70
70
|
import "zod-validation-error";
|
|
71
71
|
import { homedir as homedir$1 } from "node:os";
|
|
72
72
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
73
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
73
74
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
74
75
|
import { fileURLToPath } from "url";
|
|
75
76
|
import WsWebSocket from "ws";
|
|
@@ -1902,6 +1903,18 @@ function shouldUseParallelExecution(toolsUsed, isReadOnly = defaultIsReadOnlyToo
|
|
|
1902
1903
|
return toolsUsed.filter((tool) => isReadOnly(tool.name)).length >= 2;
|
|
1903
1904
|
}
|
|
1904
1905
|
/**
|
|
1906
|
+
* True when an error is an abort/cancellation (user stop or execution timeout)
|
|
1907
|
+
* rather than a real failure. Aborts are benign and must NOT be logged at error
|
|
1908
|
+
* severity — error logs trip the CloudWatch ERROR→LiveOps/Slack alert path and
|
|
1909
|
+
* page on routine cancellations (#8947, #8669).
|
|
1910
|
+
*/
|
|
1911
|
+
function isAbortError(error, signal) {
|
|
1912
|
+
if (signal?.aborted) return true;
|
|
1913
|
+
if (!(error instanceof Error)) return false;
|
|
1914
|
+
if (error.name === "AbortError") return true;
|
|
1915
|
+
return error.message.toLowerCase().includes("aborted");
|
|
1916
|
+
}
|
|
1917
|
+
/**
|
|
1905
1918
|
* ReAct (Reasoning and Acting) Agent
|
|
1906
1919
|
*
|
|
1907
1920
|
* This agent uses the ReAct pattern to solve problems by:
|
|
@@ -2051,6 +2064,9 @@ var ReActAgent = class extends EventEmitter {
|
|
|
2051
2064
|
let currentText = "";
|
|
2052
2065
|
const processedToolIds = /* @__PURE__ */ new Set();
|
|
2053
2066
|
let hadToolCalls = false;
|
|
2067
|
+
let thoughtEmitted = false;
|
|
2068
|
+
const iterStartInputTokens = this.totalInputTokens;
|
|
2069
|
+
const iterStartOutputTokens = this.totalOutputTokens;
|
|
2054
2070
|
if (maxHistoryIterations > 0 && iterations > 1) {
|
|
2055
2071
|
trimConversationHistory(messages, maxHistoryIterations, this.initialMessageCount);
|
|
2056
2072
|
trimSteps(this.steps, maxHistoryIterations);
|
|
@@ -2092,35 +2108,18 @@ var ReActAgent = class extends EventEmitter {
|
|
|
2092
2108
|
this.totalCacheWriteTokens += completionInfo.cacheStats.cacheWriteTokens || 0;
|
|
2093
2109
|
}
|
|
2094
2110
|
if (completionInfo.creditsUsed) this.totalCredits += completionInfo.creditsUsed;
|
|
2095
|
-
if (currentText.trim() && completionInfo.toolsUsed?.length) {
|
|
2096
|
-
const thoughtStep = {
|
|
2097
|
-
type: "thought",
|
|
2098
|
-
content: currentText.trim(),
|
|
2099
|
-
metadata: { timestamp: Date.now() }
|
|
2100
|
-
};
|
|
2101
|
-
this.steps.push(thoughtStep);
|
|
2102
|
-
this.emit("thought", thoughtStep);
|
|
2103
|
-
}
|
|
2104
|
-
if (!completionInfo.toolsUsed?.length && currentText.trim()) {
|
|
2105
|
-
finalAnswer = currentText.trim();
|
|
2106
|
-
const finalStep = {
|
|
2107
|
-
type: "final_answer",
|
|
2108
|
-
content: finalAnswer,
|
|
2109
|
-
metadata: {
|
|
2110
|
-
timestamp: Date.now(),
|
|
2111
|
-
tokenUsage: {
|
|
2112
|
-
prompt: inputTokens,
|
|
2113
|
-
completion: outputTokens,
|
|
2114
|
-
total: inputTokens + outputTokens
|
|
2115
|
-
}
|
|
2116
|
-
}
|
|
2117
|
-
};
|
|
2118
|
-
this.steps.push(finalStep);
|
|
2119
|
-
this.emit("final_answer", finalStep);
|
|
2120
|
-
iterationComplete = true;
|
|
2121
|
-
}
|
|
2122
2111
|
if (completionInfo.toolsUsed && completionInfo.toolsUsed.length > 0) {
|
|
2123
2112
|
hadToolCalls = true;
|
|
2113
|
+
if (!thoughtEmitted && currentText.trim()) {
|
|
2114
|
+
const thoughtStep = {
|
|
2115
|
+
type: "thought",
|
|
2116
|
+
content: currentText.trim(),
|
|
2117
|
+
metadata: { timestamp: Date.now() }
|
|
2118
|
+
};
|
|
2119
|
+
this.steps.push(thoughtStep);
|
|
2120
|
+
this.emit("thought", thoughtStep);
|
|
2121
|
+
thoughtEmitted = true;
|
|
2122
|
+
}
|
|
2124
2123
|
const thinkingBlocks = completionInfo.thinking || [];
|
|
2125
2124
|
const unprocessedTools = [];
|
|
2126
2125
|
for (const toolUse of completionInfo.toolsUsed) {
|
|
@@ -2159,6 +2158,26 @@ var ReActAgent = class extends EventEmitter {
|
|
|
2159
2158
|
}
|
|
2160
2159
|
}
|
|
2161
2160
|
});
|
|
2161
|
+
if (!hadToolCalls && currentText.trim()) {
|
|
2162
|
+
finalAnswer = currentText.trim();
|
|
2163
|
+
const iterInputTokens = this.totalInputTokens - iterStartInputTokens;
|
|
2164
|
+
const iterOutputTokens = this.totalOutputTokens - iterStartOutputTokens;
|
|
2165
|
+
const finalStep = {
|
|
2166
|
+
type: "final_answer",
|
|
2167
|
+
content: finalAnswer,
|
|
2168
|
+
metadata: {
|
|
2169
|
+
timestamp: Date.now(),
|
|
2170
|
+
tokenUsage: {
|
|
2171
|
+
prompt: iterInputTokens,
|
|
2172
|
+
completion: iterOutputTokens,
|
|
2173
|
+
total: iterInputTokens + iterOutputTokens
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
};
|
|
2177
|
+
this.steps.push(finalStep);
|
|
2178
|
+
this.emit("final_answer", finalStep);
|
|
2179
|
+
iterationComplete = true;
|
|
2180
|
+
}
|
|
2162
2181
|
if (iterationComplete && finalAnswer) break;
|
|
2163
2182
|
if (hadToolCalls && options.confidenceGate && this.iterationConfidences.length > 0) {
|
|
2164
2183
|
const iterAvg = this.iterationConfidences.reduce((a, b) => a + b, 0) / this.iterationConfidences.length;
|
|
@@ -2258,7 +2277,8 @@ var ReActAgent = class extends EventEmitter {
|
|
|
2258
2277
|
this.emit("complete", result);
|
|
2259
2278
|
return result;
|
|
2260
2279
|
}
|
|
2261
|
-
this.context.logger.
|
|
2280
|
+
if (isAbortError(error, options.signal)) this.context.logger.warn("[ReActAgent] Execution aborted (user cancel or timeout):", error);
|
|
2281
|
+
else this.context.logger.error("[ReActAgent] Error during execution:", error);
|
|
2262
2282
|
this.emit("error", error);
|
|
2263
2283
|
throw error;
|
|
2264
2284
|
}
|
|
@@ -2530,6 +2550,9 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
|
|
|
2530
2550
|
let finalAnswer = "";
|
|
2531
2551
|
const processedToolIds = /* @__PURE__ */ new Set();
|
|
2532
2552
|
let hadToolCalls = false;
|
|
2553
|
+
let thoughtEmitted = false;
|
|
2554
|
+
const iterStartInputTokens = this.totalInputTokens;
|
|
2555
|
+
const iterStartOutputTokens = this.totalOutputTokens;
|
|
2533
2556
|
const cacheStrategy = options.enableCaching ? {
|
|
2534
2557
|
enableCaching: true,
|
|
2535
2558
|
cacheSystemPrompt: true,
|
|
@@ -2567,41 +2590,22 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
|
|
|
2567
2590
|
this.totalCacheWriteTokens += completionInfo.cacheStats.cacheWriteTokens || 0;
|
|
2568
2591
|
}
|
|
2569
2592
|
if (completionInfo.creditsUsed) this.totalCredits += completionInfo.creditsUsed;
|
|
2570
|
-
if (currentText.trim() && completionInfo.toolsUsed?.length) {
|
|
2571
|
-
const thoughtStep = {
|
|
2572
|
-
type: "thought",
|
|
2573
|
-
content: currentText.trim(),
|
|
2574
|
-
metadata: {
|
|
2575
|
-
timestamp: Date.now(),
|
|
2576
|
-
iteration: this.iterations - 1
|
|
2577
|
-
}
|
|
2578
|
-
};
|
|
2579
|
-
this.steps.push(thoughtStep);
|
|
2580
|
-
iterationSteps.push(thoughtStep);
|
|
2581
|
-
this.emit("thought", thoughtStep);
|
|
2582
|
-
}
|
|
2583
|
-
if (!completionInfo.toolsUsed?.length && currentText.trim()) {
|
|
2584
|
-
finalAnswer = currentText.trim();
|
|
2585
|
-
const finalStep = {
|
|
2586
|
-
type: "final_answer",
|
|
2587
|
-
content: finalAnswer,
|
|
2588
|
-
metadata: {
|
|
2589
|
-
timestamp: Date.now(),
|
|
2590
|
-
iteration: this.iterations - 1,
|
|
2591
|
-
tokenUsage: {
|
|
2592
|
-
prompt: inputTokens,
|
|
2593
|
-
completion: outputTokens,
|
|
2594
|
-
total: inputTokens + outputTokens
|
|
2595
|
-
}
|
|
2596
|
-
}
|
|
2597
|
-
};
|
|
2598
|
-
this.steps.push(finalStep);
|
|
2599
|
-
iterationSteps.push(finalStep);
|
|
2600
|
-
this.emit("final_answer", finalStep);
|
|
2601
|
-
iterationComplete = true;
|
|
2602
|
-
}
|
|
2603
2593
|
if (completionInfo.toolsUsed && completionInfo.toolsUsed.length > 0) {
|
|
2604
2594
|
hadToolCalls = true;
|
|
2595
|
+
if (!thoughtEmitted && currentText.trim()) {
|
|
2596
|
+
const thoughtStep = {
|
|
2597
|
+
type: "thought",
|
|
2598
|
+
content: currentText.trim(),
|
|
2599
|
+
metadata: {
|
|
2600
|
+
timestamp: Date.now(),
|
|
2601
|
+
iteration: this.iterations - 1
|
|
2602
|
+
}
|
|
2603
|
+
};
|
|
2604
|
+
this.steps.push(thoughtStep);
|
|
2605
|
+
iterationSteps.push(thoughtStep);
|
|
2606
|
+
this.emit("thought", thoughtStep);
|
|
2607
|
+
thoughtEmitted = true;
|
|
2608
|
+
}
|
|
2605
2609
|
const thinkingBlocks = completionInfo.thinking || [];
|
|
2606
2610
|
const unprocessedTools = [];
|
|
2607
2611
|
for (const toolUse of completionInfo.toolsUsed) {
|
|
@@ -2645,6 +2649,28 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
|
|
|
2645
2649
|
}
|
|
2646
2650
|
}
|
|
2647
2651
|
});
|
|
2652
|
+
if (!hadToolCalls && currentText.trim()) {
|
|
2653
|
+
finalAnswer = currentText.trim();
|
|
2654
|
+
const iterInputTokens = this.totalInputTokens - iterStartInputTokens;
|
|
2655
|
+
const iterOutputTokens = this.totalOutputTokens - iterStartOutputTokens;
|
|
2656
|
+
const finalStep = {
|
|
2657
|
+
type: "final_answer",
|
|
2658
|
+
content: finalAnswer,
|
|
2659
|
+
metadata: {
|
|
2660
|
+
timestamp: Date.now(),
|
|
2661
|
+
iteration: this.iterations - 1,
|
|
2662
|
+
tokenUsage: {
|
|
2663
|
+
prompt: iterInputTokens,
|
|
2664
|
+
completion: iterOutputTokens,
|
|
2665
|
+
total: iterInputTokens + iterOutputTokens
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
};
|
|
2669
|
+
this.steps.push(finalStep);
|
|
2670
|
+
iterationSteps.push(finalStep);
|
|
2671
|
+
this.emit("final_answer", finalStep);
|
|
2672
|
+
iterationComplete = true;
|
|
2673
|
+
}
|
|
2648
2674
|
if (hadToolCalls && options.confidenceGate && this.iterationConfidences.length > 0) {
|
|
2649
2675
|
const iterAvg = this.iterationConfidences.reduce((a, b) => a + b, 0) / this.iterationConfidences.length;
|
|
2650
2676
|
const decision = options.confidenceGate(iterAvg, this.iterations);
|
|
@@ -2761,7 +2787,8 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
|
|
|
2761
2787
|
checkpoint: this.toCheckpoint()
|
|
2762
2788
|
};
|
|
2763
2789
|
}
|
|
2764
|
-
this.context.logger.
|
|
2790
|
+
if (isAbortError(error, options.signal)) this.context.logger.warn("[ReActAgent] Iteration aborted (user cancel or timeout):", error);
|
|
2791
|
+
else this.context.logger.error("[ReActAgent] Error during runIteration:", error);
|
|
2765
2792
|
this.emit("error", error);
|
|
2766
2793
|
throw error;
|
|
2767
2794
|
}
|
|
@@ -4117,7 +4144,7 @@ These tools are lightweight — use them naturally as part of your work, not as
|
|
|
4117
4144
|
|
|
4118
4145
|
## Working Directory
|
|
4119
4146
|
|
|
4120
|
-
The current working directory is \`${process.cwd()}\`. All relative paths in tool calls resolve from here. When using \`${TOOL_GLOB_FILES}\` or \`${TOOL_GREP_SEARCH}\` without an explicit \`dir_path\`, they search from this directory.${directoriesSection}${projectContextSection}${skillsSection}${featureModulesSection}${planModeSection}${deferredToolSection}`;
|
|
4147
|
+
The current working directory is \`${process.cwd()}\`. All relative paths in tool calls resolve from here. When using \`${TOOL_GLOB_FILES}\` or \`${TOOL_GREP_SEARCH}\` without an explicit \`dir_path\`, they search from this directory.${directoriesSection}${projectContextSection}${skillsSection}${featureModulesSection}${planModeSection}${deferredToolSection}${config?.appendSystemPrompt ? `\n\n${config.appendSystemPrompt}` : ""}`;
|
|
4121
4148
|
}
|
|
4122
4149
|
/**
|
|
4123
4150
|
* Build the minimal-variant system prompt. Reuses the project context,
|
|
@@ -4147,7 +4174,7 @@ Guidelines:
|
|
|
4147
4174
|
|
|
4148
4175
|
## Working Directory
|
|
4149
4176
|
|
|
4150
|
-
The current working directory is \`${process.cwd()}\`. All relative paths in tool calls resolve from here. When using \`${TOOL_GLOB_FILES}\` or \`${TOOL_GREP_SEARCH}\` without an explicit \`dir_path\`, they search from this directory.${directoriesSection}${projectContextSection}${skillsSection}${featureModulesSection}${planModeSection}${deferredToolSection}`;
|
|
4177
|
+
The current working directory is \`${process.cwd()}\`. All relative paths in tool calls resolve from here. When using \`${TOOL_GLOB_FILES}\` or \`${TOOL_GREP_SEARCH}\` without an explicit \`dir_path\`, they search from this directory.${directoriesSection}${projectContextSection}${skillsSection}${featureModulesSection}${planModeSection}${deferredToolSection}${config?.appendSystemPrompt ? `\n\n${config.appendSystemPrompt}` : ""}`;
|
|
4151
4178
|
}
|
|
4152
4179
|
/**
|
|
4153
4180
|
* Pick a system prompt by variant. The dispatch point for the
|
|
@@ -8477,7 +8504,8 @@ var GeminiImageService = class extends AIImageService {
|
|
|
8477
8504
|
async generateImageViaContent(prompt, model = ImageModels.GEMINI_2_5_FLASH_IMAGE) {
|
|
8478
8505
|
const response = await this.genAI.models.generateContent({
|
|
8479
8506
|
model,
|
|
8480
|
-
contents: [{ text: prompt }]
|
|
8507
|
+
contents: [{ text: prompt }],
|
|
8508
|
+
config: { responseModalities: ["IMAGE", "TEXT"] }
|
|
8481
8509
|
});
|
|
8482
8510
|
if (!response.candidates || response.candidates.length === 0) throw new Error("No candidates returned from Gemini");
|
|
8483
8511
|
const candidate = response.candidates[0];
|
|
@@ -8568,7 +8596,8 @@ var GeminiImageService = class extends AIImageService {
|
|
|
8568
8596
|
});
|
|
8569
8597
|
const response = await this.genAI.models.generateContent({
|
|
8570
8598
|
model,
|
|
8571
|
-
contents
|
|
8599
|
+
contents,
|
|
8600
|
+
config: { responseModalities: ["IMAGE", "TEXT"] }
|
|
8572
8601
|
});
|
|
8573
8602
|
if (!response.candidates || response.candidates.length === 0) throw new Error("No candidates returned from Gemini");
|
|
8574
8603
|
const candidate = response.candidates[0];
|
|
@@ -9769,8 +9798,9 @@ function registerLambdaErrorHandlers(logger) {
|
|
|
9769
9798
|
} else log.error("[Lambda] Unhandled promise rejection", logEntry);
|
|
9770
9799
|
});
|
|
9771
9800
|
process.on("uncaughtException", (error) => {
|
|
9772
|
-
const
|
|
9773
|
-
log.
|
|
9801
|
+
const category = classifyError(error);
|
|
9802
|
+
if (category.startsWith("network_")) log.warn("[Lambda] Network error (uncaught exception)", buildLogEntry(error, category));
|
|
9803
|
+
else log.error("[Lambda] Uncaught exception", buildLogEntry(error, "uncaught_exception"));
|
|
9774
9804
|
});
|
|
9775
9805
|
}
|
|
9776
9806
|
/**
|
|
@@ -19745,13 +19775,19 @@ const updateUserSchema = z.object({
|
|
|
19745
19775
|
scrollbarWidth: z.number().optional(),
|
|
19746
19776
|
experimentalFeatures: z.record(z.string(), z.boolean()).optional(),
|
|
19747
19777
|
rechartsDisplayMode: z.enum(["inline", "artifact"]).optional(),
|
|
19778
|
+
toolsCatalogCollapsed: z.boolean().optional(),
|
|
19748
19779
|
docxTemplateFileId: z.string().nullable().optional(),
|
|
19749
19780
|
contextTelemetryLevel: z.enum([
|
|
19750
19781
|
"none",
|
|
19751
19782
|
"basic",
|
|
19752
19783
|
"enhanced"
|
|
19753
19784
|
]).optional(),
|
|
19754
|
-
contextTelemetryConsentedAt: z.date().optional()
|
|
19785
|
+
contextTelemetryConsentedAt: z.date().optional(),
|
|
19786
|
+
agentModeDefault: z.enum([
|
|
19787
|
+
"off",
|
|
19788
|
+
"auto",
|
|
19789
|
+
"on"
|
|
19790
|
+
]).optional()
|
|
19755
19791
|
}).nullable().optional()
|
|
19756
19792
|
});
|
|
19757
19793
|
z.object({
|
|
@@ -19794,7 +19830,7 @@ z.object({
|
|
|
19794
19830
|
username: z.string(),
|
|
19795
19831
|
email: z.string(),
|
|
19796
19832
|
name: z.string(),
|
|
19797
|
-
inviteCode: z.string(),
|
|
19833
|
+
inviteCode: z.string().optional(),
|
|
19798
19834
|
password: z.string(),
|
|
19799
19835
|
metadata: z.object({
|
|
19800
19836
|
loginTime: z.date(),
|
|
@@ -23523,8 +23559,175 @@ const DEFAULT_AGENT_MODEL = ChatModels.CLAUDE_4_5_HAIKU;
|
|
|
23523
23559
|
*/
|
|
23524
23560
|
const DEFAULT_THOROUGHNESS = "medium";
|
|
23525
23561
|
//#endregion
|
|
23562
|
+
//#region src/agents/toolFilter.ts
|
|
23563
|
+
/**
|
|
23564
|
+
* Check if a tool name matches a pattern
|
|
23565
|
+
*
|
|
23566
|
+
* Supports wildcards (*) that match any sequence of characters.
|
|
23567
|
+
*
|
|
23568
|
+
* @param toolName - The actual tool name to check
|
|
23569
|
+
* @param pattern - The pattern to match against (may include * wildcards)
|
|
23570
|
+
* @returns true if the tool name matches the pattern
|
|
23571
|
+
*
|
|
23572
|
+
* @example
|
|
23573
|
+
* matchesToolPattern('mcp__github__create_issue', 'mcp__github__*') // true
|
|
23574
|
+
* matchesToolPattern('mcp__github__delete_repo', 'mcp__*__delete_*') // true
|
|
23575
|
+
* matchesToolPattern('file_read', 'file_read') // true
|
|
23576
|
+
* matchesToolPattern('file_read', 'file_*') // true
|
|
23577
|
+
* matchesToolPattern('file_read', '*_read') // true
|
|
23578
|
+
* matchesToolPattern('bash_execute', 'file_*') // false
|
|
23579
|
+
*/
|
|
23580
|
+
function matchesToolPattern(toolName, pattern) {
|
|
23581
|
+
const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
23582
|
+
return new RegExp(`^${regexPattern}$`).test(toolName);
|
|
23583
|
+
}
|
|
23584
|
+
/**
|
|
23585
|
+
* Check if a tool name matches any pattern in a list
|
|
23586
|
+
*
|
|
23587
|
+
* @param toolName - The tool name to check
|
|
23588
|
+
* @param patterns - Array of patterns to match against
|
|
23589
|
+
* @returns true if the tool matches any pattern
|
|
23590
|
+
*/
|
|
23591
|
+
function matchesAnyPattern(toolName, patterns) {
|
|
23592
|
+
return patterns.some((pattern) => matchesToolPattern(toolName, pattern));
|
|
23593
|
+
}
|
|
23594
|
+
/**
|
|
23595
|
+
* Filter tools based on allowed and denied patterns
|
|
23596
|
+
*
|
|
23597
|
+
* Rules:
|
|
23598
|
+
* 1. Denied patterns take precedence over allowed patterns
|
|
23599
|
+
* 2. If no allowed patterns specified, all tools are allowed (minus denied)
|
|
23600
|
+
* 3. If allowed patterns specified, only matching tools are allowed
|
|
23601
|
+
* 4. Supports wildcards in both allowed and denied patterns
|
|
23602
|
+
*
|
|
23603
|
+
* @param allTools - Complete list of available tools
|
|
23604
|
+
* @param allowedPatterns - Whitelist patterns (optional)
|
|
23605
|
+
* @param deniedPatterns - Blacklist patterns (optional)
|
|
23606
|
+
* @returns Filtered list of tools
|
|
23607
|
+
*
|
|
23608
|
+
* @example
|
|
23609
|
+
* // Allow only specific tools
|
|
23610
|
+
* filterToolsByPatterns(tools, ['file_read', 'grep_search'])
|
|
23611
|
+
*
|
|
23612
|
+
* // Allow all GitHub MCP tools except delete operations
|
|
23613
|
+
* filterToolsByPatterns(tools, ['mcp__github__*'], ['mcp__github__delete_*'])
|
|
23614
|
+
*
|
|
23615
|
+
* // Deny specific tools, allow everything else
|
|
23616
|
+
* filterToolsByPatterns(tools, undefined, ['create_file', 'edit_file'])
|
|
23617
|
+
*/
|
|
23618
|
+
function filterToolsByPatterns(allTools, allowedPatterns, deniedPatterns) {
|
|
23619
|
+
return allTools.filter((tool) => {
|
|
23620
|
+
const toolName = tool.toolSchema.name;
|
|
23621
|
+
if (deniedPatterns && deniedPatterns.length > 0) {
|
|
23622
|
+
if (matchesAnyPattern(toolName, deniedPatterns)) return false;
|
|
23623
|
+
}
|
|
23624
|
+
if (!allowedPatterns || allowedPatterns.length === 0) return true;
|
|
23625
|
+
return matchesAnyPattern(toolName, allowedPatterns);
|
|
23626
|
+
});
|
|
23627
|
+
}
|
|
23628
|
+
//#endregion
|
|
23629
|
+
//#region src/utils/processHooks.ts
|
|
23630
|
+
const HOOK_TIMEOUT_MS = 5e3;
|
|
23631
|
+
/** Does a group's matcher match the event's matcher value? `*`/empty/undefined match anything. */
|
|
23632
|
+
function matcherMatches(groupMatcher, eventMatcher) {
|
|
23633
|
+
if (groupMatcher === void 0 || groupMatcher === "" || groupMatcher === "*") return true;
|
|
23634
|
+
return groupMatcher === eventMatcher;
|
|
23635
|
+
}
|
|
23636
|
+
var ProcessHooks = class {
|
|
23637
|
+
constructor(hooks) {
|
|
23638
|
+
this.hooks = hooks;
|
|
23639
|
+
}
|
|
23640
|
+
/**
|
|
23641
|
+
* Run every command registered for `event` whose matcher matches `eventMatcher`.
|
|
23642
|
+
* `payload` is written to each command's stdin (then EOF). Best-effort: a failing
|
|
23643
|
+
* command never throws or blocks the caller's lifecycle.
|
|
23644
|
+
*/
|
|
23645
|
+
async fire(event, eventMatcher, payload) {
|
|
23646
|
+
const groups = this.hooks[event];
|
|
23647
|
+
if (!groups || groups.length === 0) return;
|
|
23648
|
+
const stdin = JSON.stringify({
|
|
23649
|
+
hook_event_name: event,
|
|
23650
|
+
...eventMatcher ? { matcher: eventMatcher } : {},
|
|
23651
|
+
...payload
|
|
23652
|
+
});
|
|
23653
|
+
const runs = [];
|
|
23654
|
+
for (const group of groups) {
|
|
23655
|
+
if (!matcherMatches(group.matcher, eventMatcher)) continue;
|
|
23656
|
+
for (const hook of group.hooks ?? []) {
|
|
23657
|
+
if (hook.type !== "command" || !hook.command) continue;
|
|
23658
|
+
runs.push(runShellCommand({
|
|
23659
|
+
command: hook.command,
|
|
23660
|
+
cwd: process.cwd(),
|
|
23661
|
+
timeoutMs: HOOK_TIMEOUT_MS,
|
|
23662
|
+
stdin
|
|
23663
|
+
}).catch(() => void 0));
|
|
23664
|
+
}
|
|
23665
|
+
}
|
|
23666
|
+
await Promise.all(runs);
|
|
23667
|
+
}
|
|
23668
|
+
/** Fired when an interactive permission prompt begins blocking. */
|
|
23669
|
+
fireNotificationPermissionPrompt(toolName) {
|
|
23670
|
+
return this.fire("Notification", "permission_prompt", { tool_name: toolName });
|
|
23671
|
+
}
|
|
23672
|
+
/** Fired after any tool completes. */
|
|
23673
|
+
firePostToolUse(toolName) {
|
|
23674
|
+
return this.fire("PostToolUse", void 0, { tool_name: toolName });
|
|
23675
|
+
}
|
|
23676
|
+
/** Fired at the end of every agent turn. */
|
|
23677
|
+
fireStop() {
|
|
23678
|
+
return this.fire("Stop");
|
|
23679
|
+
}
|
|
23680
|
+
/** Fired when the user submits a new prompt. */
|
|
23681
|
+
fireUserPromptSubmit() {
|
|
23682
|
+
return this.fire("UserPromptSubmit");
|
|
23683
|
+
}
|
|
23684
|
+
};
|
|
23685
|
+
/**
|
|
23686
|
+
* Parse a `--settings` JSON string into a {@link SettingsHooks}. Defensive:
|
|
23687
|
+
* malformed JSON or a missing/!-object `hooks` field yields `null` (no hooks),
|
|
23688
|
+
* never throws — a bad `--settings` must not brick the launch.
|
|
23689
|
+
*/
|
|
23690
|
+
function parseSettingsHooks(settingsJson) {
|
|
23691
|
+
if (!settingsJson) return null;
|
|
23692
|
+
try {
|
|
23693
|
+
const parsed = JSON.parse(settingsJson);
|
|
23694
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
23695
|
+
const hooks = parsed.hooks;
|
|
23696
|
+
if (!hooks || typeof hooks !== "object") return null;
|
|
23697
|
+
return hooks;
|
|
23698
|
+
} catch {
|
|
23699
|
+
return null;
|
|
23700
|
+
}
|
|
23701
|
+
}
|
|
23702
|
+
let cached;
|
|
23703
|
+
/**
|
|
23704
|
+
* Lazily-built process-hook singleton from `B4M_SETTINGS_JSON`. Returns null when
|
|
23705
|
+
* no hooks are configured, so call sites can `void getProcessHooks()?.fireStop()`.
|
|
23706
|
+
*/
|
|
23707
|
+
function getProcessHooks() {
|
|
23708
|
+
if (cached !== void 0) return cached;
|
|
23709
|
+
const hooks = parseSettingsHooks(process.env.B4M_SETTINGS_JSON);
|
|
23710
|
+
cached = hooks ? new ProcessHooks(hooks) : null;
|
|
23711
|
+
return cached;
|
|
23712
|
+
}
|
|
23713
|
+
//#endregion
|
|
23526
23714
|
//#region src/utils/toolsAdapter.ts
|
|
23527
23715
|
/**
|
|
23716
|
+
* Tool-name patterns auto-approved without a permission prompt, from `--allowedTools`
|
|
23717
|
+
* (claude-compat; e.g. `mcp__manifold__*`). Parsed once from B4M_ALLOWED_TOOLS — set by
|
|
23718
|
+
* bin/bike4mind-cli.mjs. Returns [] when unset so behaviour is unchanged off-host.
|
|
23719
|
+
*/
|
|
23720
|
+
let cachedAllowedTools;
|
|
23721
|
+
function getAllowedToolPatterns() {
|
|
23722
|
+
if (cachedAllowedTools === void 0) try {
|
|
23723
|
+
const raw = process.env.B4M_ALLOWED_TOOLS;
|
|
23724
|
+
cachedAllowedTools = raw ? JSON.parse(raw) : [];
|
|
23725
|
+
} catch {
|
|
23726
|
+
cachedAllowedTools = [];
|
|
23727
|
+
}
|
|
23728
|
+
return cachedAllowedTools;
|
|
23729
|
+
}
|
|
23730
|
+
/**
|
|
23528
23731
|
* Simple CLI-friendly storage adapter
|
|
23529
23732
|
* Most methods won't be called for CLI-only tools
|
|
23530
23733
|
* Silent to avoid interfering with Ink rendering
|
|
@@ -23633,6 +23836,7 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
|
|
|
23633
23836
|
toolName,
|
|
23634
23837
|
result
|
|
23635
23838
|
});
|
|
23839
|
+
getProcessHooks()?.firePostToolUse(toolName);
|
|
23636
23840
|
return result;
|
|
23637
23841
|
}
|
|
23638
23842
|
const { useCliStore } = await import("./store-DgzCTRkN.mjs");
|
|
@@ -23645,6 +23849,8 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
|
|
|
23645
23849
|
});
|
|
23646
23850
|
return result;
|
|
23647
23851
|
}
|
|
23852
|
+
const allowedPatterns = getAllowedToolPatterns();
|
|
23853
|
+
if (allowedPatterns.length > 0 && matchesAnyPattern(toolName, allowedPatterns)) return executeAndRecord();
|
|
23648
23854
|
if (!permissionManager.needsPermission(toolName, { isSandboxed })) return executeAndRecord();
|
|
23649
23855
|
if (interactionMode === "auto-accept") return executeAndRecord();
|
|
23650
23856
|
const response = await showPermissionPrompt(toolName, effectiveArgs, await generateToolPreview(toolName, args, isSandboxed));
|
|
@@ -24370,9 +24576,11 @@ var MCPClient = class {
|
|
|
24370
24576
|
customArgs;
|
|
24371
24577
|
suppressStderr;
|
|
24372
24578
|
onStderrLine;
|
|
24579
|
+
url;
|
|
24580
|
+
headers;
|
|
24373
24581
|
tools = [];
|
|
24374
24582
|
serverName;
|
|
24375
|
-
constructor({ envVariables, name, command, args, suppressStderr = false, onStderrLine }) {
|
|
24583
|
+
constructor({ envVariables, name, command, args, suppressStderr = false, onStderrLine, url, headers }) {
|
|
24376
24584
|
this.mcp = new Client({
|
|
24377
24585
|
name: "mcp-client-cli",
|
|
24378
24586
|
version: "1.0.0"
|
|
@@ -24383,9 +24591,26 @@ var MCPClient = class {
|
|
|
24383
24591
|
this.customArgs = args;
|
|
24384
24592
|
this.suppressStderr = suppressStderr;
|
|
24385
24593
|
this.onStderrLine = onStderrLine;
|
|
24594
|
+
this.url = url;
|
|
24595
|
+
this.headers = headers;
|
|
24386
24596
|
}
|
|
24387
24597
|
async connectToServer() {
|
|
24388
24598
|
try {
|
|
24599
|
+
if (this.url) {
|
|
24600
|
+
const transport = new StreamableHTTPClientTransport(new URL(this.url), { requestInit: this.headers ? { headers: this.headers } : void 0 });
|
|
24601
|
+
transport.onerror = (error) => {
|
|
24602
|
+
console.error(`[MCP] Transport error for ${this.serverName}:`, error);
|
|
24603
|
+
};
|
|
24604
|
+
this.transport = transport;
|
|
24605
|
+
await this.mcp.connect(transport);
|
|
24606
|
+
const toolsResult = await this.mcp.listTools();
|
|
24607
|
+
this.tools = toolsResult.tools.map((tool) => ({
|
|
24608
|
+
name: tool.name,
|
|
24609
|
+
description: tool.description,
|
|
24610
|
+
input_schema: tool.inputSchema
|
|
24611
|
+
}));
|
|
24612
|
+
return;
|
|
24613
|
+
}
|
|
24389
24614
|
const envVarsObject = this.envVariables.reduce((acc, env) => ({
|
|
24390
24615
|
...acc,
|
|
24391
24616
|
[env.key]: env.value
|
|
@@ -24410,7 +24635,7 @@ var MCPClient = class {
|
|
|
24410
24635
|
console.log(`[MCP] Using server: ${this.serverName} at ${serverScriptPath}`);
|
|
24411
24636
|
}
|
|
24412
24637
|
const stderrMode = this.suppressStderr ? "ignore" : this.onStderrLine ? "pipe" : void 0;
|
|
24413
|
-
const
|
|
24638
|
+
const stdioTransport = new StdioClientTransport({
|
|
24414
24639
|
command,
|
|
24415
24640
|
args,
|
|
24416
24641
|
env: {
|
|
@@ -24418,13 +24643,13 @@ var MCPClient = class {
|
|
|
24418
24643
|
...envVarsObject
|
|
24419
24644
|
},
|
|
24420
24645
|
...stderrMode && { stderr: stderrMode }
|
|
24421
|
-
};
|
|
24422
|
-
this.transport =
|
|
24423
|
-
|
|
24646
|
+
});
|
|
24647
|
+
this.transport = stdioTransport;
|
|
24648
|
+
stdioTransport.onerror = (error) => {
|
|
24424
24649
|
console.error(`[MCP] Transport error for ${this.serverName}:`, error);
|
|
24425
24650
|
};
|
|
24426
|
-
await this.mcp.connect(
|
|
24427
|
-
if (this.onStderrLine &&
|
|
24651
|
+
await this.mcp.connect(stdioTransport);
|
|
24652
|
+
if (this.onStderrLine && stdioTransport.stderr) this.readStderr(stdioTransport.stderr);
|
|
24428
24653
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
24429
24654
|
const toolsResult = await this.mcp.listTools();
|
|
24430
24655
|
this.tools = toolsResult.tools.map((tool) => {
|
|
@@ -24646,6 +24871,8 @@ var McpManager = class {
|
|
|
24646
24871
|
name: serverConfig.name,
|
|
24647
24872
|
command: serverConfig.command,
|
|
24648
24873
|
args: serverConfig.args,
|
|
24874
|
+
url: serverConfig.url,
|
|
24875
|
+
headers: serverConfig.headers,
|
|
24649
24876
|
suppressStderr: true
|
|
24650
24877
|
});
|
|
24651
24878
|
await client.connectToServer();
|
|
@@ -24703,6 +24930,7 @@ var McpManager = class {
|
|
|
24703
24930
|
const key = JSON.stringify({
|
|
24704
24931
|
command: serverConfig.command,
|
|
24705
24932
|
args: serverConfig.args,
|
|
24933
|
+
url: serverConfig.url,
|
|
24706
24934
|
env: serverConfig.env
|
|
24707
24935
|
});
|
|
24708
24936
|
return createHash("sha256").update(key).digest("hex").slice(0, 16);
|
|
@@ -25945,73 +26173,6 @@ var ApiClient = class {
|
|
|
25945
26173
|
}
|
|
25946
26174
|
};
|
|
25947
26175
|
//#endregion
|
|
25948
|
-
//#region src/agents/toolFilter.ts
|
|
25949
|
-
/**
|
|
25950
|
-
* Check if a tool name matches a pattern
|
|
25951
|
-
*
|
|
25952
|
-
* Supports wildcards (*) that match any sequence of characters.
|
|
25953
|
-
*
|
|
25954
|
-
* @param toolName - The actual tool name to check
|
|
25955
|
-
* @param pattern - The pattern to match against (may include * wildcards)
|
|
25956
|
-
* @returns true if the tool name matches the pattern
|
|
25957
|
-
*
|
|
25958
|
-
* @example
|
|
25959
|
-
* matchesToolPattern('mcp__github__create_issue', 'mcp__github__*') // true
|
|
25960
|
-
* matchesToolPattern('mcp__github__delete_repo', 'mcp__*__delete_*') // true
|
|
25961
|
-
* matchesToolPattern('file_read', 'file_read') // true
|
|
25962
|
-
* matchesToolPattern('file_read', 'file_*') // true
|
|
25963
|
-
* matchesToolPattern('file_read', '*_read') // true
|
|
25964
|
-
* matchesToolPattern('bash_execute', 'file_*') // false
|
|
25965
|
-
*/
|
|
25966
|
-
function matchesToolPattern(toolName, pattern) {
|
|
25967
|
-
const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
25968
|
-
return new RegExp(`^${regexPattern}$`).test(toolName);
|
|
25969
|
-
}
|
|
25970
|
-
/**
|
|
25971
|
-
* Check if a tool name matches any pattern in a list
|
|
25972
|
-
*
|
|
25973
|
-
* @param toolName - The tool name to check
|
|
25974
|
-
* @param patterns - Array of patterns to match against
|
|
25975
|
-
* @returns true if the tool matches any pattern
|
|
25976
|
-
*/
|
|
25977
|
-
function matchesAnyPattern(toolName, patterns) {
|
|
25978
|
-
return patterns.some((pattern) => matchesToolPattern(toolName, pattern));
|
|
25979
|
-
}
|
|
25980
|
-
/**
|
|
25981
|
-
* Filter tools based on allowed and denied patterns
|
|
25982
|
-
*
|
|
25983
|
-
* Rules:
|
|
25984
|
-
* 1. Denied patterns take precedence over allowed patterns
|
|
25985
|
-
* 2. If no allowed patterns specified, all tools are allowed (minus denied)
|
|
25986
|
-
* 3. If allowed patterns specified, only matching tools are allowed
|
|
25987
|
-
* 4. Supports wildcards in both allowed and denied patterns
|
|
25988
|
-
*
|
|
25989
|
-
* @param allTools - Complete list of available tools
|
|
25990
|
-
* @param allowedPatterns - Whitelist patterns (optional)
|
|
25991
|
-
* @param deniedPatterns - Blacklist patterns (optional)
|
|
25992
|
-
* @returns Filtered list of tools
|
|
25993
|
-
*
|
|
25994
|
-
* @example
|
|
25995
|
-
* // Allow only specific tools
|
|
25996
|
-
* filterToolsByPatterns(tools, ['file_read', 'grep_search'])
|
|
25997
|
-
*
|
|
25998
|
-
* // Allow all GitHub MCP tools except delete operations
|
|
25999
|
-
* filterToolsByPatterns(tools, ['mcp__github__*'], ['mcp__github__delete_*'])
|
|
26000
|
-
*
|
|
26001
|
-
* // Deny specific tools, allow everything else
|
|
26002
|
-
* filterToolsByPatterns(tools, undefined, ['create_file', 'edit_file'])
|
|
26003
|
-
*/
|
|
26004
|
-
function filterToolsByPatterns(allTools, allowedPatterns, deniedPatterns) {
|
|
26005
|
-
return allTools.filter((tool) => {
|
|
26006
|
-
const toolName = tool.toolSchema.name;
|
|
26007
|
-
if (deniedPatterns && deniedPatterns.length > 0) {
|
|
26008
|
-
if (matchesAnyPattern(toolName, deniedPatterns)) return false;
|
|
26009
|
-
}
|
|
26010
|
-
if (!allowedPatterns || allowedPatterns.length === 0) return true;
|
|
26011
|
-
return matchesAnyPattern(toolName, allowedPatterns);
|
|
26012
|
-
});
|
|
26013
|
-
}
|
|
26014
|
-
//#endregion
|
|
26015
26176
|
//#region src/tools/skillTool.ts
|
|
26016
26177
|
/**
|
|
26017
26178
|
* Execute a lifecycle hook script
|
|
@@ -28751,4 +28912,4 @@ function createReviewGateStore(onUpdate) {
|
|
|
28751
28912
|
};
|
|
28752
28913
|
}
|
|
28753
28914
|
//#endregion
|
|
28754
|
-
export {
|
|
28915
|
+
export { SessionStore as $, substituteArguments as A, DEFAULT_RETRY_CONFIG as B, WebSocketToolExecutor as C, ServerLlmBackend as D, WebSocketLlmBackend as E, generateCliTools as F, getPlanModeFilePath as G, clearFeatureModuleTools as H, getProcessHooks as I, isReadOnlyTool as J, buildSystemPrompt as K, ALWAYS_DENIED_FOR_AGENTS as L, extractCompactInstructions as M, loadContextFiles as N, isTransientNetworkError as O, PermissionManager as P, CommandHistoryStore as Q, DEFAULT_AGENT_MODEL as R, ApiClient as S, FallbackLlmBackend as T, registerFeatureModuleTools as U, DEFAULT_THOROUGHNESS as V, setWebSocketToolExecutor as W, CustomCommandStore as X, ReActAgent as Y, CheckpointStore as Z, createAgentDelegateTool as _, createBlockerTools as a, formatFileSize$1 as at, createSkillTool as b, createDecisionStore as c, createFindDefinitionTool as d, OAuthClient as et, createTodoStore as f, BackgroundAgentManager as g, createBackgroundAgentTools as h, createBlockerStore as i, mergeCommands as it, formatStep as j, McpManager as k, formatDecisionsOutput as l, createCoordinateTaskTool as m, createReviewGateTool as n, processFileReferences as nt, formatBlockersOutput as o, searchFiles as ot, createWriteTodosTool as p, buildSkillsPromptSection as q, formatReviewGatesOutput as r, searchCommands as rt, createDecisionLogTool as s, warmFileCache as st, createReviewGateStore as t, hasFileReferences as tt, createGetFileStructureTool as u, AgentStore as v, WebSocketConnectionManager as w, parseAgentConfig as x, SubagentOrchestrator as y, DEFAULT_MAX_ITERATIONS as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bike4mind/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -107,9 +107,9 @@
|
|
|
107
107
|
"zod": "^4.4.3",
|
|
108
108
|
"zod-validation-error": "^5.0.0",
|
|
109
109
|
"zustand": "^5.0.13",
|
|
110
|
-
"@bike4mind/fab-pipeline": "0.3.
|
|
111
|
-
"@bike4mind/llm-adapters": "0.4.
|
|
112
|
-
"@bike4mind/observability": "0.1.
|
|
110
|
+
"@bike4mind/fab-pipeline": "0.3.4",
|
|
111
|
+
"@bike4mind/llm-adapters": "0.4.4",
|
|
112
|
+
"@bike4mind/observability": "0.1.1"
|
|
113
113
|
},
|
|
114
114
|
"devDependencies": {
|
|
115
115
|
"@types/better-sqlite3": "^7.6.13",
|
|
@@ -124,11 +124,11 @@
|
|
|
124
124
|
"tsx": "^4.22.3",
|
|
125
125
|
"typescript": "^5.9.3",
|
|
126
126
|
"vitest": "^4.1.9",
|
|
127
|
-
"@bike4mind/agents": "0.15.
|
|
128
|
-
"@bike4mind/common": "2.
|
|
129
|
-
"@bike4mind/mcp": "1.
|
|
130
|
-
"@bike4mind/services": "2.
|
|
131
|
-
"@bike4mind/utils": "2.24.
|
|
127
|
+
"@bike4mind/agents": "0.15.1",
|
|
128
|
+
"@bike4mind/common": "2.111.0",
|
|
129
|
+
"@bike4mind/mcp": "1.38.0",
|
|
130
|
+
"@bike4mind/services": "2.96.0",
|
|
131
|
+
"@bike4mind/utils": "2.24.4"
|
|
132
132
|
},
|
|
133
133
|
"optionalDependencies": {
|
|
134
134
|
"@vscode/ripgrep": "^1.18.0"
|