@nomad-e/bluma-cli 0.1.45 → 0.1.47
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/main.js +420 -102
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -884,11 +884,17 @@ var useCustomInput = ({
|
|
|
884
884
|
return;
|
|
885
885
|
}
|
|
886
886
|
if (key.escape) {
|
|
887
|
+
if (globalThis.__BLUMA_SLASH_MENU_OPEN__) {
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
887
890
|
onInterrupt();
|
|
888
891
|
return;
|
|
889
892
|
}
|
|
890
893
|
if (isReadOnly) {
|
|
891
894
|
if (key.return && !key.shift) {
|
|
895
|
+
if (globalThis.__BLUMA_SLASH_MENU_OPEN__) {
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
892
898
|
if (state.text.trim().length > 0) {
|
|
893
899
|
onSubmit(state.text);
|
|
894
900
|
dispatch({ type: "SUBMIT" });
|
|
@@ -899,6 +905,10 @@ var useCustomInput = ({
|
|
|
899
905
|
dispatch({ type: "NEWLINE" });
|
|
900
906
|
return;
|
|
901
907
|
}
|
|
908
|
+
if (globalThis.__BLUMA_SLASH_MENU_OPEN__) {
|
|
909
|
+
if (key.tab) return;
|
|
910
|
+
if (key.upArrow || key.downArrow) return;
|
|
911
|
+
}
|
|
902
912
|
if (key.leftArrow) return dispatch({ type: "MOVE_CURSOR", direction: "left" });
|
|
903
913
|
if (key.rightArrow) return dispatch({ type: "MOVE_CURSOR", direction: "right" });
|
|
904
914
|
if (key.upArrow) return dispatch({ type: "MOVE_CURSOR", direction: "up" });
|
|
@@ -924,6 +934,7 @@ var useCustomInput = ({
|
|
|
924
934
|
}
|
|
925
935
|
if (key.return) {
|
|
926
936
|
if (globalThis.__BLUMA_AT_OPEN__) return;
|
|
937
|
+
if (globalThis.__BLUMA_SLASH_MENU_OPEN__) return;
|
|
927
938
|
if (globalThis.__BLUMA_SUPPRESS_SUBMIT__) {
|
|
928
939
|
globalThis.__BLUMA_SUPPRESS_SUBMIT__ = false;
|
|
929
940
|
return;
|
|
@@ -934,6 +945,10 @@ var useCustomInput = ({
|
|
|
934
945
|
}
|
|
935
946
|
return;
|
|
936
947
|
}
|
|
948
|
+
if (globalThis.__BLUMA_SLASH_MENU_OPEN__) {
|
|
949
|
+
if (key.tab) return;
|
|
950
|
+
if (key.upArrow || key.downArrow) return;
|
|
951
|
+
}
|
|
937
952
|
if (key.leftArrow) return dispatch({ type: "MOVE_CURSOR", direction: "left" });
|
|
938
953
|
if (key.rightArrow) return dispatch({ type: "MOVE_CURSOR", direction: "right" });
|
|
939
954
|
if (key.upArrow) return dispatch({ type: "MOVE_CURSOR", direction: "up" });
|
|
@@ -951,6 +966,7 @@ var useCustomInput = ({
|
|
|
951
966
|
void Promise.resolve(onChordPaste(insertAtCursor));
|
|
952
967
|
return;
|
|
953
968
|
}
|
|
969
|
+
if (key.tab && globalThis.__BLUMA_SLASH_MENU_OPEN__) return;
|
|
954
970
|
if (key.ctrl || key.meta || key.tab) return;
|
|
955
971
|
inputBuffer.current += input;
|
|
956
972
|
if (!flushScheduled.current) {
|
|
@@ -1090,8 +1106,13 @@ var getSlashCommands = () => [
|
|
|
1090
1106
|
},
|
|
1091
1107
|
{
|
|
1092
1108
|
name: "/agent",
|
|
1093
|
-
description: "
|
|
1094
|
-
category: "
|
|
1109
|
+
description: "prompt profile (not /agents): /agent [default|coordinator] \u2014 coordinator playbook in system prompt; then /init",
|
|
1110
|
+
category: "agent"
|
|
1111
|
+
},
|
|
1112
|
+
{
|
|
1113
|
+
name: "/agents",
|
|
1114
|
+
description: "list worker/agent sessions (spawn_agent children in this CLI)",
|
|
1115
|
+
category: "agent"
|
|
1095
1116
|
},
|
|
1096
1117
|
{
|
|
1097
1118
|
name: "/features",
|
|
@@ -2208,9 +2229,23 @@ var InputPrompt = memo2(({
|
|
|
2208
2229
|
const resolved = resolveInlineImageLabels(valueForAgent, map);
|
|
2209
2230
|
if (isReadOnly) {
|
|
2210
2231
|
if (trimmed.length > 0) {
|
|
2232
|
+
const routeText = resolved.trim();
|
|
2233
|
+
if (isSlashRoutingLine(routeText)) {
|
|
2234
|
+
onSubmit(resolved);
|
|
2235
|
+
clearInlineImageSlots();
|
|
2236
|
+
return;
|
|
2237
|
+
}
|
|
2238
|
+
if (routeText.startsWith("/")) {
|
|
2239
|
+
uiEventBus.emit("input_notice", {
|
|
2240
|
+
message: "Unknown or incomplete slash command while the agent runs. Type /help for the list, or finish the command name.",
|
|
2241
|
+
ts: Date.now()
|
|
2242
|
+
});
|
|
2243
|
+
clearInlineImageSlots();
|
|
2244
|
+
return;
|
|
2245
|
+
}
|
|
2211
2246
|
uiEventBus.emit("user_overlay", {
|
|
2212
2247
|
kind: "message",
|
|
2213
|
-
payload:
|
|
2248
|
+
payload: routeText,
|
|
2214
2249
|
ts: Date.now()
|
|
2215
2250
|
});
|
|
2216
2251
|
clearInlineImageSlots();
|
|
@@ -2302,7 +2337,7 @@ var InputPrompt = memo2(({
|
|
|
2302
2337
|
return { lines, cursorLine, cursorCol, totalLines: lines.length };
|
|
2303
2338
|
}, [text, cursorPosition]);
|
|
2304
2339
|
const displayData = linesData;
|
|
2305
|
-
const placeholder = isReadOnly ? "
|
|
2340
|
+
const placeholder = isReadOnly ? " Esc cancel \xB7 / opens slash palette (\u2191\u2193 Tab \xB7 Enter to apply) \xB7 Enter sends" : "";
|
|
2306
2341
|
const showPlaceholder = text.length === 0 && isReadOnly;
|
|
2307
2342
|
const slashQuery = useMemo(() => text.startsWith("/") ? text : "", [text]);
|
|
2308
2343
|
const slashSuggestions = useMemo(() => {
|
|
@@ -2310,7 +2345,7 @@ var InputPrompt = memo2(({
|
|
|
2310
2345
|
return filterSlashCommands(slashQuery);
|
|
2311
2346
|
}, [slashQuery]);
|
|
2312
2347
|
useEffect3(() => {
|
|
2313
|
-
if (
|
|
2348
|
+
if (disableWhileProcessing) {
|
|
2314
2349
|
setSlashOpen(false);
|
|
2315
2350
|
wasSlashMenuOpenRef.current = false;
|
|
2316
2351
|
return;
|
|
@@ -2326,31 +2361,41 @@ var InputPrompt = memo2(({
|
|
|
2326
2361
|
wasSlashMenuOpenRef.current = false;
|
|
2327
2362
|
setSlashOpen(false);
|
|
2328
2363
|
}
|
|
2329
|
-
}, [text,
|
|
2364
|
+
}, [text, disableWhileProcessing]);
|
|
2330
2365
|
useEffect3(() => {
|
|
2331
2366
|
if (!slashOpen || slashSuggestions.length === 0) return;
|
|
2332
2367
|
setSlashIndex((i) => Math.min(i, slashSuggestions.length - 1));
|
|
2333
2368
|
}, [slashOpen, slashSuggestions.length, slashQuery]);
|
|
2369
|
+
useLayoutEffect(() => {
|
|
2370
|
+
const open = slashOpen && slashSuggestions.length > 0 && !disableWhileProcessing && !permissionDialogOpen;
|
|
2371
|
+
globalThis.__BLUMA_SLASH_MENU_OPEN__ = open;
|
|
2372
|
+
return () => {
|
|
2373
|
+
globalThis.__BLUMA_SLASH_MENU_OPEN__ = false;
|
|
2374
|
+
};
|
|
2375
|
+
}, [slashOpen, slashSuggestions.length, disableWhileProcessing, permissionDialogOpen]);
|
|
2376
|
+
const applySlashChoice = (choice) => {
|
|
2377
|
+
if (!choice) return;
|
|
2378
|
+
setSlashOpen(false);
|
|
2379
|
+
try {
|
|
2380
|
+
setText(`${choice.name} `);
|
|
2381
|
+
} catch {
|
|
2382
|
+
permissiveOnSubmit(`${choice.name} `);
|
|
2383
|
+
}
|
|
2384
|
+
};
|
|
2334
2385
|
useInput2((input, key) => {
|
|
2335
|
-
if (!slashOpen) return;
|
|
2386
|
+
if (!slashOpen || slashSuggestions.length === 0) return;
|
|
2336
2387
|
if (key.downArrow) {
|
|
2337
2388
|
setSlashIndex((i) => Math.min(i + 1, Math.max(0, slashSuggestions.length - 1)));
|
|
2338
2389
|
} else if (key.upArrow) {
|
|
2339
2390
|
setSlashIndex((i) => Math.max(i - 1, 0));
|
|
2340
2391
|
} else if (key.return) {
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
try {
|
|
2345
|
-
setText(`${choice.name} `);
|
|
2346
|
-
} catch (e) {
|
|
2347
|
-
permissiveOnSubmit(`${choice.name} `);
|
|
2348
|
-
}
|
|
2349
|
-
}
|
|
2392
|
+
applySlashChoice(slashSuggestions[slashIndex]);
|
|
2393
|
+
} else if (key.tab) {
|
|
2394
|
+
applySlashChoice(slashSuggestions[slashIndex]);
|
|
2350
2395
|
} else if (key.escape) {
|
|
2351
2396
|
setSlashOpen(false);
|
|
2352
2397
|
}
|
|
2353
|
-
}, { isActive: slashOpen && !permissionDialogOpen });
|
|
2398
|
+
}, { isActive: slashOpen && slashSuggestions.length > 0 && !permissionDialogOpen && !disableWhileProcessing });
|
|
2354
2399
|
useEffect3(() => {
|
|
2355
2400
|
if (globalThis.__BLUMA_FORCE_CURSOR_END__) {
|
|
2356
2401
|
setText(text, text.length);
|
|
@@ -6939,7 +6984,24 @@ function readSessionLog(sessionId) {
|
|
|
6939
6984
|
}
|
|
6940
6985
|
|
|
6941
6986
|
// src/app/agent/tools/natives/agent_coordination.ts
|
|
6942
|
-
function
|
|
6987
|
+
function readUserContextFromEnv() {
|
|
6988
|
+
const raw = process.env.BLUMA_USER_CONTEXT_JSON;
|
|
6989
|
+
if (!raw) return null;
|
|
6990
|
+
try {
|
|
6991
|
+
const parsed = JSON.parse(raw);
|
|
6992
|
+
return {
|
|
6993
|
+
conversationId: parsed.conversationId ?? null,
|
|
6994
|
+
userId: parsed.userId ?? null,
|
|
6995
|
+
userName: parsed.userName ?? null,
|
|
6996
|
+
userEmail: parsed.userEmail ?? null,
|
|
6997
|
+
companyId: parsed.companyId ?? null,
|
|
6998
|
+
companyName: parsed.companyName ?? null
|
|
6999
|
+
};
|
|
7000
|
+
} catch {
|
|
7001
|
+
return null;
|
|
7002
|
+
}
|
|
7003
|
+
}
|
|
7004
|
+
function buildWorkerPayload(sessionId, args, parentSessionId, userContext) {
|
|
6943
7005
|
return {
|
|
6944
7006
|
message_id: sessionId,
|
|
6945
7007
|
session_id: sessionId,
|
|
@@ -6952,6 +7014,7 @@ function buildWorkerPayload(sessionId, args, parentSessionId) {
|
|
|
6952
7014
|
worker_title: args.title || null,
|
|
6953
7015
|
worker_role: args.agent_type || "worker"
|
|
6954
7016
|
},
|
|
7017
|
+
user_context: userContext || void 0,
|
|
6955
7018
|
metadata: {
|
|
6956
7019
|
sandbox: process.env.BLUMA_SANDBOX === "true",
|
|
6957
7020
|
sandbox_name: process.env.BLUMA_SANDBOX_NAME || void 0,
|
|
@@ -6995,8 +7058,9 @@ async function spawnAgent(args) {
|
|
|
6995
7058
|
}
|
|
6996
7059
|
const sessionId = uuidv43();
|
|
6997
7060
|
const parentSessionId = process.env.BLUMA_SESSION_ID || null;
|
|
7061
|
+
const userContext = readUserContextFromEnv();
|
|
6998
7062
|
const title = args.title || `worker:${args.agent_type || "worker"}`;
|
|
6999
|
-
const payload = buildWorkerPayload(sessionId, args, parentSessionId);
|
|
7063
|
+
const payload = buildWorkerPayload(sessionId, args, parentSessionId, userContext);
|
|
7000
7064
|
const payloadDir = fs16.mkdtempSync(path18.join(os10.tmpdir(), "bluma-worker-"));
|
|
7001
7065
|
const payloadPath = path18.join(payloadDir, `${sessionId}.json`);
|
|
7002
7066
|
fs16.writeFileSync(payloadPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
@@ -7012,7 +7076,9 @@ async function spawnAgent(args) {
|
|
|
7012
7076
|
background: true,
|
|
7013
7077
|
coordinator_worker: true,
|
|
7014
7078
|
parent_session_id: parentSessionId,
|
|
7015
|
-
agent_type: args.agent_type || "worker"
|
|
7079
|
+
agent_type: args.agent_type || "worker",
|
|
7080
|
+
user_id: userContext?.userId ?? null,
|
|
7081
|
+
company_id: userContext?.companyId ?? null
|
|
7016
7082
|
}
|
|
7017
7083
|
});
|
|
7018
7084
|
const child = spawn4(
|
|
@@ -8635,6 +8701,212 @@ function getPlugin(name) {
|
|
|
8635
8701
|
return listPlugins().find((plugin) => plugin.name === q) || null;
|
|
8636
8702
|
}
|
|
8637
8703
|
|
|
8704
|
+
// src/app/agent/utils/coordinator_prompt.ts
|
|
8705
|
+
var COORDINATOR_SYSTEM_PROMPT = `
|
|
8706
|
+
# BluMa Coordinator Mode
|
|
8707
|
+
|
|
8708
|
+
You are the **BluMa Coordinator** - a worker orchestrator for software engineering.
|
|
8709
|
+
|
|
8710
|
+
## 1. Your Role
|
|
8711
|
+
|
|
8712
|
+
You do **NOT execute tasks directly**. Your job is to:
|
|
8713
|
+
- **Orchestrate workers** to research, implement, and verify changes
|
|
8714
|
+
- **Synthesize results** and communicate with the user
|
|
8715
|
+
- **Answer questions directly** when possible \u2014 don't delegate work you can handle without tools
|
|
8716
|
+
- **Read-only tools** (\`read_file_lines\`, \`grep\`, etc.) are fine for **light** coordinator checks (e.g. verify a path before writing a worker spec); heavy exploration belongs in workers
|
|
8717
|
+
- **Break complex tasks** into parallelizable work items
|
|
8718
|
+
|
|
8719
|
+
Every message you send is to the **user**. Worker results are internal notifications.
|
|
8720
|
+
|
|
8721
|
+
## 2. Your Main Tools
|
|
8722
|
+
|
|
8723
|
+
| Tool | Usage |
|
|
8724
|
+
|------|-------|
|
|
8725
|
+
| \`spawn_agent\` | Create a new worker |
|
|
8726
|
+
| \`wait_agent\` | Wait for worker completion |
|
|
8727
|
+
| \`list_agents\` | List active/completed workers |
|
|
8728
|
+
|
|
8729
|
+
### Tool contract (BluMa)
|
|
8730
|
+
|
|
8731
|
+
- \`spawn_agent\` returns JSON with \`session_id\` (and \`success\`). That id is the only stable handle for follow-up.
|
|
8732
|
+
- Call \`wait_agent\` with \`{ "session_id": "<id>" }\`. Default \`timeout_ms\` is short (~30s); for audits, refactors, or test runs use a **large** \`timeout_ms\` (e.g. \`600000\`) or poll with \`list_agents\` if appropriate.
|
|
8733
|
+
- \`list_agents\` accepts optional \`parent_session_id\` and \`status\` filters.
|
|
8734
|
+
- **Parallelism:** In a single turn you may issue **multiple** \`spawn_agent\` calls back-to-back so independent workers start together; then \`wait_agent\` on each \`session_id\` (order as needed).
|
|
8735
|
+
- You do **not** receive XML or push notifications from workers \u2014 completion and payload come from \`wait_agent\` (and \`result\` inside that JSON when present).
|
|
8736
|
+
|
|
8737
|
+
### When to call \`spawn_agent\`:
|
|
8738
|
+
|
|
8739
|
+
- **Research**: "Investigate the authentication structure"
|
|
8740
|
+
- **Implementation**: "Implement feature X in file Y"
|
|
8741
|
+
- **Verification**: "Run tests and verify they pass"
|
|
8742
|
+
- **Refactoring**: "Refactor module Z to use modern patterns"
|
|
8743
|
+
|
|
8744
|
+
**Concurrency Tip**: Launch **independent workers in parallel** whenever possible.
|
|
8745
|
+
|
|
8746
|
+
## 3. Task Workflow
|
|
8747
|
+
|
|
8748
|
+
### Phases
|
|
8749
|
+
|
|
8750
|
+
| Phase | Who | Purpose |
|
|
8751
|
+
|-------|-----|---------|
|
|
8752
|
+
| **Research** | Workers (parallel) | Investigate codebase, find files, understand problem |
|
|
8753
|
+
| **Synthesis** | **You** (coordinator) | Read findings, understand problem, craft implementation specs |
|
|
8754
|
+
| **Implementation** | Workers | Make targeted changes per spec, commit |
|
|
8755
|
+
| **Verification** | Workers | Test changes work |
|
|
8756
|
+
|
|
8757
|
+
### Concurrency is Your Superpower
|
|
8758
|
+
|
|
8759
|
+
**Workers are async.** Launch independent tasks **concurrently** - don't serialize work that can run in parallel.
|
|
8760
|
+
|
|
8761
|
+
**Manage concurrency:**
|
|
8762
|
+
- Read-only tasks (research) - run in parallel freely
|
|
8763
|
+
- Write-heavy tasks (implementation) - one at a time per file set
|
|
8764
|
+
- Verification can run alongside implementation on different file areas
|
|
8765
|
+
|
|
8766
|
+
## 4. Writing Worker Prompts
|
|
8767
|
+
|
|
8768
|
+
**Workers CANNOT see your conversation.** Every prompt must be **self-contained** with everything the worker needs.
|
|
8769
|
+
|
|
8770
|
+
### ALWAYS Synthesize - Your Most Important Job
|
|
8771
|
+
|
|
8772
|
+
When workers report research findings, **you must understand them before directing follow-up**.
|
|
8773
|
+
|
|
8774
|
+
**NEVER write:**
|
|
8775
|
+
- "Based on your findings, implement the fix"
|
|
8776
|
+
- "The worker found an issue in the auth module. Please fix it."
|
|
8777
|
+
|
|
8778
|
+
**ALWAYS write:**
|
|
8779
|
+
- "Fix the null pointer in src/auth/validate.ts:42. The user field is undefined when Session.expired is true but the token is still cached. Add a null check before accessing user.id - if null, return 401 with 'Session expired'. Commit and report the hash."
|
|
8780
|
+
|
|
8781
|
+
### Add a Purpose Statement
|
|
8782
|
+
|
|
8783
|
+
Include a brief purpose so workers can calibrate depth:
|
|
8784
|
+
- "This research will inform a PR description - focus on user-facing changes."
|
|
8785
|
+
- "I need this to plan an implementation - report file paths, line numbers, and type signatures."
|
|
8786
|
+
- "This is a quick check before we merge - just verify the happy path."
|
|
8787
|
+
|
|
8788
|
+
### Continue vs Spawn Fresh
|
|
8789
|
+
|
|
8790
|
+
| Situation | Mechanism | Why |
|
|
8791
|
+
|-----------|-----------|-----|
|
|
8792
|
+
| Research explored exactly the files that need editing | **Continue** (wait + new prompt) | Worker already has files in context + now gets a clear plan |
|
|
8793
|
+
| Research was broad but implementation is narrow | **Spawn fresh** | Avoid dragging along exploration noise; focused context is cleaner |
|
|
8794
|
+
| Correcting a failure or extending recent work | **Continue** | Worker has the error context and knows what it just tried |
|
|
8795
|
+
| Verifying code a different worker just wrote | **Spawn fresh** | Verifier should see the code with fresh eyes |
|
|
8796
|
+
| First implementation used the wrong approach entirely | **Spawn fresh** | Wrong-approach context pollutes the retry |
|
|
8797
|
+
| Completely unrelated task | **Spawn fresh** | No useful context to reuse |
|
|
8798
|
+
|
|
8799
|
+
**There is no universal default.** Think about how much of the worker's context overlaps with the next task.
|
|
8800
|
+
|
|
8801
|
+
### Prompt Examples
|
|
8802
|
+
|
|
8803
|
+
**Good examples:**
|
|
8804
|
+
|
|
8805
|
+
1. **Implementation**: "Fix the null pointer in src/auth/validate.ts:42. The user field can be undefined when the session expires. Add a null check and return early with an appropriate error. Commit and report the hash."
|
|
8806
|
+
|
|
8807
|
+
2. **Precise git operation**: "Create a new branch from main called 'fix/session-expiry'. Cherry-pick only commit abc123 onto it. Push and create a draft PR targeting main. Add reviewers. Report the PR URL."
|
|
8808
|
+
|
|
8809
|
+
3. **Correction (continued worker, short)**: "Two tests still failing at lines 58 and 72 - update the assertions to match the new error message."
|
|
8810
|
+
|
|
8811
|
+
**Bad examples:**
|
|
8812
|
+
|
|
8813
|
+
1. "Fix the bug we discussed" - no context, workers can't see your conversation
|
|
8814
|
+
2. "Based on your findings, implement" - lazy delegation; synthesize the findings yourself
|
|
8815
|
+
3. "Create a PR for the recent changes" - ambiguous scope: which changes? which branch?
|
|
8816
|
+
4. "Something went wrong with the tests, can you look?" - no error message, no file path, no direction
|
|
8817
|
+
|
|
8818
|
+
## 5. What Real Verification Looks Like
|
|
8819
|
+
|
|
8820
|
+
Verification means **proving the code works**, not confirming it exists.
|
|
8821
|
+
|
|
8822
|
+
- Run tests **with the feature enabled** - not just "tests pass"
|
|
8823
|
+
- Run typechecks and **investigate errors** - don't dismiss as "unrelated"
|
|
8824
|
+
- Be skeptical - if something looks off, dig in
|
|
8825
|
+
- **Test independently** - prove the change works, don't rubber-stamp
|
|
8826
|
+
|
|
8827
|
+
## 6. Example Session
|
|
8828
|
+
|
|
8829
|
+
**User**: "There's a null pointer in the auth module. Can you fix it?"
|
|
8830
|
+
|
|
8831
|
+
**You (Coordinator)**:
|
|
8832
|
+
\`\`\`
|
|
8833
|
+
Let me investigate first.
|
|
8834
|
+
|
|
8835
|
+
spawn_agent({
|
|
8836
|
+
task: "Investigate the auth module in src/auth/. Find where null pointer exceptions could occur around session handling and token validation. Report specific file paths, line numbers, and types involved. Do NOT modify files.",
|
|
8837
|
+
title: "Research: Auth Bug Investigation",
|
|
8838
|
+
agent_type: "researcher"
|
|
8839
|
+
})
|
|
8840
|
+
|
|
8841
|
+
spawn_agent({
|
|
8842
|
+
task: "Find all test files related to src/auth/. Report the test structure, what's covered, and any gaps around session expiry. Do NOT modify files.",
|
|
8843
|
+
title: "Research: Auth Test Coverage",
|
|
8844
|
+
agent_type: "researcher"
|
|
8845
|
+
})
|
|
8846
|
+
|
|
8847
|
+
Investigating from two angles - I'll report back with findings.
|
|
8848
|
+
\`\`\`
|
|
8849
|
+
|
|
8850
|
+
**You** call \`wait_agent({ session_id: "<id-from-spawn>", timeout_ms: 600000 })\` for each research worker. Tool result shape is JSON, e.g. \`completed: true\`, \`session\` summary, and \`result\` with the worker's last structured output \u2014 **synthesize from that**, not from a fictional XML envelope.
|
|
8851
|
+
|
|
8852
|
+
**After workers finish**, you have concrete paths and line numbers from their \`result\` / messages.
|
|
8853
|
+
|
|
8854
|
+
**You (Coordinator)**:
|
|
8855
|
+
\`\`\`
|
|
8856
|
+
Found the bug - null pointer in validate.ts:42.
|
|
8857
|
+
|
|
8858
|
+
spawn_agent({
|
|
8859
|
+
task: "Fix the null pointer in src/auth/validate.ts:42. Add a null check before accessing user.id - if null, return 401 with 'Session expired'. Run relevant tests and typecheck. Commit and report the hash.",
|
|
8860
|
+
title: "Fix: Auth Null Pointer",
|
|
8861
|
+
agent_type: "implementer"
|
|
8862
|
+
})
|
|
8863
|
+
|
|
8864
|
+
Fix in progress.
|
|
8865
|
+
\`\`\`
|
|
8866
|
+
|
|
8867
|
+
**User**: "How's it going?"
|
|
8868
|
+
|
|
8869
|
+
**You**:
|
|
8870
|
+
\`\`\`
|
|
8871
|
+
Fix for the null pointer in progress. Waiting for worker to complete tests and commit.
|
|
8872
|
+
\`\`\`
|
|
8873
|
+
|
|
8874
|
+
## 7. Final Tips
|
|
8875
|
+
|
|
8876
|
+
### Parallelism Tips
|
|
8877
|
+
- Launch 2-4 research workers in parallel
|
|
8878
|
+
- Group implementations by file/module
|
|
8879
|
+
- Verification can be parallel if testing different modules
|
|
8880
|
+
|
|
8881
|
+
### Communication Tips
|
|
8882
|
+
- Always tell the user what you launched
|
|
8883
|
+
- Don't fabricate or predict worker results
|
|
8884
|
+
- Summarize new information as it arrives from workers
|
|
8885
|
+
- Be transparent about progress
|
|
8886
|
+
|
|
8887
|
+
### Quality Tips
|
|
8888
|
+
- Synthesis > lazy delegation
|
|
8889
|
+
- Specific > vague
|
|
8890
|
+
- File paths + line numbers > "in module X"
|
|
8891
|
+
- "Prove it works" > "Confirm it exists"
|
|
8892
|
+
|
|
8893
|
+
---
|
|
8894
|
+
|
|
8895
|
+
**Remember**: You are a **Coordinator**. Your value is in **intelligent orchestration** and **synthesis**; delegate implementation and deep exploration to workers whenever that reduces risk or speeds parallel work.
|
|
8896
|
+
`;
|
|
8897
|
+
function getCoordinatorSystemPrompt() {
|
|
8898
|
+
return COORDINATOR_SYSTEM_PROMPT;
|
|
8899
|
+
}
|
|
8900
|
+
function isCoordinatorModeEnabled() {
|
|
8901
|
+
return process.env.BLUMA_COORDINATOR_MODE === "true";
|
|
8902
|
+
}
|
|
8903
|
+
function enableCoordinatorMode() {
|
|
8904
|
+
process.env.BLUMA_COORDINATOR_MODE = "true";
|
|
8905
|
+
}
|
|
8906
|
+
function disableCoordinatorMode() {
|
|
8907
|
+
delete process.env.BLUMA_COORDINATOR_MODE;
|
|
8908
|
+
}
|
|
8909
|
+
|
|
8638
8910
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
8639
8911
|
function getNodeVersion() {
|
|
8640
8912
|
try {
|
|
@@ -8769,9 +9041,12 @@ function buildCoordinatorModePrompt(mode) {
|
|
|
8769
9041
|
if (mode !== "coordinator") {
|
|
8770
9042
|
return "";
|
|
8771
9043
|
}
|
|
9044
|
+
const playbook = getCoordinatorSystemPrompt().trim();
|
|
8772
9045
|
return `
|
|
8773
9046
|
<coordinator_mode>
|
|
8774
|
-
**
|
|
9047
|
+
**Runtime: coordinator mode is active.** The following playbook is binding.
|
|
9048
|
+
|
|
9049
|
+
${playbook}
|
|
8775
9050
|
</coordinator_mode>
|
|
8776
9051
|
`;
|
|
8777
9052
|
}
|
|
@@ -9251,7 +9526,6 @@ function buildFactorHeaders(ctx) {
|
|
|
9251
9526
|
"X-Company-Name": encodeHeader(ctx.companyName)
|
|
9252
9527
|
};
|
|
9253
9528
|
}
|
|
9254
|
-
var STREAM_TOOL_PARTIAL_THROTTLE_MS = 75;
|
|
9255
9529
|
function applyDeltaToolCallsToAccumulator(toolCallsAccumulator, deltaToolCalls) {
|
|
9256
9530
|
if (!deltaToolCalls?.length) {
|
|
9257
9531
|
return false;
|
|
@@ -9272,28 +9546,6 @@ function applyDeltaToolCallsToAccumulator(toolCallsAccumulator, deltaToolCalls)
|
|
|
9272
9546
|
}
|
|
9273
9547
|
return true;
|
|
9274
9548
|
}
|
|
9275
|
-
function snapshotPartialToolCalls(toolCallsAccumulator) {
|
|
9276
|
-
return Array.from(toolCallsAccumulator.entries()).sort((a, b) => a[0] - b[0]).map(([index, acc]) => ({
|
|
9277
|
-
index,
|
|
9278
|
-
id: acc.id,
|
|
9279
|
-
type: acc.type,
|
|
9280
|
-
function: {
|
|
9281
|
-
name: acc.function.name,
|
|
9282
|
-
arguments: acc.function.arguments
|
|
9283
|
-
}
|
|
9284
|
-
}));
|
|
9285
|
-
}
|
|
9286
|
-
function normalizedMessageToolCallsToPartial(toolCalls) {
|
|
9287
|
-
return toolCalls.map((tc, i) => ({
|
|
9288
|
-
index: typeof tc.index === "number" ? tc.index : i,
|
|
9289
|
-
id: String(tc.id ?? ""),
|
|
9290
|
-
type: String(tc.type ?? "function"),
|
|
9291
|
-
function: {
|
|
9292
|
-
name: String(tc.function?.name ?? ""),
|
|
9293
|
-
arguments: typeof tc.function?.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function?.arguments ?? {})
|
|
9294
|
-
}
|
|
9295
|
-
}));
|
|
9296
|
-
}
|
|
9297
9549
|
function normalizeFactorBaseUrl(raw) {
|
|
9298
9550
|
let u = raw.trim().replace(/\/$/, "");
|
|
9299
9551
|
u = u.replace(/^http:\/\/http:\/\//i, "http://");
|
|
@@ -9394,24 +9646,17 @@ var LLMService = class {
|
|
|
9394
9646
|
{ headers: this.requestHeaders(params.userContext) }
|
|
9395
9647
|
);
|
|
9396
9648
|
const toolCallsAccumulator = /* @__PURE__ */ new Map();
|
|
9397
|
-
let lastPartialEmitAt = 0;
|
|
9398
9649
|
for await (const chunk of stream) {
|
|
9399
9650
|
const choice = chunk.choices[0];
|
|
9400
9651
|
if (!choice) continue;
|
|
9401
9652
|
const delta = choice.delta;
|
|
9402
|
-
|
|
9653
|
+
applyDeltaToolCallsToAccumulator(toolCallsAccumulator, delta?.tool_calls);
|
|
9403
9654
|
const reasoning = delta?.reasoning_content || delta?.reasoning || "";
|
|
9404
9655
|
const fullToolCalls = choice.finish_reason === "tool_calls" ? Array.from(toolCallsAccumulator.values()) : void 0;
|
|
9405
|
-
let tool_calls_partial;
|
|
9406
|
-
if (hadToolDelta && toolCallsAccumulator.size > 0 && !fullToolCalls && Date.now() - lastPartialEmitAt >= STREAM_TOOL_PARTIAL_THROTTLE_MS) {
|
|
9407
|
-
lastPartialEmitAt = Date.now();
|
|
9408
|
-
tool_calls_partial = snapshotPartialToolCalls(toolCallsAccumulator);
|
|
9409
|
-
}
|
|
9410
9656
|
yield {
|
|
9411
9657
|
delta: delta?.content || "",
|
|
9412
9658
|
reasoning,
|
|
9413
9659
|
tool_calls: fullToolCalls,
|
|
9414
|
-
tool_calls_partial,
|
|
9415
9660
|
finish_reason: choice.finish_reason
|
|
9416
9661
|
};
|
|
9417
9662
|
}
|
|
@@ -10114,6 +10359,14 @@ var BluMaAgent = class {
|
|
|
10114
10359
|
turnId,
|
|
10115
10360
|
sessionId: userContextInput.sessionId || this.sessionId
|
|
10116
10361
|
};
|
|
10362
|
+
process.env.BLUMA_USER_CONTEXT_JSON = JSON.stringify({
|
|
10363
|
+
conversationId: this.activeTurnContext.conversationId ?? null,
|
|
10364
|
+
userId: this.activeTurnContext.userId ?? null,
|
|
10365
|
+
userName: this.activeTurnContext.userName ?? null,
|
|
10366
|
+
userEmail: this.activeTurnContext.userEmail ?? null,
|
|
10367
|
+
companyId: this.activeTurnContext.companyId ?? null,
|
|
10368
|
+
companyName: this.activeTurnContext.companyName ?? null
|
|
10369
|
+
});
|
|
10117
10370
|
const userContent = buildUserMessageContent(inputText, process.cwd());
|
|
10118
10371
|
this.history.push({ role: "user", content: userContent });
|
|
10119
10372
|
this.emptyAssistantReplySteps = 0;
|
|
@@ -10594,11 +10847,6 @@ ${editData.error.display}`;
|
|
|
10594
10847
|
if (hasContent) {
|
|
10595
10848
|
this.eventBus.emit("stream_chunk", { delta: content });
|
|
10596
10849
|
}
|
|
10597
|
-
if (hasTools) {
|
|
10598
|
-
this.eventBus.emit("stream_tool_calls_partial", {
|
|
10599
|
-
calls: normalizedMessageToolCallsToPartial(message2.tool_calls)
|
|
10600
|
-
});
|
|
10601
|
-
}
|
|
10602
10850
|
const omitAssistantFlush = hasTools && message2.tool_calls.some((tc) => String(tc?.function?.name ?? "").includes("message"));
|
|
10603
10851
|
this.eventBus.emit("stream_end", { omitAssistantFlush });
|
|
10604
10852
|
}
|
|
@@ -10639,13 +10887,6 @@ ${editData.error.display}`;
|
|
|
10639
10887
|
if (chunk.tool_calls) {
|
|
10640
10888
|
toolCalls = chunk.tool_calls;
|
|
10641
10889
|
}
|
|
10642
|
-
if (chunk.tool_calls_partial && chunk.tool_calls_partial.length > 0) {
|
|
10643
|
-
if (!hasEmittedStart) {
|
|
10644
|
-
this.eventBus.emit("stream_start", {});
|
|
10645
|
-
hasEmittedStart = true;
|
|
10646
|
-
}
|
|
10647
|
-
this.eventBus.emit("stream_tool_calls_partial", { calls: chunk.tool_calls_partial });
|
|
10648
|
-
}
|
|
10649
10890
|
}
|
|
10650
10891
|
const omitAssistantFlush = Array.isArray(toolCalls) && toolCalls.some((tc) => String(tc?.function?.name ?? "").includes("message"));
|
|
10651
10892
|
if (hasEmittedStart) {
|
|
@@ -11475,8 +11716,67 @@ var Agent = class {
|
|
|
11475
11716
|
if (inputText === "/init" || inputText.startsWith("/init ")) {
|
|
11476
11717
|
this.routeManager.registerRoute("/init", this.dispatchToSubAgent);
|
|
11477
11718
|
}
|
|
11719
|
+
if (inputText === "/coordinator" || inputText.startsWith("/coordinator ")) {
|
|
11720
|
+
this.routeManager.registerRoute("/coordinator", this.handleCoordinatorCommand.bind(this));
|
|
11721
|
+
}
|
|
11478
11722
|
await this.routeManager.handleRoute({ content: inputText, userContext: resolvedUserContext });
|
|
11479
11723
|
}
|
|
11724
|
+
/**
|
|
11725
|
+
* Handler para o comando /coordinator
|
|
11726
|
+
* /coordinator enable - Ativa o Coordinator Mode
|
|
11727
|
+
* /coordinator disable - Desativa o Coordinator Mode
|
|
11728
|
+
* /coordinator status - Mostra status atual
|
|
11729
|
+
* /coordinator - Toggle (ativa se desativado, desativa se ativado)
|
|
11730
|
+
*/
|
|
11731
|
+
async handleCoordinatorCommand(payload) {
|
|
11732
|
+
const inputText = payload.content.trim();
|
|
11733
|
+
const args = inputText.split(/\s+/).slice(1);
|
|
11734
|
+
const subcommand = args[0]?.toLowerCase();
|
|
11735
|
+
let message2;
|
|
11736
|
+
let messageType = "info";
|
|
11737
|
+
switch (subcommand) {
|
|
11738
|
+
case "enable":
|
|
11739
|
+
enableCoordinatorMode();
|
|
11740
|
+
message2 = "\u2705 Coordinator Mode ATIVADO!\n\nO BluMa agora atuar\xE1 como Coordinator - orquestrando workers em vez de executar diretamente.\n\nUse spawn_agent() para delegar tarefas, wait_agent() para aguardar resultados, e list_agents() para gerenciar workers.";
|
|
11741
|
+
messageType = "success";
|
|
11742
|
+
break;
|
|
11743
|
+
case "disable":
|
|
11744
|
+
disableCoordinatorMode();
|
|
11745
|
+
message2 = "\u23F9\uFE0F Coordinator Mode DESATIVADO.\n\nO BluMa voltar\xE1 ao modo padr\xE3o de execu\xE7\xE3o direta.";
|
|
11746
|
+
messageType = "info";
|
|
11747
|
+
break;
|
|
11748
|
+
case "status":
|
|
11749
|
+
case "info":
|
|
11750
|
+
const status = isCoordinatorModeEnabled() ? "\u2705 ATIVO" : "\u274C INATIVO";
|
|
11751
|
+
message2 = `\u{1F4CA} Coordinator Mode Status: ${status}
|
|
11752
|
+
|
|
11753
|
+
Quando ativo, o BluMa atua como Coordinator - orquestrando workers para:
|
|
11754
|
+
\u2022 Pesquisa paralela (m\xFAltiplos workers investigando diferentes aspectos)
|
|
11755
|
+
\u2022 Implementa\xE7\xE3o delegada (workers especializados por m\xF3dulo)
|
|
11756
|
+
\u2022 Verifica\xE7\xE3o independente (workers testam mudan\xE7as de outros workers)
|
|
11757
|
+
|
|
11758
|
+
Tools principais:
|
|
11759
|
+
\u2022 spawn_agent() - Criar worker
|
|
11760
|
+
\u2022 wait_agent() - Aguardar conclus\xE3o
|
|
11761
|
+
\u2022 list_agents() - Listar workers ativos`;
|
|
11762
|
+
break;
|
|
11763
|
+
default:
|
|
11764
|
+
if (isCoordinatorModeEnabled()) {
|
|
11765
|
+
disableCoordinatorMode();
|
|
11766
|
+
message2 = "\u23F9\uFE0F Coordinator Mode DESATIVADO (toggle).";
|
|
11767
|
+
messageType = "info";
|
|
11768
|
+
} else {
|
|
11769
|
+
enableCoordinatorMode();
|
|
11770
|
+
message2 = '\u2705 Coordinator Mode ATIVADO!\n\nOrquestrando workers em vez de executar diretamente.\nDica: Use "help" para ver exemplos de como usar spawn_agent().';
|
|
11771
|
+
messageType = "success";
|
|
11772
|
+
}
|
|
11773
|
+
}
|
|
11774
|
+
this.eventBus.emit("backend_message", {
|
|
11775
|
+
type: messageType,
|
|
11776
|
+
code: "coordinator_mode",
|
|
11777
|
+
message: message2
|
|
11778
|
+
});
|
|
11779
|
+
}
|
|
11480
11780
|
async handleToolResponse(decisionData) {
|
|
11481
11781
|
await this.core.handleToolResponse(decisionData);
|
|
11482
11782
|
}
|
|
@@ -13543,6 +13843,32 @@ var SlashCommands = ({
|
|
|
13543
13843
|
if (cmd === "sessions") {
|
|
13544
13844
|
return showSessions();
|
|
13545
13845
|
}
|
|
13846
|
+
if (cmd === "agents") {
|
|
13847
|
+
const entries = listSessions().filter((e) => e.kind === "agent");
|
|
13848
|
+
if (entries.length === 0) {
|
|
13849
|
+
return outBox(
|
|
13850
|
+
/* @__PURE__ */ jsxs15(Fragment6, { children: [
|
|
13851
|
+
/* @__PURE__ */ jsx17(Text15, { bold: true, color: BLUMA_TERMINAL.claude, children: "Worker agents" }),
|
|
13852
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " No agent sessions in this CLI registry. Use the " }),
|
|
13853
|
+
/* @__PURE__ */ jsx17(Text15, { bold: true, children: "spawn_agent" }),
|
|
13854
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " tool from chat, or " }),
|
|
13855
|
+
/* @__PURE__ */ jsx17(Text15, { bold: true, children: "/agent coordinator" }),
|
|
13856
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " then ask the model to delegate." })
|
|
13857
|
+
] })
|
|
13858
|
+
);
|
|
13859
|
+
}
|
|
13860
|
+
return outBox(
|
|
13861
|
+
/* @__PURE__ */ jsxs15(Fragment6, { children: [
|
|
13862
|
+
/* @__PURE__ */ jsx17(Text15, { bold: true, color: BLUMA_TERMINAL.claude, children: "Worker agents" }),
|
|
13863
|
+
/* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
|
|
13864
|
+
" ",
|
|
13865
|
+
entries.length,
|
|
13866
|
+
" session(s)"
|
|
13867
|
+
] }),
|
|
13868
|
+
/* @__PURE__ */ jsx17(Box16, { marginTop: 1, flexDirection: "column", children: entries.map((e) => /* @__PURE__ */ jsx17(Text15, { dimColor: true, wrap: "wrap", children: formatSessionLine(e) }, e.sessionId)) })
|
|
13869
|
+
] })
|
|
13870
|
+
);
|
|
13871
|
+
}
|
|
13546
13872
|
if (cmd === "bridge") {
|
|
13547
13873
|
return renderBridgePanel();
|
|
13548
13874
|
}
|
|
@@ -13624,7 +13950,9 @@ var SlashCommands = ({
|
|
|
13624
13950
|
const cfg = getRuntimeConfig();
|
|
13625
13951
|
return usageBox(
|
|
13626
13952
|
"Agent mode",
|
|
13627
|
-
`current: ${cfg.agentMode}
|
|
13953
|
+
`current: ${cfg.agentMode}
|
|
13954
|
+
set: /agent default | /agent coordinator
|
|
13955
|
+
(list workers: /agents \u2014 not the same as /agent)`
|
|
13628
13956
|
);
|
|
13629
13957
|
}
|
|
13630
13958
|
if (cmd === "features") {
|
|
@@ -14038,10 +14366,9 @@ function applyStreamEndFlush(params) {
|
|
|
14038
14366
|
}
|
|
14039
14367
|
|
|
14040
14368
|
// src/app/ui/components/StreamingText.tsx
|
|
14041
|
-
import {
|
|
14369
|
+
import { jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
14042
14370
|
var THROTTLE_MS = 50;
|
|
14043
14371
|
var MAX_VISIBLE_LINES = 20;
|
|
14044
|
-
var ARGS_PREVIEW_MAX = 72;
|
|
14045
14372
|
var StreamingTextComponent = ({
|
|
14046
14373
|
eventBus,
|
|
14047
14374
|
onReasoningComplete,
|
|
@@ -14049,7 +14376,6 @@ var StreamingTextComponent = ({
|
|
|
14049
14376
|
}) => {
|
|
14050
14377
|
const [reasoning, setReasoning] = useState6("");
|
|
14051
14378
|
const [assistantContent, setAssistantContent] = useState6("");
|
|
14052
|
-
const [partialToolCalls, setPartialToolCalls] = useState6([]);
|
|
14053
14379
|
const [isStreaming, setIsStreaming] = useState6(false);
|
|
14054
14380
|
const reasoningRef = useRef5("");
|
|
14055
14381
|
const contentRef = useRef5("");
|
|
@@ -14082,7 +14408,6 @@ var StreamingTextComponent = ({
|
|
|
14082
14408
|
contentRef.current = "";
|
|
14083
14409
|
setReasoning("");
|
|
14084
14410
|
setAssistantContent("");
|
|
14085
|
-
setPartialToolCalls([]);
|
|
14086
14411
|
lastUpdateRef.current = 0;
|
|
14087
14412
|
setIsStreaming(true);
|
|
14088
14413
|
};
|
|
@@ -14098,11 +14423,6 @@ var StreamingTextComponent = ({
|
|
|
14098
14423
|
scheduleFlush();
|
|
14099
14424
|
}
|
|
14100
14425
|
};
|
|
14101
|
-
const handleToolCallsPartial = (data) => {
|
|
14102
|
-
if (Array.isArray(data.calls) && data.calls.length > 0) {
|
|
14103
|
-
setPartialToolCalls(data.calls);
|
|
14104
|
-
}
|
|
14105
|
-
};
|
|
14106
14426
|
const handleEnd = (payload) => {
|
|
14107
14427
|
if (streamEndHandledRef.current) return;
|
|
14108
14428
|
streamEndHandledRef.current = true;
|
|
@@ -14120,24 +14440,21 @@ var StreamingTextComponent = ({
|
|
|
14120
14440
|
});
|
|
14121
14441
|
setReasoning("");
|
|
14122
14442
|
setAssistantContent("");
|
|
14123
|
-
setPartialToolCalls([]);
|
|
14124
14443
|
reasoningRef.current = "";
|
|
14125
14444
|
contentRef.current = "";
|
|
14126
14445
|
};
|
|
14127
14446
|
eventBus.on("stream_start", handleStart);
|
|
14128
14447
|
eventBus.on("stream_reasoning_chunk", handleReasoningChunk);
|
|
14129
14448
|
eventBus.on("stream_chunk", handleContentChunk);
|
|
14130
|
-
eventBus.on("stream_tool_calls_partial", handleToolCallsPartial);
|
|
14131
14449
|
eventBus.on("stream_end", handleEnd);
|
|
14132
14450
|
return () => {
|
|
14133
14451
|
eventBus.off("stream_start", handleStart);
|
|
14134
14452
|
eventBus.off("stream_reasoning_chunk", handleReasoningChunk);
|
|
14135
14453
|
eventBus.off("stream_chunk", handleContentChunk);
|
|
14136
|
-
eventBus.off("stream_tool_calls_partial", handleToolCallsPartial);
|
|
14137
14454
|
eventBus.off("stream_end", handleEnd);
|
|
14138
14455
|
};
|
|
14139
14456
|
}, [eventBus]);
|
|
14140
|
-
if (!isStreaming || !reasoning && !assistantContent
|
|
14457
|
+
if (!isStreaming || !reasoning && !assistantContent) {
|
|
14141
14458
|
return null;
|
|
14142
14459
|
}
|
|
14143
14460
|
const renderLines = (text, dim) => {
|
|
@@ -14157,31 +14474,9 @@ var StreamingTextComponent = ({
|
|
|
14157
14474
|
displayLines.map((line, i) => /* @__PURE__ */ jsx21(Text19, { dimColor: dim, color: dim ? void 0 : BLUMA_TERMINAL.m3OnSurface, children: line }, i))
|
|
14158
14475
|
] });
|
|
14159
14476
|
};
|
|
14160
|
-
const formatArgsPreview = (raw) => {
|
|
14161
|
-
const s = String(raw ?? "");
|
|
14162
|
-
if (s.length <= ARGS_PREVIEW_MAX) return s;
|
|
14163
|
-
return `${s.slice(0, ARGS_PREVIEW_MAX - 1)}\u2026`;
|
|
14164
|
-
};
|
|
14165
14477
|
return /* @__PURE__ */ jsxs19(ChatBlock, { marginBottom: 1, children: [
|
|
14166
14478
|
reasoning ? /* @__PURE__ */ jsx21(Box20, { flexDirection: "column", paddingLeft: 2, children: renderLines(reasoning, true) }) : null,
|
|
14167
|
-
assistantContent ? /* @__PURE__ */ jsx21(MessageResponse, { children: renderLines(assistantContent, false) }) : null
|
|
14168
|
-
partialToolCalls.length > 0 ? /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", paddingLeft: 2, marginTop: assistantContent || reasoning ? 1 : 0, children: [
|
|
14169
|
-
/* @__PURE__ */ jsx21(Text19, { dimColor: true, children: "tools (streaming)" }),
|
|
14170
|
-
partialToolCalls.map((tc) => {
|
|
14171
|
-
const name = tc.function?.name?.trim() || "\u2026";
|
|
14172
|
-
const args = formatArgsPreview(tc.function?.arguments ?? "");
|
|
14173
|
-
return /* @__PURE__ */ jsxs19(Text19, { dimColor: true, wrap: "wrap", children: [
|
|
14174
|
-
/* @__PURE__ */ jsxs19(Text19, { color: BLUMA_TERMINAL.suggestion, children: [
|
|
14175
|
-
"\u2022 ",
|
|
14176
|
-
name
|
|
14177
|
-
] }),
|
|
14178
|
-
args ? /* @__PURE__ */ jsxs19(Fragment7, { children: [
|
|
14179
|
-
/* @__PURE__ */ jsx21(Text19, { dimColor: true, children: " " }),
|
|
14180
|
-
/* @__PURE__ */ jsx21(Text19, { dimColor: true, italic: true, children: args })
|
|
14181
|
-
] }) : null
|
|
14182
|
-
] }, `${tc.index}-${tc.id}`);
|
|
14183
|
-
})
|
|
14184
|
-
] }) : null
|
|
14479
|
+
assistantContent ? /* @__PURE__ */ jsx21(MessageResponse, { children: renderLines(assistantContent, false) }) : null
|
|
14185
14480
|
] });
|
|
14186
14481
|
};
|
|
14187
14482
|
var StreamingText = memo12(StreamingTextComponent);
|
|
@@ -14455,7 +14750,20 @@ var AppComponent = ({ eventBus, sessionId, cliVersion }) => {
|
|
|
14455
14750
|
}, [isProcessing, eventBus]);
|
|
14456
14751
|
const handleSubmit = useCallback3(
|
|
14457
14752
|
(text) => {
|
|
14458
|
-
if (!text ||
|
|
14753
|
+
if (!text || !agentInstance.current) return;
|
|
14754
|
+
const trimmedForSlash = text.trim();
|
|
14755
|
+
if (isProcessing && !isSlashRoutingLine(trimmedForSlash)) {
|
|
14756
|
+
if (trimmedForSlash.startsWith("/")) {
|
|
14757
|
+
setHistory((prev) => [
|
|
14758
|
+
...prev,
|
|
14759
|
+
{
|
|
14760
|
+
id: prev.length,
|
|
14761
|
+
component: /* @__PURE__ */ jsx24(ChatMeta, { children: "Slash command not recognized or incomplete. Type /help for the list." })
|
|
14762
|
+
}
|
|
14763
|
+
]);
|
|
14764
|
+
}
|
|
14765
|
+
return;
|
|
14766
|
+
}
|
|
14459
14767
|
lastReasoningTextRef.current = null;
|
|
14460
14768
|
lastStreamAssistantKeyRef.current = null;
|
|
14461
14769
|
if (/^\/img\s+/i.test(text) || /^\/image\s+/i.test(text)) {
|
|
@@ -14896,11 +15204,21 @@ Please use command_status to check the result and report back to the user.`;
|
|
|
14896
15204
|
const handleUiOverlay = (data) => {
|
|
14897
15205
|
eventBus.emit("user_overlay", data);
|
|
14898
15206
|
};
|
|
15207
|
+
const handleInputNotice = (data) => {
|
|
15208
|
+
const msg = String(data.message || "").trim();
|
|
15209
|
+
if (!msg) return;
|
|
15210
|
+
setHistory((prev) => [
|
|
15211
|
+
...prev,
|
|
15212
|
+
{ id: prev.length, component: /* @__PURE__ */ jsx24(ChatMeta, { children: msg }) }
|
|
15213
|
+
]);
|
|
15214
|
+
};
|
|
14899
15215
|
uiEventBus.on("user_overlay", handleUiOverlay);
|
|
15216
|
+
uiEventBus.on("input_notice", handleInputNotice);
|
|
14900
15217
|
eventBus.on("backend_message", handleBackendMessage);
|
|
14901
15218
|
initializeAgent();
|
|
14902
15219
|
return () => {
|
|
14903
15220
|
uiEventBus.off("user_overlay", handleUiOverlay);
|
|
15221
|
+
uiEventBus.off("input_notice", handleInputNotice);
|
|
14904
15222
|
eventBus.off("backend_message", handleBackendMessage);
|
|
14905
15223
|
};
|
|
14906
15224
|
}, [eventBus, sessionId, handleConfirmation]);
|