kimiflare 0.29.1 → 0.31.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 +862 -109
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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,8 +1630,20 @@ ${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++;
|
|
1575
1634
|
}
|
|
1576
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.`;
|
|
1644
|
+
}
|
|
1645
|
+
opts2.messages.push({ role: "system", content: budgetMsg });
|
|
1646
|
+
}
|
|
1577
1647
|
if (opts2.sessionId && lastUsage) {
|
|
1578
1648
|
void logTurnDebug({
|
|
1579
1649
|
sessionId: opts2.sessionId,
|
|
@@ -1587,7 +1657,9 @@ ${sandboxResult.output}` : sandboxResult.output;
|
|
|
1587
1657
|
});
|
|
1588
1658
|
}
|
|
1589
1659
|
}
|
|
1590
|
-
|
|
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,199 @@ 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.
|
|
1982
|
+
|
|
1983
|
+
# Critical hand-off rule
|
|
1984
|
+
|
|
1985
|
+
When your Brief is complete, you MUST call the hand_off tool to transfer control to the next agent. Simply saying you have handed off is NOT sufficient \u2014 the tool call is required. If you do not call hand_off, your work will be stranded and the next agent will never run.
|
|
1986
|
+
|
|
1987
|
+
You MUST include the full Brief text in your final assistant message BEFORE calling the hand_off tool. The next agent receives your last assistant message in its entirety \u2014 no summarization, no truncation. If you produce the Brief in one message and then call hand_off in a separate message with only "Handing off now," the next agent will see only "Handing off now" and will not know what to implement.
|
|
1988
|
+
|
|
1989
|
+
Correct: One assistant message containing the full Brief + the hand_off tool call.
|
|
1990
|
+
Incorrect: Brief in message N, then "Handing off" + hand_off in message N+1.
|
|
1991
|
+
Incorrect: Saying "I have handed off" without calling the hand_off tool.
|
|
1992
|
+
|
|
1993
|
+
`;
|
|
1994
|
+
case "coding":
|
|
1995
|
+
return `You are the Coding Agent in kimiflare. You write, modify, debug, and reason about code. You receive tasks from the General Agent or research briefs from the Research Agent. Your audience is sometimes the user directly, sometimes another agent.
|
|
1996
|
+
|
|
1997
|
+
# Your job
|
|
1998
|
+
|
|
1999
|
+
Implement the task as scoped. Correctly, narrowly, and in a way that fits the codebase you're working in. Stop when it's done.
|
|
2000
|
+
|
|
2001
|
+
# How to think
|
|
2002
|
+
|
|
2003
|
+
1. Read before you write. Look at the existing code \u2014 patterns, utilities, conventions, naming. Match the codebase's style, don't impose your own. The repo should look like one author wrote it even after you've worked in it.
|
|
2004
|
+
|
|
2005
|
+
2. Stay in scope. Touch what the task requires and nothing else. If you notice something else worth fixing, mention it \u2014 don't fix it uninvited. Scope creep is the most common way coding agents make things worse.
|
|
2006
|
+
|
|
2007
|
+
3. Trust the runtime. When something doesn't work, run it, read the actual error, and update your understanding. Don't argue with reality based on what the docs or types said. The runtime is the source of truth.
|
|
2008
|
+
|
|
2009
|
+
4. Be honest about uncertainty before acting, not after. "I'm going to try X \u2014 if it fails I'll try Y" is right. Confident execution followed by silent breakage is wrong.
|
|
2010
|
+
|
|
2011
|
+
5. Ask only when ambiguity is load-bearing. If a choice would meaningfully change the result and you can't infer the user's intent, ask. If it's a trivial choice, make it and move on.
|
|
2012
|
+
|
|
2013
|
+
6. Done means done. Working, fitting the codebase, tests passing where applicable, loose ends named. Not "the command exited zero." Don't claim done when you only have passing.
|
|
2014
|
+
|
|
2015
|
+
# Working style
|
|
2016
|
+
|
|
2017
|
+
- Small, verifiable steps over large speculative ones.
|
|
2018
|
+
- Run the code. Read the output. Believe the output.
|
|
2019
|
+
- Prefer existing utilities over new ones. Prefer the codebase's patterns over your defaults.
|
|
2020
|
+
- New dependencies are a real cost. Justify them or skip them.
|
|
2021
|
+
- Comments narrate why, not what. If the code needs a comment to explain what it does, the code is probably wrong.
|
|
2022
|
+
|
|
2023
|
+
# Voice
|
|
2024
|
+
|
|
2025
|
+
Direct. No throat-clearing, no narration of obvious steps, no celebration of completion. When you explain something, explain only what isn't already visible in the code or output.
|
|
2026
|
+
|
|
2027
|
+
# Output
|
|
2028
|
+
|
|
2029
|
+
Show the work \u2014 the diff, the file, the command output \u2014 and a one- or two-line summary of what you did and anything the next agent or the user should know. That's it. No "I hope this helps." No "let me know if you'd like me to..."
|
|
2030
|
+
|
|
2031
|
+
If something didn't work or you couldn't finish cleanly, say so plainly with what you tried and what you'd try next.
|
|
2032
|
+
|
|
2033
|
+
# Things that are not your job
|
|
2034
|
+
|
|
2035
|
+
- Investigating broad questions (Research Agent's job).
|
|
2036
|
+
- Routing or chatting (General Agent's job).
|
|
2037
|
+
- Improving the codebase beyond the task at hand.
|
|
2038
|
+
- Producing long explanations of code the reader can read.
|
|
2039
|
+
|
|
2040
|
+
# Receiving work from the Research Agent
|
|
2041
|
+
|
|
2042
|
+
When you are activated after a Research Agent hand-off, the full Research Brief is included in the system message that precedes your turn. Read it carefully \u2014 it contains the decision, findings, recommendation, confidence levels, open questions, and risks. Do not ask the user to repeat what the Research Agent already determined.
|
|
2043
|
+
|
|
2044
|
+
When your implementation is complete, you MUST call the hand_off tool to return to the General Agent. Simply saying you are done is NOT sufficient \u2014 the tool call is required. If you do not call hand_off, your work will be stranded and the General Agent will never run.
|
|
2045
|
+
|
|
2046
|
+
`;
|
|
2047
|
+
case "generalist":
|
|
2048
|
+
return `You are the General Agent in kimiflare. You are the user's primary point of contact. Behind you are two specialists: the Research Agent (investigation, analysis, synthesis) and the Coding Agent (writing, modifying, and reasoning about code).
|
|
2049
|
+
|
|
2050
|
+
# Your job
|
|
2051
|
+
|
|
2052
|
+
Triage. Route. Stay out of the way. Handle small stuff. Present specialist work cleanly.
|
|
2053
|
+
|
|
2054
|
+
You are fast and light by design. Substantive thinking is not your job \u2014 it's the specialists' job. Your job is to recognize what kind of help the user needs and get them to the right agent quickly, or to handle the request yourself if it's small enough that routing would be overkill.
|
|
2055
|
+
|
|
2056
|
+
# How to think
|
|
2057
|
+
|
|
2058
|
+
1. Default to routing. If a request involves real investigation, real synthesis, or real code work, call hand_off to the appropriate specialist. Do not try to answer it yourself just because you can produce something plausible-sounding.
|
|
2059
|
+
|
|
2060
|
+
2. Route on partial information. You don't need to fully understand the request before routing \u2014 the specialist will ask follow-ups if needed. Spending three turns clarifying before handoff is worse than handing off now and letting the specialist clarify.
|
|
2061
|
+
|
|
2062
|
+
3. Handle the small stuff yourself. Greetings, clarifications, "what can you do," confirming what just happened, one-line factual answers, formatting preferences, scope adjustments \u2014 these don't need a specialist. Be quick.
|
|
2063
|
+
|
|
2064
|
+
4. Notice escalation. A conversation that started small can become a research or coding task. When it does, route. Don't keep answering out of inertia.
|
|
2065
|
+
|
|
2066
|
+
5. Do not editorialize the specialists' output. When work comes back from Research or Coding, present it. Don't summarize it back at the user with your own framing on top. The user can read.
|
|
2067
|
+
|
|
2068
|
+
# Routing rules
|
|
2069
|
+
|
|
2070
|
+
Call hand_off to Research Agent when the user wants:
|
|
2071
|
+
- Information you don't already have, or that may have changed.
|
|
2072
|
+
- Comparison, evaluation, or recommendation between options.
|
|
2073
|
+
- Synthesis across multiple sources.
|
|
2074
|
+
- Investigation of an unfamiliar codebase or library.
|
|
2075
|
+
- Anything where being wrong has real cost.
|
|
2076
|
+
|
|
2077
|
+
Call hand_off to Coding Agent when the user wants:
|
|
2078
|
+
- Code written, modified, debugged, or reviewed.
|
|
2079
|
+
- A file created, edited, or restructured.
|
|
2080
|
+
- A concrete build/run/test action taken.
|
|
2081
|
+
|
|
2082
|
+
Handle yourself when:
|
|
2083
|
+
- The user is making conversation.
|
|
2084
|
+
- The user is asking what you (collectively) can do.
|
|
2085
|
+
- The answer is one line and you're confident.
|
|
2086
|
+
- The user is correcting or adjusting a previous handoff.
|
|
2087
|
+
- Work has come back from a specialist and you're presenting it to the user.
|
|
2088
|
+
|
|
2089
|
+
When in doubt, route. The cost of an unnecessary handoff is small. The cost of you confidently producing wrong work is large.
|
|
2090
|
+
|
|
2091
|
+
# Voice
|
|
2092
|
+
|
|
2093
|
+
Warm, quick, natural. Short sentences. No corporate softeners, no "I'd be happy to," no "great question." Talk like a competent person who respects the user's time.
|
|
2094
|
+
|
|
2095
|
+
# Handoff style
|
|
2096
|
+
|
|
2097
|
+
When you route, say so plainly in one line. "Handing this to the research agent \u2014 back in a moment." or "Coding agent will take this one." Then stop. Don't fill the wait with chatter.
|
|
2098
|
+
|
|
2099
|
+
# Things that are not your job
|
|
2100
|
+
|
|
2101
|
+
- Producing research findings.
|
|
2102
|
+
- Writing or analyzing code.
|
|
2103
|
+
- Synthesizing across many sources.
|
|
2104
|
+
- Long explanations of anything.
|
|
2105
|
+
|
|
2106
|
+
If you find yourself drafting a long response, stop and ask whether this should have been routed. Usually it should have been.
|
|
2107
|
+
|
|
2108
|
+
`;
|
|
2109
|
+
default:
|
|
2110
|
+
return "";
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
1850
2113
|
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.
|
|
2114
|
+
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
2115
|
|
|
1853
2116
|
How to work:
|
|
1854
2117
|
- Prefer calling tools over guessing. Read files before editing them. Use \`glob\` and \`grep\` to explore code before assuming structure.
|
|
@@ -2637,6 +2900,47 @@ var init_memory = __esm({
|
|
|
2637
2900
|
}
|
|
2638
2901
|
});
|
|
2639
2902
|
|
|
2903
|
+
// src/tools/hand-off.ts
|
|
2904
|
+
var handOffTool;
|
|
2905
|
+
var init_hand_off = __esm({
|
|
2906
|
+
"src/tools/hand-off.ts"() {
|
|
2907
|
+
"use strict";
|
|
2908
|
+
handOffTool = {
|
|
2909
|
+
name: "hand_off",
|
|
2910
|
+
needsPermission: false,
|
|
2911
|
+
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.
|
|
2912
|
+
|
|
2913
|
+
Parameters:
|
|
2914
|
+
- target: the agent to hand off to (e.g., "coding", "generalist")
|
|
2915
|
+
- reason: optional one-sentence summary of what was completed`,
|
|
2916
|
+
parameters: {
|
|
2917
|
+
type: "object",
|
|
2918
|
+
properties: {
|
|
2919
|
+
target: {
|
|
2920
|
+
type: "string",
|
|
2921
|
+
description: 'Agent to hand off to: "coding", "generalist", or another agent role'
|
|
2922
|
+
},
|
|
2923
|
+
reason: {
|
|
2924
|
+
type: "string",
|
|
2925
|
+
description: "One-sentence summary of what was completed and why hand-off is appropriate"
|
|
2926
|
+
}
|
|
2927
|
+
},
|
|
2928
|
+
required: ["target"],
|
|
2929
|
+
additionalProperties: false
|
|
2930
|
+
},
|
|
2931
|
+
async run(args, _ctx) {
|
|
2932
|
+
const target = args.target;
|
|
2933
|
+
const reason = args.reason ?? "";
|
|
2934
|
+
return {
|
|
2935
|
+
content: `Hand-off requested to ${target} agent.${reason ? ` Reason: ${reason}` : ""}`,
|
|
2936
|
+
rawBytes: 0,
|
|
2937
|
+
reducedBytes: 0
|
|
2938
|
+
};
|
|
2939
|
+
}
|
|
2940
|
+
};
|
|
2941
|
+
}
|
|
2942
|
+
});
|
|
2943
|
+
|
|
2640
2944
|
// src/tools/artifact-store.ts
|
|
2641
2945
|
var ToolArtifactStore;
|
|
2642
2946
|
var init_artifact_store = __esm({
|
|
@@ -3125,6 +3429,7 @@ var init_executor = __esm({
|
|
|
3125
3429
|
init_web_fetch();
|
|
3126
3430
|
init_tasks();
|
|
3127
3431
|
init_memory();
|
|
3432
|
+
init_hand_off();
|
|
3128
3433
|
init_artifact_store();
|
|
3129
3434
|
init_reducer();
|
|
3130
3435
|
init_expand_artifact();
|
|
@@ -3139,7 +3444,8 @@ var init_executor = __esm({
|
|
|
3139
3444
|
tasksSetTool,
|
|
3140
3445
|
memoryRememberTool,
|
|
3141
3446
|
memoryRecallTool,
|
|
3142
|
-
memoryForgetTool
|
|
3447
|
+
memoryForgetTool,
|
|
3448
|
+
handOffTool
|
|
3143
3449
|
];
|
|
3144
3450
|
ToolExecutor = class {
|
|
3145
3451
|
sessionAllowed = /* @__PURE__ */ new Set();
|
|
@@ -5654,6 +5960,7 @@ var init_agent_session = __esm({
|
|
|
5654
5960
|
"lsp_rename",
|
|
5655
5961
|
"lsp_typeDefinition",
|
|
5656
5962
|
"lsp_workspaceSymbol",
|
|
5963
|
+
"hand_off",
|
|
5657
5964
|
"memory_recall",
|
|
5658
5965
|
"read",
|
|
5659
5966
|
"tasks_set",
|
|
@@ -5662,6 +5969,7 @@ var init_agent_session = __esm({
|
|
|
5662
5969
|
CODING_TOOL_NAMES = [
|
|
5663
5970
|
"bash",
|
|
5664
5971
|
"edit",
|
|
5972
|
+
"hand_off",
|
|
5665
5973
|
"lsp_codeAction",
|
|
5666
5974
|
"lsp_definition",
|
|
5667
5975
|
"lsp_diagnostics",
|
|
@@ -5678,6 +5986,7 @@ var init_agent_session = __esm({
|
|
|
5678
5986
|
"write"
|
|
5679
5987
|
].sort((a, b) => a.localeCompare(b));
|
|
5680
5988
|
GENERALIST_TOOL_NAMES = [
|
|
5989
|
+
"hand_off",
|
|
5681
5990
|
"memory_forget",
|
|
5682
5991
|
"memory_recall",
|
|
5683
5992
|
"memory_remember",
|
|
@@ -5885,6 +6194,38 @@ var init_orchestrator = __esm({
|
|
|
5885
6194
|
getAutoSwitch() {
|
|
5886
6195
|
return this.autoSwitch;
|
|
5887
6196
|
}
|
|
6197
|
+
/** Scan the session's last assistant message for a hand_off tool call.
|
|
6198
|
+
* Returns the target role if found, null otherwise. */
|
|
6199
|
+
detectHandOff(messages) {
|
|
6200
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
6201
|
+
const m = messages[i];
|
|
6202
|
+
if (m.role === "assistant" && m.tool_calls) {
|
|
6203
|
+
for (const tc of m.tool_calls) {
|
|
6204
|
+
if (tc.function.name === "hand_off") {
|
|
6205
|
+
try {
|
|
6206
|
+
const args = JSON.parse(tc.function.arguments);
|
|
6207
|
+
if (args.target) return args.target;
|
|
6208
|
+
} catch {
|
|
6209
|
+
}
|
|
6210
|
+
}
|
|
6211
|
+
}
|
|
6212
|
+
break;
|
|
6213
|
+
}
|
|
6214
|
+
}
|
|
6215
|
+
return null;
|
|
6216
|
+
}
|
|
6217
|
+
/** Extract the last assistant message content from a session.
|
|
6218
|
+
* Returns the full text in its entirety — no truncation, no summarization.
|
|
6219
|
+
* If the agent has no text content, falls back to synthesis. */
|
|
6220
|
+
extractDeliverable(session) {
|
|
6221
|
+
for (let i = session.messages.length - 1; i >= 0; i--) {
|
|
6222
|
+
const m = session.messages[i];
|
|
6223
|
+
if (m.role === "assistant" && typeof m.content === "string" && m.content.trim().length > 0) {
|
|
6224
|
+
return m.content;
|
|
6225
|
+
}
|
|
6226
|
+
}
|
|
6227
|
+
return null;
|
|
6228
|
+
}
|
|
5888
6229
|
getToolsForRole(role) {
|
|
5889
6230
|
const base = getAgentTools(role, this.opts.customAgents);
|
|
5890
6231
|
if (this.opts.lspTools.length > 0) {
|
|
@@ -6040,6 +6381,119 @@ ${summary}`
|
|
|
6040
6381
|
agentRole: this.activeRole
|
|
6041
6382
|
});
|
|
6042
6383
|
session.recentToolCalls = session.recentToolCalls.slice(-8);
|
|
6384
|
+
const handOffTarget = this.detectHandOff(session.messages);
|
|
6385
|
+
if (handOffTarget && handOffTarget !== this.activeRole) {
|
|
6386
|
+
const fromRole = this.activeRole;
|
|
6387
|
+
const fromSession = this.sessions.get(fromRole);
|
|
6388
|
+
const deliverable = this.extractDeliverable(fromSession);
|
|
6389
|
+
let handoffContent = "";
|
|
6390
|
+
if (deliverable) {
|
|
6391
|
+
handoffContent = `[hand-off from ${fromRole} agent \u2014 agent requested hand-off]
|
|
6392
|
+
|
|
6393
|
+
The ${fromRole} agent completed its work. Here is their full deliverable:
|
|
6394
|
+
|
|
6395
|
+
${deliverable}`;
|
|
6396
|
+
} else {
|
|
6397
|
+
const summary = await this.synthesizeHandoff(fromRole, handOffTarget);
|
|
6398
|
+
handoffContent = `[hand-off from ${fromRole} agent \u2014 agent requested hand-off]
|
|
6399
|
+
${summary}`;
|
|
6400
|
+
}
|
|
6401
|
+
this.activeRole = handOffTarget;
|
|
6402
|
+
const newSession = this.getActiveSession();
|
|
6403
|
+
if (handoffContent) {
|
|
6404
|
+
newSession.messages.push({
|
|
6405
|
+
role: "system",
|
|
6406
|
+
content: handoffContent
|
|
6407
|
+
});
|
|
6408
|
+
}
|
|
6409
|
+
newSession.messages.push(userMessage);
|
|
6410
|
+
this.turnCounts.set(handOffTarget, 0);
|
|
6411
|
+
const targetTools = this.getToolsForRole(this.activeRole);
|
|
6412
|
+
const targetCustomAgent = this.opts.customAgents?.find((a) => a.name === this.activeRole);
|
|
6413
|
+
const targetConfig = this.resolveAgentConfig(this.activeRole);
|
|
6414
|
+
const targetModel = targetCustomAgent?.model ?? targetConfig.model ?? this.opts.model;
|
|
6415
|
+
const targetReasoning = targetCustomAgent?.reasoningEffort ?? targetConfig.reasoningEffort ?? this.opts.reasoningEffort;
|
|
6416
|
+
if (targetCustomAgent?.systemPrompt && !newSession.messages.some((m) => m.role === "system" && m.content === targetCustomAgent.systemPrompt)) {
|
|
6417
|
+
newSession.messages.unshift({
|
|
6418
|
+
role: "system",
|
|
6419
|
+
content: targetCustomAgent.systemPrompt
|
|
6420
|
+
});
|
|
6421
|
+
}
|
|
6422
|
+
await runAgentTurn({
|
|
6423
|
+
accountId: this.opts.accountId,
|
|
6424
|
+
apiToken: this.opts.apiToken,
|
|
6425
|
+
model: targetModel,
|
|
6426
|
+
gateway: this.opts.gateway,
|
|
6427
|
+
messages: newSession.messages,
|
|
6428
|
+
tools: [...targetTools, ...this.opts.mcpTools],
|
|
6429
|
+
executor: this.opts.executor,
|
|
6430
|
+
cwd: this.opts.cwd,
|
|
6431
|
+
signal: this.opts.signal,
|
|
6432
|
+
reasoningEffort: targetReasoning,
|
|
6433
|
+
coauthor: this.opts.coauthor,
|
|
6434
|
+
sessionId: this.opts.sessionId,
|
|
6435
|
+
memoryManager: this.opts.memoryManager,
|
|
6436
|
+
keepLastImageTurns: this.opts.keepLastImageTurns,
|
|
6437
|
+
codeMode: this.opts.codeMode,
|
|
6438
|
+
onFileChange: this.opts.onFileChange,
|
|
6439
|
+
callbacks: this.opts.callbacks,
|
|
6440
|
+
recentToolCalls: newSession.recentToolCalls,
|
|
6441
|
+
agentRole: this.activeRole
|
|
6442
|
+
});
|
|
6443
|
+
newSession.recentToolCalls = newSession.recentToolCalls.slice(-8);
|
|
6444
|
+
}
|
|
6445
|
+
if (!handOffTarget && this.activeRole !== "generalist") {
|
|
6446
|
+
const deliverable = this.extractDeliverable(session);
|
|
6447
|
+
if (deliverable && deliverable.length >= 300) {
|
|
6448
|
+
const fromRole = this.activeRole;
|
|
6449
|
+
const handoffContent = `[hand-off from ${fromRole} agent \u2014 auto-detected completion (hand_off tool was not called)]
|
|
6450
|
+
|
|
6451
|
+
The ${fromRole} agent completed its work. Here is their full deliverable:
|
|
6452
|
+
|
|
6453
|
+
${deliverable}`;
|
|
6454
|
+
this.activeRole = "generalist";
|
|
6455
|
+
const newSession = this.getActiveSession();
|
|
6456
|
+
newSession.messages.push({
|
|
6457
|
+
role: "system",
|
|
6458
|
+
content: handoffContent
|
|
6459
|
+
});
|
|
6460
|
+
newSession.messages.push(userMessage);
|
|
6461
|
+
this.turnCounts.set("generalist", 0);
|
|
6462
|
+
const targetTools = this.getToolsForRole(this.activeRole);
|
|
6463
|
+
const targetCustomAgent = this.opts.customAgents?.find((a) => a.name === this.activeRole);
|
|
6464
|
+
const targetConfig = this.resolveAgentConfig(this.activeRole);
|
|
6465
|
+
const targetModel = targetCustomAgent?.model ?? targetConfig.model ?? this.opts.model;
|
|
6466
|
+
const targetReasoning = targetCustomAgent?.reasoningEffort ?? targetConfig.reasoningEffort ?? this.opts.reasoningEffort;
|
|
6467
|
+
if (targetCustomAgent?.systemPrompt && !newSession.messages.some((m) => m.role === "system" && m.content === targetCustomAgent.systemPrompt)) {
|
|
6468
|
+
newSession.messages.unshift({
|
|
6469
|
+
role: "system",
|
|
6470
|
+
content: targetCustomAgent.systemPrompt
|
|
6471
|
+
});
|
|
6472
|
+
}
|
|
6473
|
+
await runAgentTurn({
|
|
6474
|
+
accountId: this.opts.accountId,
|
|
6475
|
+
apiToken: this.opts.apiToken,
|
|
6476
|
+
model: targetModel,
|
|
6477
|
+
gateway: this.opts.gateway,
|
|
6478
|
+
messages: newSession.messages,
|
|
6479
|
+
tools: [...targetTools, ...this.opts.mcpTools],
|
|
6480
|
+
executor: this.opts.executor,
|
|
6481
|
+
cwd: this.opts.cwd,
|
|
6482
|
+
signal: this.opts.signal,
|
|
6483
|
+
reasoningEffort: targetReasoning,
|
|
6484
|
+
coauthor: this.opts.coauthor,
|
|
6485
|
+
sessionId: this.opts.sessionId,
|
|
6486
|
+
memoryManager: this.opts.memoryManager,
|
|
6487
|
+
keepLastImageTurns: this.opts.keepLastImageTurns,
|
|
6488
|
+
codeMode: this.opts.codeMode,
|
|
6489
|
+
onFileChange: this.opts.onFileChange,
|
|
6490
|
+
callbacks: this.opts.callbacks,
|
|
6491
|
+
recentToolCalls: newSession.recentToolCalls,
|
|
6492
|
+
agentRole: this.activeRole
|
|
6493
|
+
});
|
|
6494
|
+
newSession.recentToolCalls = newSession.recentToolCalls.slice(-8);
|
|
6495
|
+
}
|
|
6496
|
+
}
|
|
6043
6497
|
this.turnCounts.set(this.activeRole, (this.turnCounts.get(this.activeRole) ?? 0) + 1);
|
|
6044
6498
|
}
|
|
6045
6499
|
async handOff(toRole) {
|
|
@@ -7499,6 +7953,11 @@ var init_chat = __esm({
|
|
|
7499
7953
|
}
|
|
7500
7954
|
if (evt.kind === "assistant") {
|
|
7501
7955
|
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingLeft: 2, children: [
|
|
7956
|
+
evt.agentRole && /* @__PURE__ */ jsx4(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsxs4(Text4, { color: theme.info.color, dimColor: theme.info.dim, children: [
|
|
7957
|
+
"\u25C6 ",
|
|
7958
|
+
evt.agentRole,
|
|
7959
|
+
" agent"
|
|
7960
|
+
] }) }),
|
|
7502
7961
|
showReasoning && evt.reasoning ? /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs4(Text4, { color: theme.reasoning.color, dimColor: theme.reasoning.dim, children: [
|
|
7503
7962
|
"thinking\u2026",
|
|
7504
7963
|
" ",
|
|
@@ -9132,7 +9591,7 @@ function themeNames() {
|
|
|
9132
9591
|
function themeList() {
|
|
9133
9592
|
return Object.values(THEMES);
|
|
9134
9593
|
}
|
|
9135
|
-
var dark, light, highContrast, dracula, nord, monokai, solarizedDark, solarizedLight, tokyoNight, gruvboxDark, catppuccinMocha, rosePine, THEMES, DEFAULT_THEME_NAME;
|
|
9594
|
+
var dark, light, highContrast, dracula, nord, monokai, solarizedDark, solarizedLight, tokyoNight, gruvboxDark, catppuccinMocha, rosePine, oneDark, ayu, nightOwl, palenight, THEMES, DEFAULT_THEME_NAME;
|
|
9136
9595
|
var init_theme = __esm({
|
|
9137
9596
|
"src/ui/theme.ts"() {
|
|
9138
9597
|
"use strict";
|
|
@@ -9328,6 +9787,70 @@ var init_theme = __esm({
|
|
|
9328
9787
|
accent: "#ebbcba",
|
|
9329
9788
|
modeBadge: { plan: "#31748f", auto: "#9ccfd8", edit: "#ebbcba" }
|
|
9330
9789
|
};
|
|
9790
|
+
oneDark = {
|
|
9791
|
+
name: "one-dark",
|
|
9792
|
+
label: "one-dark (Atom's iconic dark \u2014 blue & purple)",
|
|
9793
|
+
user: "#61afef",
|
|
9794
|
+
assistant: void 0,
|
|
9795
|
+
reasoning: { color: "#5c6370", dim: true },
|
|
9796
|
+
info: { color: "#5c6370", dim: true },
|
|
9797
|
+
error: "#e06c75",
|
|
9798
|
+
warn: "#e5c07b",
|
|
9799
|
+
tool: "#c678dd",
|
|
9800
|
+
spinner: "#61afef",
|
|
9801
|
+
permission: "#e5c07b",
|
|
9802
|
+
queue: { color: "#5c6370", dim: true },
|
|
9803
|
+
accent: "#c678dd",
|
|
9804
|
+
modeBadge: { plan: "#61afef", auto: "#98c379", edit: "#c678dd" }
|
|
9805
|
+
};
|
|
9806
|
+
ayu = {
|
|
9807
|
+
name: "ayu",
|
|
9808
|
+
label: "ayu (clean modern \u2014 orange & cyan)",
|
|
9809
|
+
user: "#39bae6",
|
|
9810
|
+
assistant: void 0,
|
|
9811
|
+
reasoning: { color: "#4d5566", dim: true },
|
|
9812
|
+
info: { color: "#4d5566", dim: true },
|
|
9813
|
+
error: "#f07178",
|
|
9814
|
+
warn: "#ffb454",
|
|
9815
|
+
tool: "#73b8ff",
|
|
9816
|
+
spinner: "#39bae6",
|
|
9817
|
+
permission: "#ffb454",
|
|
9818
|
+
queue: { color: "#4d5566", dim: true },
|
|
9819
|
+
accent: "#39bae6",
|
|
9820
|
+
modeBadge: { plan: "#39bae6", auto: "#7ee787", edit: "#ffb454" }
|
|
9821
|
+
};
|
|
9822
|
+
nightOwl = {
|
|
9823
|
+
name: "night-owl",
|
|
9824
|
+
label: "night-owl (deep navy \u2014 cyan & red)",
|
|
9825
|
+
user: "#82aaff",
|
|
9826
|
+
assistant: void 0,
|
|
9827
|
+
reasoning: { color: "#4d6885", dim: true },
|
|
9828
|
+
info: { color: "#4d6885", dim: true },
|
|
9829
|
+
error: "#ef5350",
|
|
9830
|
+
warn: "#ffca28",
|
|
9831
|
+
tool: "#c792ea",
|
|
9832
|
+
spinner: "#82aaff",
|
|
9833
|
+
permission: "#ffca28",
|
|
9834
|
+
queue: { color: "#4d6885", dim: true },
|
|
9835
|
+
accent: "#c792ea",
|
|
9836
|
+
modeBadge: { plan: "#82aaff", auto: "#7ee787", edit: "#c792ea" }
|
|
9837
|
+
};
|
|
9838
|
+
palenight = {
|
|
9839
|
+
name: "palenight",
|
|
9840
|
+
label: "palenight (Material pale \u2014 purple & cyan)",
|
|
9841
|
+
user: "#82b1ff",
|
|
9842
|
+
assistant: void 0,
|
|
9843
|
+
reasoning: { color: "#4c566a", dim: true },
|
|
9844
|
+
info: { color: "#4c566a", dim: true },
|
|
9845
|
+
error: "#f07178",
|
|
9846
|
+
warn: "#ffcb6b",
|
|
9847
|
+
tool: "#c792ea",
|
|
9848
|
+
spinner: "#82b1ff",
|
|
9849
|
+
permission: "#ffcb6b",
|
|
9850
|
+
queue: { color: "#4c566a", dim: true },
|
|
9851
|
+
accent: "#c792ea",
|
|
9852
|
+
modeBadge: { plan: "#82b1ff", auto: "#c3e88d", edit: "#c792ea" }
|
|
9853
|
+
};
|
|
9331
9854
|
THEMES = {
|
|
9332
9855
|
dark,
|
|
9333
9856
|
light,
|
|
@@ -9340,7 +9863,11 @@ var init_theme = __esm({
|
|
|
9340
9863
|
"tokyo-night": tokyoNight,
|
|
9341
9864
|
"gruvbox-dark": gruvboxDark,
|
|
9342
9865
|
"catppuccin-mocha": catppuccinMocha,
|
|
9343
|
-
"rose-pine": rosePine
|
|
9866
|
+
"rose-pine": rosePine,
|
|
9867
|
+
"one-dark": oneDark,
|
|
9868
|
+
ayu,
|
|
9869
|
+
"night-owl": nightOwl,
|
|
9870
|
+
palenight
|
|
9344
9871
|
};
|
|
9345
9872
|
DEFAULT_THEME_NAME = "dark";
|
|
9346
9873
|
}
|
|
@@ -10107,6 +10634,45 @@ var init_renderer2 = __esm({
|
|
|
10107
10634
|
}
|
|
10108
10635
|
});
|
|
10109
10636
|
|
|
10637
|
+
// src/commands/builtins.ts
|
|
10638
|
+
var BUILTIN_COMMANDS, BUILTIN_COMMAND_NAMES;
|
|
10639
|
+
var init_builtins = __esm({
|
|
10640
|
+
"src/commands/builtins.ts"() {
|
|
10641
|
+
"use strict";
|
|
10642
|
+
BUILTIN_COMMANDS = [
|
|
10643
|
+
{ name: "help", description: "Show keybindings and command list", source: "builtin" },
|
|
10644
|
+
{ name: "model", description: "Show current model", source: "builtin" },
|
|
10645
|
+
{ name: "mode", argHint: "edit|plan|auto", description: "Switch agent mode", source: "builtin" },
|
|
10646
|
+
{ name: "plan", description: "Switch to plan mode", source: "builtin" },
|
|
10647
|
+
{ name: "auto", description: "Switch to auto mode", source: "builtin" },
|
|
10648
|
+
{ name: "edit", description: "Switch to edit mode", source: "builtin" },
|
|
10649
|
+
{ name: "thinking", argHint: "low|medium|high", description: "Set reasoning effort", source: "builtin" },
|
|
10650
|
+
{ name: "effort", argHint: "low|medium|high", description: "Alias for /thinking", source: "builtin" },
|
|
10651
|
+
{ name: "reasoning", description: "Toggle reasoning visibility", source: "builtin" },
|
|
10652
|
+
{ name: "theme", description: "Open theme picker", source: "builtin" },
|
|
10653
|
+
{ name: "agent", argHint: "[on|off|status]", description: "Multi-agent mode controls", source: "builtin" },
|
|
10654
|
+
{ name: "memory", argHint: "[on|off|clear|search ...]", description: "Manage memory", source: "builtin" },
|
|
10655
|
+
{ name: "cost", argHint: "[on|off]", description: "Show cost report or toggle attribution", source: "builtin" },
|
|
10656
|
+
{ name: "gateway", argHint: "[status|off|<id>|cache-ttl|skip-cache|...]", description: "Manage AI Gateway", source: "builtin" },
|
|
10657
|
+
{ name: "mcp", argHint: "[list|reload]", description: "Manage MCP servers", source: "builtin" },
|
|
10658
|
+
{ name: "lsp", argHint: "[config|list|reload|scope]", description: "Manage language servers", source: "builtin" },
|
|
10659
|
+
{ name: "command", argHint: "[create|edit|delete|list]", description: "Manage custom slash commands", source: "builtin" },
|
|
10660
|
+
{ name: "resume", description: "Pick a past conversation to resume", source: "builtin" },
|
|
10661
|
+
{ name: "compact", description: "Summarize old turns to free context", source: "builtin" },
|
|
10662
|
+
{ name: "clear", description: "Clear current conversation", source: "builtin" },
|
|
10663
|
+
{ name: "init", description: "Scan repo and write KIMI.md", source: "builtin" },
|
|
10664
|
+
{ name: "update", description: "Check for updates", source: "builtin" },
|
|
10665
|
+
{ name: "hello", description: "Send a voice note to the creator", source: "builtin" },
|
|
10666
|
+
{ name: "logout", description: "Clear stored credentials", source: "builtin" },
|
|
10667
|
+
{ name: "exit", description: "Exit kimiflare", source: "builtin" },
|
|
10668
|
+
{ name: "quit", description: "Alias for /exit", source: "builtin" }
|
|
10669
|
+
];
|
|
10670
|
+
BUILTIN_COMMAND_NAMES = new Set(
|
|
10671
|
+
BUILTIN_COMMANDS.map((c) => c.name.toLowerCase())
|
|
10672
|
+
);
|
|
10673
|
+
}
|
|
10674
|
+
});
|
|
10675
|
+
|
|
10110
10676
|
// src/commands/save.ts
|
|
10111
10677
|
import { mkdir as mkdir9, writeFile as writeFile9, unlink as unlink2 } from "fs/promises";
|
|
10112
10678
|
import { dirname as dirname6 } from "path";
|
|
@@ -11268,6 +11834,125 @@ var init_file_picker = __esm({
|
|
|
11268
11834
|
}
|
|
11269
11835
|
});
|
|
11270
11836
|
|
|
11837
|
+
// src/ui/slash-picker.tsx
|
|
11838
|
+
import { Box as Box18, Text as Text19 } from "ink";
|
|
11839
|
+
import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
11840
|
+
function sourceBadge(source) {
|
|
11841
|
+
if (source === "builtin") return "";
|
|
11842
|
+
if (source === "project") return "project";
|
|
11843
|
+
return "global";
|
|
11844
|
+
}
|
|
11845
|
+
function commandLabel(item) {
|
|
11846
|
+
return `/${item.name}${item.argHint ? ` ${item.argHint}` : ""}`;
|
|
11847
|
+
}
|
|
11848
|
+
function SlashPicker({ items, selectedIndex, theme, query }) {
|
|
11849
|
+
let startIndex = 0;
|
|
11850
|
+
if (selectedIndex >= VISIBLE_LIMIT2) {
|
|
11851
|
+
startIndex = selectedIndex - VISIBLE_LIMIT2 + 1;
|
|
11852
|
+
}
|
|
11853
|
+
const visible = items.slice(startIndex, startIndex + VISIBLE_LIMIT2);
|
|
11854
|
+
const hasMoreAbove = startIndex > 0;
|
|
11855
|
+
const hasMoreBelow = items.length > startIndex + VISIBLE_LIMIT2;
|
|
11856
|
+
const longestLabel = visible.reduce((m, it) => Math.max(m, commandLabel(it).length), 0);
|
|
11857
|
+
const nameColWidth = Math.max(NAME_COL_MIN_WIDTH, longestLabel + NAME_DESC_GAP);
|
|
11858
|
+
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
|
|
11859
|
+
/* @__PURE__ */ jsx19(Text19, { color: theme.accent, bold: true, children: query ? `Commands matching "/${query}"` : "Slash commands" }),
|
|
11860
|
+
/* @__PURE__ */ jsx19(Text19, { color: theme.info.color, children: "Arrow keys to navigate, Enter to select, Esc to cancel." }),
|
|
11861
|
+
/* @__PURE__ */ jsxs18(Box18, { marginTop: 1, flexDirection: "column", children: [
|
|
11862
|
+
visible.length === 0 && /* @__PURE__ */ jsx19(Text19, { color: theme.info.color, dimColor: true, children: "No matches" }),
|
|
11863
|
+
hasMoreAbove && /* @__PURE__ */ jsxs18(Text19, { color: theme.info.color, dimColor: true, children: [
|
|
11864
|
+
"\u2026 ",
|
|
11865
|
+
startIndex,
|
|
11866
|
+
" more above"
|
|
11867
|
+
] }),
|
|
11868
|
+
visible.map((item, i) => {
|
|
11869
|
+
const actualIndex = startIndex + i;
|
|
11870
|
+
const isSelected = actualIndex === selectedIndex;
|
|
11871
|
+
const nameCol = commandLabel(item).padEnd(nameColWidth);
|
|
11872
|
+
const badge = sourceBadge(item.source);
|
|
11873
|
+
return /* @__PURE__ */ jsxs18(Text19, { color: isSelected ? theme.accent : void 0, bold: isSelected, children: [
|
|
11874
|
+
isSelected ? "\u203A " : " ",
|
|
11875
|
+
nameCol,
|
|
11876
|
+
/* @__PURE__ */ jsxs18(Text19, { color: theme.info.color, dimColor: true, children: [
|
|
11877
|
+
item.description,
|
|
11878
|
+
badge && ` [${badge}]`
|
|
11879
|
+
] })
|
|
11880
|
+
] }, item.name);
|
|
11881
|
+
}),
|
|
11882
|
+
hasMoreBelow && /* @__PURE__ */ jsxs18(Text19, { color: theme.info.color, dimColor: true, children: [
|
|
11883
|
+
"\u2026 ",
|
|
11884
|
+
items.length - (startIndex + VISIBLE_LIMIT2),
|
|
11885
|
+
" more below"
|
|
11886
|
+
] })
|
|
11887
|
+
] })
|
|
11888
|
+
] });
|
|
11889
|
+
}
|
|
11890
|
+
var VISIBLE_LIMIT2, NAME_COL_MIN_WIDTH, NAME_DESC_GAP;
|
|
11891
|
+
var init_slash_picker = __esm({
|
|
11892
|
+
"src/ui/slash-picker.tsx"() {
|
|
11893
|
+
"use strict";
|
|
11894
|
+
VISIBLE_LIMIT2 = 7;
|
|
11895
|
+
NAME_COL_MIN_WIDTH = 18;
|
|
11896
|
+
NAME_DESC_GAP = 2;
|
|
11897
|
+
}
|
|
11898
|
+
});
|
|
11899
|
+
|
|
11900
|
+
// src/util/fuzzy.ts
|
|
11901
|
+
function fuzzyMatch(query, text) {
|
|
11902
|
+
const q = query.toLowerCase();
|
|
11903
|
+
const t = text.toLowerCase();
|
|
11904
|
+
if (q.length === 0) return { matches: true, score: 0 };
|
|
11905
|
+
if (q.length > t.length) return { matches: false, score: 0 };
|
|
11906
|
+
let qi = 0;
|
|
11907
|
+
let score = 0;
|
|
11908
|
+
let lastMatch = -1;
|
|
11909
|
+
let consecutive = 0;
|
|
11910
|
+
for (let i = 0; i < t.length && qi < q.length; i++) {
|
|
11911
|
+
if (t[i] !== q[qi]) continue;
|
|
11912
|
+
const isWordBoundary = i === 0 || /[\s\-_./:]/.test(t[i - 1]);
|
|
11913
|
+
if (lastMatch === i - 1) {
|
|
11914
|
+
consecutive++;
|
|
11915
|
+
score -= consecutive * 5;
|
|
11916
|
+
} else {
|
|
11917
|
+
consecutive = 0;
|
|
11918
|
+
if (lastMatch >= 0) score += (i - lastMatch - 1) * 2;
|
|
11919
|
+
}
|
|
11920
|
+
if (isWordBoundary) score -= 10;
|
|
11921
|
+
score += i * 0.1;
|
|
11922
|
+
lastMatch = i;
|
|
11923
|
+
qi++;
|
|
11924
|
+
}
|
|
11925
|
+
if (qi < q.length) return { matches: false, score: 0 };
|
|
11926
|
+
return { matches: true, score };
|
|
11927
|
+
}
|
|
11928
|
+
function fuzzyFilter(items, query, getText) {
|
|
11929
|
+
const trimmed = query.trim();
|
|
11930
|
+
if (trimmed.length === 0) return items;
|
|
11931
|
+
const tokens = trimmed.split(/\s+/);
|
|
11932
|
+
const scored = [];
|
|
11933
|
+
for (const item of items) {
|
|
11934
|
+
const text = getText(item);
|
|
11935
|
+
let total = 0;
|
|
11936
|
+
let allMatch = true;
|
|
11937
|
+
for (const token of tokens) {
|
|
11938
|
+
const m = fuzzyMatch(token, text);
|
|
11939
|
+
if (!m.matches) {
|
|
11940
|
+
allMatch = false;
|
|
11941
|
+
break;
|
|
11942
|
+
}
|
|
11943
|
+
total += m.score;
|
|
11944
|
+
}
|
|
11945
|
+
if (allMatch) scored.push({ item, score: total });
|
|
11946
|
+
}
|
|
11947
|
+
scored.sort((a, b) => a.score - b.score);
|
|
11948
|
+
return scored.map((s) => s.item);
|
|
11949
|
+
}
|
|
11950
|
+
var init_fuzzy = __esm({
|
|
11951
|
+
"src/util/fuzzy.ts"() {
|
|
11952
|
+
"use strict";
|
|
11953
|
+
}
|
|
11954
|
+
});
|
|
11955
|
+
|
|
11271
11956
|
// src/cost-attribution/tui-report.ts
|
|
11272
11957
|
var tui_report_exports = {};
|
|
11273
11958
|
__export(tui_report_exports, {
|
|
@@ -11360,11 +12045,13 @@ var app_exports = {};
|
|
|
11360
12045
|
__export(app_exports, {
|
|
11361
12046
|
buildFilePickerIgnoreList: () => buildFilePickerIgnoreList,
|
|
11362
12047
|
filterPickerItems: () => filterPickerItems,
|
|
12048
|
+
insertSlashCommand: () => insertSlashCommand,
|
|
11363
12049
|
renderApp: () => renderApp,
|
|
11364
|
-
shouldOpenMentionPicker: () => shouldOpenMentionPicker
|
|
12050
|
+
shouldOpenMentionPicker: () => shouldOpenMentionPicker,
|
|
12051
|
+
shouldOpenSlashPicker: () => shouldOpenSlashPicker
|
|
11365
12052
|
});
|
|
11366
12053
|
import React12, { useState as useState9, useRef as useRef3, useEffect as useEffect4, useCallback } from "react";
|
|
11367
|
-
import { Box as
|
|
12054
|
+
import { Box as Box19, Text as Text20, useApp, useInput as useInput5, render } from "ink";
|
|
11368
12055
|
import SelectInput8 from "ink-select-input";
|
|
11369
12056
|
import { existsSync as existsSync2, statSync as statSync3 } from "fs";
|
|
11370
12057
|
import { join as join17 } from "path";
|
|
@@ -11373,7 +12060,7 @@ import { spawn as spawn4 } from "child_process";
|
|
|
11373
12060
|
import { platform as platform2 } from "os";
|
|
11374
12061
|
import fg4 from "fast-glob";
|
|
11375
12062
|
import { readFileSync as readFileSync3 } from "fs";
|
|
11376
|
-
import { jsx as
|
|
12063
|
+
import { jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
11377
12064
|
function buildFilePickerIgnoreList(cwd) {
|
|
11378
12065
|
const hardcoded = [
|
|
11379
12066
|
// Dependencies
|
|
@@ -11482,6 +12169,18 @@ function shouldOpenMentionPicker(input, cursorOffset, pickerCancelOffset) {
|
|
|
11482
12169
|
}
|
|
11483
12170
|
return false;
|
|
11484
12171
|
}
|
|
12172
|
+
function shouldOpenSlashPicker(input, cursorOffset, cancelOffset) {
|
|
12173
|
+
if (cancelOffset === cursorOffset) return false;
|
|
12174
|
+
if (cursorOffset === 0 || input[cursorOffset - 1] !== "/") return false;
|
|
12175
|
+
return /^\s*$/.test(input.slice(0, cursorOffset - 1));
|
|
12176
|
+
}
|
|
12177
|
+
function insertSlashCommand(input, anchor, name) {
|
|
12178
|
+
let tokenEnd = anchor + 1;
|
|
12179
|
+
while (tokenEnd < input.length && !/\s/.test(input[tokenEnd])) tokenEnd++;
|
|
12180
|
+
const head = input.slice(0, anchor + 1) + name;
|
|
12181
|
+
const tail = " " + input.slice(tokenEnd).replace(/^\s+/, "");
|
|
12182
|
+
return { value: head + tail, cursor: head.length + 1 };
|
|
12183
|
+
}
|
|
11485
12184
|
function gatewayFromConfig(cfg) {
|
|
11486
12185
|
if (!cfg.aiGatewayId) return void 0;
|
|
11487
12186
|
return {
|
|
@@ -11529,14 +12228,14 @@ function compactEventsVisual(prev, keepLastTurns) {
|
|
|
11529
12228
|
...kept
|
|
11530
12229
|
];
|
|
11531
12230
|
}
|
|
11532
|
-
function makePrefixMessages(cacheStable, model, mode, tools) {
|
|
12231
|
+
function makePrefixMessages(cacheStable, model, mode, tools, role) {
|
|
11533
12232
|
if (cacheStable) {
|
|
11534
|
-
return buildSystemMessages({ cwd: process.cwd(), tools, model, mode });
|
|
12233
|
+
return buildSystemMessages({ cwd: process.cwd(), tools, model, mode, role });
|
|
11535
12234
|
}
|
|
11536
12235
|
return [
|
|
11537
12236
|
{
|
|
11538
12237
|
role: "system",
|
|
11539
|
-
content: buildSystemPrompt({ cwd: process.cwd(), tools, model, mode })
|
|
12238
|
+
content: buildSystemPrompt({ cwd: process.cwd(), tools, model, mode, role })
|
|
11540
12239
|
}
|
|
11541
12240
|
];
|
|
11542
12241
|
}
|
|
@@ -11616,9 +12315,10 @@ function App({
|
|
|
11616
12315
|
const [hasUpdate, setHasUpdate] = useState9(initialUpdateResult?.hasUpdate ?? false);
|
|
11617
12316
|
const [latestVersion, setLatestVersion] = useState9(initialUpdateResult?.latestVersion ?? null);
|
|
11618
12317
|
const [cursorOffset, setCursorOffset] = useState9(0);
|
|
11619
|
-
const [
|
|
11620
|
-
const [
|
|
11621
|
-
const
|
|
12318
|
+
const [activePicker, setActivePicker] = useState9(null);
|
|
12319
|
+
const [filePickerItems, setFilePickerItems] = useState9([]);
|
|
12320
|
+
const filePickerLoadedRef = useRef3(false);
|
|
12321
|
+
const [customCommandsVersion, setCustomCommandsVersion] = useState9(0);
|
|
11622
12322
|
const cacheStableRef = useRef3(initialCfg?.cacheStablePrompts !== false);
|
|
11623
12323
|
const messagesRef = useRef3(
|
|
11624
12324
|
makePrefixMessages(cacheStableRef.current, cfg?.model ?? DEFAULT_MODEL, "edit", ALL_TOOLS)
|
|
@@ -11654,28 +12354,42 @@ function App({
|
|
|
11654
12354
|
const flushTimeoutRef = useRef3(null);
|
|
11655
12355
|
const customCommandsRef = useRef3([]);
|
|
11656
12356
|
const pickerCancelRef = useRef3(null);
|
|
11657
|
-
const
|
|
12357
|
+
const pickerAnchor = activePicker?.anchor ?? null;
|
|
12358
|
+
const pickerKind = activePicker?.kind ?? null;
|
|
12359
|
+
const pickerQuery = React12.useMemo(() => {
|
|
11658
12360
|
if (pickerAnchor === null) return null;
|
|
11659
|
-
|
|
11660
|
-
return { query, anchor: pickerAnchor };
|
|
12361
|
+
return input.slice(pickerAnchor + 1, cursorOffset);
|
|
11661
12362
|
}, [input, cursorOffset, pickerAnchor]);
|
|
11662
|
-
const
|
|
11663
|
-
if (
|
|
11664
|
-
return filterPickerItems(
|
|
11665
|
-
}, [
|
|
12363
|
+
const filteredFileItems = React12.useMemo(() => {
|
|
12364
|
+
if (pickerKind !== "file" || pickerQuery === null) return [];
|
|
12365
|
+
return filterPickerItems(filePickerItems, pickerQuery);
|
|
12366
|
+
}, [pickerKind, filePickerItems, pickerQuery]);
|
|
12367
|
+
const allSlashCommands = React12.useMemo(() => {
|
|
12368
|
+
const customs = customCommandsRef.current.filter((c) => !BUILTIN_COMMAND_NAMES.has(c.name.toLowerCase())).map((c) => ({
|
|
12369
|
+
name: c.name,
|
|
12370
|
+
description: c.description ?? "",
|
|
12371
|
+
source: c.source
|
|
12372
|
+
}));
|
|
12373
|
+
return [...BUILTIN_COMMANDS, ...customs];
|
|
12374
|
+
}, [customCommandsVersion]);
|
|
12375
|
+
const filteredSlashItems = React12.useMemo(() => {
|
|
12376
|
+
if (pickerKind !== "slash" || pickerQuery === null) return [];
|
|
12377
|
+
return fuzzyFilter(allSlashCommands, pickerQuery, (c) => c.name).slice(0, 50);
|
|
12378
|
+
}, [pickerKind, allSlashCommands, pickerQuery]);
|
|
11666
12379
|
useEffect4(() => {
|
|
11667
|
-
if (
|
|
11668
|
-
|
|
11669
|
-
|
|
12380
|
+
if (activePicker !== null) {
|
|
12381
|
+
const trigger = activePicker.kind === "file" ? "@" : "/";
|
|
12382
|
+
if (cursorOffset < activePicker.anchor) {
|
|
12383
|
+
setActivePicker(null);
|
|
11670
12384
|
return;
|
|
11671
12385
|
}
|
|
11672
|
-
if (input[
|
|
11673
|
-
|
|
12386
|
+
if (input[activePicker.anchor] !== trigger) {
|
|
12387
|
+
setActivePicker(null);
|
|
11674
12388
|
return;
|
|
11675
12389
|
}
|
|
11676
|
-
const query = input.slice(
|
|
11677
|
-
if (query
|
|
11678
|
-
|
|
12390
|
+
const query = input.slice(activePicker.anchor + 1, cursorOffset);
|
|
12391
|
+
if (/\s/.test(query)) {
|
|
12392
|
+
setActivePicker(null);
|
|
11679
12393
|
return;
|
|
11680
12394
|
}
|
|
11681
12395
|
return;
|
|
@@ -11685,9 +12399,9 @@ function App({
|
|
|
11685
12399
|
return;
|
|
11686
12400
|
}
|
|
11687
12401
|
if (filePickerEnabled && shouldOpenMentionPicker(input, cursorOffset, pickerCancelRef.current)) {
|
|
11688
|
-
|
|
11689
|
-
|
|
11690
|
-
|
|
12402
|
+
setActivePicker({ kind: "file", anchor: cursorOffset - 1, selected: 0 });
|
|
12403
|
+
if (!filePickerLoadedRef.current) {
|
|
12404
|
+
filePickerLoadedRef.current = true;
|
|
11691
12405
|
const cwd = process.cwd();
|
|
11692
12406
|
void fg4("**/*", {
|
|
11693
12407
|
cwd,
|
|
@@ -11707,38 +12421,87 @@ function App({
|
|
|
11707
12421
|
if (!a.isDirectory && b.isDirectory) return 1;
|
|
11708
12422
|
return a.name.localeCompare(b.name);
|
|
11709
12423
|
});
|
|
11710
|
-
|
|
12424
|
+
setFilePickerItems(items);
|
|
11711
12425
|
}).catch(() => {
|
|
11712
|
-
|
|
12426
|
+
setFilePickerItems([]);
|
|
11713
12427
|
});
|
|
11714
12428
|
}
|
|
12429
|
+
return;
|
|
12430
|
+
}
|
|
12431
|
+
if (shouldOpenSlashPicker(input, cursorOffset, pickerCancelRef.current)) {
|
|
12432
|
+
setActivePicker({ kind: "slash", anchor: cursorOffset - 1, selected: 0 });
|
|
12433
|
+
return;
|
|
12434
|
+
}
|
|
12435
|
+
}, [input, cursorOffset, activePicker, filePickerEnabled]);
|
|
12436
|
+
useEffect4(() => {
|
|
12437
|
+
if (activePicker?.kind !== "file") return;
|
|
12438
|
+
const max = Math.max(0, filteredFileItems.length - 1);
|
|
12439
|
+
if (activePicker.selected > max) {
|
|
12440
|
+
setActivePicker({ ...activePicker, selected: max });
|
|
11715
12441
|
}
|
|
11716
|
-
}, [
|
|
12442
|
+
}, [filteredFileItems.length, activePicker]);
|
|
11717
12443
|
useEffect4(() => {
|
|
11718
|
-
if (
|
|
11719
|
-
|
|
12444
|
+
if (activePicker?.kind !== "slash") return;
|
|
12445
|
+
const max = Math.max(0, filteredSlashItems.length - 1);
|
|
12446
|
+
if (activePicker.selected > max) {
|
|
12447
|
+
setActivePicker({ ...activePicker, selected: max });
|
|
11720
12448
|
}
|
|
11721
|
-
}, [
|
|
12449
|
+
}, [filteredSlashItems.length, activePicker]);
|
|
11722
12450
|
const handlePickerUp = useCallback(() => {
|
|
11723
|
-
|
|
12451
|
+
setActivePicker((p) => {
|
|
12452
|
+
if (!p) return null;
|
|
12453
|
+
const next = Math.max(0, p.selected - 1);
|
|
12454
|
+
return next === p.selected ? p : { ...p, selected: next };
|
|
12455
|
+
});
|
|
11724
12456
|
}, []);
|
|
11725
12457
|
const handlePickerDown = useCallback(() => {
|
|
11726
|
-
|
|
11727
|
-
|
|
12458
|
+
setActivePicker((p) => {
|
|
12459
|
+
if (!p) return null;
|
|
12460
|
+
const max = p.kind === "file" ? Math.max(0, filteredFileItems.length - 1) : Math.max(0, filteredSlashItems.length - 1);
|
|
12461
|
+
const next = Math.min(max, p.selected + 1);
|
|
12462
|
+
return next === p.selected ? p : { ...p, selected: next };
|
|
12463
|
+
});
|
|
12464
|
+
}, [filteredFileItems.length, filteredSlashItems.length]);
|
|
11728
12465
|
const handlePickerSelect = useCallback(() => {
|
|
11729
|
-
if (!
|
|
11730
|
-
|
|
12466
|
+
if (!activePicker) return;
|
|
12467
|
+
if (activePicker.kind === "file") {
|
|
12468
|
+
const item2 = filteredFileItems[activePicker.selected];
|
|
12469
|
+
if (!item2) return;
|
|
12470
|
+
const insert = item2.name + (item2.isDirectory ? "/" : " ");
|
|
12471
|
+
const newInput = input.slice(0, activePicker.anchor) + insert + input.slice(cursorOffset);
|
|
12472
|
+
setInput(newInput);
|
|
12473
|
+
setCursorOffset(activePicker.anchor + insert.length);
|
|
12474
|
+
setActivePicker(null);
|
|
12475
|
+
return;
|
|
12476
|
+
}
|
|
12477
|
+
const item = filteredSlashItems[activePicker.selected];
|
|
11731
12478
|
if (!item) return;
|
|
11732
|
-
const
|
|
11733
|
-
|
|
11734
|
-
|
|
11735
|
-
|
|
11736
|
-
|
|
11737
|
-
}, [mentionState, filteredPickerItems, pickerSelected, input, cursorOffset]);
|
|
12479
|
+
const { value, cursor } = insertSlashCommand(input, activePicker.anchor, item.name);
|
|
12480
|
+
setInput(value);
|
|
12481
|
+
setCursorOffset(cursor);
|
|
12482
|
+
setActivePicker(null);
|
|
12483
|
+
}, [activePicker, filteredFileItems, filteredSlashItems, input, cursorOffset]);
|
|
11738
12484
|
const handlePickerCancel = useCallback(() => {
|
|
11739
12485
|
pickerCancelRef.current = cursorOffset;
|
|
11740
|
-
|
|
12486
|
+
setActivePicker(null);
|
|
11741
12487
|
}, [cursorOffset]);
|
|
12488
|
+
useEffect4(() => {
|
|
12489
|
+
const modalActive = showThemePicker || showHelpMenu || commandWizard !== null || commandPicker !== null || commandToDelete !== null || showCommandList || showLspWizard || resumeSessions !== null || perm !== null;
|
|
12490
|
+
if (modalActive && activePicker !== null) {
|
|
12491
|
+
setActivePicker(null);
|
|
12492
|
+
}
|
|
12493
|
+
}, [
|
|
12494
|
+
showThemePicker,
|
|
12495
|
+
showHelpMenu,
|
|
12496
|
+
commandWizard,
|
|
12497
|
+
commandPicker,
|
|
12498
|
+
commandToDelete,
|
|
12499
|
+
showCommandList,
|
|
12500
|
+
showLspWizard,
|
|
12501
|
+
resumeSessions,
|
|
12502
|
+
perm,
|
|
12503
|
+
activePicker
|
|
12504
|
+
]);
|
|
11742
12505
|
useEffect4(() => {
|
|
11743
12506
|
if (!cfg) return;
|
|
11744
12507
|
void Promise.resolve().then(() => (init_sessions(), sessions_exports)).then(
|
|
@@ -11819,6 +12582,7 @@ function App({
|
|
|
11819
12582
|
}
|
|
11820
12583
|
void loadCustomCommands(process.cwd()).then(({ commands, warnings }) => {
|
|
11821
12584
|
customCommandsRef.current = commands;
|
|
12585
|
+
setCustomCommandsVersion((v) => v + 1);
|
|
11822
12586
|
for (const w of warnings) {
|
|
11823
12587
|
setEvents((e) => [...e, { kind: "info", key: mkKey(), text: `commands: ${w}` }]);
|
|
11824
12588
|
}
|
|
@@ -11844,6 +12608,7 @@ function App({
|
|
|
11844
12608
|
const reloadCustomCommands = useCallback(async () => {
|
|
11845
12609
|
const { commands, warnings } = await loadCustomCommands(process.cwd());
|
|
11846
12610
|
customCommandsRef.current = commands;
|
|
12611
|
+
setCustomCommandsVersion((v) => v + 1);
|
|
11847
12612
|
for (const w of warnings) {
|
|
11848
12613
|
setEvents((e) => [...e, { kind: "info", key: mkKey(), text: `commands: ${w}` }]);
|
|
11849
12614
|
}
|
|
@@ -12407,9 +13172,10 @@ function App({
|
|
|
12407
13172
|
onAssistantStart: () => {
|
|
12408
13173
|
const id = nextAssistantId++;
|
|
12409
13174
|
activeAsstIdRef.current = id;
|
|
13175
|
+
const role = orchestratorRef.current?.getActiveRole();
|
|
12410
13176
|
setEvents((e) => [
|
|
12411
13177
|
...e,
|
|
12412
|
-
{ kind: "assistant", key: `asst_${id}`, id, text: "", reasoning: "", streaming: true }
|
|
13178
|
+
{ kind: "assistant", key: `asst_${id}`, id, text: "", reasoning: "", streaming: true, agentRole: role }
|
|
12413
13179
|
]);
|
|
12414
13180
|
},
|
|
12415
13181
|
onReasoningDelta: (d) => {
|
|
@@ -13422,9 +14188,10 @@ ${lines.join("\n")}` }]);
|
|
|
13422
14188
|
onAssistantStart: () => {
|
|
13423
14189
|
const id = nextAssistantId++;
|
|
13424
14190
|
activeAsstIdRef.current = id;
|
|
14191
|
+
const role = orchestratorRef.current?.getActiveRole();
|
|
13425
14192
|
setEvents((e) => [
|
|
13426
14193
|
...e,
|
|
13427
|
-
{ kind: "assistant", key: `asst_${id}`, id, text: "", reasoning: "", streaming: true }
|
|
14194
|
+
{ kind: "assistant", key: `asst_${id}`, id, text: "", reasoning: "", streaming: true, agentRole: role }
|
|
13428
14195
|
]);
|
|
13429
14196
|
},
|
|
13430
14197
|
onReasoningDelta: (d) => {
|
|
@@ -13570,7 +14337,8 @@ ${lines.join("\n")}` }]);
|
|
|
13570
14337
|
cacheStableRef.current,
|
|
13571
14338
|
overrideModel ?? cfg.model,
|
|
13572
14339
|
modeRef.current,
|
|
13573
|
-
[...ALL_TOOLS, ...mcpToolsRef.current, ...lspToolsRef.current]
|
|
14340
|
+
[...ALL_TOOLS, ...mcpToolsRef.current, ...lspToolsRef.current],
|
|
14341
|
+
orchestratorRef.current.getActiveRole()
|
|
13574
14342
|
);
|
|
13575
14343
|
activeSession2.messages.unshift(...prefix);
|
|
13576
14344
|
}
|
|
@@ -13777,7 +14545,7 @@ ${lines.join("\n")}` }]);
|
|
|
13777
14545
|
}
|
|
13778
14546
|
}, [usage]);
|
|
13779
14547
|
if (!cfg) {
|
|
13780
|
-
return /* @__PURE__ */
|
|
14548
|
+
return /* @__PURE__ */ jsx20(
|
|
13781
14549
|
Onboarding,
|
|
13782
14550
|
{
|
|
13783
14551
|
onDone: (newCfg) => {
|
|
@@ -13791,13 +14559,13 @@ ${lines.join("\n")}` }]);
|
|
|
13791
14559
|
);
|
|
13792
14560
|
}
|
|
13793
14561
|
if (resumeSessions !== null) {
|
|
13794
|
-
return /* @__PURE__ */
|
|
14562
|
+
return /* @__PURE__ */ jsx20(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsx20(ResumePicker, { sessions: resumeSessions, onPick: handleResumePick, theme }) });
|
|
13795
14563
|
}
|
|
13796
14564
|
if (showThemePicker) {
|
|
13797
|
-
return /* @__PURE__ */
|
|
14565
|
+
return /* @__PURE__ */ jsx20(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsx20(ThemePicker, { themes: themeList(), current: theme, onPick: handleThemePick, onPreview: (t) => setTheme(t) }) });
|
|
13798
14566
|
}
|
|
13799
14567
|
if (showHelpMenu) {
|
|
13800
|
-
return /* @__PURE__ */
|
|
14568
|
+
return /* @__PURE__ */ jsx20(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsx20(
|
|
13801
14569
|
HelpMenu,
|
|
13802
14570
|
{
|
|
13803
14571
|
theme,
|
|
@@ -13812,7 +14580,7 @@ ${lines.join("\n")}` }]);
|
|
|
13812
14580
|
) });
|
|
13813
14581
|
}
|
|
13814
14582
|
if (showLspWizard) {
|
|
13815
|
-
return /* @__PURE__ */
|
|
14583
|
+
return /* @__PURE__ */ jsx20(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsx20(
|
|
13816
14584
|
LspWizard,
|
|
13817
14585
|
{
|
|
13818
14586
|
theme,
|
|
@@ -13850,7 +14618,7 @@ ${lines.join("\n")}` }]);
|
|
|
13850
14618
|
) });
|
|
13851
14619
|
}
|
|
13852
14620
|
if (commandWizard) {
|
|
13853
|
-
return /* @__PURE__ */
|
|
14621
|
+
return /* @__PURE__ */ jsx20(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsx20(
|
|
13854
14622
|
CommandWizard,
|
|
13855
14623
|
{
|
|
13856
14624
|
theme,
|
|
@@ -13864,7 +14632,7 @@ ${lines.join("\n")}` }]);
|
|
|
13864
14632
|
) });
|
|
13865
14633
|
}
|
|
13866
14634
|
if (commandPicker) {
|
|
13867
|
-
return /* @__PURE__ */
|
|
14635
|
+
return /* @__PURE__ */ jsx20(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsx20(
|
|
13868
14636
|
CommandPicker,
|
|
13869
14637
|
{
|
|
13870
14638
|
theme,
|
|
@@ -13883,14 +14651,14 @@ ${lines.join("\n")}` }]);
|
|
|
13883
14651
|
) });
|
|
13884
14652
|
}
|
|
13885
14653
|
if (commandToDelete) {
|
|
13886
|
-
return /* @__PURE__ */
|
|
13887
|
-
/* @__PURE__ */
|
|
14654
|
+
return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
|
|
14655
|
+
/* @__PURE__ */ jsxs19(Text20, { color: theme.accent, bold: true, children: [
|
|
13888
14656
|
"Delete /",
|
|
13889
14657
|
commandToDelete.name,
|
|
13890
14658
|
"?"
|
|
13891
14659
|
] }),
|
|
13892
|
-
/* @__PURE__ */
|
|
13893
|
-
/* @__PURE__ */
|
|
14660
|
+
/* @__PURE__ */ jsx20(Text20, { color: theme.info.color, dimColor: true, children: commandToDelete.filepath }),
|
|
14661
|
+
/* @__PURE__ */ jsx20(Box19, { marginTop: 1, children: /* @__PURE__ */ jsx20(
|
|
13894
14662
|
SelectInput8,
|
|
13895
14663
|
{
|
|
13896
14664
|
items: [
|
|
@@ -13909,7 +14677,7 @@ ${lines.join("\n")}` }]);
|
|
|
13909
14677
|
] });
|
|
13910
14678
|
}
|
|
13911
14679
|
if (showCommandList) {
|
|
13912
|
-
return /* @__PURE__ */
|
|
14680
|
+
return /* @__PURE__ */ jsx20(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsx20(
|
|
13913
14681
|
CommandList,
|
|
13914
14682
|
{
|
|
13915
14683
|
theme,
|
|
@@ -13919,9 +14687,9 @@ ${lines.join("\n")}` }]);
|
|
|
13919
14687
|
) });
|
|
13920
14688
|
}
|
|
13921
14689
|
const hasConversation = events.some((e) => e.kind === "user" || e.kind === "assistant");
|
|
13922
|
-
return /* @__PURE__ */
|
|
13923
|
-
!hasConversation && events.length === 0 ? /* @__PURE__ */
|
|
13924
|
-
perm ? /* @__PURE__ */
|
|
14690
|
+
return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
|
|
14691
|
+
!hasConversation && events.length === 0 ? /* @__PURE__ */ jsx20(Welcome, { theme, accountId: cfg.accountId }) : /* @__PURE__ */ jsx20(ChatView, { events, showReasoning, theme, verbose }),
|
|
14692
|
+
perm ? /* @__PURE__ */ jsx20(
|
|
13925
14693
|
PermissionModal,
|
|
13926
14694
|
{
|
|
13927
14695
|
tool: perm.tool,
|
|
@@ -13933,8 +14701,8 @@ ${lines.join("\n")}` }]);
|
|
|
13933
14701
|
setPerm(null);
|
|
13934
14702
|
}
|
|
13935
14703
|
}
|
|
13936
|
-
) : /* @__PURE__ */
|
|
13937
|
-
tasks.length > 0 && /* @__PURE__ */
|
|
14704
|
+
) : /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", marginTop: 1, children: [
|
|
14705
|
+
tasks.length > 0 && /* @__PURE__ */ jsx20(
|
|
13938
14706
|
TaskList,
|
|
13939
14707
|
{
|
|
13940
14708
|
tasks,
|
|
@@ -13943,11 +14711,11 @@ ${lines.join("\n")}` }]);
|
|
|
13943
14711
|
tokensDelta: Math.max(0, (usage?.prompt_tokens ?? 0) - tasksStartTokens)
|
|
13944
14712
|
}
|
|
13945
14713
|
),
|
|
13946
|
-
queue.length > 0 && /* @__PURE__ */
|
|
14714
|
+
queue.length > 0 && /* @__PURE__ */ jsx20(Box19, { flexDirection: "column", marginBottom: 1, children: queue.map((q, i) => /* @__PURE__ */ jsxs19(Text20, { color: theme.queue.color, dimColor: theme.queue.dim, children: [
|
|
13947
14715
|
"\u23F3 ",
|
|
13948
14716
|
q.display
|
|
13949
14717
|
] }, `queue_${i}`)) }),
|
|
13950
|
-
/* @__PURE__ */
|
|
14718
|
+
/* @__PURE__ */ jsx20(
|
|
13951
14719
|
StatusBar,
|
|
13952
14720
|
{
|
|
13953
14721
|
model: cfg.model,
|
|
@@ -13965,18 +14733,27 @@ ${lines.join("\n")}` }]);
|
|
|
13965
14733
|
codeMode
|
|
13966
14734
|
}
|
|
13967
14735
|
),
|
|
13968
|
-
|
|
14736
|
+
activePicker?.kind === "file" && /* @__PURE__ */ jsx20(
|
|
13969
14737
|
FilePicker,
|
|
13970
14738
|
{
|
|
13971
|
-
items:
|
|
13972
|
-
selectedIndex:
|
|
14739
|
+
items: filteredFileItems,
|
|
14740
|
+
selectedIndex: activePicker.selected,
|
|
14741
|
+
theme,
|
|
14742
|
+
query: pickerQuery ?? ""
|
|
14743
|
+
}
|
|
14744
|
+
),
|
|
14745
|
+
activePicker?.kind === "slash" && /* @__PURE__ */ jsx20(
|
|
14746
|
+
SlashPicker,
|
|
14747
|
+
{
|
|
14748
|
+
items: filteredSlashItems,
|
|
14749
|
+
selectedIndex: activePicker.selected,
|
|
13973
14750
|
theme,
|
|
13974
|
-
query:
|
|
14751
|
+
query: pickerQuery ?? ""
|
|
13975
14752
|
}
|
|
13976
14753
|
),
|
|
13977
|
-
/* @__PURE__ */
|
|
13978
|
-
/* @__PURE__ */
|
|
13979
|
-
/* @__PURE__ */
|
|
14754
|
+
/* @__PURE__ */ jsxs19(Box19, { marginTop: 1, children: [
|
|
14755
|
+
/* @__PURE__ */ jsx20(Text20, { color: theme.accent, children: "\u203A " }),
|
|
14756
|
+
/* @__PURE__ */ jsx20(
|
|
13980
14757
|
CustomTextInput,
|
|
13981
14758
|
{
|
|
13982
14759
|
value: input,
|
|
@@ -13985,7 +14762,7 @@ ${lines.join("\n")}` }]);
|
|
|
13985
14762
|
enablePaste: true,
|
|
13986
14763
|
cursorOffset,
|
|
13987
14764
|
onCursorChange: setCursorOffset,
|
|
13988
|
-
pickerActive:
|
|
14765
|
+
pickerActive: activePicker !== null,
|
|
13989
14766
|
onPickerUp: handlePickerUp,
|
|
13990
14767
|
onPickerDown: handlePickerDown,
|
|
13991
14768
|
onPickerSelect: handlePickerSelect,
|
|
@@ -14033,7 +14810,7 @@ ${lines.join("\n")}` }]);
|
|
|
14033
14810
|
}
|
|
14034
14811
|
async function renderApp(cfg, updateResult, lspScope = "global", lspProjectPath = null) {
|
|
14035
14812
|
const instance = render(
|
|
14036
|
-
/* @__PURE__ */
|
|
14813
|
+
/* @__PURE__ */ jsx20(
|
|
14037
14814
|
App,
|
|
14038
14815
|
{
|
|
14039
14816
|
initialCfg: cfg,
|
|
@@ -14048,7 +14825,7 @@ async function renderApp(cfg, updateResult, lspScope = "global", lspProjectPath
|
|
|
14048
14825
|
);
|
|
14049
14826
|
await instance.waitUntilExit();
|
|
14050
14827
|
}
|
|
14051
|
-
var MAX_GITIGNORE_SIZE, FEEDBACK_WORKER_URL, CONTEXT_LIMIT, AUTO_COMPACT_SUGGEST_PCT, MAX_EVENTS, nextAssistantId, nextKey, mkKey, MAX_IMAGES_PER_MESSAGE,
|
|
14828
|
+
var MAX_GITIGNORE_SIZE, FEEDBACK_WORKER_URL, CONTEXT_LIMIT, AUTO_COMPACT_SUGGEST_PCT, MAX_EVENTS, nextAssistantId, nextKey, mkKey, MAX_IMAGES_PER_MESSAGE, EFFORT_DESCRIPTIONS;
|
|
14052
14829
|
var init_app = __esm({
|
|
14053
14830
|
"src/app.tsx"() {
|
|
14054
14831
|
"use strict";
|
|
@@ -14087,6 +14864,7 @@ var init_app = __esm({
|
|
|
14087
14864
|
init_version();
|
|
14088
14865
|
init_loader();
|
|
14089
14866
|
init_renderer2();
|
|
14867
|
+
init_builtins();
|
|
14090
14868
|
init_save();
|
|
14091
14869
|
init_command_wizard();
|
|
14092
14870
|
init_command_picker();
|
|
@@ -14095,6 +14873,8 @@ var init_app = __esm({
|
|
|
14095
14873
|
init_lsp_config();
|
|
14096
14874
|
init_lsp_nudge();
|
|
14097
14875
|
init_file_picker();
|
|
14876
|
+
init_slash_picker();
|
|
14877
|
+
init_fuzzy();
|
|
14098
14878
|
MAX_GITIGNORE_SIZE = 1 * 1024 * 1024;
|
|
14099
14879
|
FEEDBACK_WORKER_URL = "https://kimiflare-feedback.sina-b35.workers.dev";
|
|
14100
14880
|
CONTEXT_LIMIT = 262e3;
|
|
@@ -14104,33 +14884,6 @@ var init_app = __esm({
|
|
|
14104
14884
|
nextKey = 1;
|
|
14105
14885
|
mkKey = () => `evt_${nextKey++}`;
|
|
14106
14886
|
MAX_IMAGES_PER_MESSAGE = 10;
|
|
14107
|
-
BUILTIN_COMMAND_NAMES = /* @__PURE__ */ new Set([
|
|
14108
|
-
"exit",
|
|
14109
|
-
"quit",
|
|
14110
|
-
"clear",
|
|
14111
|
-
"reasoning",
|
|
14112
|
-
"cost",
|
|
14113
|
-
"model",
|
|
14114
|
-
"thinking",
|
|
14115
|
-
"effort",
|
|
14116
|
-
"theme",
|
|
14117
|
-
"mode",
|
|
14118
|
-
"plan",
|
|
14119
|
-
"auto",
|
|
14120
|
-
"edit",
|
|
14121
|
-
"resume",
|
|
14122
|
-
"compact",
|
|
14123
|
-
"init",
|
|
14124
|
-
"update",
|
|
14125
|
-
"mcp",
|
|
14126
|
-
"logout",
|
|
14127
|
-
"help",
|
|
14128
|
-
"memory",
|
|
14129
|
-
"gateway",
|
|
14130
|
-
"hello",
|
|
14131
|
-
"community",
|
|
14132
|
-
"agent"
|
|
14133
|
-
]);
|
|
14134
14887
|
EFFORT_DESCRIPTIONS = {
|
|
14135
14888
|
low: "low \u2014 fastest; lightest reasoning. Best for simple Q&A, small edits, quick coordination.",
|
|
14136
14889
|
medium: "medium \u2014 balanced (default). Solid quality on most edits, fast on trivial prompts.",
|