@kmugalkhod/lightcode 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/cli.js +2083 -1115
- package/package.json +1 -1
- package/server.js +571 -316
package/server.js
CHANGED
|
@@ -147,7 +147,7 @@ var init_src = __esm(() => {
|
|
|
147
147
|
});
|
|
148
148
|
|
|
149
149
|
// packages/ai/src/constants.ts
|
|
150
|
-
var MAX_TOOL_TEXT_OUTPUT_CHARS = 12000, DEFAULT_TOOL_TEXT_OUTPUT_CHARS = 6000, MAX_TOOL_LIST_ENTRIES = 200, MAX_TOOL_SEARCH_RESULTS = 200, ANTHROPIC_TOOL_OPTIONAL_PARAMETER_BUDGET = 24;
|
|
150
|
+
var MAX_TOOL_TEXT_OUTPUT_CHARS = 12000, DEFAULT_TOOL_TEXT_OUTPUT_CHARS = 6000, MAX_TOOL_LIST_ENTRIES = 200, MAX_TOOL_SEARCH_RESULTS = 200, ANTHROPIC_TOOL_OPTIONAL_PARAMETER_BUDGET = 24, TODO_CONTENT_MAX_CHARS = 200, TODO_ACTIVE_FORM_MAX_CHARS = 80, PROGRESS_NOTE_PRUNE_MIN_CHARS = 200;
|
|
151
151
|
|
|
152
152
|
// packages/ai/src/coding-agent-modes.ts
|
|
153
153
|
import { z } from "zod";
|
|
@@ -747,9 +747,10 @@ var init_schema = __esm(() => {
|
|
|
747
747
|
|
|
748
748
|
// packages/ai/src/edit-file/schema.ts
|
|
749
749
|
import { z as z6 } from "zod";
|
|
750
|
-
var editFileDescription
|
|
750
|
+
var editFileDescription, editFileInputSchema, editFileProviderInputSchema, editFileOutputSchema;
|
|
751
751
|
var init_schema2 = __esm(() => {
|
|
752
752
|
init_base_schemas();
|
|
753
|
+
editFileDescription = "Edit a file via search and replace. Use this for focused, small edits. " + "Line endings (CRLF/LF) and surrounding whitespace are matched tolerantly, so " + "copy the lines as shown by read_file. The search text must match exactly one " + "place \u2014 include enough surrounding context to make it unique, or set " + "replaceAll: true to change every occurrence.";
|
|
753
754
|
editFileInputSchema = z6.object({
|
|
754
755
|
path: boundedPathSchema,
|
|
755
756
|
search: z6.string().min(1),
|
|
@@ -1044,20 +1045,22 @@ var init_schema10 = __esm(() => {
|
|
|
1044
1045
|
todoPrioritySchema = z14.enum(["low", "medium", "high"]);
|
|
1045
1046
|
todoItemSchema = z14.object({
|
|
1046
1047
|
id: z14.string().min(1).max(120).optional(),
|
|
1047
|
-
content: z14.string().min(1).max(
|
|
1048
|
+
content: z14.string().min(1).max(TODO_CONTENT_MAX_CHARS),
|
|
1048
1049
|
status: todoStatusSchema,
|
|
1049
1050
|
priority: todoPrioritySchema.optional().default("medium"),
|
|
1050
|
-
notes: z14.string().max(1000).optional()
|
|
1051
|
+
notes: z14.string().max(1000).optional(),
|
|
1052
|
+
activeForm: z14.string().min(1).max(TODO_ACTIVE_FORM_MAX_CHARS).optional()
|
|
1051
1053
|
});
|
|
1052
1054
|
todoItemProviderSchema = z14.object({
|
|
1053
|
-
content: z14.string().min(1).max(
|
|
1054
|
-
status: todoStatusSchema
|
|
1055
|
+
content: z14.string().min(1).max(TODO_CONTENT_MAX_CHARS),
|
|
1056
|
+
status: todoStatusSchema,
|
|
1057
|
+
activeForm: z14.string().min(1).max(TODO_ACTIVE_FORM_MAX_CHARS).optional().describe(`Short present-tense active label for this task, e.g. "Adding tests" or "Editing schema". Set it primarily for the in_progress item so the UI can show what's actively happening; the UI falls back to content when omitted.`)
|
|
1055
1058
|
});
|
|
1056
1059
|
todoWriteInputSchema = z14.object({
|
|
1057
1060
|
todos: z14.array(todoItemSchema).max(50)
|
|
1058
1061
|
});
|
|
1059
1062
|
todoWriteProviderInputSchema = z14.object({
|
|
1060
|
-
todos: z14.array(todoItemProviderSchema)
|
|
1063
|
+
todos: z14.array(todoItemProviderSchema).max(20)
|
|
1061
1064
|
});
|
|
1062
1065
|
todoWriteOutputSchema = z14.object({
|
|
1063
1066
|
sessionId: z14.string(),
|
|
@@ -1069,7 +1072,8 @@ var init_schema10 = __esm(() => {
|
|
|
1069
1072
|
inProgress: z14.number().int().nonnegative(),
|
|
1070
1073
|
completed: z14.number().int().nonnegative(),
|
|
1071
1074
|
canceled: z14.number().int().nonnegative()
|
|
1072
|
-
})
|
|
1075
|
+
}),
|
|
1076
|
+
warnings: z14.array(z14.string()).optional()
|
|
1073
1077
|
});
|
|
1074
1078
|
});
|
|
1075
1079
|
|
|
@@ -1903,14 +1907,139 @@ ${environmentContext.trim()}` : "");
|
|
|
1903
1907
|
var cwdPromptPlaceholder = "{cwd}", defaultCodingAgentSystemPrompt, toolCallingDisciplineAddendum;
|
|
1904
1908
|
var init_coding_agent_prompt = __esm(() => {
|
|
1905
1909
|
init_coding_agent_modes();
|
|
1906
|
-
defaultCodingAgentSystemPrompt = "You are a coding agent working inside the user's project at this working directory: {cwd}. " + 'You can already see the project \u2014 proactively read it. When the user asks you to review, explain, or work on "my code"/"this", inspect the working directory yourself with list_files, glob_search, grep, and read_file; NEVER ask the user to paste or share code that lives in this directory \u2014 open it. ' + "Use tools for filesystem and codebase tasks instead of guessing, and only interact with files under the working directory. " + "Prefer dedicated tools \u2014 glob_search for paths, grep for text search, structured git tools for repository inspection \u2014 and bash only when those are not enough. " + `
|
|
1910
|
+
defaultCodingAgentSystemPrompt = "You are a coding agent working inside the user's project at this working directory: {cwd}. " + 'You can already see the project \u2014 proactively read it. When the user asks you to review, explain, or work on "my code"/"this", inspect the working directory yourself with list_files, glob_search, grep, and read_file; NEVER ask the user to paste or share code that lives in this directory \u2014 open it. ' + "Use tools for filesystem and codebase tasks instead of guessing, and only interact with files under the working directory. " + "Prefer dedicated tools \u2014 glob_search for paths, grep for text search, structured git tools for repository inspection \u2014 and bash only when those are not enough. " + `Inspect context before risky operations. Progress notes: before a major tool action you may emit ONE short note (one sentence, ~15 words) saying what you are about to do. After a tool returns, mention a finding only if it changes your plan, and state it as fact grounded in what the tool actually returned \u2014 cite the file/line/symbol. NEVER claim you found, changed, or fixed something before a tool result confirms it. Skip notes for routine reads, and do not restate the todo list in prose.
|
|
1911
|
+
|
|
1912
|
+
` + `Task list discipline \u2014 use the todo_write tool to keep a visible, evolving checklist:
|
|
1913
|
+
` + `- Create the list FIRST. For any task with roughly three or more distinct steps, or any multi-file or multi-stage work, call todo_write as your very first action \u2014 before reading or editing \u2014 with every step listed and all of them set to pending.
|
|
1914
|
+
` + `- Keep exactly ONE item in_progress at a time. Mark an item in_progress immediately before you start it; never have two items in_progress at once.
|
|
1915
|
+
` + `- Mark an item completed only after a tool result confirms it is actually done (test passed, file written, build clean) \u2014 not on intent. Do this the MOMENT it is confirmed; do not batch completions or wait until the end of the turn.
|
|
1916
|
+
` + `- Completions are monotonic: once a task is completed, do not silently move it back to pending or in_progress. Reopen it only if it genuinely regressed.
|
|
1917
|
+
` + `- Every todo_write call is a FULL REWRITE of the list: always send every task with its current status (re-state the completed and pending ones too \u2014 never send only the changed item). Keep each item terse (a short label, not a paragraph).
|
|
1918
|
+
` + `- Skip the list for trivial, single-step, or purely conversational requests \u2014 a checklist there is just noise.
|
|
1907
1919
|
|
|
1908
1920
|
` + `You are an agent \u2014 keep working until the request is fully resolved. Never stop with a partial answer; if you say you will do something, do it in the same turn by calling a tool. Do not finish while any todo is pending or in_progress \u2014 end your turn only when the task is complete or you are blocked on input that only the user can provide.
|
|
1909
1921
|
|
|
1922
|
+
` + `When the task is complete, end with a short summary (a few lines) grounded in the todos you actually completed and the real file diffs \u2014 name the files you edited, do not list steps you did not verify, and do not repeat the todo card verbatim. If anything is incomplete or you are blocked, say so plainly.
|
|
1923
|
+
|
|
1910
1924
|
` + "Long-running processes (dev servers, watchers): never run them as plain foreground bash \u2014 the bash tool enforces a timeout and will kill them. Start them detached with output redirected to a log " + '(Windows: start /B cmd /c "npm run dev > dev.log 2>&1"; Unix: nohup npm run dev > dev.log 2>&1 &), then read the log to confirm startup and the actual port (do not assume the default). ' + "Never kill processes or free ports you did not open yourself.";
|
|
1911
1925
|
toolCallingDisciplineAddendum = "Invoke tools only through the function-calling interface. " + "Never write tool calls as XML or plain text such as <tool_call>, <function_call>, <invoke>, or bare JSON in your message. " + "Tool arguments must be valid JSON matching the tool's schema. " + "Call one tool at a time and wait for its result before deciding the next step.";
|
|
1912
1926
|
});
|
|
1913
1927
|
|
|
1928
|
+
// packages/ai/src/common/coerce-tool-input.ts
|
|
1929
|
+
function coercePrimitive(current, expected) {
|
|
1930
|
+
if (current === undefined || current === null) {
|
|
1931
|
+
return NO_CHANGE;
|
|
1932
|
+
}
|
|
1933
|
+
switch (expected) {
|
|
1934
|
+
case "number": {
|
|
1935
|
+
if (typeof current === "string") {
|
|
1936
|
+
const trimmed = current.trim();
|
|
1937
|
+
if (trimmed !== "" && Number.isFinite(Number(trimmed))) {
|
|
1938
|
+
return Number(trimmed);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
return NO_CHANGE;
|
|
1942
|
+
}
|
|
1943
|
+
case "boolean": {
|
|
1944
|
+
if (typeof current === "string") {
|
|
1945
|
+
const lowered = current.trim().toLowerCase();
|
|
1946
|
+
if (lowered === "true" || lowered === "1" || lowered === "yes") {
|
|
1947
|
+
return true;
|
|
1948
|
+
}
|
|
1949
|
+
if (lowered === "false" || lowered === "0" || lowered === "no") {
|
|
1950
|
+
return false;
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
if (current === 1)
|
|
1954
|
+
return true;
|
|
1955
|
+
if (current === 0)
|
|
1956
|
+
return false;
|
|
1957
|
+
return NO_CHANGE;
|
|
1958
|
+
}
|
|
1959
|
+
case "string": {
|
|
1960
|
+
if (typeof current === "number" || typeof current === "boolean") {
|
|
1961
|
+
return String(current);
|
|
1962
|
+
}
|
|
1963
|
+
return NO_CHANGE;
|
|
1964
|
+
}
|
|
1965
|
+
case "object":
|
|
1966
|
+
case "array": {
|
|
1967
|
+
if (typeof current === "string") {
|
|
1968
|
+
try {
|
|
1969
|
+
const parsed = JSON.parse(current);
|
|
1970
|
+
const isArray = Array.isArray(parsed);
|
|
1971
|
+
const matches = expected === "array" ? isArray : parsed !== null && typeof parsed === "object" && !isArray;
|
|
1972
|
+
if (matches) {
|
|
1973
|
+
return parsed;
|
|
1974
|
+
}
|
|
1975
|
+
} catch {}
|
|
1976
|
+
}
|
|
1977
|
+
return NO_CHANGE;
|
|
1978
|
+
}
|
|
1979
|
+
default:
|
|
1980
|
+
return NO_CHANGE;
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
function coerceAtPath(root, path3, expected) {
|
|
1984
|
+
if (path3.length === 0 || root === null || typeof root !== "object") {
|
|
1985
|
+
return false;
|
|
1986
|
+
}
|
|
1987
|
+
let parent = root;
|
|
1988
|
+
for (let index = 0;index < path3.length - 1; index += 1) {
|
|
1989
|
+
const next = parent[path3[index]];
|
|
1990
|
+
if (next === null || typeof next !== "object") {
|
|
1991
|
+
return false;
|
|
1992
|
+
}
|
|
1993
|
+
parent = next;
|
|
1994
|
+
}
|
|
1995
|
+
const key = path3[path3.length - 1];
|
|
1996
|
+
const coerced = coercePrimitive(parent[key], expected);
|
|
1997
|
+
if (coerced === NO_CHANGE) {
|
|
1998
|
+
return false;
|
|
1999
|
+
}
|
|
2000
|
+
parent[key] = coerced;
|
|
2001
|
+
return true;
|
|
2002
|
+
}
|
|
2003
|
+
function coerceToolInputToSchema(value, schema) {
|
|
2004
|
+
if (value === null || typeof value !== "object") {
|
|
2005
|
+
return null;
|
|
2006
|
+
}
|
|
2007
|
+
let current;
|
|
2008
|
+
try {
|
|
2009
|
+
current = structuredClone(value);
|
|
2010
|
+
} catch {
|
|
2011
|
+
current = value;
|
|
2012
|
+
}
|
|
2013
|
+
for (let pass = 0;pass < MAX_PASSES; pass += 1) {
|
|
2014
|
+
const result = schema.safeParse(current);
|
|
2015
|
+
if (result.success) {
|
|
2016
|
+
return result.data;
|
|
2017
|
+
}
|
|
2018
|
+
let changed = false;
|
|
2019
|
+
for (const issue of result.error.issues) {
|
|
2020
|
+
if (issue.code !== "invalid_type") {
|
|
2021
|
+
continue;
|
|
2022
|
+
}
|
|
2023
|
+
const expected = issue.expected;
|
|
2024
|
+
if (typeof expected !== "string") {
|
|
2025
|
+
continue;
|
|
2026
|
+
}
|
|
2027
|
+
if (coerceAtPath(current, issue.path, expected)) {
|
|
2028
|
+
changed = true;
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
if (!changed) {
|
|
2032
|
+
break;
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
const final = schema.safeParse(current);
|
|
2036
|
+
return final.success ? final.data : null;
|
|
2037
|
+
}
|
|
2038
|
+
var NO_CHANGE, MAX_PASSES = 4;
|
|
2039
|
+
var init_coerce_tool_input = __esm(() => {
|
|
2040
|
+
NO_CHANGE = Symbol("no-change");
|
|
2041
|
+
});
|
|
2042
|
+
|
|
1914
2043
|
// packages/ai/src/providers/tool-call-json-repair.ts
|
|
1915
2044
|
import { parsePartialJson } from "ai";
|
|
1916
2045
|
async function repairToolJson(raw) {
|
|
@@ -2105,15 +2234,19 @@ async function repairCodingAgentToolCall({
|
|
|
2105
2234
|
if (!repairedInput || typeof repairedInput !== "object") {
|
|
2106
2235
|
return null;
|
|
2107
2236
|
}
|
|
2237
|
+
const schema = codingToolProviderInputSchemas[toolCall.toolName];
|
|
2238
|
+
const coerced = schema ? coerceToolInputToSchema(repairedInput, schema) : null;
|
|
2108
2239
|
return {
|
|
2109
2240
|
...toolCall,
|
|
2110
|
-
input: JSON.stringify(repairedInput)
|
|
2241
|
+
input: JSON.stringify(coerced ?? repairedInput)
|
|
2111
2242
|
};
|
|
2112
2243
|
}
|
|
2113
2244
|
return null;
|
|
2114
2245
|
}
|
|
2115
2246
|
var toolNameAliases;
|
|
2116
2247
|
var init_coding_agent_tool_repair = __esm(() => {
|
|
2248
|
+
init_agent_tools();
|
|
2249
|
+
init_coerce_tool_input();
|
|
2117
2250
|
init_tool_call_json_repair();
|
|
2118
2251
|
toolNameAliases = {
|
|
2119
2252
|
read: "read_file",
|
|
@@ -2202,19 +2335,6 @@ function getLastUserText(messages, prompt) {
|
|
|
2202
2335
|
}
|
|
2203
2336
|
return collectTextFromUnknown(prompt).trim();
|
|
2204
2337
|
}
|
|
2205
|
-
function pushUniqueTool(tools, toolName) {
|
|
2206
|
-
if (!tools.includes(toolName)) {
|
|
2207
|
-
tools.push(toolName);
|
|
2208
|
-
}
|
|
2209
|
-
}
|
|
2210
|
-
function includesAny(text, tokens) {
|
|
2211
|
-
return tokens.some((token) => {
|
|
2212
|
-
if (token.includes("://") || token.includes(" ")) {
|
|
2213
|
-
return text.includes(token);
|
|
2214
|
-
}
|
|
2215
|
-
return new RegExp(`(^|[^a-z0-9])${token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}([^a-z0-9]|$)`).test(text);
|
|
2216
|
-
});
|
|
2217
|
-
}
|
|
2218
2338
|
function selectCodingAgentIntentTools({
|
|
2219
2339
|
mode,
|
|
2220
2340
|
prompt,
|
|
@@ -2225,225 +2345,7 @@ function selectCodingAgentIntentTools({
|
|
|
2225
2345
|
if (!normalizedText || casualPromptPattern.test(normalizedText)) {
|
|
2226
2346
|
return [];
|
|
2227
2347
|
}
|
|
2228
|
-
|
|
2229
|
-
const hasContinuationIntent = includesAny(normalizedText, [
|
|
2230
|
-
"continue",
|
|
2231
|
-
"proceed",
|
|
2232
|
-
"go on",
|
|
2233
|
-
"go ahead",
|
|
2234
|
-
"keep going",
|
|
2235
|
-
"carry on",
|
|
2236
|
-
"resume",
|
|
2237
|
-
"finish",
|
|
2238
|
-
"next step"
|
|
2239
|
-
]);
|
|
2240
|
-
const hasCodeIntent = includesAny(normalizedText, [
|
|
2241
|
-
"file",
|
|
2242
|
-
"folder",
|
|
2243
|
-
"code",
|
|
2244
|
-
"repo",
|
|
2245
|
-
"project",
|
|
2246
|
-
"implement",
|
|
2247
|
-
"fix",
|
|
2248
|
-
"add",
|
|
2249
|
-
"build",
|
|
2250
|
-
"refactor",
|
|
2251
|
-
"change",
|
|
2252
|
-
"create",
|
|
2253
|
-
"update",
|
|
2254
|
-
"edit",
|
|
2255
|
-
"delete",
|
|
2256
|
-
"run",
|
|
2257
|
-
"test",
|
|
2258
|
-
"typecheck",
|
|
2259
|
-
"debug",
|
|
2260
|
-
"error",
|
|
2261
|
-
"issue",
|
|
2262
|
-
"problem",
|
|
2263
|
-
"fails",
|
|
2264
|
-
"failed",
|
|
2265
|
-
"failing",
|
|
2266
|
-
"bug",
|
|
2267
|
-
"crash",
|
|
2268
|
-
"slow",
|
|
2269
|
-
"slowness",
|
|
2270
|
-
"not working",
|
|
2271
|
-
"epic",
|
|
2272
|
-
"ticket"
|
|
2273
|
-
]);
|
|
2274
|
-
const hasReadIntent = includesAny(normalizedText, [
|
|
2275
|
-
"read",
|
|
2276
|
-
"list",
|
|
2277
|
-
"find",
|
|
2278
|
-
"search",
|
|
2279
|
-
"grep",
|
|
2280
|
-
"glob",
|
|
2281
|
-
"inspect",
|
|
2282
|
-
"analyse",
|
|
2283
|
-
"analyze",
|
|
2284
|
-
"check"
|
|
2285
|
-
]);
|
|
2286
|
-
const workspaceReferents = [
|
|
2287
|
-
"this",
|
|
2288
|
-
"these",
|
|
2289
|
-
"that",
|
|
2290
|
-
"here",
|
|
2291
|
-
"code",
|
|
2292
|
-
"codebase",
|
|
2293
|
-
"repo",
|
|
2294
|
-
"repository",
|
|
2295
|
-
"project",
|
|
2296
|
-
"file",
|
|
2297
|
-
"files",
|
|
2298
|
-
"directory",
|
|
2299
|
-
"folder",
|
|
2300
|
-
"app",
|
|
2301
|
-
"application",
|
|
2302
|
-
"module",
|
|
2303
|
-
"my"
|
|
2304
|
-
];
|
|
2305
|
-
const strongReviewIntent = includesAny(normalizedText, [
|
|
2306
|
-
"review",
|
|
2307
|
-
"audit",
|
|
2308
|
-
"walk through",
|
|
2309
|
-
"walkthrough",
|
|
2310
|
-
"code review"
|
|
2311
|
-
]);
|
|
2312
|
-
const softReviewIntent = includesAny(normalizedText, [
|
|
2313
|
-
"explain",
|
|
2314
|
-
"understand",
|
|
2315
|
-
"summarize",
|
|
2316
|
-
"summarise",
|
|
2317
|
-
"overview",
|
|
2318
|
-
"describe",
|
|
2319
|
-
"explore",
|
|
2320
|
-
"look at"
|
|
2321
|
-
]) && includesAny(normalizedText, workspaceReferents);
|
|
2322
|
-
const hasReviewIntent = strongReviewIntent || softReviewIntent;
|
|
2323
|
-
const hasWriteIntent = mode === "build" && (hasContinuationIntent || includesAny(normalizedText, [
|
|
2324
|
-
"write",
|
|
2325
|
-
"edit",
|
|
2326
|
-
"change",
|
|
2327
|
-
"create",
|
|
2328
|
-
"implement",
|
|
2329
|
-
"fix",
|
|
2330
|
-
"update",
|
|
2331
|
-
"delete",
|
|
2332
|
-
"remove",
|
|
2333
|
-
"rename"
|
|
2334
|
-
]));
|
|
2335
|
-
const hasShellIntent = mode === "build" && (hasContinuationIntent || includesAny(normalizedText, [
|
|
2336
|
-
"run",
|
|
2337
|
-
"command",
|
|
2338
|
-
"terminal",
|
|
2339
|
-
"shell",
|
|
2340
|
-
"bash",
|
|
2341
|
-
"powershell",
|
|
2342
|
-
"bun",
|
|
2343
|
-
"tests",
|
|
2344
|
-
"run test",
|
|
2345
|
-
"run tests",
|
|
2346
|
-
"typecheck"
|
|
2347
|
-
]));
|
|
2348
|
-
const hasGitIntent = includesAny(normalizedText, [
|
|
2349
|
-
"git",
|
|
2350
|
-
"diff",
|
|
2351
|
-
"status",
|
|
2352
|
-
"commit",
|
|
2353
|
-
"log",
|
|
2354
|
-
"show",
|
|
2355
|
-
"revision",
|
|
2356
|
-
"changes"
|
|
2357
|
-
]);
|
|
2358
|
-
const hasTodoIntent = includesAny(normalizedText, [
|
|
2359
|
-
"todo",
|
|
2360
|
-
"tasks",
|
|
2361
|
-
"checklist",
|
|
2362
|
-
"steps",
|
|
2363
|
-
"epic",
|
|
2364
|
-
"ticket"
|
|
2365
|
-
]);
|
|
2366
|
-
const hasWebIntent = includesAny(normalizedText, [
|
|
2367
|
-
"http://",
|
|
2368
|
-
"https://",
|
|
2369
|
-
"url",
|
|
2370
|
-
"fetch",
|
|
2371
|
-
"website",
|
|
2372
|
-
"web",
|
|
2373
|
-
"online",
|
|
2374
|
-
"search internet",
|
|
2375
|
-
"latest"
|
|
2376
|
-
]);
|
|
2377
|
-
const explicitToolSearch = includesAny(normalizedText, [
|
|
2378
|
-
"tool_search",
|
|
2379
|
-
"tool search",
|
|
2380
|
-
"find tool",
|
|
2381
|
-
"available tool"
|
|
2382
|
-
]);
|
|
2383
|
-
const hasSkillIntent = includesAny(normalizedText, [
|
|
2384
|
-
"skill",
|
|
2385
|
-
"skills",
|
|
2386
|
-
"load skill"
|
|
2387
|
-
]);
|
|
2388
|
-
const hasMcpIntent = includesAny(normalizedText, [
|
|
2389
|
-
"mcp",
|
|
2390
|
-
"resource",
|
|
2391
|
-
"resources",
|
|
2392
|
-
"mcp tool",
|
|
2393
|
-
"mcp server"
|
|
2394
|
-
]);
|
|
2395
|
-
if (hasCodeIntent || hasContinuationIntent || hasReviewIntent || hasReadIntent && !hasGitIntent && !hasWebIntent && !explicitToolSearch) {
|
|
2396
|
-
for (const toolName of baseReadTools) {
|
|
2397
|
-
pushUniqueTool(selectedTools, toolName);
|
|
2398
|
-
}
|
|
2399
|
-
}
|
|
2400
|
-
if (hasGitIntent) {
|
|
2401
|
-
if (includesAny(normalizedText, ["status", "state", "dirty", "working tree"]) || !includesAny(normalizedText, ["diff", "log", "show", "history", "commit"])) {
|
|
2402
|
-
pushUniqueTool(selectedTools, "git_status");
|
|
2403
|
-
}
|
|
2404
|
-
if (includesAny(normalizedText, ["diff", "changes", "patch"])) {
|
|
2405
|
-
pushUniqueTool(selectedTools, "git_diff");
|
|
2406
|
-
}
|
|
2407
|
-
if (includesAny(normalizedText, ["log", "history", "commits"])) {
|
|
2408
|
-
pushUniqueTool(selectedTools, "git_log");
|
|
2409
|
-
}
|
|
2410
|
-
if (includesAny(normalizedText, ["show", "revision", "commit"])) {
|
|
2411
|
-
pushUniqueTool(selectedTools, "git_show");
|
|
2412
|
-
}
|
|
2413
|
-
}
|
|
2414
|
-
if (hasTodoIntent && mode === "build") {
|
|
2415
|
-
pushUniqueTool(selectedTools, "todo_write");
|
|
2416
|
-
}
|
|
2417
|
-
if (explicitToolSearch) {
|
|
2418
|
-
pushUniqueTool(selectedTools, "tool_search");
|
|
2419
|
-
}
|
|
2420
|
-
if (hasSkillIntent) {
|
|
2421
|
-
pushUniqueTool(selectedTools, "skill");
|
|
2422
|
-
}
|
|
2423
|
-
if (hasMcpIntent) {
|
|
2424
|
-
pushUniqueTool(selectedTools, "list_mcp_resources");
|
|
2425
|
-
if (includesAny(normalizedText, ["read", "resource", "resources"])) {
|
|
2426
|
-
pushUniqueTool(selectedTools, "read_mcp_resource");
|
|
2427
|
-
}
|
|
2428
|
-
if (mode === "build" && includesAny(normalizedText, ["call", "execute", "run"])) {
|
|
2429
|
-
pushUniqueTool(selectedTools, "call_mcp_tool");
|
|
2430
|
-
}
|
|
2431
|
-
}
|
|
2432
|
-
if (hasWriteIntent) {
|
|
2433
|
-
pushUniqueTool(selectedTools, "write_file");
|
|
2434
|
-
pushUniqueTool(selectedTools, "edit_file");
|
|
2435
|
-
}
|
|
2436
|
-
if (hasShellIntent) {
|
|
2437
|
-
pushUniqueTool(selectedTools, "bash");
|
|
2438
|
-
}
|
|
2439
|
-
if (hasWebIntent) {
|
|
2440
|
-
pushUniqueTool(selectedTools, "web_fetch");
|
|
2441
|
-
pushUniqueTool(selectedTools, "web_search");
|
|
2442
|
-
}
|
|
2443
|
-
if (mode === "plan" && includesAny(normalizedText, ["question", "ask", "clarify"])) {
|
|
2444
|
-
pushUniqueTool(selectedTools, "request_user_input");
|
|
2445
|
-
}
|
|
2446
|
-
return selectedTools;
|
|
2348
|
+
return [...getCodingAgentModeDefinition(mode).activeTools];
|
|
2447
2349
|
}
|
|
2448
2350
|
function limitProviderActiveTools(tools, maxTools = maxProviderActiveTools) {
|
|
2449
2351
|
if (tools.length <= maxTools) {
|
|
@@ -2452,15 +2354,10 @@ function limitProviderActiveTools(tools, maxTools = maxProviderActiveTools) {
|
|
|
2452
2354
|
const toolSet = new Set(tools);
|
|
2453
2355
|
return providerToolPriority.filter((toolName) => toolSet.has(toolName)).slice(0, maxTools);
|
|
2454
2356
|
}
|
|
2455
|
-
var casualPromptPattern,
|
|
2357
|
+
var casualPromptPattern, maxProviderActiveTools = 24, providerToolPriority;
|
|
2456
2358
|
var init_intent_tool_selection = __esm(() => {
|
|
2359
|
+
init_coding_agent_modes();
|
|
2457
2360
|
casualPromptPattern = /^(hi|hello|hey|how are you|how r you|what'?s up|whats up|sup|thanks|thank you|ok|okay|yes|no|nice|cool|great)[\s?!.,]*$/i;
|
|
2458
|
-
baseReadTools = [
|
|
2459
|
-
"list_files",
|
|
2460
|
-
"glob_search",
|
|
2461
|
-
"read_file",
|
|
2462
|
-
"grep"
|
|
2463
|
-
];
|
|
2464
2361
|
providerToolPriority = [
|
|
2465
2362
|
"list_files",
|
|
2466
2363
|
"glob_search",
|
|
@@ -2714,17 +2611,33 @@ function normalizeContextOptimizerConfig(config) {
|
|
|
2714
2611
|
}
|
|
2715
2612
|
function resolveContextWindowTokens({
|
|
2716
2613
|
config,
|
|
2717
|
-
modelContextWindow
|
|
2614
|
+
modelContextWindow,
|
|
2615
|
+
endpointLimitTokens
|
|
2616
|
+
}) {
|
|
2617
|
+
const resolved = config.contextWindowOverride ?? modelContextWindow ?? DEFAULT_CONTEXT_WINDOW_TOKENS;
|
|
2618
|
+
if (typeof endpointLimitTokens === "number" && Number.isFinite(endpointLimitTokens) && endpointLimitTokens > 0) {
|
|
2619
|
+
return Math.min(resolved, endpointLimitTokens);
|
|
2620
|
+
}
|
|
2621
|
+
return resolved;
|
|
2622
|
+
}
|
|
2623
|
+
function resolveInputBudgetTokens({
|
|
2624
|
+
contextWindow,
|
|
2625
|
+
reservedOutputTokens,
|
|
2626
|
+
safetyFraction = DEFAULT_INPUT_BUDGET_SAFETY_FRACTION
|
|
2718
2627
|
}) {
|
|
2719
|
-
|
|
2628
|
+
const reserved = typeof reservedOutputTokens === "number" && Number.isFinite(reservedOutputTokens) && reservedOutputTokens > 0 ? reservedOutputTokens : 0;
|
|
2629
|
+
const clampedFraction = Math.min(1, Math.max(0.1, safetyFraction));
|
|
2630
|
+
const usable = Math.max(0, contextWindow - reserved);
|
|
2631
|
+
return Math.max(0, Math.floor(usable * clampedFraction));
|
|
2720
2632
|
}
|
|
2721
|
-
var DEFAULT_CONTEXT_WINDOW_TOKENS = 128000, contextOptimizerConfigSchema, resolvedContextOptimizerConfigSchema, defaultContextOptimizerConfig;
|
|
2633
|
+
var DEFAULT_CONTEXT_WINDOW_TOKENS = 128000, contextOptimizerConfigSchema, resolvedContextOptimizerConfigSchema, defaultContextOptimizerConfig, DEFAULT_INPUT_BUDGET_SAFETY_FRACTION = 0.92;
|
|
2722
2634
|
var init_config3 = __esm(() => {
|
|
2723
2635
|
contextOptimizerConfigSchema = z26.object({
|
|
2724
2636
|
autoCompact: z26.boolean().optional(),
|
|
2725
2637
|
compactAtFraction: z26.number().min(0.3).max(0.95).optional(),
|
|
2726
2638
|
pruneAtFraction: z26.number().min(0.2).max(0.9).optional(),
|
|
2727
2639
|
preserveRecentMessages: z26.number().int().min(2).max(100).optional(),
|
|
2640
|
+
maxCoverageTokensPerCompaction: z26.number().int().min(2000).max(200000).optional(),
|
|
2728
2641
|
contextWindowOverride: z26.number().int().min(8000).max(1e7).optional(),
|
|
2729
2642
|
summaryMaxChars: z26.number().int().min(200).max(50000).optional(),
|
|
2730
2643
|
aggressivePruneWhenUncached: z26.boolean().optional(),
|
|
@@ -2738,6 +2651,7 @@ var init_config3 = __esm(() => {
|
|
|
2738
2651
|
compactAtFraction: z26.number().min(0.3).max(0.95),
|
|
2739
2652
|
pruneAtFraction: z26.number().min(0.2).max(0.9),
|
|
2740
2653
|
preserveRecentMessages: z26.number().int().min(2).max(100),
|
|
2654
|
+
maxCoverageTokensPerCompaction: z26.number().int().min(2000).max(200000),
|
|
2741
2655
|
contextWindowOverride: z26.number().int().min(8000).max(1e7).nullable(),
|
|
2742
2656
|
summaryMaxChars: z26.number().int().min(200).max(50000),
|
|
2743
2657
|
aggressivePruneWhenUncached: z26.boolean(),
|
|
@@ -2750,6 +2664,7 @@ var init_config3 = __esm(() => {
|
|
|
2750
2664
|
compactAtFraction: 0.7,
|
|
2751
2665
|
pruneAtFraction: 0.6,
|
|
2752
2666
|
preserveRecentMessages: 6,
|
|
2667
|
+
maxCoverageTokensPerCompaction: 12000,
|
|
2753
2668
|
contextWindowOverride: null,
|
|
2754
2669
|
summaryMaxChars: 4000,
|
|
2755
2670
|
aggressivePruneWhenUncached: true,
|
|
@@ -2829,10 +2744,44 @@ var init_estimate = __esm(() => {
|
|
|
2829
2744
|
totalTokens: z27.number().nonnegative().optional()
|
|
2830
2745
|
}),
|
|
2831
2746
|
modelId: z27.string().optional(),
|
|
2832
|
-
finishReason: languageModelFinishReasonSchema.optional()
|
|
2747
|
+
finishReason: languageModelFinishReasonSchema.optional(),
|
|
2748
|
+
context: z27.object({
|
|
2749
|
+
inputTokens: z27.number().nonnegative(),
|
|
2750
|
+
contextWindow: z27.number().positive(),
|
|
2751
|
+
compactedMessages: z27.number().nonnegative().optional()
|
|
2752
|
+
}).optional()
|
|
2833
2753
|
}).loose();
|
|
2834
2754
|
});
|
|
2835
2755
|
|
|
2756
|
+
// packages/ai/src/common/output-utils.ts
|
|
2757
|
+
function omissionMarker(omitted) {
|
|
2758
|
+
return `
|
|
2759
|
+
|
|
2760
|
+
... [${omitted} characters omitted; re-read a line range with read_file or raise maxChars] ...
|
|
2761
|
+
|
|
2762
|
+
`;
|
|
2763
|
+
}
|
|
2764
|
+
function truncateText(text, maxChars = DEFAULT_TOOL_TEXT_OUTPUT_CHARS) {
|
|
2765
|
+
if (text.length <= maxChars) {
|
|
2766
|
+
return {
|
|
2767
|
+
text,
|
|
2768
|
+
truncated: false
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2771
|
+
const markerReserve = omissionMarker(text.length).length;
|
|
2772
|
+
const usable = Math.max(0, maxChars - markerReserve);
|
|
2773
|
+
const headChars = Math.floor(usable * 0.7);
|
|
2774
|
+
const tailChars = usable - headChars;
|
|
2775
|
+
const head = text.slice(0, headChars);
|
|
2776
|
+
const tail = tailChars > 0 ? text.slice(text.length - tailChars) : "";
|
|
2777
|
+
const omitted = text.length - head.length - tail.length;
|
|
2778
|
+
return {
|
|
2779
|
+
text: `${head}${omissionMarker(omitted)}${tail}`,
|
|
2780
|
+
truncated: true
|
|
2781
|
+
};
|
|
2782
|
+
}
|
|
2783
|
+
var init_output_utils = () => {};
|
|
2784
|
+
|
|
2836
2785
|
// packages/ai/src/context/heuristic-summary.ts
|
|
2837
2786
|
function truncateInline(text, maxChars) {
|
|
2838
2787
|
const normalized = text.replace(/\s+/g, " ").trim();
|
|
@@ -3081,6 +3030,16 @@ function getPartState(part) {
|
|
|
3081
3030
|
const state = isRecord3(part) ? Reflect.get(part, "state") : undefined;
|
|
3082
3031
|
return typeof state === "string" ? state : null;
|
|
3083
3032
|
}
|
|
3033
|
+
function getPlainTextPartContent(part) {
|
|
3034
|
+
if (!isRecord3(part) || Reflect.get(part, "type") !== "text") {
|
|
3035
|
+
return null;
|
|
3036
|
+
}
|
|
3037
|
+
const text = Reflect.get(part, "text");
|
|
3038
|
+
return typeof text === "string" ? text : null;
|
|
3039
|
+
}
|
|
3040
|
+
function messageHasToolPart(message) {
|
|
3041
|
+
return message.parts.some((part) => getToolNameFromPart(part) !== null);
|
|
3042
|
+
}
|
|
3084
3043
|
function computePruneCutoffIndex(messages, preserveRecentMessages, quantizeUserTurns = DEFAULT_PRUNE_QUANTIZE_USER_TURNS) {
|
|
3085
3044
|
const candidate = messages.length - preserveRecentMessages;
|
|
3086
3045
|
if (candidate <= 0) {
|
|
@@ -3130,15 +3089,29 @@ function pruneToolOutputs(messages, options) {
|
|
|
3130
3089
|
});
|
|
3131
3090
|
let elidedToolOutputs = 0;
|
|
3132
3091
|
let dedupedFileReads = 0;
|
|
3092
|
+
let elidedProgressNotes = 0;
|
|
3133
3093
|
let savedChars = 0;
|
|
3134
3094
|
const prunedMessages = messages.map((message, messageIndex) => {
|
|
3135
3095
|
if (messageIndex >= cutoff) {
|
|
3136
3096
|
return message;
|
|
3137
3097
|
}
|
|
3098
|
+
const noteCandidate = message.role === "assistant" && messageHasToolPart(message);
|
|
3138
3099
|
let changed = false;
|
|
3139
3100
|
const parts = message.parts.map((part, partIndex) => {
|
|
3140
3101
|
const toolName = getToolNameFromPart(part);
|
|
3141
3102
|
if (!toolName || getPartState(part) !== "output-available") {
|
|
3103
|
+
if (noteCandidate && toolName === null) {
|
|
3104
|
+
const text = getPlainTextPartContent(part);
|
|
3105
|
+
if (text !== null && text !== PROGRESS_NOTE_ELISION_STUB && text.length > PROGRESS_NOTE_PRUNE_MIN_CHARS) {
|
|
3106
|
+
elidedProgressNotes += 1;
|
|
3107
|
+
savedChars += Math.max(0, text.length - PROGRESS_NOTE_ELISION_STUB.length);
|
|
3108
|
+
changed = true;
|
|
3109
|
+
return {
|
|
3110
|
+
...part,
|
|
3111
|
+
text: PROGRESS_NOTE_ELISION_STUB
|
|
3112
|
+
};
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3142
3115
|
return part;
|
|
3143
3116
|
}
|
|
3144
3117
|
const output = isRecord3(part) ? Reflect.get(part, "output") : undefined;
|
|
@@ -3155,7 +3128,7 @@ function pruneToolOutputs(messages, options) {
|
|
|
3155
3128
|
}
|
|
3156
3129
|
const stub = {
|
|
3157
3130
|
lightcodeElided: true,
|
|
3158
|
-
note: isStaleRead ? "Earlier read of this file elided;
|
|
3131
|
+
note: isStaleRead ? "Earlier read of this file elided; its current content is already shown later in this conversation \u2014 do not read it again." : "Large tool output elided to preserve context budget. Re-run the tool only if this output is needed again.",
|
|
3159
3132
|
originalChars: serializedOutput.length
|
|
3160
3133
|
};
|
|
3161
3134
|
if (isStaleRead) {
|
|
@@ -3173,15 +3146,137 @@ function pruneToolOutputs(messages, options) {
|
|
|
3173
3146
|
messages: prunedMessages,
|
|
3174
3147
|
elidedToolOutputs,
|
|
3175
3148
|
dedupedFileReads,
|
|
3149
|
+
elidedProgressNotes,
|
|
3176
3150
|
savedChars
|
|
3177
3151
|
};
|
|
3178
3152
|
}
|
|
3179
|
-
var DEFAULT_PRUNE_MIN_OUTPUT_CHARS = 2000, DEFAULT_PRUNE_QUANTIZE_USER_TURNS = 4;
|
|
3153
|
+
var DEFAULT_PRUNE_MIN_OUTPUT_CHARS = 2000, PROGRESS_NOTE_ELISION_STUB = "[progress note elided]", DEFAULT_PRUNE_QUANTIZE_USER_TURNS = 4;
|
|
3180
3154
|
var init_tier1_prune = __esm(() => {
|
|
3181
3155
|
init_estimate();
|
|
3182
3156
|
init_message_parts();
|
|
3183
3157
|
});
|
|
3184
3158
|
|
|
3159
|
+
// packages/ai/src/context/fit-to-budget.ts
|
|
3160
|
+
function estimateStructuralTokens(messages) {
|
|
3161
|
+
return messages.reduce((total, message) => total + estimateMessageTokens(message), 0);
|
|
3162
|
+
}
|
|
3163
|
+
function getPartState2(part) {
|
|
3164
|
+
const state = isRecord3(part) ? Reflect.get(part, "state") : undefined;
|
|
3165
|
+
return typeof state === "string" ? state : null;
|
|
3166
|
+
}
|
|
3167
|
+
function elideOutput(part, originalChars) {
|
|
3168
|
+
const stub = {
|
|
3169
|
+
lightcodeElided: true,
|
|
3170
|
+
note: "Tool output elided to fit the model's context budget. Re-run the tool if this output is needed again.",
|
|
3171
|
+
originalChars
|
|
3172
|
+
};
|
|
3173
|
+
return { ...part, output: stub };
|
|
3174
|
+
}
|
|
3175
|
+
function elideToolOutputsInMessage(message) {
|
|
3176
|
+
let elided = 0;
|
|
3177
|
+
const parts = message.parts.map((part) => {
|
|
3178
|
+
if (!getToolNameFromPart(part) || getPartState2(part) !== "output-available") {
|
|
3179
|
+
return part;
|
|
3180
|
+
}
|
|
3181
|
+
const output = isRecord3(part) ? Reflect.get(part, "output") : undefined;
|
|
3182
|
+
if (output === undefined || isElidedToolOutput(output)) {
|
|
3183
|
+
return part;
|
|
3184
|
+
}
|
|
3185
|
+
elided += 1;
|
|
3186
|
+
return elideOutput(part, safeStringify(output).length);
|
|
3187
|
+
});
|
|
3188
|
+
return elided > 0 ? { message: { ...message, parts }, elided } : { message, elided: 0 };
|
|
3189
|
+
}
|
|
3190
|
+
function truncateTextPartsInMessage(message, capChars) {
|
|
3191
|
+
let truncated = 0;
|
|
3192
|
+
const parts = message.parts.map((part) => {
|
|
3193
|
+
if (!isRecord3(part)) {
|
|
3194
|
+
return part;
|
|
3195
|
+
}
|
|
3196
|
+
const text = Reflect.get(part, "text");
|
|
3197
|
+
if (typeof text !== "string" || text.length <= capChars) {
|
|
3198
|
+
return part;
|
|
3199
|
+
}
|
|
3200
|
+
const result = truncateText(text, capChars);
|
|
3201
|
+
if (!result.truncated) {
|
|
3202
|
+
return part;
|
|
3203
|
+
}
|
|
3204
|
+
truncated += 1;
|
|
3205
|
+
return { ...part, text: result.text };
|
|
3206
|
+
});
|
|
3207
|
+
return truncated > 0 ? { message: { ...message, parts }, truncated } : { message, truncated: 0 };
|
|
3208
|
+
}
|
|
3209
|
+
function fitMessagesToBudget(messages, options) {
|
|
3210
|
+
const budget = Math.max(0, Math.floor(options.inputBudgetTokens));
|
|
3211
|
+
const preserveRecentMessages = Math.max(0, options.preserveRecentMessages);
|
|
3212
|
+
let working = [...messages];
|
|
3213
|
+
let elidedToolOutputs = 0;
|
|
3214
|
+
let droppedMessages = 0;
|
|
3215
|
+
let truncatedTextParts = 0;
|
|
3216
|
+
const fits = () => estimateStructuralTokens(working) <= budget;
|
|
3217
|
+
if (fits()) {
|
|
3218
|
+
return {
|
|
3219
|
+
messages: working,
|
|
3220
|
+
elidedToolOutputs: 0,
|
|
3221
|
+
droppedMessages: 0,
|
|
3222
|
+
truncatedTextParts: 0,
|
|
3223
|
+
estimatedTokens: estimateStructuralTokens(working),
|
|
3224
|
+
fitted: false,
|
|
3225
|
+
withinBudget: true
|
|
3226
|
+
};
|
|
3227
|
+
}
|
|
3228
|
+
const preserveFrom = Math.max(0, working.length - preserveRecentMessages);
|
|
3229
|
+
for (let index = 0;index < preserveFrom && !fits(); index += 1) {
|
|
3230
|
+
const { message, elided } = elideToolOutputsInMessage(working[index]);
|
|
3231
|
+
if (elided > 0) {
|
|
3232
|
+
working[index] = message;
|
|
3233
|
+
elidedToolOutputs += elided;
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
const firstIsSummary = isSummaryMessage(working[0]);
|
|
3237
|
+
let dropFrom = firstIsSummary ? 1 : 0;
|
|
3238
|
+
while (!fits() && working.length - dropFrom > preserveRecentMessages && dropFrom < working.length) {
|
|
3239
|
+
working.splice(dropFrom, 1);
|
|
3240
|
+
droppedMessages += 1;
|
|
3241
|
+
}
|
|
3242
|
+
if (!fits()) {
|
|
3243
|
+
working = working.map((message) => {
|
|
3244
|
+
const { message: elidedMessage, elided } = elideToolOutputsInMessage(message);
|
|
3245
|
+
elidedToolOutputs += elided;
|
|
3246
|
+
return elidedMessage;
|
|
3247
|
+
});
|
|
3248
|
+
let cap = MAX_TOOL_TEXT_OUTPUT_CHARS;
|
|
3249
|
+
while (!fits() && cap >= MIN_TRUNCATION_CAP_CHARS) {
|
|
3250
|
+
working = working.map((message) => {
|
|
3251
|
+
const { message: truncatedMessage, truncated } = truncateTextPartsInMessage(message, cap);
|
|
3252
|
+
truncatedTextParts += truncated;
|
|
3253
|
+
return truncatedMessage;
|
|
3254
|
+
});
|
|
3255
|
+
cap = Math.floor(cap / 2);
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
return {
|
|
3259
|
+
messages: working,
|
|
3260
|
+
elidedToolOutputs,
|
|
3261
|
+
droppedMessages,
|
|
3262
|
+
truncatedTextParts,
|
|
3263
|
+
estimatedTokens: estimateStructuralTokens(working),
|
|
3264
|
+
fitted: elidedToolOutputs > 0 || droppedMessages > 0 || truncatedTextParts > 0,
|
|
3265
|
+
withinBudget: fits()
|
|
3266
|
+
};
|
|
3267
|
+
}
|
|
3268
|
+
function isSummaryMessage(message) {
|
|
3269
|
+
return message?.id === contextSummaryMessageId;
|
|
3270
|
+
}
|
|
3271
|
+
var MIN_TRUNCATION_CAP_CHARS = 256;
|
|
3272
|
+
var init_fit_to_budget = __esm(() => {
|
|
3273
|
+
init_output_utils();
|
|
3274
|
+
init_estimate();
|
|
3275
|
+
init_heuristic_summary();
|
|
3276
|
+
init_message_parts();
|
|
3277
|
+
init_tier1_prune();
|
|
3278
|
+
});
|
|
3279
|
+
|
|
3185
3280
|
// packages/ai/src/context/build-provider-view.ts
|
|
3186
3281
|
function computeCompactionCutoffIndex(messages, preserveRecentMessages, afterAnchorIndex) {
|
|
3187
3282
|
const candidate = messages.length - preserveRecentMessages;
|
|
@@ -3195,11 +3290,32 @@ function computeCompactionCutoffIndex(messages, preserveRecentMessages, afterAnc
|
|
|
3195
3290
|
}
|
|
3196
3291
|
return afterAnchorIndex;
|
|
3197
3292
|
}
|
|
3293
|
+
function capCoverageToTokenBudget(coveredMessages, maxTokens) {
|
|
3294
|
+
if (coveredMessages.length <= 1) {
|
|
3295
|
+
return [...coveredMessages];
|
|
3296
|
+
}
|
|
3297
|
+
let total = 0;
|
|
3298
|
+
const capped = [];
|
|
3299
|
+
for (const message of coveredMessages) {
|
|
3300
|
+
const messageTokens = estimateStructuralTokens([message]);
|
|
3301
|
+
if (capped.length > 0 && total + messageTokens > maxTokens) {
|
|
3302
|
+
break;
|
|
3303
|
+
}
|
|
3304
|
+
capped.push(message);
|
|
3305
|
+
total += messageTokens;
|
|
3306
|
+
}
|
|
3307
|
+
return capped;
|
|
3308
|
+
}
|
|
3198
3309
|
function buildProviderView(options) {
|
|
3199
3310
|
const { messages, contextState, config } = options;
|
|
3200
3311
|
const contextWindow = resolveContextWindowTokens({
|
|
3201
3312
|
config,
|
|
3202
|
-
modelContextWindow: options.modelContextWindow
|
|
3313
|
+
modelContextWindow: options.modelContextWindow,
|
|
3314
|
+
endpointLimitTokens: options.endpointLimitTokens
|
|
3315
|
+
});
|
|
3316
|
+
const inputBudgetTokens = resolveInputBudgetTokens({
|
|
3317
|
+
contextWindow,
|
|
3318
|
+
reservedOutputTokens: options.reservedOutputTokens
|
|
3203
3319
|
});
|
|
3204
3320
|
let anchorResolved = false;
|
|
3205
3321
|
let afterAnchorIndex = 0;
|
|
@@ -3223,7 +3339,8 @@ function buildProviderView(options) {
|
|
|
3223
3339
|
let tier1 = null;
|
|
3224
3340
|
const uncachedActive = options.cacheActive === false && config.aggressivePruneWhenUncached;
|
|
3225
3341
|
const pruneFraction = uncachedActive ? config.uncachedPruneAtFraction : config.pruneAtFraction;
|
|
3226
|
-
|
|
3342
|
+
const triggerTokens = (currentEstimateTokens) => Math.max(currentEstimateTokens, estimateStructuralTokens(providerMessages), typeof options.measuredInputTokens === "number" ? options.measuredInputTokens : 0);
|
|
3343
|
+
if (triggerTokens(estimate.tokens) > pruneFraction * inputBudgetTokens) {
|
|
3227
3344
|
tier1 = pruneToolOutputs(providerMessages, {
|
|
3228
3345
|
preserveRecentMessages: config.preserveRecentMessages,
|
|
3229
3346
|
minOutputChars: uncachedActive ? config.uncachedPruneMinOutputChars : undefined,
|
|
@@ -3238,15 +3355,15 @@ function buildProviderView(options) {
|
|
|
3238
3355
|
}
|
|
3239
3356
|
}
|
|
3240
3357
|
const compactionCutoffIndex = computeCompactionCutoffIndex(messages, config.preserveRecentMessages, afterAnchorIndex);
|
|
3241
|
-
const coveredMessages = messages.slice(afterAnchorIndex, compactionCutoffIndex);
|
|
3242
|
-
const overCompactThreshold = estimate.tokens > config.compactAtFraction *
|
|
3358
|
+
const coveredMessages = capCoverageToTokenBudget(messages.slice(afterAnchorIndex, compactionCutoffIndex), config.maxCoverageTokensPerCompaction);
|
|
3359
|
+
const overCompactThreshold = triggerTokens(estimate.tokens) > config.compactAtFraction * inputBudgetTokens;
|
|
3243
3360
|
let compactionBlockedReason = null;
|
|
3244
3361
|
if (overCompactThreshold) {
|
|
3245
3362
|
if (coveredMessages.length === 0) {
|
|
3246
3363
|
compactionBlockedReason = "not_enough_messages";
|
|
3247
3364
|
} else if ((options.pendingInteractionCount ?? 0) > 0) {
|
|
3248
3365
|
compactionBlockedReason = "pending_interactions";
|
|
3249
|
-
} else if (hasUnresolvedToolWork(
|
|
3366
|
+
} else if (hasUnresolvedToolWork(coveredMessages)) {
|
|
3250
3367
|
compactionBlockedReason = "unresolved_tool_work";
|
|
3251
3368
|
}
|
|
3252
3369
|
}
|
|
@@ -3254,6 +3371,7 @@ function buildProviderView(options) {
|
|
|
3254
3371
|
providerMessages,
|
|
3255
3372
|
estimate,
|
|
3256
3373
|
contextWindow,
|
|
3374
|
+
inputBudgetTokens,
|
|
3257
3375
|
needsCompaction: overCompactThreshold && compactionBlockedReason === null,
|
|
3258
3376
|
compactionBlockedReason,
|
|
3259
3377
|
anchorResolved,
|
|
@@ -3265,6 +3383,7 @@ function buildProviderView(options) {
|
|
|
3265
3383
|
var init_build_provider_view = __esm(() => {
|
|
3266
3384
|
init_config3();
|
|
3267
3385
|
init_estimate();
|
|
3386
|
+
init_fit_to_budget();
|
|
3268
3387
|
init_heuristic_summary();
|
|
3269
3388
|
init_message_parts();
|
|
3270
3389
|
init_tier1_prune();
|
|
@@ -3310,6 +3429,7 @@ var init_context = __esm(() => {
|
|
|
3310
3429
|
init_config3();
|
|
3311
3430
|
init_context_state();
|
|
3312
3431
|
init_estimate();
|
|
3432
|
+
init_fit_to_budget();
|
|
3313
3433
|
init_heuristic_summary();
|
|
3314
3434
|
init_message_parts();
|
|
3315
3435
|
init_normalize_provider_messages();
|
|
@@ -3583,7 +3703,7 @@ var init_lightcode_config = __esm(() => {
|
|
|
3583
3703
|
context: defaultContextOptimizerConfig,
|
|
3584
3704
|
maxOutputTokens: 32768,
|
|
3585
3705
|
maxSteps: 30,
|
|
3586
|
-
maxRetries:
|
|
3706
|
+
maxRetries: 3,
|
|
3587
3707
|
autoContinue: defaultAutoContinueConfig,
|
|
3588
3708
|
headroom: defaultHeadroomConfig
|
|
3589
3709
|
};
|
|
@@ -4097,6 +4217,10 @@ async function extractToolCalls(text, knownToolNames) {
|
|
|
4097
4217
|
while ((match = DEEPSEEK_WRAPPER_RE.exec(text)) !== null) {
|
|
4098
4218
|
segments.push({ start: match.index, end: match.index + match[0].length, call: null });
|
|
4099
4219
|
}
|
|
4220
|
+
FUNCTION_CALLS_WRAPPER_RE.lastIndex = 0;
|
|
4221
|
+
while ((match = FUNCTION_CALLS_WRAPPER_RE.exec(text)) !== null) {
|
|
4222
|
+
segments.push({ start: match.index, end: match.index + match[0].length, call: null });
|
|
4223
|
+
}
|
|
4100
4224
|
const covered = (idx) => segments.some((s) => idx >= s.start && idx < s.end);
|
|
4101
4225
|
const orphanIdx = findOrphanMarker(text, covered);
|
|
4102
4226
|
if (orphanIdx !== -1) {
|
|
@@ -4184,7 +4308,7 @@ async function parseBareTrailingJson(text, knownToolNames) {
|
|
|
4184
4308
|
return null;
|
|
4185
4309
|
return { start: idx, payload };
|
|
4186
4310
|
}
|
|
4187
|
-
var TOOL_CALL_OPEN_MARKERS, MAX_MARKER_LENGTH, MARKER_CONFIRMATION_WINDOW = 256, BLOCK_CLOSE_MARKERS, CLOSED_BLOCK_RES, DEEPSEEK_WRAPPER_RE, PARAMETER_TAG_RE, PARAMETER_EQ_RE;
|
|
4311
|
+
var TOOL_CALL_OPEN_MARKERS, MAX_MARKER_LENGTH, MARKER_CONFIRMATION_WINDOW = 256, BLOCK_CLOSE_MARKERS, CLOSED_BLOCK_RES, DEEPSEEK_WRAPPER_RE, FUNCTION_CALLS_WRAPPER_RE, PARAMETER_TAG_RE, PARAMETER_EQ_RE;
|
|
4188
4312
|
var init_xml_tool_call_parser = __esm(() => {
|
|
4189
4313
|
init_tool_call_json_repair();
|
|
4190
4314
|
TOOL_CALL_OPEN_MARKERS = [
|
|
@@ -4211,6 +4335,7 @@ var init_xml_tool_call_parser = __esm(() => {
|
|
|
4211
4335
|
deepseek: /<\uFF5Ctool\u2581call\u2581begin\uFF5C>\s*(?:function)?\s*(?:<\uFF5Ctool\u2581sep\uFF5C>)?\s*([\w.-]+)\s*\n?([\s\S]*?)<\uFF5Ctool\u2581call\u2581end\uFF5C>/g
|
|
4212
4336
|
};
|
|
4213
4337
|
DEEPSEEK_WRAPPER_RE = /<\uFF5Ctool\u2581calls\u2581(?:begin|end)\uFF5C>/g;
|
|
4338
|
+
FUNCTION_CALLS_WRAPPER_RE = /<\/?function_calls\s*>/gi;
|
|
4214
4339
|
PARAMETER_TAG_RE = /<parameter\s+name=["']?([\w.-]+)["']?\s*>([\s\S]*?)(?:<\/parameter>|(?=<parameter\s)|$)/gi;
|
|
4215
4340
|
PARAMETER_EQ_RE = /<parameter=([\w.-]+)>([\s\S]*?)(?=<parameter=|<\/function>|$)/gi;
|
|
4216
4341
|
});
|
|
@@ -5099,6 +5224,14 @@ function isProviderBillingOrQuotaError(error) {
|
|
|
5099
5224
|
function isContextOverflowMessage(message) {
|
|
5100
5225
|
return contextOverflowPattern.test(message);
|
|
5101
5226
|
}
|
|
5227
|
+
function extractContextLimitTokens(message) {
|
|
5228
|
+
const match = contextLimitPattern.exec(message);
|
|
5229
|
+
if (!match) {
|
|
5230
|
+
return;
|
|
5231
|
+
}
|
|
5232
|
+
const value = Number.parseInt(match[1], 10);
|
|
5233
|
+
return Number.isFinite(value) && value > 0 ? value : undefined;
|
|
5234
|
+
}
|
|
5102
5235
|
function isAbortError(error) {
|
|
5103
5236
|
return error instanceof Error && error.name === "AbortError" || normalizeErrorMessage(getErrorMessage(error)).includes("aborted");
|
|
5104
5237
|
}
|
|
@@ -5139,7 +5272,8 @@ function classifyChatError(error) {
|
|
|
5139
5272
|
kind,
|
|
5140
5273
|
statusCode: error.statusCode,
|
|
5141
5274
|
retryable,
|
|
5142
|
-
message: message2
|
|
5275
|
+
message: message2,
|
|
5276
|
+
contextLimitTokens: kind === "context_overflow" ? extractContextLimitTokens(message2) : undefined
|
|
5143
5277
|
};
|
|
5144
5278
|
}
|
|
5145
5279
|
return {
|
|
@@ -5153,7 +5287,12 @@ function classifyChatError(error) {
|
|
|
5153
5287
|
return { kind: "aborted", retryable: false, message };
|
|
5154
5288
|
}
|
|
5155
5289
|
if (isContextOverflowMessage(message)) {
|
|
5156
|
-
return {
|
|
5290
|
+
return {
|
|
5291
|
+
kind: "context_overflow",
|
|
5292
|
+
retryable: true,
|
|
5293
|
+
message,
|
|
5294
|
+
contextLimitTokens: extractContextLimitTokens(message)
|
|
5295
|
+
};
|
|
5157
5296
|
}
|
|
5158
5297
|
if (isProviderBillingOrQuotaError(error)) {
|
|
5159
5298
|
return { kind: "billing", retryable: false, message };
|
|
@@ -5219,7 +5358,7 @@ function logChatDisconnectEvent({
|
|
|
5219
5358
|
message: getErrorMessage(error)
|
|
5220
5359
|
});
|
|
5221
5360
|
}
|
|
5222
|
-
var chatFailureCounters, logger2, retryableStatusCodes, contextOverflowPattern;
|
|
5361
|
+
var chatFailureCounters, logger2, retryableStatusCodes, contextOverflowPattern, contextLimitPattern;
|
|
5223
5362
|
var init_chat_observability = __esm(() => {
|
|
5224
5363
|
init_src();
|
|
5225
5364
|
chatFailureCounters = {
|
|
@@ -5238,6 +5377,7 @@ var init_chat_observability = __esm(() => {
|
|
|
5238
5377
|
logger2 = createLogger("chat");
|
|
5239
5378
|
retryableStatusCodes = new Set([408, 409, 429, 500, 502, 503, 504]);
|
|
5240
5379
|
contextOverflowPattern = /(context window|context length|context_length|maximum context|prompt is too long|input is too long|too many tokens|exceeds? the (?:maximum|model'?s) (?:context|token)|max(?:imum)? input tokens)/i;
|
|
5380
|
+
contextLimitPattern = /(?:maximum context (?:length|window)|max(?:imum)? input (?:length|tokens)|context (?:length|window) of)\D{0,40}?(\d{4,})/i;
|
|
5241
5381
|
});
|
|
5242
5382
|
|
|
5243
5383
|
// packages/db/generated/prisma/internal/class.ts
|
|
@@ -6035,6 +6175,22 @@ function mergeFinishedMessagesIntoFullHistory({
|
|
|
6035
6175
|
return [...fullMessages, ...appendedMessages];
|
|
6036
6176
|
}
|
|
6037
6177
|
|
|
6178
|
+
// packages/ai/src/tool-call-repeat-guard.ts
|
|
6179
|
+
var guardedTools, lastCallByTurn;
|
|
6180
|
+
var init_tool_call_repeat_guard = __esm(() => {
|
|
6181
|
+
guardedTools = new Set([
|
|
6182
|
+
"read_file",
|
|
6183
|
+
"list_files",
|
|
6184
|
+
"glob_search",
|
|
6185
|
+
"grep",
|
|
6186
|
+
"git_status",
|
|
6187
|
+
"git_diff",
|
|
6188
|
+
"git_log",
|
|
6189
|
+
"git_show"
|
|
6190
|
+
]);
|
|
6191
|
+
lastCallByTurn = new Map;
|
|
6192
|
+
});
|
|
6193
|
+
|
|
6038
6194
|
// packages/ai/src/common/resolve-within-workspace.ts
|
|
6039
6195
|
import path8 from "path";
|
|
6040
6196
|
import {
|
|
@@ -6133,9 +6289,6 @@ var init_resolve_within_workspace = __esm(() => {
|
|
|
6133
6289
|
WORKSPACE = defaultWorkspaceContext.root;
|
|
6134
6290
|
});
|
|
6135
6291
|
|
|
6136
|
-
// packages/ai/src/common/output-utils.ts
|
|
6137
|
-
var init_output_utils = () => {};
|
|
6138
|
-
|
|
6139
6292
|
// packages/ai/src/bash/runtime.ts
|
|
6140
6293
|
import { exec } from "child_process";
|
|
6141
6294
|
import { promisify } from "util";
|
|
@@ -6171,11 +6324,15 @@ var init_runtime5 = __esm(() => {
|
|
|
6171
6324
|
import { createTwoFilesPatch } from "diff";
|
|
6172
6325
|
var init_unified_diff = () => {};
|
|
6173
6326
|
|
|
6327
|
+
// packages/ai/src/edit-file/match.ts
|
|
6328
|
+
var init_match = () => {};
|
|
6329
|
+
|
|
6174
6330
|
// packages/ai/src/edit-file/runtime.ts
|
|
6175
6331
|
var init_runtime6 = __esm(() => {
|
|
6176
6332
|
init_runtime5();
|
|
6177
6333
|
init_resolve_within_workspace();
|
|
6178
6334
|
init_unified_diff();
|
|
6335
|
+
init_match();
|
|
6179
6336
|
init_schema2();
|
|
6180
6337
|
});
|
|
6181
6338
|
|
|
@@ -6413,6 +6570,8 @@ var init_runtime16 = __esm(() => {
|
|
|
6413
6570
|
// packages/ai/src/runtime-registry.ts
|
|
6414
6571
|
var init_runtime_registry = __esm(() => {
|
|
6415
6572
|
init_agent_tools();
|
|
6573
|
+
init_coerce_tool_input();
|
|
6574
|
+
init_tool_call_repeat_guard();
|
|
6416
6575
|
init_runtime4();
|
|
6417
6576
|
init_runtime6();
|
|
6418
6577
|
init_runtime7();
|
|
@@ -6722,6 +6881,24 @@ var init_overflow_recovery = __esm(() => {
|
|
|
6722
6881
|
overflowRecoveryRounds = new Map;
|
|
6723
6882
|
});
|
|
6724
6883
|
|
|
6884
|
+
// apps/server/src/lib/learned-context-limit.ts
|
|
6885
|
+
function noteLearnedContextLimit(modelId, limitTokens) {
|
|
6886
|
+
if (!modelId || typeof limitTokens !== "number" || !Number.isFinite(limitTokens) || limitTokens <= 0) {
|
|
6887
|
+
return;
|
|
6888
|
+
}
|
|
6889
|
+
const existing = learnedLimits.get(modelId);
|
|
6890
|
+
if (existing === undefined || limitTokens < existing) {
|
|
6891
|
+
learnedLimits.set(modelId, Math.floor(limitTokens));
|
|
6892
|
+
}
|
|
6893
|
+
}
|
|
6894
|
+
function getLearnedContextLimit(modelId) {
|
|
6895
|
+
return learnedLimits.get(modelId) ?? null;
|
|
6896
|
+
}
|
|
6897
|
+
var learnedLimits;
|
|
6898
|
+
var init_learned_context_limit = __esm(() => {
|
|
6899
|
+
learnedLimits = new Map;
|
|
6900
|
+
});
|
|
6901
|
+
|
|
6725
6902
|
// apps/server/src/lib/sse-heartbeat.ts
|
|
6726
6903
|
function endsWithEventBoundary(chunk) {
|
|
6727
6904
|
const length = chunk.length;
|
|
@@ -6947,6 +7124,16 @@ function getAnthropicProviderOptions(modelId) {
|
|
|
6947
7124
|
}
|
|
6948
7125
|
};
|
|
6949
7126
|
}
|
|
7127
|
+
function getOpenRouterProviderOptions() {
|
|
7128
|
+
return {
|
|
7129
|
+
openrouter: {
|
|
7130
|
+
provider: {
|
|
7131
|
+
allow_fallbacks: true,
|
|
7132
|
+
require_parameters: true
|
|
7133
|
+
}
|
|
7134
|
+
}
|
|
7135
|
+
};
|
|
7136
|
+
}
|
|
6950
7137
|
function resolveHeadroomRouting(config5, realBaseUrl) {
|
|
6951
7138
|
const headroom = config5.headroom;
|
|
6952
7139
|
if (!headroom?.enabled) {
|
|
@@ -7011,12 +7198,8 @@ function resolveOpenAITransportModel({
|
|
|
7011
7198
|
if (isOpenRouter) {
|
|
7012
7199
|
const referer = getEnvValue(env, "OPENROUTER_HTTP_REFERER");
|
|
7013
7200
|
const title = getEnvValue(env, "OPENROUTER_APP_TITLE", "LIGHTCODE_APP_TITLE");
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
}
|
|
7017
|
-
if (title) {
|
|
7018
|
-
headers["X-OpenRouter-Title"] = title;
|
|
7019
|
-
}
|
|
7201
|
+
headers["HTTP-Referer"] = referer ?? "https://github.com/kmugalkhod/lightcode";
|
|
7202
|
+
headers["X-Title"] = title ?? "Lightcode";
|
|
7020
7203
|
}
|
|
7021
7204
|
const provider = createOpenAICompatible({
|
|
7022
7205
|
name: config5.provider,
|
|
@@ -7033,6 +7216,7 @@ function resolveOpenAITransportModel({
|
|
|
7033
7216
|
headroomRouted,
|
|
7034
7217
|
model: withXmlToolCallSupport(provider(configuredModel)),
|
|
7035
7218
|
needsToolCallDiscipline: true,
|
|
7219
|
+
providerOptions: isOpenRouter ? getOpenRouterProviderOptions() : undefined,
|
|
7036
7220
|
contextWindow: capabilities?.contextLength ?? getModelContextWindow(config5.provider, configuredModel),
|
|
7037
7221
|
missingCredentialHints: apiKey ? [] : isOpenCodeZen ? ["Set OPENCODE_API_KEY to call OpenCode Zen models."] : isOpenRouter ? ["Set OPENROUTER_API_KEY to call OpenRouter models."] : [
|
|
7038
7222
|
"Set LIGHTCODE_OPENAI_COMPATIBLE_API_KEY or OPENAI_API_KEY if your OpenAI-compatible endpoint requires authentication."
|
|
@@ -7323,6 +7507,26 @@ var init_session_auto_title = __esm(() => {
|
|
|
7323
7507
|
import { readFile as readFile2, readdir } from "fs/promises";
|
|
7324
7508
|
import { platform as platform3 } from "os";
|
|
7325
7509
|
import path11 from "path";
|
|
7510
|
+
function formatAvailableSkills(skills) {
|
|
7511
|
+
if (skills.length === 0) {
|
|
7512
|
+
return "";
|
|
7513
|
+
}
|
|
7514
|
+
const shown = skills.slice(0, maxListedSkills);
|
|
7515
|
+
const lines = shown.map((skill) => `- ${skill.name}${skill.description ? `: ${skill.description}` : ""}`);
|
|
7516
|
+
const more = skills.length > shown.length ? `
|
|
7517
|
+
\u2026 and ${skills.length - shown.length} more` : "";
|
|
7518
|
+
return `Available skills (load by name with the skill tool):
|
|
7519
|
+
${lines.join(`
|
|
7520
|
+
`)}${more}`;
|
|
7521
|
+
}
|
|
7522
|
+
function buildAvailableSkills(cwd) {
|
|
7523
|
+
try {
|
|
7524
|
+
return formatAvailableSkills(listSkills({ cwd }));
|
|
7525
|
+
} catch (error) {
|
|
7526
|
+
logger7.warn("workspace_skills_failed", { cwd, error: getErrorMessage(error) });
|
|
7527
|
+
return "";
|
|
7528
|
+
}
|
|
7529
|
+
}
|
|
7326
7530
|
function describePlatform() {
|
|
7327
7531
|
const map = {
|
|
7328
7532
|
darwin: "macOS",
|
|
@@ -7398,6 +7602,7 @@ async function buildWorkspaceContext({
|
|
|
7398
7602
|
buildGitSummary(cwd),
|
|
7399
7603
|
buildProjectDocs(cwd)
|
|
7400
7604
|
]);
|
|
7605
|
+
const skills = buildAvailableSkills(cwd);
|
|
7401
7606
|
const today = new Date().toISOString().slice(0, 10);
|
|
7402
7607
|
const parts = [
|
|
7403
7608
|
"<environment>",
|
|
@@ -7413,6 +7618,8 @@ async function buildWorkspaceContext({
|
|
|
7413
7618
|
docs ? `
|
|
7414
7619
|
Project instructions:
|
|
7415
7620
|
${docs}` : "",
|
|
7621
|
+
skills ? `
|
|
7622
|
+
${skills}` : "",
|
|
7416
7623
|
"</environment>"
|
|
7417
7624
|
];
|
|
7418
7625
|
const block = parts.join(`
|
|
@@ -7421,7 +7628,7 @@ ${docs}` : "",
|
|
|
7421
7628
|
\u2026 (environment truncated)
|
|
7422
7629
|
</environment>` : block;
|
|
7423
7630
|
}
|
|
7424
|
-
var logger7, projectDocNames, maxListedEntries = 40, maxDocChars = 1500, maxBlockChars = 8000;
|
|
7631
|
+
var logger7, projectDocNames, maxListedEntries = 40, maxListedSkills = 20, maxDocChars = 1500, maxBlockChars = 8000;
|
|
7425
7632
|
var init_workspace_context = __esm(() => {
|
|
7426
7633
|
init_runtime17();
|
|
7427
7634
|
init_src();
|
|
@@ -7460,7 +7667,8 @@ function messagesHaveToolActivity(messages) {
|
|
|
7460
7667
|
function shouldUseFastChatPath({
|
|
7461
7668
|
messages,
|
|
7462
7669
|
mode,
|
|
7463
|
-
allowedTools
|
|
7670
|
+
allowedTools,
|
|
7671
|
+
availableSkillNames = []
|
|
7464
7672
|
}) {
|
|
7465
7673
|
if (allowedTools && allowedTools.length > 0) {
|
|
7466
7674
|
return false;
|
|
@@ -7471,7 +7679,8 @@ function shouldUseFastChatPath({
|
|
|
7471
7679
|
return selectCodingAgentIntentTools({
|
|
7472
7680
|
mode,
|
|
7473
7681
|
prompt: "",
|
|
7474
|
-
messages
|
|
7682
|
+
messages,
|
|
7683
|
+
availableSkillNames
|
|
7475
7684
|
}).length === 0;
|
|
7476
7685
|
}
|
|
7477
7686
|
async function countPendingChatInteractions(sessionId) {
|
|
@@ -7553,6 +7762,8 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7553
7762
|
}
|
|
7554
7763
|
const contextConfig = lightcodeConfigResult.config.context;
|
|
7555
7764
|
const cacheActive = resolvedProviderModel.provider === "anthropic";
|
|
7765
|
+
const reservedOutputTokens = resolveMaxOutputTokens(lightcodeConfigResult.config.maxOutputTokens, lightcodeConfigDefaults.maxOutputTokens, resolvedProviderModel.maxCompletionTokens);
|
|
7766
|
+
const endpointLimitTokens = getLearnedContextLimit(chatModelId);
|
|
7556
7767
|
const pendingInteractionCount = await countPendingChatInteractions(sessionId);
|
|
7557
7768
|
let contextState = await loadSessionContextStateSafe(sessionId);
|
|
7558
7769
|
let view = buildProviderView({
|
|
@@ -7560,44 +7771,56 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7560
7771
|
contextState,
|
|
7561
7772
|
config: contextConfig,
|
|
7562
7773
|
modelContextWindow: resolvedProviderModel.contextWindow,
|
|
7774
|
+
reservedOutputTokens,
|
|
7775
|
+
endpointLimitTokens,
|
|
7563
7776
|
pendingInteractionCount,
|
|
7564
7777
|
cacheActive
|
|
7565
7778
|
});
|
|
7566
|
-
if (
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
contextState
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7779
|
+
if (contextConfig.autoCompact) {
|
|
7780
|
+
let compactionRound = 0;
|
|
7781
|
+
while (view.needsCompaction && compactionRound < MAX_COMPACTION_ROUNDS) {
|
|
7782
|
+
compactionRound += 1;
|
|
7783
|
+
try {
|
|
7784
|
+
const estimatedTokensBefore = view.estimate.tokens;
|
|
7785
|
+
const compaction = await compactSessionContext({
|
|
7786
|
+
sessionId,
|
|
7787
|
+
coveredMessages: view.coveredMessages,
|
|
7788
|
+
previousState: contextState,
|
|
7789
|
+
model: resolvedProviderModel.model,
|
|
7790
|
+
modelId: chatModelId,
|
|
7791
|
+
cwd,
|
|
7792
|
+
config: contextConfig,
|
|
7793
|
+
estimatedTokens: estimatedTokensBefore
|
|
7794
|
+
});
|
|
7795
|
+
contextState = compaction.state;
|
|
7796
|
+
view = buildProviderView({
|
|
7797
|
+
messages: validatedMessages,
|
|
7798
|
+
contextState,
|
|
7799
|
+
config: contextConfig,
|
|
7800
|
+
modelContextWindow: resolvedProviderModel.contextWindow,
|
|
7801
|
+
reservedOutputTokens,
|
|
7802
|
+
endpointLimitTokens,
|
|
7803
|
+
pendingInteractionCount,
|
|
7804
|
+
cacheActive
|
|
7805
|
+
});
|
|
7806
|
+
logger8.info("context_auto_compacted", {
|
|
7807
|
+
sessionId,
|
|
7808
|
+
round: compactionRound,
|
|
7809
|
+
tier: compaction.state.tier,
|
|
7810
|
+
usedFallback: compaction.usedFallback,
|
|
7811
|
+
coveredMessageCount: compaction.state.coveredMessageCount,
|
|
7812
|
+
estimatedTokensBefore,
|
|
7813
|
+
estimatedTokensAfter: view.estimate.tokens,
|
|
7814
|
+
stillNeedsCompaction: view.needsCompaction
|
|
7815
|
+
});
|
|
7816
|
+
} catch (error) {
|
|
7817
|
+
logger8.error("context_compaction_failed", {
|
|
7818
|
+
sessionId,
|
|
7819
|
+
round: compactionRound,
|
|
7820
|
+
error: getErrorMessage(error)
|
|
7821
|
+
});
|
|
7822
|
+
break;
|
|
7823
|
+
}
|
|
7601
7824
|
}
|
|
7602
7825
|
}
|
|
7603
7826
|
const overflowPreserve = getOverflowPreserveRecentMessages(sessionId);
|
|
@@ -7612,6 +7835,8 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7612
7835
|
contextState,
|
|
7613
7836
|
config: overflowConfig,
|
|
7614
7837
|
modelContextWindow: resolvedProviderModel.contextWindow,
|
|
7838
|
+
reservedOutputTokens,
|
|
7839
|
+
endpointLimitTokens,
|
|
7615
7840
|
pendingInteractionCount: 0,
|
|
7616
7841
|
cacheActive
|
|
7617
7842
|
});
|
|
@@ -7633,6 +7858,8 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7633
7858
|
contextState,
|
|
7634
7859
|
config: overflowConfig,
|
|
7635
7860
|
modelContextWindow: resolvedProviderModel.contextWindow,
|
|
7861
|
+
reservedOutputTokens,
|
|
7862
|
+
endpointLimitTokens,
|
|
7636
7863
|
pendingInteractionCount: 0,
|
|
7637
7864
|
cacheActive
|
|
7638
7865
|
});
|
|
@@ -7651,7 +7878,24 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7651
7878
|
});
|
|
7652
7879
|
}
|
|
7653
7880
|
}
|
|
7654
|
-
const
|
|
7881
|
+
const fitted = fitMessagesToBudget(view.providerMessages, {
|
|
7882
|
+
inputBudgetTokens: view.inputBudgetTokens,
|
|
7883
|
+
preserveRecentMessages: contextConfig.preserveRecentMessages
|
|
7884
|
+
});
|
|
7885
|
+
if (fitted.fitted) {
|
|
7886
|
+
logger8.warn("context_fit_to_budget", {
|
|
7887
|
+
sessionId,
|
|
7888
|
+
inputBudgetTokens: view.inputBudgetTokens,
|
|
7889
|
+
contextWindow: view.contextWindow,
|
|
7890
|
+
estimatedTokensBefore: estimateStructuralTokens(view.providerMessages),
|
|
7891
|
+
estimatedTokensAfter: fitted.estimatedTokens,
|
|
7892
|
+
elidedToolOutputs: fitted.elidedToolOutputs,
|
|
7893
|
+
droppedMessages: fitted.droppedMessages,
|
|
7894
|
+
truncatedTextParts: fitted.truncatedTextParts,
|
|
7895
|
+
withinBudget: fitted.withinBudget
|
|
7896
|
+
});
|
|
7897
|
+
}
|
|
7898
|
+
const providerMessages = normalizeProviderMessages(fitted.messages);
|
|
7655
7899
|
let overflowNotedThisRequest = false;
|
|
7656
7900
|
const noteOverflowForSession = () => {
|
|
7657
7901
|
if (overflowNotedThisRequest) {
|
|
@@ -7745,15 +7989,22 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7745
7989
|
totalTokens: part.totalUsage.totalTokens
|
|
7746
7990
|
},
|
|
7747
7991
|
modelId: chatModelId,
|
|
7748
|
-
finishReason: part.finishReason
|
|
7992
|
+
finishReason: part.finishReason,
|
|
7993
|
+
context: {
|
|
7994
|
+
inputTokens: view.estimate.tokens,
|
|
7995
|
+
contextWindow: view.contextWindow,
|
|
7996
|
+
compactedMessages: contextState?.coveredMessageCount ?? 0
|
|
7997
|
+
}
|
|
7749
7998
|
};
|
|
7750
7999
|
};
|
|
7751
8000
|
const environmentContext = await buildWorkspaceContext({ cwd });
|
|
8001
|
+
const availableSkillNames = listSkills({ cwd }).map((skill) => skill.name);
|
|
7752
8002
|
try {
|
|
7753
8003
|
if (shouldUseFastChatPath({
|
|
7754
8004
|
messages: providerMessages,
|
|
7755
8005
|
mode,
|
|
7756
|
-
allowedTools
|
|
8006
|
+
allowedTools,
|
|
8007
|
+
availableSkillNames
|
|
7757
8008
|
})) {
|
|
7758
8009
|
logger8.info("chat_fast_path", {
|
|
7759
8010
|
sessionId,
|
|
@@ -7793,6 +8044,7 @@ ${environmentContext}`,
|
|
|
7793
8044
|
onError: (error) => {
|
|
7794
8045
|
const classified = classifyChatError(error);
|
|
7795
8046
|
if (classified.kind === "context_overflow") {
|
|
8047
|
+
noteLearnedContextLimit(chatModelId, classified.contextLimitTokens);
|
|
7796
8048
|
noteOverflowForSession();
|
|
7797
8049
|
}
|
|
7798
8050
|
incrementChatFailureCounter(chatFailureClassForErrorKind(classified.kind), { sessionId, statusCode: classified.statusCode });
|
|
@@ -7810,6 +8062,7 @@ ${environmentContext}`,
|
|
|
7810
8062
|
const mapStreamError = (error) => {
|
|
7811
8063
|
const classified = classifyChatError(error);
|
|
7812
8064
|
if (classified.kind === "context_overflow") {
|
|
8065
|
+
noteLearnedContextLimit(chatModelId, classified.contextLimitTokens);
|
|
7813
8066
|
noteOverflowForSession();
|
|
7814
8067
|
}
|
|
7815
8068
|
incrementChatFailureCounter(chatFailureClassForErrorKind(classified.kind), {
|
|
@@ -7864,6 +8117,7 @@ ${environmentContext}`,
|
|
|
7864
8117
|
} catch (error) {
|
|
7865
8118
|
const classified = classifyChatError(error);
|
|
7866
8119
|
if (classified.kind === "context_overflow") {
|
|
8120
|
+
noteLearnedContextLimit(chatModelId, classified.contextLimitTokens);
|
|
7867
8121
|
noteContextOverflow(sessionId);
|
|
7868
8122
|
}
|
|
7869
8123
|
incrementChatFailureCounter(chatFailureClassForErrorKind(classified.kind), {
|
|
@@ -7894,7 +8148,7 @@ ${environmentContext}`,
|
|
|
7894
8148
|
}, 500);
|
|
7895
8149
|
}
|
|
7896
8150
|
}
|
|
7897
|
-
var logger8, recoverableDisconnectMessage = "Connection interrupted. Please retry or regenerate your last message.", providerBillingOrQuotaMessage, fastChatSystemPrompt, fastChatMaxOutputTokens = 512, fastChatRecentMessageCount = 6;
|
|
8151
|
+
var logger8, recoverableDisconnectMessage = "Connection interrupted. Please retry or regenerate your last message.", providerBillingOrQuotaMessage, fastChatSystemPrompt, fastChatMaxOutputTokens = 512, fastChatRecentMessageCount = 6, MAX_COMPACTION_ROUNDS = 3;
|
|
7898
8152
|
var init_chat_stream = __esm(() => {
|
|
7899
8153
|
init_src2();
|
|
7900
8154
|
init_src();
|
|
@@ -7903,6 +8157,7 @@ var init_chat_stream = __esm(() => {
|
|
|
7903
8157
|
init_chat_store();
|
|
7904
8158
|
init_context_compaction();
|
|
7905
8159
|
init_overflow_recovery();
|
|
8160
|
+
init_learned_context_limit();
|
|
7906
8161
|
init_sse_heartbeat();
|
|
7907
8162
|
init_context_state_store();
|
|
7908
8163
|
init_session_auto_title();
|