kimiflare 0.29.1 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1359,6 +1359,13 @@ Use console.log() to return results. Only console.log output will be sent back t
1359
1359
  const recentToolCalls = opts2.recentToolCalls ?? [];
1360
1360
  const LOOP_WINDOW = 8;
1361
1361
  const LOOP_THRESHOLD = 2;
1362
+ const webFetchHistory = [];
1363
+ const MAX_WEB_FETCH_PER_TURN = 5;
1364
+ const WEB_FETCH_DOMAIN_THRESHOLD = 2;
1365
+ let totalToolCallsThisTurn = 0;
1366
+ const BUDGET_CHECK_INTERVAL = 3;
1367
+ const SOFT_BUDGET = 5;
1368
+ const HARD_BUDGET = 15;
1362
1369
  for (let iter = 0; iter < max; iter++) {
1363
1370
  turn++;
1364
1371
  const previousMessages = opts2.messages.slice();
@@ -1513,6 +1520,57 @@ Use console.log() to return results. Only console.log output will be sent back t
1513
1520
  if (recentToolCalls.length > LOOP_WINDOW) recentToolCalls.shift();
1514
1521
  continue;
1515
1522
  }
1523
+ if (tc.function.name === "web_fetch") {
1524
+ const args = JSON.parse(tc.function.arguments || "{}");
1525
+ const url = args.url || "";
1526
+ try {
1527
+ const domain = new URL(url).hostname;
1528
+ const domainCount = webFetchHistory.filter((h) => h.domain === domain).length;
1529
+ const totalWebFetches = webFetchHistory.length;
1530
+ if (totalWebFetches >= MAX_WEB_FETCH_PER_TURN) {
1531
+ const warning = `Research budget exceeded: you have already made ${MAX_WEB_FETCH_PER_TURN} web requests this turn. Synthesize what you have learned instead of fetching more pages.`;
1532
+ const budgetResult = {
1533
+ tool_call_id: tc.id,
1534
+ name: "web_fetch",
1535
+ content: warning,
1536
+ ok: false
1537
+ };
1538
+ toolResults.push(budgetResult);
1539
+ opts2.messages.push({
1540
+ role: "tool",
1541
+ tool_call_id: tc.id,
1542
+ content: sanitizeString(warning),
1543
+ name: "web_fetch"
1544
+ });
1545
+ opts2.callbacks.onToolResult?.(budgetResult);
1546
+ recentToolCalls.push(loopSignature);
1547
+ if (recentToolCalls.length > LOOP_WINDOW) recentToolCalls.shift();
1548
+ continue;
1549
+ }
1550
+ if (domainCount >= WEB_FETCH_DOMAIN_THRESHOLD) {
1551
+ const warning = `Loop detected: you have fetched from ${domain} multiple times. Consider a different approach or synthesize existing findings.`;
1552
+ const loopResult = {
1553
+ tool_call_id: tc.id,
1554
+ name: "web_fetch",
1555
+ content: warning,
1556
+ ok: false
1557
+ };
1558
+ toolResults.push(loopResult);
1559
+ opts2.messages.push({
1560
+ role: "tool",
1561
+ tool_call_id: tc.id,
1562
+ content: sanitizeString(warning),
1563
+ name: "web_fetch"
1564
+ });
1565
+ opts2.callbacks.onToolResult?.(loopResult);
1566
+ recentToolCalls.push(loopSignature);
1567
+ if (recentToolCalls.length > LOOP_WINDOW) recentToolCalls.shift();
1568
+ continue;
1569
+ }
1570
+ webFetchHistory.push({ url, domain });
1571
+ } catch {
1572
+ }
1573
+ }
1516
1574
  if (codeMode && tc.function.name === "execute_code") {
1517
1575
  const args = JSON.parse(tc.function.arguments || "{}");
1518
1576
  const code = args.code || "";
@@ -1572,7 +1630,19 @@ ${sandboxResult.output}` : sandboxResult.output;
1572
1630
  opts2.callbacks.onToolResult?.(result);
1573
1631
  recentToolCalls.push(loopSignature);
1574
1632
  if (recentToolCalls.length > LOOP_WINDOW) recentToolCalls.shift();
1633
+ totalToolCallsThisTurn++;
1634
+ }
1635
+ }
1636
+ if (totalToolCallsThisTurn > 0 && totalToolCallsThisTurn % BUDGET_CHECK_INTERVAL === 0) {
1637
+ let budgetMsg = "";
1638
+ if (totalToolCallsThisTurn >= HARD_BUDGET) {
1639
+ budgetMsg = `BUDGET CHECK: You have made ${totalToolCallsThisTurn} tool calls. This is the substantial-question budget. Produce your deliverable now unless you can name a specific gap that would change the recommendation.`;
1640
+ } else if (totalToolCallsThisTurn >= SOFT_BUDGET) {
1641
+ budgetMsg = `BUDGET CHECK: You have made ${totalToolCallsThisTurn} tool calls. If this is a routine question, produce your deliverable now. If substantial, justify the next call in one sentence.`;
1642
+ } else {
1643
+ budgetMsg = `BUDGET CHECK: You have made ${totalToolCallsThisTurn} tool calls. Assess: is the next call worth more than what you already have? If yes, justify in one sentence. If no, produce your deliverable.`;
1575
1644
  }
1645
+ opts2.messages.push({ role: "system", content: budgetMsg });
1576
1646
  }
1577
1647
  if (opts2.sessionId && lastUsage) {
1578
1648
  void logTurnDebug({
@@ -1587,7 +1657,9 @@ ${sandboxResult.output}` : sandboxResult.output;
1587
1657
  });
1588
1658
  }
1589
1659
  }
1590
- throw new Error(`kimiflare: tool iteration limit reached (${opts2.maxToolIterations ?? 50})`);
1660
+ const pauseMsg = `Paused after ${opts2.maxToolIterations ?? 50} tool calls. The user may say "go on" to continue. If you have a partial deliverable (Research Brief, Implementation Notes, etc.), include it in your next response.`;
1661
+ opts2.messages.push({ role: "system", content: pauseMsg });
1662
+ throw new Error(`kimiflare: tool iteration limit reached (${opts2.maxToolIterations ?? 50}). Say "go on" to continue, or ask me to focus on a specific area.`);
1591
1663
  }
1592
1664
  function validateToolArguments(raw) {
1593
1665
  if (!raw || !raw.trim()) return "{}";
@@ -1847,8 +1919,82 @@ function loadContextFile(cwd) {
1847
1919
  }
1848
1920
  return null;
1849
1921
  }
1922
+ function buildRolePrefix(role) {
1923
+ switch (role) {
1924
+ case "research":
1925
+ return `You are the Research Agent in kimiflare. You investigate technical questions on behalf of a Coding Agent that will act on your output. You are not talking to a human. The Coding Agent is your reader.
1926
+
1927
+ # Your job
1928
+
1929
+ Produce the smallest research artifact that lets the Coding Agent act correctly and confidently on the task it has been given. Not the most thorough \u2014 the smallest sufficient one. Research exists to enable action. If you are not reducing the Coding Agent's uncertainty about a concrete next step, you are wasting tokens.
1930
+
1931
+ # How to think
1932
+
1933
+ 1. Start by naming the decision. Before any tool call, write down \u2014 for yourself \u2014 what decision your research is meant to enable. "Pick a library." "Choose between approach A and B." "Determine if X is possible." If you can't name the decision in one sentence, ask the Coding Agent for it before researching.
1934
+
1935
+ 2. Surface area before depth. First pass is always shallow and wide: the shape of the problem, the vocabulary, the obvious candidates, the known landmines. Only then go deep, and only on what the decision actually hinges on.
1936
+
1937
+ 3. Hold hypotheses loosely and visibly. Form a working hypothesis early \u2014 it directs attention \u2014 but mark it as a hypothesis and look actively for evidence against it. Sycophantic research is useless research.
1938
+
1939
+ 4. Budget is real. You have a finite tool-call budget per task. Default to ~5 calls for routine questions, up to ~15 for substantial ones. After every 3 calls, ask yourself: is the next call worth more than what I already have? Usually it isn't. Stop earlier than feels comfortable.
1940
+
1941
+ 5. Separate finding from inference from recommendation. Sources said X. I infer Y. Therefore Z for our case. Keep these layers visible so the Coding Agent can audit any of them.
1942
+
1943
+ 6. Know when to recommend running the code instead. Sometimes the cheapest research is letting the runtime answer. Say so when true.
1944
+
1945
+ # When to stop
1946
+
1947
+ Stop when all of these are true:
1948
+ - The named decision can be made from what you have.
1949
+ - Remaining uncertainties are named, not hidden.
1950
+ - The next tool call would predictably add little.
1951
+
1952
+ Do not stop just because you found something. Do not stop just because you ran out of patience. Stop on the criteria above, and only those.
1953
+
1954
+ # Output format
1955
+
1956
+ You are writing for an agent, not a person. No preamble, no narrative, no "in this report we will." Structure:
1957
+
1958
+ - DECISION: one sentence \u2014 what this research enables.
1959
+ - FINDINGS: scannable facts, with source attribution. Include version numbers, exact APIs, error strings, file paths, code snippets where relevant.
1960
+ - RECOMMENDATION: what the Coding Agent should do, concretely.
1961
+ - CONFIDENCE: per claim where it varies. "High / Medium / Low" is fine.
1962
+ - OPEN QUESTIONS: things you couldn't resolve. Mark each as either "blocking" (Coding Agent should ask the user before proceeding) or "non-blocking" (try and see).
1963
+ - RISKS: what could go wrong if the Recommendation is followed, including the strongest counter-argument you found.
1964
+
1965
+ # Voice
1966
+
1967
+ Terse. Direct. No hedging prose, but explicit uncertainty in the Confidence and Open Questions sections. No apologies, no throat-clearing, no "I hope this helps."
1968
+
1969
+ # Addressing the user
1970
+
1971
+ You do not address the user. If you must reference what you're about to ask the Coding Agent, phrase it as a description of the request, not a request itself: "Will instruct the Coding Agent to..." \u2014 never "please do X." The user is overhearing, not participating.
1972
+
1973
+ # Things that are not research
1974
+
1975
+ - Restating the task back at length.
1976
+ - Listing every option without ranking them.
1977
+ - Producing an essay when a table would do.
1978
+ - Continuing to search after the decision can already be made.
1979
+ - Hiding uncertainty inside confident prose.
1980
+
1981
+ When in doubt, deliver the smaller artifact sooner. When your Brief is complete, call the hand_off tool to pass your findings to the Coding Agent.
1982
+
1983
+ `;
1984
+ case "coding":
1985
+ return `You are the Coding Agent of kimiflare. You write code, edit files, and execute tools directly. Do not ask the user to do your work for you. Implement the changes yourself.
1986
+
1987
+ `;
1988
+ case "generalist":
1989
+ return `You are the Generalist Agent of kimiflare. You handle conversational queries, memory management, and high-level task coordination.
1990
+
1991
+ `;
1992
+ default:
1993
+ return "";
1994
+ }
1995
+ }
1850
1996
  function buildStaticPrefix(opts2) {
1851
- return `You are kimiflare, an interactive coding assistant running in the user's terminal. You act on the user's local filesystem through the tools listed below. You are powered by the ${opts2.model} model on Cloudflare Workers AI.
1997
+ return buildRolePrefix(opts2.role) + `You are kimiflare, an interactive coding assistant running in the user's terminal. You act on the user's local filesystem through the tools listed below. You are powered by the ${opts2.model} model on Cloudflare Workers AI.
1852
1998
 
1853
1999
  How to work:
1854
2000
  - Prefer calling tools over guessing. Read files before editing them. Use \`glob\` and \`grep\` to explore code before assuming structure.
@@ -2637,6 +2783,47 @@ var init_memory = __esm({
2637
2783
  }
2638
2784
  });
2639
2785
 
2786
+ // src/tools/hand-off.ts
2787
+ var handOffTool;
2788
+ var init_hand_off = __esm({
2789
+ "src/tools/hand-off.ts"() {
2790
+ "use strict";
2791
+ handOffTool = {
2792
+ name: "hand_off",
2793
+ needsPermission: false,
2794
+ description: `Signal that your work is complete and request a hand-off to another agent. Use this when you have produced your deliverable (Research Brief, Implementation Notes, etc.) and the next agent should take over.
2795
+
2796
+ Parameters:
2797
+ - target: the agent to hand off to (e.g., "coding", "generalist")
2798
+ - reason: optional one-sentence summary of what was completed`,
2799
+ parameters: {
2800
+ type: "object",
2801
+ properties: {
2802
+ target: {
2803
+ type: "string",
2804
+ description: 'Agent to hand off to: "coding", "generalist", or another agent role'
2805
+ },
2806
+ reason: {
2807
+ type: "string",
2808
+ description: "One-sentence summary of what was completed and why hand-off is appropriate"
2809
+ }
2810
+ },
2811
+ required: ["target"],
2812
+ additionalProperties: false
2813
+ },
2814
+ async run(args, _ctx) {
2815
+ const target = args.target;
2816
+ const reason = args.reason ?? "";
2817
+ return {
2818
+ content: `Hand-off requested to ${target} agent.${reason ? ` Reason: ${reason}` : ""}`,
2819
+ rawBytes: 0,
2820
+ reducedBytes: 0
2821
+ };
2822
+ }
2823
+ };
2824
+ }
2825
+ });
2826
+
2640
2827
  // src/tools/artifact-store.ts
2641
2828
  var ToolArtifactStore;
2642
2829
  var init_artifact_store = __esm({
@@ -3125,6 +3312,7 @@ var init_executor = __esm({
3125
3312
  init_web_fetch();
3126
3313
  init_tasks();
3127
3314
  init_memory();
3315
+ init_hand_off();
3128
3316
  init_artifact_store();
3129
3317
  init_reducer();
3130
3318
  init_expand_artifact();
@@ -3139,7 +3327,8 @@ var init_executor = __esm({
3139
3327
  tasksSetTool,
3140
3328
  memoryRememberTool,
3141
3329
  memoryRecallTool,
3142
- memoryForgetTool
3330
+ memoryForgetTool,
3331
+ handOffTool
3143
3332
  ];
3144
3333
  ToolExecutor = class {
3145
3334
  sessionAllowed = /* @__PURE__ */ new Set();
@@ -5654,6 +5843,7 @@ var init_agent_session = __esm({
5654
5843
  "lsp_rename",
5655
5844
  "lsp_typeDefinition",
5656
5845
  "lsp_workspaceSymbol",
5846
+ "hand_off",
5657
5847
  "memory_recall",
5658
5848
  "read",
5659
5849
  "tasks_set",
@@ -5885,6 +6075,26 @@ var init_orchestrator = __esm({
5885
6075
  getAutoSwitch() {
5886
6076
  return this.autoSwitch;
5887
6077
  }
6078
+ /** Scan the session's last assistant message for a hand_off tool call.
6079
+ * Returns the target role if found, null otherwise. */
6080
+ detectHandOff(messages) {
6081
+ for (let i = messages.length - 1; i >= 0; i--) {
6082
+ const m = messages[i];
6083
+ if (m.role === "assistant" && m.tool_calls) {
6084
+ for (const tc of m.tool_calls) {
6085
+ if (tc.function.name === "hand_off") {
6086
+ try {
6087
+ const args = JSON.parse(tc.function.arguments);
6088
+ if (args.target) return args.target;
6089
+ } catch {
6090
+ }
6091
+ }
6092
+ }
6093
+ break;
6094
+ }
6095
+ }
6096
+ return null;
6097
+ }
5888
6098
  getToolsForRole(role) {
5889
6099
  const base = getAgentTools(role, this.opts.customAgents);
5890
6100
  if (this.opts.lspTools.length > 0) {
@@ -6040,6 +6250,20 @@ ${summary}`
6040
6250
  agentRole: this.activeRole
6041
6251
  });
6042
6252
  session.recentToolCalls = session.recentToolCalls.slice(-8);
6253
+ const handOffTarget = this.detectHandOff(session.messages);
6254
+ if (handOffTarget && handOffTarget !== this.activeRole) {
6255
+ const summary = await this.synthesizeHandoff(this.activeRole, handOffTarget);
6256
+ this.activeRole = handOffTarget;
6257
+ const newSession = this.getActiveSession();
6258
+ if (summary) {
6259
+ newSession.messages.push({
6260
+ role: "system",
6261
+ content: `[hand-off from ${this.activeRole} agent \u2014 agent requested hand-off]
6262
+ ${summary}`
6263
+ });
6264
+ }
6265
+ this.turnCounts.set(handOffTarget, 0);
6266
+ }
6043
6267
  this.turnCounts.set(this.activeRole, (this.turnCounts.get(this.activeRole) ?? 0) + 1);
6044
6268
  }
6045
6269
  async handOff(toRole) {
@@ -7499,6 +7723,11 @@ var init_chat = __esm({
7499
7723
  }
7500
7724
  if (evt.kind === "assistant") {
7501
7725
  return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingLeft: 2, children: [
7726
+ evt.agentRole && /* @__PURE__ */ jsx4(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsxs4(Text4, { color: theme.info.color, dimColor: theme.info.dim, children: [
7727
+ "\u25C6 ",
7728
+ evt.agentRole,
7729
+ " agent"
7730
+ ] }) }),
7502
7731
  showReasoning && evt.reasoning ? /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs4(Text4, { color: theme.reasoning.color, dimColor: theme.reasoning.dim, children: [
7503
7732
  "thinking\u2026",
7504
7733
  " ",
@@ -9132,7 +9361,7 @@ function themeNames() {
9132
9361
  function themeList() {
9133
9362
  return Object.values(THEMES);
9134
9363
  }
9135
- var dark, light, highContrast, dracula, nord, monokai, solarizedDark, solarizedLight, tokyoNight, gruvboxDark, catppuccinMocha, rosePine, THEMES, DEFAULT_THEME_NAME;
9364
+ var dark, light, highContrast, dracula, nord, monokai, solarizedDark, solarizedLight, tokyoNight, gruvboxDark, catppuccinMocha, rosePine, oneDark, ayu, nightOwl, palenight, THEMES, DEFAULT_THEME_NAME;
9136
9365
  var init_theme = __esm({
9137
9366
  "src/ui/theme.ts"() {
9138
9367
  "use strict";
@@ -9328,6 +9557,70 @@ var init_theme = __esm({
9328
9557
  accent: "#ebbcba",
9329
9558
  modeBadge: { plan: "#31748f", auto: "#9ccfd8", edit: "#ebbcba" }
9330
9559
  };
9560
+ oneDark = {
9561
+ name: "one-dark",
9562
+ label: "one-dark (Atom's iconic dark \u2014 blue & purple)",
9563
+ user: "#61afef",
9564
+ assistant: void 0,
9565
+ reasoning: { color: "#5c6370", dim: true },
9566
+ info: { color: "#5c6370", dim: true },
9567
+ error: "#e06c75",
9568
+ warn: "#e5c07b",
9569
+ tool: "#c678dd",
9570
+ spinner: "#61afef",
9571
+ permission: "#e5c07b",
9572
+ queue: { color: "#5c6370", dim: true },
9573
+ accent: "#c678dd",
9574
+ modeBadge: { plan: "#61afef", auto: "#98c379", edit: "#c678dd" }
9575
+ };
9576
+ ayu = {
9577
+ name: "ayu",
9578
+ label: "ayu (clean modern \u2014 orange & cyan)",
9579
+ user: "#39bae6",
9580
+ assistant: void 0,
9581
+ reasoning: { color: "#4d5566", dim: true },
9582
+ info: { color: "#4d5566", dim: true },
9583
+ error: "#f07178",
9584
+ warn: "#ffb454",
9585
+ tool: "#73b8ff",
9586
+ spinner: "#39bae6",
9587
+ permission: "#ffb454",
9588
+ queue: { color: "#4d5566", dim: true },
9589
+ accent: "#39bae6",
9590
+ modeBadge: { plan: "#39bae6", auto: "#7ee787", edit: "#ffb454" }
9591
+ };
9592
+ nightOwl = {
9593
+ name: "night-owl",
9594
+ label: "night-owl (deep navy \u2014 cyan & red)",
9595
+ user: "#82aaff",
9596
+ assistant: void 0,
9597
+ reasoning: { color: "#4d6885", dim: true },
9598
+ info: { color: "#4d6885", dim: true },
9599
+ error: "#ef5350",
9600
+ warn: "#ffca28",
9601
+ tool: "#c792ea",
9602
+ spinner: "#82aaff",
9603
+ permission: "#ffca28",
9604
+ queue: { color: "#4d6885", dim: true },
9605
+ accent: "#c792ea",
9606
+ modeBadge: { plan: "#82aaff", auto: "#7ee787", edit: "#c792ea" }
9607
+ };
9608
+ palenight = {
9609
+ name: "palenight",
9610
+ label: "palenight (Material pale \u2014 purple & cyan)",
9611
+ user: "#82b1ff",
9612
+ assistant: void 0,
9613
+ reasoning: { color: "#4c566a", dim: true },
9614
+ info: { color: "#4c566a", dim: true },
9615
+ error: "#f07178",
9616
+ warn: "#ffcb6b",
9617
+ tool: "#c792ea",
9618
+ spinner: "#82b1ff",
9619
+ permission: "#ffcb6b",
9620
+ queue: { color: "#4c566a", dim: true },
9621
+ accent: "#c792ea",
9622
+ modeBadge: { plan: "#82b1ff", auto: "#c3e88d", edit: "#c792ea" }
9623
+ };
9331
9624
  THEMES = {
9332
9625
  dark,
9333
9626
  light,
@@ -9340,7 +9633,11 @@ var init_theme = __esm({
9340
9633
  "tokyo-night": tokyoNight,
9341
9634
  "gruvbox-dark": gruvboxDark,
9342
9635
  "catppuccin-mocha": catppuccinMocha,
9343
- "rose-pine": rosePine
9636
+ "rose-pine": rosePine,
9637
+ "one-dark": oneDark,
9638
+ ayu,
9639
+ "night-owl": nightOwl,
9640
+ palenight
9344
9641
  };
9345
9642
  DEFAULT_THEME_NAME = "dark";
9346
9643
  }
@@ -11529,14 +11826,14 @@ function compactEventsVisual(prev, keepLastTurns) {
11529
11826
  ...kept
11530
11827
  ];
11531
11828
  }
11532
- function makePrefixMessages(cacheStable, model, mode, tools) {
11829
+ function makePrefixMessages(cacheStable, model, mode, tools, role) {
11533
11830
  if (cacheStable) {
11534
- return buildSystemMessages({ cwd: process.cwd(), tools, model, mode });
11831
+ return buildSystemMessages({ cwd: process.cwd(), tools, model, mode, role });
11535
11832
  }
11536
11833
  return [
11537
11834
  {
11538
11835
  role: "system",
11539
- content: buildSystemPrompt({ cwd: process.cwd(), tools, model, mode })
11836
+ content: buildSystemPrompt({ cwd: process.cwd(), tools, model, mode, role })
11540
11837
  }
11541
11838
  ];
11542
11839
  }
@@ -12407,9 +12704,10 @@ function App({
12407
12704
  onAssistantStart: () => {
12408
12705
  const id = nextAssistantId++;
12409
12706
  activeAsstIdRef.current = id;
12707
+ const role = orchestratorRef.current?.getActiveRole();
12410
12708
  setEvents((e) => [
12411
12709
  ...e,
12412
- { kind: "assistant", key: `asst_${id}`, id, text: "", reasoning: "", streaming: true }
12710
+ { kind: "assistant", key: `asst_${id}`, id, text: "", reasoning: "", streaming: true, agentRole: role }
12413
12711
  ]);
12414
12712
  },
12415
12713
  onReasoningDelta: (d) => {
@@ -13422,9 +13720,10 @@ ${lines.join("\n")}` }]);
13422
13720
  onAssistantStart: () => {
13423
13721
  const id = nextAssistantId++;
13424
13722
  activeAsstIdRef.current = id;
13723
+ const role = orchestratorRef.current?.getActiveRole();
13425
13724
  setEvents((e) => [
13426
13725
  ...e,
13427
- { kind: "assistant", key: `asst_${id}`, id, text: "", reasoning: "", streaming: true }
13726
+ { kind: "assistant", key: `asst_${id}`, id, text: "", reasoning: "", streaming: true, agentRole: role }
13428
13727
  ]);
13429
13728
  },
13430
13729
  onReasoningDelta: (d) => {
@@ -13570,7 +13869,8 @@ ${lines.join("\n")}` }]);
13570
13869
  cacheStableRef.current,
13571
13870
  overrideModel ?? cfg.model,
13572
13871
  modeRef.current,
13573
- [...ALL_TOOLS, ...mcpToolsRef.current, ...lspToolsRef.current]
13872
+ [...ALL_TOOLS, ...mcpToolsRef.current, ...lspToolsRef.current],
13873
+ orchestratorRef.current.getActiveRole()
13574
13874
  );
13575
13875
  activeSession2.messages.unshift(...prefix);
13576
13876
  }