@kmugalkhod/lightcode 0.18.5 → 0.19.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/assets/web/{index-89rgdkry.js → index-1cpyqcg4.js} +56 -56
- package/assets/web/index.html +1 -1
- package/cli.js +82 -44
- package/package.json +1 -1
- package/server.js +395 -147
package/server.js
CHANGED
|
@@ -635,7 +635,7 @@ var init_web_auth = __esm(() => {
|
|
|
635
635
|
});
|
|
636
636
|
|
|
637
637
|
// packages/ai/src/constants.ts
|
|
638
|
-
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;
|
|
638
|
+
var MAX_TOOL_TEXT_OUTPUT_CHARS = 12000, DEFAULT_TOOL_TEXT_OUTPUT_CHARS = 6000, READ_FILE_OUTPUT_CHARS = 24000, 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;
|
|
639
639
|
|
|
640
640
|
// packages/ai/src/coding-agent-modes.ts
|
|
641
641
|
import { z as z2 } from "zod";
|
|
@@ -2002,14 +2002,15 @@ var init_schema8 = __esm(() => {
|
|
|
2002
2002
|
|
|
2003
2003
|
// packages/ai/src/read-file/schema.ts
|
|
2004
2004
|
import { z as z19 } from "zod";
|
|
2005
|
-
var readFileDescription
|
|
2005
|
+
var readFileDescription, readFileInputSchema, readFileProviderInputSchema, readFileOutputSchema;
|
|
2006
2006
|
var init_schema9 = __esm(() => {
|
|
2007
2007
|
init_base_schemas();
|
|
2008
|
+
readFileDescription = "Read file content from the working directory. Returns whole lines, up to about 24,000 characters (roughly 600 lines) per call, so read a file in one call instead of in slices. " + "If the file is longer, the result stops at endLine and nextStartLine says where to continue. " + "To inspect one spot in a large file, grep for it first, then read a generous startLine/endLine range around it.";
|
|
2008
2009
|
readFileInputSchema = z19.object({
|
|
2009
2010
|
path: boundedPathSchema,
|
|
2010
2011
|
startLine: positiveLineSchema.optional(),
|
|
2011
2012
|
endLine: positiveLineSchema.optional(),
|
|
2012
|
-
maxChars: integerRangeSchema(1,
|
|
2013
|
+
maxChars: integerRangeSchema(1, READ_FILE_OUTPUT_CHARS, "maxChars").optional()
|
|
2013
2014
|
}).superRefine((value, ctx) => {
|
|
2014
2015
|
if (value.startLine !== undefined && value.endLine !== undefined && value.endLine < value.startLine) {
|
|
2015
2016
|
ctx.addIssue({
|
|
@@ -2030,7 +2031,8 @@ var init_schema9 = __esm(() => {
|
|
|
2030
2031
|
endLine: z19.number().int().min(1),
|
|
2031
2032
|
content: z19.string(),
|
|
2032
2033
|
totalLines: z19.number().int().nonnegative(),
|
|
2033
|
-
truncated: z19.boolean()
|
|
2034
|
+
truncated: z19.boolean(),
|
|
2035
|
+
nextStartLine: z19.number().int().min(1).optional()
|
|
2034
2036
|
});
|
|
2035
2037
|
});
|
|
2036
2038
|
|
|
@@ -2350,7 +2352,7 @@ var init_tool_registry = __esm(() => {
|
|
|
2350
2352
|
permissionMode: "read-only",
|
|
2351
2353
|
execution: "server",
|
|
2352
2354
|
availability: "git-workspace",
|
|
2353
|
-
activation: "
|
|
2355
|
+
activation: "core",
|
|
2354
2356
|
outputPolicy: "artifact-if-large"
|
|
2355
2357
|
},
|
|
2356
2358
|
git_diff: {
|
|
@@ -2363,7 +2365,7 @@ var init_tool_registry = __esm(() => {
|
|
|
2363
2365
|
permissionMode: "read-only",
|
|
2364
2366
|
execution: "server",
|
|
2365
2367
|
availability: "git-workspace",
|
|
2366
|
-
activation: "
|
|
2368
|
+
activation: "core",
|
|
2367
2369
|
outputPolicy: "artifact-if-large"
|
|
2368
2370
|
},
|
|
2369
2371
|
git_log: {
|
|
@@ -2376,7 +2378,7 @@ var init_tool_registry = __esm(() => {
|
|
|
2376
2378
|
permissionMode: "read-only",
|
|
2377
2379
|
execution: "server",
|
|
2378
2380
|
availability: "git-workspace",
|
|
2379
|
-
activation: "
|
|
2381
|
+
activation: "core",
|
|
2380
2382
|
outputPolicy: "artifact-if-large"
|
|
2381
2383
|
},
|
|
2382
2384
|
git_show: {
|
|
@@ -2389,7 +2391,7 @@ var init_tool_registry = __esm(() => {
|
|
|
2389
2391
|
permissionMode: "read-only",
|
|
2390
2392
|
execution: "server",
|
|
2391
2393
|
availability: "git-workspace",
|
|
2392
|
-
activation: "
|
|
2394
|
+
activation: "core",
|
|
2393
2395
|
outputPolicy: "artifact-if-large"
|
|
2394
2396
|
},
|
|
2395
2397
|
tool_search: {
|
|
@@ -2790,6 +2792,7 @@ var init_agent_tools = __esm(() => {
|
|
|
2790
2792
|
contextWindow: z27.number().int().positive().optional(),
|
|
2791
2793
|
preserveRecentTokens: z27.number().int().nonnegative().optional(),
|
|
2792
2794
|
originalInputTokens: z27.number().int().nonnegative().optional(),
|
|
2795
|
+
promptCacheActive: z27.boolean().optional(),
|
|
2793
2796
|
assemblyAbortSignal: z27.custom().optional(),
|
|
2794
2797
|
turnKey: z27.string().min(1).max(200).optional()
|
|
2795
2798
|
});
|
|
@@ -3068,10 +3071,10 @@ ${environmentContext.trim()}` : "");
|
|
|
3068
3071
|
var cwdPromptPlaceholder = "{cwd}", defaultCodingAgentSystemPrompt, toolCallingDisciplineAddendum;
|
|
3069
3072
|
var init_coding_agent_prompt = __esm(() => {
|
|
3070
3073
|
init_coding_agent_modes();
|
|
3071
|
-
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. " + "Gather context efficiently \u2014 every tool round trip costs the user seconds. Locate before you read: use grep or glob_search to find the relevant files and line numbers, then read
|
|
3074
|
+
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. " + "Gather context efficiently \u2014 every tool round trip costs the user seconds. The environment section maps the repository's files: use it to go straight to the right paths instead of listing directories. Locate before you read: use grep or glob_search to find the relevant files and line numbers, then read whole files (read_file returns up to about 600 lines per call) or, in larger files, a range of a few hundred lines around the spot \u2014 never many small slices, and never re-read content you already have. When several reads or searches do not depend on each other, request them together in one step. Skip generated output (dist/, build/, minified bundles, node_modules) unless the task is about it. Stop exploring once you have enough evidence to act or answer. " + "Check the available skill descriptions for every task. When a skill is explicitly named or clearly matches the task, load it with the skill tool before acting and follow its instructions within the user's scope and tool permissions. Skill files may be installed outside the workspace; use the skill tool to read their relative supporting resources. Never claim to have used a skill you have not loaded. " + `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.
|
|
3072
3075
|
|
|
3073
3076
|
` + `Task list discipline \u2014 use the todo_write tool to keep a visible, evolving checklist:
|
|
3074
|
-
` + `- Create the list
|
|
3077
|
+
` + `- Create the list in your first step. For any task with roughly three or more distinct steps, or any multi-file or multi-stage work, call todo_write in your first response with every step listed and all of them set to pending \u2014 request it together with your first reads or searches, not as a separate step, and always before any edit.
|
|
3075
3078
|
` + `- 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.
|
|
3076
3079
|
` + `- 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.
|
|
3077
3080
|
` + `- 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.
|
|
@@ -3481,20 +3484,26 @@ function collectTextFromUnknown(value) {
|
|
|
3481
3484
|
}
|
|
3482
3485
|
return "";
|
|
3483
3486
|
}
|
|
3484
|
-
function
|
|
3487
|
+
function getUserTexts(messages, prompt) {
|
|
3488
|
+
const texts = [];
|
|
3485
3489
|
if (Array.isArray(messages)) {
|
|
3486
|
-
for (
|
|
3487
|
-
const message = messages[index];
|
|
3490
|
+
for (const message of messages) {
|
|
3488
3491
|
if (!isRecord4(message) || message.role !== "user") {
|
|
3489
3492
|
continue;
|
|
3490
3493
|
}
|
|
3491
3494
|
const text = collectTextFromUnknown(message).trim();
|
|
3492
3495
|
if (text) {
|
|
3493
|
-
|
|
3496
|
+
texts.push(text);
|
|
3494
3497
|
}
|
|
3495
3498
|
}
|
|
3496
3499
|
}
|
|
3497
|
-
|
|
3500
|
+
if (texts.length === 0) {
|
|
3501
|
+
const promptText = collectTextFromUnknown(prompt).trim();
|
|
3502
|
+
if (promptText) {
|
|
3503
|
+
texts.push(promptText);
|
|
3504
|
+
}
|
|
3505
|
+
}
|
|
3506
|
+
return texts;
|
|
3498
3507
|
}
|
|
3499
3508
|
function escapeRegExp(value) {
|
|
3500
3509
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -3520,23 +3529,20 @@ function selectCodingAgentIntentTools({
|
|
|
3520
3529
|
messages,
|
|
3521
3530
|
availableSkillNames = []
|
|
3522
3531
|
}) {
|
|
3523
|
-
const
|
|
3524
|
-
|
|
3525
|
-
if (!normalizedText || casualPromptPattern.test(normalizedText)) {
|
|
3532
|
+
const userTexts = getUserTexts(messages, prompt);
|
|
3533
|
+
if (userTexts.every((text) => casualPromptPattern.test(text))) {
|
|
3526
3534
|
return [];
|
|
3527
3535
|
}
|
|
3536
|
+
const latestText = userTexts.at(-1) ?? "";
|
|
3528
3537
|
const modeTools = getRegistryToolsForMode(mode);
|
|
3529
3538
|
const selected = new Set(modeTools.filter((toolName) => codingToolRegistry[toolName].activation === "core"));
|
|
3530
|
-
if (
|
|
3531
|
-
addTools(selected, gitToolNames);
|
|
3532
|
-
}
|
|
3533
|
-
if (webIntentPattern.test(userText)) {
|
|
3539
|
+
if (webIntentPattern.test(latestText)) {
|
|
3534
3540
|
addTools(selected, webToolNames);
|
|
3535
3541
|
}
|
|
3536
|
-
if (mcpIntentPattern.test(
|
|
3542
|
+
if (userTexts.some((text) => mcpIntentPattern.test(text))) {
|
|
3537
3543
|
addTools(selected, mcpToolNames);
|
|
3538
3544
|
}
|
|
3539
|
-
if (skillIntentPattern.test(
|
|
3545
|
+
if (userTexts.some((text) => skillIntentPattern.test(text) || requestsAvailableSkill(text, availableSkillNames))) {
|
|
3540
3546
|
selected.add("skill");
|
|
3541
3547
|
}
|
|
3542
3548
|
return modeTools.filter((toolName) => selected.has(toolName));
|
|
@@ -3616,21 +3622,14 @@ function limitProviderActiveTools(tools, maxTools = maxProviderActiveTools) {
|
|
|
3616
3622
|
const toolSet = new Set(tools);
|
|
3617
3623
|
return providerToolPriority.filter((toolName) => toolSet.has(toolName)).slice(0, maxTools);
|
|
3618
3624
|
}
|
|
3619
|
-
var casualPromptPattern,
|
|
3625
|
+
var casualPromptPattern, webIntentPattern, mcpIntentPattern, skillIntentPattern, webToolNames, mcpToolNames, maxProviderActiveTools = 24, providerToolPriority;
|
|
3620
3626
|
var init_intent_tool_selection = __esm(() => {
|
|
3621
3627
|
init_coding_agent_modes();
|
|
3622
3628
|
init_tool_registry();
|
|
3623
3629
|
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;
|
|
3624
|
-
gitIntentPattern = /(?:\bgit\b|\bcommits?\b|\bstaged?\b|\bunstaged\b|\buntracked\b|\bworking tree\b|\brepositor(?:y|ies)\s+(?:status|diff|history|changes))/i;
|
|
3625
3630
|
webIntentPattern = /(?:\bhttps?:\/\/|\bwww\.|\bweb(?:site|page)?\b|\binternet\b|\bonline\b|\burl\b|\bbrowser\b|\bsearch\s+(?:the\s+)?web\b|\bbrowse\s+(?:the\s+web|online|for)\b|\blook\s+up\b|\b(?:latest|current|recent|today'?s)\s+(?:news|price|weather|docs?|documentation|release|version)\b)/i;
|
|
3626
3631
|
mcpIntentPattern = /(?:\bmcp\b|\bmodel context protocol\b)/i;
|
|
3627
3632
|
skillIntentPattern = /\bskills?\b/i;
|
|
3628
|
-
gitToolNames = [
|
|
3629
|
-
"git_status",
|
|
3630
|
-
"git_diff",
|
|
3631
|
-
"git_log",
|
|
3632
|
-
"git_show"
|
|
3633
|
-
];
|
|
3634
3633
|
webToolNames = [
|
|
3635
3634
|
"web_fetch",
|
|
3636
3635
|
"web_search"
|
|
@@ -5160,19 +5159,44 @@ async function executeReadFile(input, workspaceContext = getDefaultWorkspaceCont
|
|
|
5160
5159
|
const fileContent = await fs4.readFile(resolvedPath, "utf8");
|
|
5161
5160
|
const lines = fileContent.split(/\r?\n/);
|
|
5162
5161
|
const { start, end } = clampLineRange(lines.length, parsedInput.startLine, parsedInput.endLine);
|
|
5163
|
-
const
|
|
5164
|
-
|
|
5165
|
-
const
|
|
5166
|
-
const truncated = truncateText(selectedContent, maxChars);
|
|
5162
|
+
const maxChars = parsedInput.maxChars ?? READ_FILE_OUTPUT_CHARS;
|
|
5163
|
+
const taken = takeWholeLines(lines.slice(start - 1, end), maxChars);
|
|
5164
|
+
const lastLine = start + taken.lineCount - 1;
|
|
5167
5165
|
return readFileOutputSchema.parse({
|
|
5168
5166
|
path: relativePath,
|
|
5169
5167
|
startLine: start,
|
|
5170
|
-
endLine:
|
|
5171
|
-
content:
|
|
5168
|
+
endLine: lastLine,
|
|
5169
|
+
content: taken.text,
|
|
5172
5170
|
totalLines: lines.length,
|
|
5173
|
-
truncated:
|
|
5171
|
+
truncated: taken.truncated,
|
|
5172
|
+
...lastLine < end ? { nextStartLine: lastLine + 1 } : {}
|
|
5174
5173
|
});
|
|
5175
5174
|
}
|
|
5175
|
+
function takeWholeLines(lines, maxChars) {
|
|
5176
|
+
let used = 0;
|
|
5177
|
+
let lineCount = 0;
|
|
5178
|
+
for (const line of lines) {
|
|
5179
|
+
const cost = line.length + (lineCount > 0 ? 1 : 0);
|
|
5180
|
+
if (used + cost > maxChars) {
|
|
5181
|
+
break;
|
|
5182
|
+
}
|
|
5183
|
+
used += cost;
|
|
5184
|
+
lineCount += 1;
|
|
5185
|
+
}
|
|
5186
|
+
if (lineCount === 0 && lines.length > 0) {
|
|
5187
|
+
return {
|
|
5188
|
+
text: truncateText(lines[0] ?? "", maxChars).text,
|
|
5189
|
+
lineCount: 1,
|
|
5190
|
+
truncated: true
|
|
5191
|
+
};
|
|
5192
|
+
}
|
|
5193
|
+
return {
|
|
5194
|
+
text: lines.slice(0, lineCount).join(`
|
|
5195
|
+
`),
|
|
5196
|
+
lineCount,
|
|
5197
|
+
truncated: lineCount < lines.length
|
|
5198
|
+
};
|
|
5199
|
+
}
|
|
5176
5200
|
var init_runtime8 = __esm(() => {
|
|
5177
5201
|
init_resolve_within_workspace();
|
|
5178
5202
|
init_output_utils();
|
|
@@ -6444,10 +6468,12 @@ function buildProviderView(options) {
|
|
|
6444
6468
|
}
|
|
6445
6469
|
const tokenBoundedCutoffIndex = computeTokenBoundedCompactionCutoffIndex(messages, preserveRecentTokens, afterAnchorIndex);
|
|
6446
6470
|
const userIndicesAfterAnchor = messages.map((message, index) => ({ message, index })).filter(({ message, index }) => index >= afterAnchorIndex && message.role === "user").map(({ index }) => index);
|
|
6447
|
-
const
|
|
6471
|
+
const rollingKeepUserTurns = Math.max(1, Math.ceil(config.uncachedRollingCompactionUserTurns / 2));
|
|
6472
|
+
const rollingTurnCutoffIndex = userIndicesAfterAnchor.length > rollingKeepUserTurns ? userIndicesAfterAnchor[userIndicesAfterAnchor.length - rollingKeepUserTurns] ?? afterAnchorIndex : afterAnchorIndex;
|
|
6448
6473
|
const overCompactThreshold = triggerTokens(estimate.tokens) > config.compactAtFraction * inputBudgetTokens;
|
|
6474
|
+
const rollingActive = (options.rollingCompaction ?? options.cacheActive === false) && config.aggressivePruneWhenUncached;
|
|
6449
6475
|
const userTurnsAfterAnchor = messages.slice(afterAnchorIndex).filter((message) => message.role === "user").length;
|
|
6450
|
-
const rollingThresholdReached =
|
|
6476
|
+
const rollingThresholdReached = rollingActive && userTurnsAfterAnchor > (options.continueRolling ? rollingKeepUserTurns : config.uncachedRollingCompactionUserTurns);
|
|
6451
6477
|
const compactionCutoffIndex = rollingThresholdReached ? Math.max(tokenBoundedCutoffIndex, rollingTurnCutoffIndex) : tokenBoundedCutoffIndex;
|
|
6452
6478
|
const coveredMessages = capCoverageToTokenBudget(messages.slice(afterAnchorIndex, compactionCutoffIndex), config.maxCoverageTokensPerCompaction);
|
|
6453
6479
|
let compactionBlockedReason = null;
|
|
@@ -6901,6 +6927,14 @@ import path12 from "path";
|
|
|
6901
6927
|
import {
|
|
6902
6928
|
toolModelMessageSchema
|
|
6903
6929
|
} from "ai";
|
|
6930
|
+
function resolveToolOutputArtifactThresholds({
|
|
6931
|
+
promptCacheActive
|
|
6932
|
+
}) {
|
|
6933
|
+
return {
|
|
6934
|
+
thresholdCharacters: promptCacheActive ? DEFAULT_CURRENT_TURN_TOOL_OUTPUT_THRESHOLD_CHARACTERS : DEFAULT_TOOL_OUTPUT_ARTIFACT_THRESHOLD_CHARACTERS,
|
|
6935
|
+
currentTurnThresholdCharacters: DEFAULT_CURRENT_TURN_TOOL_OUTPUT_THRESHOLD_CHARACTERS
|
|
6936
|
+
};
|
|
6937
|
+
}
|
|
6904
6938
|
function isRecord10(value) {
|
|
6905
6939
|
return typeof value === "object" && value !== null;
|
|
6906
6940
|
}
|
|
@@ -7138,7 +7172,7 @@ async function artifactizeLargeToolOutputs(messages, options = {}) {
|
|
|
7138
7172
|
savedCharacters
|
|
7139
7173
|
};
|
|
7140
7174
|
}
|
|
7141
|
-
var TOOL_OUTPUT_ARTIFACT_HANDLE_PREFIX = "lightcode-artifact://tool-output/sha256/", DEFAULT_TOOL_OUTPUT_ARTIFACT_THRESHOLD_CHARACTERS, DEFAULT_CURRENT_TURN_TOOL_OUTPUT_THRESHOLD_CHARACTERS =
|
|
7175
|
+
var TOOL_OUTPUT_ARTIFACT_HANDLE_PREFIX = "lightcode-artifact://tool-output/sha256/", DEFAULT_TOOL_OUTPUT_ARTIFACT_THRESHOLD_CHARACTERS, DEFAULT_CURRENT_TURN_TOOL_OUTPUT_THRESHOLD_CHARACTERS = 32000, DEFAULT_TOOL_OUTPUT_ARTIFACT_PREVIEW_CHARACTERS = 2400, MIN_TOOL_OUTPUT_ARTIFACT_PREVIEW_CHARACTERS = 512, SHA256_PATTERN, ARTIFACT_PREVIEW_MARKER = "[Lightcode tool-output artifact]";
|
|
7142
7176
|
var init_tool_output_artifacts = __esm(() => {
|
|
7143
7177
|
init_src();
|
|
7144
7178
|
DEFAULT_TOOL_OUTPUT_ARTIFACT_THRESHOLD_CHARACTERS = DEFAULT_TOOL_TEXT_OUTPUT_CHARS;
|
|
@@ -8625,7 +8659,9 @@ function createCodingAgent({
|
|
|
8625
8659
|
});
|
|
8626
8660
|
const artifactized = await artifactizeLargeToolOutputs(messages, {
|
|
8627
8661
|
signal: context.assemblyAbortSignal,
|
|
8628
|
-
|
|
8662
|
+
...resolveToolOutputArtifactThresholds({
|
|
8663
|
+
promptCacheActive: context.promptCacheActive ?? false
|
|
8664
|
+
})
|
|
8629
8665
|
});
|
|
8630
8666
|
const assembled = assembler.assembleModelMessages(artifactized.messages, {
|
|
8631
8667
|
preserveRecentTokens: context.preserveRecentTokens
|
|
@@ -8691,6 +8727,7 @@ function createCodingAgent({
|
|
|
8691
8727
|
reservedOutputTokens: options.maxOutputTokens,
|
|
8692
8728
|
preserveRecentTokens: options.preserveRecentTokens,
|
|
8693
8729
|
originalInputTokens: options.originalInputTokens,
|
|
8730
|
+
promptCacheActive: options.promptCacheActive,
|
|
8694
8731
|
assemblyAbortSignal: options.assemblyAbortSignal
|
|
8695
8732
|
},
|
|
8696
8733
|
instructions
|
|
@@ -13350,7 +13387,8 @@ function summarizeRawModel(raw3) {
|
|
|
13350
13387
|
const pricingRaw = Reflect.get(raw3, "pricing");
|
|
13351
13388
|
const pricing = typeof pricingRaw === "object" && pricingRaw !== null ? {
|
|
13352
13389
|
prompt: typeof Reflect.get(pricingRaw, "prompt") === "string" ? Reflect.get(pricingRaw, "prompt") : null,
|
|
13353
|
-
completion: typeof Reflect.get(pricingRaw, "completion") === "string" ? Reflect.get(pricingRaw, "completion") : null
|
|
13390
|
+
completion: typeof Reflect.get(pricingRaw, "completion") === "string" ? Reflect.get(pricingRaw, "completion") : null,
|
|
13391
|
+
inputCacheRead: typeof Reflect.get(pricingRaw, "input_cache_read") === "string" ? Reflect.get(pricingRaw, "input_cache_read") : null
|
|
13354
13392
|
} : null;
|
|
13355
13393
|
return {
|
|
13356
13394
|
id,
|
|
@@ -13380,12 +13418,12 @@ async function listOpenRouterModels({
|
|
|
13380
13418
|
forceRefresh = false
|
|
13381
13419
|
} = {}) {
|
|
13382
13420
|
const cache = readCache();
|
|
13383
|
-
if (!forceRefresh && cache && Date.now() - cache.fetchedAt < cacheTtlMs) {
|
|
13421
|
+
if (!forceRefresh && cache && cache.version === modelsCacheVersion && Date.now() - cache.fetchedAt < cacheTtlMs) {
|
|
13384
13422
|
return { models: cache.models, fromCache: true };
|
|
13385
13423
|
}
|
|
13386
13424
|
try {
|
|
13387
13425
|
const models = await fetchModelsFromApi();
|
|
13388
|
-
writeCache({ fetchedAt: Date.now(), models });
|
|
13426
|
+
writeCache({ version: modelsCacheVersion, fetchedAt: Date.now(), models });
|
|
13389
13427
|
return { models, fromCache: false };
|
|
13390
13428
|
} catch (error) {
|
|
13391
13429
|
logger5.warn("openrouter_models_fetch_failed", {
|
|
@@ -13423,12 +13461,14 @@ function toCapabilityPricing(pricing) {
|
|
|
13423
13461
|
if (!Number.isFinite(promptPerToken) || !Number.isFinite(completionPerToken)) {
|
|
13424
13462
|
return null;
|
|
13425
13463
|
}
|
|
13464
|
+
const cacheReadPerToken = pricing.inputCacheRead ? Number.parseFloat(pricing.inputCacheRead) : Number.NaN;
|
|
13426
13465
|
return {
|
|
13427
13466
|
inputPerMTok: promptPerToken * 1e6,
|
|
13428
|
-
outputPerMTok: completionPerToken * 1e6
|
|
13467
|
+
outputPerMTok: completionPerToken * 1e6,
|
|
13468
|
+
cachedInputPerMTok: Number.isFinite(cacheReadPerToken) && cacheReadPerToken > 0 ? cacheReadPerToken * 1e6 : null
|
|
13429
13469
|
};
|
|
13430
13470
|
}
|
|
13431
|
-
var logger5, openRouterModelsUrl = "https://openrouter.ai/api/v1/models", cacheTtlMs, fetchTimeoutMs = 15000, openRouterModelSummarySchema, modelsCacheSchema;
|
|
13471
|
+
var logger5, openRouterModelsUrl = "https://openrouter.ai/api/v1/models", cacheTtlMs, fetchTimeoutMs = 15000, modelsCacheVersion = 2, openRouterModelSummarySchema, modelsCacheSchema;
|
|
13432
13472
|
var init_openrouter_models = __esm(() => {
|
|
13433
13473
|
init_src();
|
|
13434
13474
|
init_network_fetch();
|
|
@@ -13443,10 +13483,12 @@ var init_openrouter_models = __esm(() => {
|
|
|
13443
13483
|
supportsReasoning: z40.boolean(),
|
|
13444
13484
|
pricing: z40.object({
|
|
13445
13485
|
prompt: z40.string().nullable(),
|
|
13446
|
-
completion: z40.string().nullable()
|
|
13486
|
+
completion: z40.string().nullable(),
|
|
13487
|
+
inputCacheRead: z40.string().nullable().default(null)
|
|
13447
13488
|
}).nullable()
|
|
13448
13489
|
});
|
|
13449
13490
|
modelsCacheSchema = z40.object({
|
|
13491
|
+
version: z40.number().int().optional(),
|
|
13450
13492
|
fetchedAt: z40.number(),
|
|
13451
13493
|
models: z40.array(openRouterModelSummarySchema)
|
|
13452
13494
|
});
|
|
@@ -13730,6 +13772,7 @@ function resolveAnthropicModel({
|
|
|
13730
13772
|
contextWindow: getModelContextWindow("anthropic", modelId),
|
|
13731
13773
|
missingCredentialHints,
|
|
13732
13774
|
supportsPromptCaching: true,
|
|
13775
|
+
rollingCompaction: false,
|
|
13733
13776
|
pricing: getAnthropicModelPricing(modelId),
|
|
13734
13777
|
webSearchCapability,
|
|
13735
13778
|
providerTools: webSearchCapability.execution === "provider" ? {
|
|
@@ -13745,7 +13788,7 @@ function resolveCapabilityPricing(capabilities, cachedInputFactor) {
|
|
|
13745
13788
|
return {
|
|
13746
13789
|
inputPerMTok: pricing.inputPerMTok,
|
|
13747
13790
|
outputPerMTok: pricing.outputPerMTok,
|
|
13748
|
-
cachedInputPerMTok: cachedInputFactor === null ? null : pricing.inputPerMTok * cachedInputFactor
|
|
13791
|
+
cachedInputPerMTok: pricing.cachedInputPerMTok ?? (cachedInputFactor === null ? null : pricing.inputPerMTok * cachedInputFactor)
|
|
13749
13792
|
};
|
|
13750
13793
|
}
|
|
13751
13794
|
function resolveOpenAITransportModel({
|
|
@@ -13794,6 +13837,8 @@ function resolveOpenAITransportModel({
|
|
|
13794
13837
|
env,
|
|
13795
13838
|
storedCredentials
|
|
13796
13839
|
});
|
|
13840
|
+
const openRouterAnthropicFamily = isOpenRouter && isOpenRouterAnthropicFamilyModel(configuredModel);
|
|
13841
|
+
const openRouterCatalogCaching = isOpenRouter && (capabilities?.pricing?.cachedInputPerMTok ?? 0) > 0;
|
|
13797
13842
|
return {
|
|
13798
13843
|
provider: config6.provider,
|
|
13799
13844
|
configuredModel,
|
|
@@ -13807,8 +13852,9 @@ function resolveOpenAITransportModel({
|
|
|
13807
13852
|
contextWindow: capabilities?.contextLength ?? getModelContextWindow(config6.provider, configuredModel),
|
|
13808
13853
|
missingCredentialHints: apiKey ? [] : isOpenCodeZen ? ["Set OPENCODE_API_KEY to call OpenCode Zen models."] : isOpenRouter ? ["Set OPENROUTER_API_KEY to call OpenRouter models."] : [],
|
|
13809
13854
|
maxCompletionTokens: capabilities?.maxCompletionTokens ?? null,
|
|
13810
|
-
supportsPromptCaching:
|
|
13811
|
-
|
|
13855
|
+
supportsPromptCaching: openRouterAnthropicFamily || openRouterCatalogCaching,
|
|
13856
|
+
rollingCompaction: !openRouterAnthropicFamily,
|
|
13857
|
+
pricing: resolveCapabilityPricing(capabilities, openRouterAnthropicFamily ? 0.1 : null),
|
|
13812
13858
|
webSearchCapability,
|
|
13813
13859
|
providerTools: openRouterProvider && webSearchCapability.execution === "provider" ? {
|
|
13814
13860
|
web_search: createOpenRouterWebSearchTool(openRouterProvider, webSearchCapability)
|
|
@@ -14066,6 +14112,7 @@ var init_subagent_task_store = __esm(() => {
|
|
|
14066
14112
|
});
|
|
14067
14113
|
|
|
14068
14114
|
// apps/server/src/lib/workspace-context.ts
|
|
14115
|
+
import { execFile as execFile3 } from "child_process";
|
|
14069
14116
|
import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
|
|
14070
14117
|
import {
|
|
14071
14118
|
chmod as chmod2,
|
|
@@ -14080,6 +14127,7 @@ import {
|
|
|
14080
14127
|
} from "fs/promises";
|
|
14081
14128
|
import { platform as platform3 } from "os";
|
|
14082
14129
|
import path21 from "path";
|
|
14130
|
+
import { promisify as promisify4 } from "util";
|
|
14083
14131
|
import { z as z41 } from "zod";
|
|
14084
14132
|
function isContainedPath(root, candidate) {
|
|
14085
14133
|
const relative = path21.relative(root, candidate);
|
|
@@ -14088,12 +14136,16 @@ function isContainedPath(root, candidate) {
|
|
|
14088
14136
|
function escapeAttribute(value) {
|
|
14089
14137
|
return value.replaceAll("&", "&").replaceAll('"', """).replaceAll("<", "<").replaceAll(">", ">");
|
|
14090
14138
|
}
|
|
14091
|
-
function
|
|
14139
|
+
function sessionStatePath({
|
|
14140
|
+
directory,
|
|
14092
14141
|
sessionId,
|
|
14093
14142
|
dataDir
|
|
14094
14143
|
}) {
|
|
14095
14144
|
const sessionKey = createHash5("sha256").update(sessionId).digest("hex");
|
|
14096
|
-
return path21.join(dataDir ?? getLightcodeDataDir(),
|
|
14145
|
+
return path21.join(dataDir ?? getLightcodeDataDir(), directory, `${sessionKey}.json`);
|
|
14146
|
+
}
|
|
14147
|
+
function instructionEpochStatePath(options) {
|
|
14148
|
+
return sessionStatePath({ directory: "instruction-epochs", ...options });
|
|
14097
14149
|
}
|
|
14098
14150
|
async function loadInstructionEpochs(statePath) {
|
|
14099
14151
|
try {
|
|
@@ -14114,6 +14166,9 @@ async function loadInstructionEpochs(statePath) {
|
|
|
14114
14166
|
return [];
|
|
14115
14167
|
}
|
|
14116
14168
|
async function persistInstructionEpochs(statePath, epochs) {
|
|
14169
|
+
await writePrivateJsonFile(statePath, { epochs });
|
|
14170
|
+
}
|
|
14171
|
+
async function writePrivateJsonFile(statePath, value) {
|
|
14117
14172
|
const directory = path21.dirname(statePath);
|
|
14118
14173
|
await mkdir3(directory, { recursive: true, mode: 448 });
|
|
14119
14174
|
await chmod2(directory, 448).catch(() => {
|
|
@@ -14121,7 +14176,7 @@ async function persistInstructionEpochs(statePath, epochs) {
|
|
|
14121
14176
|
});
|
|
14122
14177
|
const temporaryPath = `${statePath}.${process.pid}.${randomUUID3()}.tmp`;
|
|
14123
14178
|
try {
|
|
14124
|
-
await writeFile2(temporaryPath, `${JSON.stringify(
|
|
14179
|
+
await writeFile2(temporaryPath, `${JSON.stringify(value, null, 2)}
|
|
14125
14180
|
`, { encoding: "utf8", flag: "wx", mode: 384 });
|
|
14126
14181
|
await rename2(temporaryPath, statePath);
|
|
14127
14182
|
await chmod2(statePath, 384).catch(() => {
|
|
@@ -14370,6 +14425,138 @@ async function buildDirectoryListing(cwd) {
|
|
|
14370
14425
|
return "(unable to list directory)";
|
|
14371
14426
|
}
|
|
14372
14427
|
}
|
|
14428
|
+
async function listRepositoryFiles(cwd) {
|
|
14429
|
+
try {
|
|
14430
|
+
const { stdout } = await execFileAsync3("git", ["ls-files", "--cached", "--others", "--exclude-standard", "-z"], {
|
|
14431
|
+
cwd,
|
|
14432
|
+
encoding: "utf8",
|
|
14433
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
14434
|
+
timeout: repositoryFileListTimeoutMs,
|
|
14435
|
+
windowsHide: true
|
|
14436
|
+
});
|
|
14437
|
+
return [...new Set(stdout.split("\x00").filter(Boolean))];
|
|
14438
|
+
} catch {
|
|
14439
|
+
return null;
|
|
14440
|
+
}
|
|
14441
|
+
}
|
|
14442
|
+
function createRepositoryTreeNode() {
|
|
14443
|
+
return { directories: new Map, files: [], fileCount: 0 };
|
|
14444
|
+
}
|
|
14445
|
+
function compareNames(left, right) {
|
|
14446
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
14447
|
+
}
|
|
14448
|
+
function visibleSubdirectories(node) {
|
|
14449
|
+
const all = [...node.directories.entries()].sort(([left], [right]) => compareNames(left, right)).map(([name, child]) => ({ name, node: child }));
|
|
14450
|
+
return {
|
|
14451
|
+
shown: all.slice(0, maxRepositoryMapChildren),
|
|
14452
|
+
hiddenCount: Math.max(0, all.length - maxRepositoryMapChildren)
|
|
14453
|
+
};
|
|
14454
|
+
}
|
|
14455
|
+
function subdirectoryLines(node, depth) {
|
|
14456
|
+
const indent = " ".repeat(depth);
|
|
14457
|
+
const { shown, hiddenCount } = visibleSubdirectories(node);
|
|
14458
|
+
const lines = shown.map(({ name, node: child }) => `${indent}${name}/ (${child.fileCount})`);
|
|
14459
|
+
if (hiddenCount > 0) {
|
|
14460
|
+
lines.push(`${indent}\u2026 ${hiddenCount} more folders`);
|
|
14461
|
+
}
|
|
14462
|
+
return lines;
|
|
14463
|
+
}
|
|
14464
|
+
function fileNameLines(node, depth) {
|
|
14465
|
+
const indent = " ".repeat(depth);
|
|
14466
|
+
const files = [...node.files].sort(compareNames);
|
|
14467
|
+
const lines = files.slice(0, maxRepositoryMapChildren).map((name) => `${indent}${name}`);
|
|
14468
|
+
if (files.length > lines.length) {
|
|
14469
|
+
lines.push(`${indent}\u2026 ${files.length - lines.length} more files`);
|
|
14470
|
+
}
|
|
14471
|
+
return lines;
|
|
14472
|
+
}
|
|
14473
|
+
function filePlaceholderLines(node, depth) {
|
|
14474
|
+
return node.files.length > 0 ? [`${" ".repeat(depth)}\u2026 ${node.files.length} files`] : [];
|
|
14475
|
+
}
|
|
14476
|
+
function linesCost(lines) {
|
|
14477
|
+
return lines.reduce((sum, line) => sum + line.length + 1, 0);
|
|
14478
|
+
}
|
|
14479
|
+
function formatRepositoryMap(paths, maxChars = maxRepositoryMapChars) {
|
|
14480
|
+
const root = createRepositoryTreeNode();
|
|
14481
|
+
for (const filePath of paths) {
|
|
14482
|
+
const segments = filePath.split("/").filter(Boolean);
|
|
14483
|
+
const fileName = segments.pop();
|
|
14484
|
+
if (!fileName) {
|
|
14485
|
+
continue;
|
|
14486
|
+
}
|
|
14487
|
+
let current = root;
|
|
14488
|
+
current.fileCount += 1;
|
|
14489
|
+
for (const segment of segments) {
|
|
14490
|
+
let child = current.directories.get(segment);
|
|
14491
|
+
if (!child) {
|
|
14492
|
+
child = createRepositoryTreeNode();
|
|
14493
|
+
current.directories.set(segment, child);
|
|
14494
|
+
}
|
|
14495
|
+
child.fileCount += 1;
|
|
14496
|
+
current = child;
|
|
14497
|
+
}
|
|
14498
|
+
current.files.push(fileName);
|
|
14499
|
+
}
|
|
14500
|
+
if (root.fileCount === 0) {
|
|
14501
|
+
return "(no files)";
|
|
14502
|
+
}
|
|
14503
|
+
const order = [
|
|
14504
|
+
{ node: root, depth: 0 }
|
|
14505
|
+
];
|
|
14506
|
+
const expanded = new Set;
|
|
14507
|
+
let used = 0;
|
|
14508
|
+
for (let index = 0;index < order.length; index += 1) {
|
|
14509
|
+
const { node, depth } = order[index];
|
|
14510
|
+
const cost = linesCost(subdirectoryLines(node, depth)) + linesCost(filePlaceholderLines(node, depth));
|
|
14511
|
+
if (node !== root && used + cost > maxChars) {
|
|
14512
|
+
continue;
|
|
14513
|
+
}
|
|
14514
|
+
used += cost;
|
|
14515
|
+
expanded.add(node);
|
|
14516
|
+
for (const { name, node: child } of visibleSubdirectories(node).shown) {
|
|
14517
|
+
if (!name.startsWith(".")) {
|
|
14518
|
+
order.push({ node: child, depth: depth + 1 });
|
|
14519
|
+
}
|
|
14520
|
+
}
|
|
14521
|
+
}
|
|
14522
|
+
const fileCandidates = order.filter(({ node }) => expanded.has(node) && node.files.length > 0).sort((left, right) => left.node === root ? -1 : right.node === root ? 1 : left.node.files.length - right.node.files.length);
|
|
14523
|
+
const filesShown = new Set;
|
|
14524
|
+
for (const { node, depth } of fileCandidates) {
|
|
14525
|
+
const delta = linesCost(fileNameLines(node, depth)) - linesCost(filePlaceholderLines(node, depth));
|
|
14526
|
+
if (used + delta > maxChars) {
|
|
14527
|
+
continue;
|
|
14528
|
+
}
|
|
14529
|
+
used += delta;
|
|
14530
|
+
filesShown.add(node);
|
|
14531
|
+
}
|
|
14532
|
+
const lines = [];
|
|
14533
|
+
const render = (node, depth) => {
|
|
14534
|
+
const { shown } = visibleSubdirectories(node);
|
|
14535
|
+
subdirectoryLines(node, depth).forEach((line, index) => {
|
|
14536
|
+
lines.push(line);
|
|
14537
|
+
const child = shown[index]?.node;
|
|
14538
|
+
if (child && expanded.has(child)) {
|
|
14539
|
+
render(child, depth + 1);
|
|
14540
|
+
}
|
|
14541
|
+
});
|
|
14542
|
+
lines.push(...filesShown.has(node) ? fileNameLines(node, depth) : filePlaceholderLines(node, depth));
|
|
14543
|
+
};
|
|
14544
|
+
render(root, 0);
|
|
14545
|
+
return lines.join(`
|
|
14546
|
+
`);
|
|
14547
|
+
}
|
|
14548
|
+
async function buildRepositoryOverview(cwd) {
|
|
14549
|
+
const files = await listRepositoryFiles(cwd);
|
|
14550
|
+
if (!files) {
|
|
14551
|
+
return ["Top-level entries:", await buildDirectoryListing(cwd)].join(`
|
|
14552
|
+
`);
|
|
14553
|
+
}
|
|
14554
|
+
return [
|
|
14555
|
+
`Repository files (${files.length}; dir/ (N) = N files inside, "\u2026" = more not shown; use glob_search or list_files for the rest):`,
|
|
14556
|
+
formatRepositoryMap(files)
|
|
14557
|
+
].join(`
|
|
14558
|
+
`);
|
|
14559
|
+
}
|
|
14373
14560
|
function buildGitSummary(cwd) {
|
|
14374
14561
|
const cached = gitSummaryCache.get(cwd);
|
|
14375
14562
|
if (cached && cached.expiresAt > Date.now()) {
|
|
@@ -14449,13 +14636,98 @@ async function buildWorkspaceInstructionBaseline({
|
|
|
14449
14636
|
`)
|
|
14450
14637
|
};
|
|
14451
14638
|
}
|
|
14639
|
+
function memoizeEnvironmentSnapshot(statePath, snapshot) {
|
|
14640
|
+
environmentSnapshotMemo.delete(statePath);
|
|
14641
|
+
environmentSnapshotMemo.set(statePath, snapshot);
|
|
14642
|
+
if (environmentSnapshotMemo.size > maxMemoizedEnvironmentSnapshots) {
|
|
14643
|
+
const oldest = environmentSnapshotMemo.keys().next().value;
|
|
14644
|
+
if (oldest !== undefined) {
|
|
14645
|
+
environmentSnapshotMemo.delete(oldest);
|
|
14646
|
+
}
|
|
14647
|
+
}
|
|
14648
|
+
return snapshot;
|
|
14649
|
+
}
|
|
14650
|
+
async function captureEnvironmentSnapshot(cwd) {
|
|
14651
|
+
const [overview, git] = await Promise.all([
|
|
14652
|
+
buildRepositoryOverview(cwd),
|
|
14653
|
+
buildGitSummary(cwd)
|
|
14654
|
+
]);
|
|
14655
|
+
const capturedAt = new Date().toISOString();
|
|
14656
|
+
return {
|
|
14657
|
+
cwd,
|
|
14658
|
+
capturedAt,
|
|
14659
|
+
body: [
|
|
14660
|
+
`Working directory: ${cwd}`,
|
|
14661
|
+
`Platform: ${describePlatform()}`,
|
|
14662
|
+
`Date: ${capturedAt.slice(0, 10)} (session start)`,
|
|
14663
|
+
"",
|
|
14664
|
+
"Git (snapshot from session start; call git_status for the current state):",
|
|
14665
|
+
git,
|
|
14666
|
+
"",
|
|
14667
|
+
overview
|
|
14668
|
+
].join(`
|
|
14669
|
+
`)
|
|
14670
|
+
};
|
|
14671
|
+
}
|
|
14672
|
+
async function loadEnvironmentSnapshot(statePath, cwd) {
|
|
14673
|
+
const memoized = environmentSnapshotMemo.get(statePath);
|
|
14674
|
+
if (memoized?.cwd === cwd) {
|
|
14675
|
+
return memoized;
|
|
14676
|
+
}
|
|
14677
|
+
try {
|
|
14678
|
+
const parsed = environmentSnapshotSchema.safeParse(JSON.parse(await readFile4(statePath, "utf8")));
|
|
14679
|
+
if (parsed.success && parsed.data.cwd === cwd) {
|
|
14680
|
+
return memoizeEnvironmentSnapshot(statePath, parsed.data);
|
|
14681
|
+
}
|
|
14682
|
+
if (!parsed.success) {
|
|
14683
|
+
logger7.warn("workspace_environment_snapshot_invalid", { statePath });
|
|
14684
|
+
}
|
|
14685
|
+
} catch (error) {
|
|
14686
|
+
const code = isRecord3(error) ? Reflect.get(error, "code") : undefined;
|
|
14687
|
+
if (code !== "ENOENT") {
|
|
14688
|
+
logger7.warn("workspace_environment_snapshot_load_failed", {
|
|
14689
|
+
statePath,
|
|
14690
|
+
error: getErrorMessage(error)
|
|
14691
|
+
});
|
|
14692
|
+
}
|
|
14693
|
+
}
|
|
14694
|
+
return null;
|
|
14695
|
+
}
|
|
14696
|
+
async function resolveEnvironmentSnapshot({
|
|
14697
|
+
cwd,
|
|
14698
|
+
sessionId,
|
|
14699
|
+
dataDir
|
|
14700
|
+
}) {
|
|
14701
|
+
if (!sessionId) {
|
|
14702
|
+
return captureEnvironmentSnapshot(cwd);
|
|
14703
|
+
}
|
|
14704
|
+
const statePath = sessionStatePath({
|
|
14705
|
+
directory: "session-environment",
|
|
14706
|
+
sessionId,
|
|
14707
|
+
dataDir
|
|
14708
|
+
});
|
|
14709
|
+
const existing = await loadEnvironmentSnapshot(statePath, cwd);
|
|
14710
|
+
if (existing) {
|
|
14711
|
+
return existing;
|
|
14712
|
+
}
|
|
14713
|
+
const snapshot = memoizeEnvironmentSnapshot(statePath, await captureEnvironmentSnapshot(cwd));
|
|
14714
|
+
try {
|
|
14715
|
+
await writePrivateJsonFile(statePath, snapshot);
|
|
14716
|
+
} catch (error) {
|
|
14717
|
+
logger7.warn("workspace_environment_snapshot_persist_failed", {
|
|
14718
|
+
statePath,
|
|
14719
|
+
error: getErrorMessage(error)
|
|
14720
|
+
});
|
|
14721
|
+
}
|
|
14722
|
+
return snapshot;
|
|
14723
|
+
}
|
|
14452
14724
|
async function buildWorkspaceContext({
|
|
14453
14725
|
cwd,
|
|
14454
14726
|
sessionId,
|
|
14455
14727
|
relatedPaths,
|
|
14456
14728
|
dataDir
|
|
14457
14729
|
}) {
|
|
14458
|
-
const [baseline, nestedInstructions,
|
|
14730
|
+
const [baseline, nestedInstructions, snapshot] = await Promise.all([
|
|
14459
14731
|
buildWorkspaceInstructionBaseline({ cwd }),
|
|
14460
14732
|
buildNestedWorkspaceInstructionBlock({
|
|
14461
14733
|
cwd,
|
|
@@ -14463,26 +14735,16 @@ async function buildWorkspaceContext({
|
|
|
14463
14735
|
relatedPaths,
|
|
14464
14736
|
dataDir
|
|
14465
14737
|
}),
|
|
14466
|
-
|
|
14467
|
-
buildGitSummary(cwd)
|
|
14738
|
+
resolveEnvironmentSnapshot({ cwd, sessionId, dataDir })
|
|
14468
14739
|
]);
|
|
14469
14740
|
const skills = buildAvailableSkills(cwd);
|
|
14470
|
-
const today = new Date().toISOString().slice(0, 10);
|
|
14471
14741
|
const parts = [
|
|
14472
14742
|
baseline.block,
|
|
14473
14743
|
nestedInstructions ? `
|
|
14474
14744
|
${nestedInstructions}` : "",
|
|
14475
14745
|
"",
|
|
14476
14746
|
"<environment>",
|
|
14477
|
-
|
|
14478
|
-
`Platform: ${describePlatform()}`,
|
|
14479
|
-
`Date: ${today}`,
|
|
14480
|
-
"",
|
|
14481
|
-
"Git:",
|
|
14482
|
-
git,
|
|
14483
|
-
"",
|
|
14484
|
-
"Top-level entries:",
|
|
14485
|
-
listing,
|
|
14747
|
+
snapshot.body,
|
|
14486
14748
|
skills ? `
|
|
14487
14749
|
${skills}` : "",
|
|
14488
14750
|
"</environment>"
|
|
@@ -14493,48 +14755,12 @@ ${skills}` : "",
|
|
|
14493
14755
|
\u2026 (environment truncated)
|
|
14494
14756
|
</environment>` : block;
|
|
14495
14757
|
}
|
|
14496
|
-
|
|
14497
|
-
cwd,
|
|
14498
|
-
sessionId,
|
|
14499
|
-
relatedPaths,
|
|
14500
|
-
dataDir
|
|
14501
|
-
}) {
|
|
14502
|
-
const [baseline, nestedInstructions, git] = await Promise.all([
|
|
14503
|
-
buildWorkspaceInstructionBaseline({ cwd }),
|
|
14504
|
-
buildNestedWorkspaceInstructionBlock({
|
|
14505
|
-
cwd,
|
|
14506
|
-
sessionId,
|
|
14507
|
-
relatedPaths,
|
|
14508
|
-
dataDir
|
|
14509
|
-
}),
|
|
14510
|
-
buildGitSummary(cwd)
|
|
14511
|
-
]);
|
|
14512
|
-
const skills = buildAvailableSkills(cwd);
|
|
14513
|
-
const today = new Date().toISOString().slice(0, 10);
|
|
14514
|
-
return [
|
|
14515
|
-
baseline.block,
|
|
14516
|
-
nestedInstructions ? `
|
|
14517
|
-
${nestedInstructions}` : "",
|
|
14518
|
-
"",
|
|
14519
|
-
"<environment>",
|
|
14520
|
-
`Working directory: ${cwd}`,
|
|
14521
|
-
`Platform: ${describePlatform()}`,
|
|
14522
|
-
`Date: ${today}`,
|
|
14523
|
-
"(Top-level listing is available through list_files; current git state follows.)",
|
|
14524
|
-
"",
|
|
14525
|
-
"Git:",
|
|
14526
|
-
git,
|
|
14527
|
-
skills ? `
|
|
14528
|
-
${skills}` : "",
|
|
14529
|
-
"</environment>"
|
|
14530
|
-
].join(`
|
|
14531
|
-
`);
|
|
14532
|
-
}
|
|
14533
|
-
var logger7, projectInstructionNames, maxListedEntries = 40, maxInstructionChars = 6000, maxNestedInstructionChars = 6000, maxNestedInstructionFileChars = 1600, maxBlockChars = 18000, instructionEpochSchema, instructionEpochStateSchema, gitSummaryCacheTtlMs = 5000, gitSummaryCache;
|
|
14758
|
+
var execFileAsync3, logger7, projectInstructionNames, maxListedEntries = 40, maxInstructionChars = 6000, maxNestedInstructionChars = 6000, maxNestedInstructionFileChars = 1600, maxRepositoryMapChars = 6000, maxRepositoryMapChildren = 32, maxBlockChars = 24000, repositoryFileListTimeoutMs = 5000, instructionEpochSchema, instructionEpochStateSchema, gitSummaryCacheTtlMs = 5000, gitSummaryCache, environmentSnapshotSchema, maxMemoizedEnvironmentSnapshots = 256, environmentSnapshotMemo;
|
|
14534
14759
|
var init_workspace_context = __esm(() => {
|
|
14535
14760
|
init_runtime17();
|
|
14536
14761
|
init_src2();
|
|
14537
14762
|
init_src();
|
|
14763
|
+
execFileAsync3 = promisify4(execFile3);
|
|
14538
14764
|
logger7 = createLogger("workspace-context");
|
|
14539
14765
|
projectInstructionNames = ["AGENTS.md", "CLAUDE.md"];
|
|
14540
14766
|
instructionEpochSchema = z41.object({
|
|
@@ -14548,6 +14774,12 @@ var init_workspace_context = __esm(() => {
|
|
|
14548
14774
|
epochs: z41.array(instructionEpochSchema)
|
|
14549
14775
|
});
|
|
14550
14776
|
gitSummaryCache = new Map;
|
|
14777
|
+
environmentSnapshotSchema = z41.object({
|
|
14778
|
+
cwd: z41.string().min(1),
|
|
14779
|
+
capturedAt: z41.string().min(1),
|
|
14780
|
+
body: z41.string()
|
|
14781
|
+
});
|
|
14782
|
+
environmentSnapshotMemo = new Map;
|
|
14551
14783
|
});
|
|
14552
14784
|
|
|
14553
14785
|
// apps/server/src/lib/subagent-runner.ts
|
|
@@ -15138,6 +15370,7 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
15138
15370
|
});
|
|
15139
15371
|
const contextConfig = lightcodeConfigResult.config.context;
|
|
15140
15372
|
const cacheActive = resolvedProviderModel.supportsPromptCaching;
|
|
15373
|
+
const rollingCompaction = resolvedProviderModel.rollingCompaction ?? !cacheActive;
|
|
15141
15374
|
const endpointLimitTokens = getLearnedContextLimit(chatModelId);
|
|
15142
15375
|
const effectiveContextWindow = resolveContextWindowTokens({
|
|
15143
15376
|
config: contextConfig,
|
|
@@ -15145,9 +15378,12 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
15145
15378
|
endpointLimitTokens
|
|
15146
15379
|
});
|
|
15147
15380
|
const reservedOutputTokens = Math.min(resolveMaxOutputTokens(lightcodeConfigResult.config.maxOutputTokens, lightcodeConfigDefaults.maxOutputTokens, resolvedProviderModel.maxCompletionTokens), Math.max(1024, Math.floor(effectiveContextWindow / 2)));
|
|
15148
|
-
const isFirstAssistantTurn = !validatedMessages.some((message) => message.role === "assistant");
|
|
15149
15381
|
const relatedPaths = collectRelatedWorkspacePaths(validatedMessages);
|
|
15150
|
-
const environmentContext =
|
|
15382
|
+
const environmentContext = await buildWorkspaceContext({
|
|
15383
|
+
cwd,
|
|
15384
|
+
sessionId,
|
|
15385
|
+
relatedPaths
|
|
15386
|
+
});
|
|
15151
15387
|
const availableSkillNames = listSkills({ cwd }).map((skill) => skill.name);
|
|
15152
15388
|
const useFastChatPath = shouldUseFastChatPath({
|
|
15153
15389
|
messages: providerInputMessages,
|
|
@@ -15217,6 +15453,7 @@ ${environmentContext}` : agentCallSettings.instructions;
|
|
|
15217
15453
|
endpointLimitTokens,
|
|
15218
15454
|
pendingInteractionCount,
|
|
15219
15455
|
cacheActive,
|
|
15456
|
+
rollingCompaction,
|
|
15220
15457
|
fixedInputTokens: turnAssembler.fixedInputTokens
|
|
15221
15458
|
});
|
|
15222
15459
|
if (contextConfig.autoCompact) {
|
|
@@ -15247,6 +15484,7 @@ ${environmentContext}` : agentCallSettings.instructions;
|
|
|
15247
15484
|
endpointLimitTokens,
|
|
15248
15485
|
pendingInteractionCount,
|
|
15249
15486
|
cacheActive,
|
|
15487
|
+
rollingCompaction,
|
|
15250
15488
|
fixedInputTokens: turnAssembler.fixedInputTokens
|
|
15251
15489
|
});
|
|
15252
15490
|
logger11.info("context_auto_compacted", {
|
|
@@ -15289,6 +15527,7 @@ ${environmentContext}` : agentCallSettings.instructions;
|
|
|
15289
15527
|
endpointLimitTokens,
|
|
15290
15528
|
pendingInteractionCount: 0,
|
|
15291
15529
|
cacheActive,
|
|
15530
|
+
rollingCompaction,
|
|
15292
15531
|
fixedInputTokens: turnAssembler.fixedInputTokens
|
|
15293
15532
|
});
|
|
15294
15533
|
if (overflowView.coveredMessages.length > 0) {
|
|
@@ -15315,6 +15554,7 @@ ${environmentContext}` : agentCallSettings.instructions;
|
|
|
15315
15554
|
endpointLimitTokens,
|
|
15316
15555
|
pendingInteractionCount: 0,
|
|
15317
15556
|
cacheActive,
|
|
15557
|
+
rollingCompaction,
|
|
15318
15558
|
fixedInputTokens: turnAssembler.fixedInputTokens
|
|
15319
15559
|
});
|
|
15320
15560
|
logger11.info("context_overflow_recovery", {
|
|
@@ -15368,7 +15608,7 @@ ${environmentContext}` : agentCallSettings.instructions;
|
|
|
15368
15608
|
noteContextOverflow(sessionId);
|
|
15369
15609
|
};
|
|
15370
15610
|
const maybeRunRollingCompaction = async (finishedMessages) => {
|
|
15371
|
-
if (
|
|
15611
|
+
if (!rollingCompaction || !contextConfig.autoCompact) {
|
|
15372
15612
|
return;
|
|
15373
15613
|
}
|
|
15374
15614
|
if (rollingCompactionInFlight.has(sessionId)) {
|
|
@@ -15376,38 +15616,45 @@ ${environmentContext}` : agentCallSettings.instructions;
|
|
|
15376
15616
|
}
|
|
15377
15617
|
rollingCompactionInFlight.add(sessionId);
|
|
15378
15618
|
try {
|
|
15379
|
-
|
|
15380
|
-
const
|
|
15381
|
-
|
|
15382
|
-
|
|
15383
|
-
|
|
15384
|
-
|
|
15385
|
-
|
|
15386
|
-
|
|
15387
|
-
|
|
15388
|
-
|
|
15389
|
-
|
|
15390
|
-
|
|
15391
|
-
|
|
15392
|
-
|
|
15619
|
+
let latestState = await loadSessionContextStateSafe(sessionId);
|
|
15620
|
+
const pendingInteractionCount2 = await countPendingChatInteractions(sessionId);
|
|
15621
|
+
for (let round = 1;round <= MAX_ROLLING_COMPACTION_ROUNDS; round += 1) {
|
|
15622
|
+
const rollingView = buildProviderView({
|
|
15623
|
+
messages: finishedMessages,
|
|
15624
|
+
contextState: latestState,
|
|
15625
|
+
config: contextConfig,
|
|
15626
|
+
modelContextWindow: resolvedProviderModel.contextWindow,
|
|
15627
|
+
reservedOutputTokens: budgetReservedOutputTokens,
|
|
15628
|
+
endpointLimitTokens,
|
|
15629
|
+
pendingInteractionCount: pendingInteractionCount2,
|
|
15630
|
+
cacheActive,
|
|
15631
|
+
rollingCompaction,
|
|
15632
|
+
continueRolling: round > 1,
|
|
15633
|
+
fixedInputTokens: turnAssembler.fixedInputTokens
|
|
15634
|
+
});
|
|
15635
|
+
if (!rollingView.needsRollingCompaction) {
|
|
15636
|
+
return;
|
|
15637
|
+
}
|
|
15638
|
+
const compaction = await compactSessionContext({
|
|
15639
|
+
sessionId,
|
|
15640
|
+
coveredMessages: rollingView.coveredMessages,
|
|
15641
|
+
previousState: latestState,
|
|
15642
|
+
model: resolvedProviderModel.model,
|
|
15643
|
+
modelId: chatModelId,
|
|
15644
|
+
cwd,
|
|
15645
|
+
config: contextConfig,
|
|
15646
|
+
estimatedTokens: rollingView.estimate.tokens,
|
|
15647
|
+
contextWindow: rollingView.contextWindow
|
|
15648
|
+
});
|
|
15649
|
+
latestState = compaction.state;
|
|
15650
|
+
logger11.info("context_rolling_compaction", {
|
|
15651
|
+
sessionId,
|
|
15652
|
+
round,
|
|
15653
|
+
tier: compaction.state.tier,
|
|
15654
|
+
usedFallback: compaction.usedFallback,
|
|
15655
|
+
coveredMessageCount: compaction.state.coveredMessageCount
|
|
15656
|
+
});
|
|
15393
15657
|
}
|
|
15394
|
-
const compaction = await compactSessionContext({
|
|
15395
|
-
sessionId,
|
|
15396
|
-
coveredMessages: rollingView.coveredMessages,
|
|
15397
|
-
previousState: latestState,
|
|
15398
|
-
model: resolvedProviderModel.model,
|
|
15399
|
-
modelId: chatModelId,
|
|
15400
|
-
cwd,
|
|
15401
|
-
config: contextConfig,
|
|
15402
|
-
estimatedTokens: rollingView.estimate.tokens,
|
|
15403
|
-
contextWindow: rollingView.contextWindow
|
|
15404
|
-
});
|
|
15405
|
-
logger11.info("context_rolling_compaction", {
|
|
15406
|
-
sessionId,
|
|
15407
|
-
tier: compaction.state.tier,
|
|
15408
|
-
usedFallback: compaction.usedFallback,
|
|
15409
|
-
coveredMessageCount: compaction.state.coveredMessageCount
|
|
15410
|
-
});
|
|
15411
15658
|
} catch (error) {
|
|
15412
15659
|
logger11.error("context_rolling_compaction_failed", {
|
|
15413
15660
|
sessionId,
|
|
@@ -15624,6 +15871,7 @@ ${environmentContext}` : agentCallSettings.instructions;
|
|
|
15624
15871
|
contextWindow: view.contextWindow,
|
|
15625
15872
|
preserveRecentTokens: view.preserveRecentTokens,
|
|
15626
15873
|
originalInputTokens: estimateStructuralTokens(providerInputMessages) + turnAssembler.fixedInputTokens,
|
|
15874
|
+
promptCacheActive: cacheActive,
|
|
15627
15875
|
assemblyAbortSignal: requestAbortSignal,
|
|
15628
15876
|
turnKey: [...validatedMessages].reverse().find((message) => message.role === "user")?.id
|
|
15629
15877
|
},
|
|
@@ -15689,7 +15937,7 @@ ${environmentContext}` : agentCallSettings.instructions;
|
|
|
15689
15937
|
}, 500);
|
|
15690
15938
|
}
|
|
15691
15939
|
}
|
|
15692
|
-
var logger11, recoverableDisconnectMessage = "Connection interrupted. Please retry or regenerate your last message.", providerBillingOrQuotaMessage, fastChatSystemPrompt, fastChatMaxOutputTokens = 512, MAX_COMPACTION_ROUNDS = 1, rollingCompactionInFlight;
|
|
15940
|
+
var logger11, recoverableDisconnectMessage = "Connection interrupted. Please retry or regenerate your last message.", providerBillingOrQuotaMessage, fastChatSystemPrompt, fastChatMaxOutputTokens = 512, MAX_COMPACTION_ROUNDS = 1, MAX_ROLLING_COMPACTION_ROUNDS = 4, rollingCompactionInFlight;
|
|
15693
15941
|
var init_chat_stream = __esm(() => {
|
|
15694
15942
|
init_src2();
|
|
15695
15943
|
init_src();
|
|
@@ -15751,11 +15999,7 @@ async function assembleStoredSessionProviderTurn({
|
|
|
15751
15999
|
messages,
|
|
15752
16000
|
cwd: session.cwd
|
|
15753
16001
|
});
|
|
15754
|
-
const environmentContext =
|
|
15755
|
-
cwd: session.cwd,
|
|
15756
|
-
sessionId: session.id,
|
|
15757
|
-
relatedPaths: collectRelatedWorkspacePaths(messages)
|
|
15758
|
-
}) : await buildWorkspaceContext({
|
|
16002
|
+
const environmentContext = await buildWorkspaceContext({
|
|
15759
16003
|
cwd: session.cwd,
|
|
15760
16004
|
sessionId: session.id,
|
|
15761
16005
|
relatedPaths: collectRelatedWorkspacePaths(messages)
|
|
@@ -15808,6 +16052,7 @@ async function assembleStoredSessionProviderTurn({
|
|
|
15808
16052
|
endpointLimitTokens,
|
|
15809
16053
|
reservedOutputTokens,
|
|
15810
16054
|
cacheActive: resolvedProviderModel.supportsPromptCaching,
|
|
16055
|
+
rollingCompaction: resolvedProviderModel.rollingCompaction ?? !resolvedProviderModel.supportsPromptCaching,
|
|
15811
16056
|
fixedInputTokens: baseAssembler.fixedInputTokens
|
|
15812
16057
|
});
|
|
15813
16058
|
const assembler = new ProviderTurnAssembler({
|
|
@@ -15820,7 +16065,10 @@ async function assembleStoredSessionProviderTurn({
|
|
|
15820
16065
|
});
|
|
15821
16066
|
const modelMessages = await convertToModelMessages(normalizeProviderMessages(view.providerMessages), { tools });
|
|
15822
16067
|
const artifactized = await artifactizeLargeToolOutputs(modelMessages, {
|
|
15823
|
-
signal: abortSignal
|
|
16068
|
+
signal: abortSignal,
|
|
16069
|
+
...resolveToolOutputArtifactThresholds({
|
|
16070
|
+
promptCacheActive: resolvedProviderModel.supportsPromptCaching
|
|
16071
|
+
})
|
|
15824
16072
|
});
|
|
15825
16073
|
const assembled = assembler.assembleModelMessages(artifactized.messages, { preserveRecentTokens: view.preserveRecentTokens });
|
|
15826
16074
|
return { view, assembled };
|