@kmugalkhod/lightcode 0.3.0 → 0.5.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 +20 -1
- package/cli.js +138 -39
- package/lightcode.cjs +63 -18
- package/package.json +2 -1
- package/server.js +408 -65
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, 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;
|
|
151
151
|
|
|
152
152
|
// packages/ai/src/coding-agent-modes.ts
|
|
153
153
|
import { z } from "zod";
|
|
@@ -724,13 +724,13 @@ var init_base_schemas = __esm(() => {
|
|
|
724
724
|
|
|
725
725
|
// packages/ai/src/bash/schema.ts
|
|
726
726
|
import { z as z5 } from "zod";
|
|
727
|
-
var bashDescription = "Run a shell command from workspace root with a timeout. Commands are classified before execution and may require approval.", bashInputSchema, bashProviderInputSchema, bashOutputSchema;
|
|
727
|
+
var bashDescription = "Run a shell command from workspace root with a timeout. Bound noisy output at the source (head/tail/grep/wc, or write to a file then read a range) rather than dumping everything \u2014 output is truncated head+tail and wastes tokens. Commands are classified before execution and may require approval.", bashInputSchema, bashProviderInputSchema, bashOutputSchema;
|
|
728
728
|
var init_schema = __esm(() => {
|
|
729
729
|
init_base_schemas();
|
|
730
730
|
bashInputSchema = z5.object({
|
|
731
731
|
command: z5.string().min(1).max(4000),
|
|
732
732
|
timeoutMs: integerRangeSchema(1000, 120000, "timeoutMs").optional().default(30000),
|
|
733
|
-
maxOutputChars: integerRangeSchema(200, MAX_TOOL_TEXT_OUTPUT_CHARS, "maxOutputChars").optional().default(
|
|
733
|
+
maxOutputChars: integerRangeSchema(200, MAX_TOOL_TEXT_OUTPUT_CHARS, "maxOutputChars").optional().default(DEFAULT_TOOL_TEXT_OUTPUT_CHARS)
|
|
734
734
|
});
|
|
735
735
|
bashProviderInputSchema = z5.object({
|
|
736
736
|
command: z5.string().min(1).max(4000)
|
|
@@ -840,7 +840,7 @@ var init_schema5 = __esm(() => {
|
|
|
840
840
|
path: boundedPathSchema.optional(),
|
|
841
841
|
staged: z9.boolean().optional().default(false),
|
|
842
842
|
statOnly: z9.boolean().optional().default(false),
|
|
843
|
-
maxChars: integerRangeSchema(200, MAX_TOOL_TEXT_OUTPUT_CHARS, "maxChars").optional().default(
|
|
843
|
+
maxChars: integerRangeSchema(200, MAX_TOOL_TEXT_OUTPUT_CHARS, "maxChars").optional().default(DEFAULT_TOOL_TEXT_OUTPUT_CHARS)
|
|
844
844
|
});
|
|
845
845
|
gitDiffProviderInputSchema = z9.object({
|
|
846
846
|
path: boundedPathSchema.optional(),
|
|
@@ -855,7 +855,7 @@ var init_schema5 = __esm(() => {
|
|
|
855
855
|
gitShowInputSchema = z9.object({
|
|
856
856
|
revision: z9.string().min(1).max(240),
|
|
857
857
|
path: boundedPathSchema.optional(),
|
|
858
|
-
maxChars: integerRangeSchema(200, MAX_TOOL_TEXT_OUTPUT_CHARS, "maxChars").optional().default(
|
|
858
|
+
maxChars: integerRangeSchema(200, MAX_TOOL_TEXT_OUTPUT_CHARS, "maxChars").optional().default(DEFAULT_TOOL_TEXT_OUTPUT_CHARS)
|
|
859
859
|
});
|
|
860
860
|
gitShowProviderInputSchema = z9.object({
|
|
861
861
|
revision: z9.string().min(1).max(240),
|
|
@@ -952,7 +952,7 @@ var init_schema6 = __esm(() => {
|
|
|
952
952
|
|
|
953
953
|
// packages/ai/src/read-file/schema.ts
|
|
954
954
|
import { z as z11 } from "zod";
|
|
955
|
-
var readFileDescription = "Read file content from the working directory
|
|
955
|
+
var readFileDescription = "Read file content from the working directory. Prefer a startLine/endLine range to read only the region you need instead of the whole file \u2014 large files are truncated (head+tail) and waste tokens. Raise maxChars only when you truly need more.", readFileInputSchema, readFileProviderInputSchema, readFileOutputSchema;
|
|
956
956
|
var init_schema7 = __esm(() => {
|
|
957
957
|
init_base_schemas();
|
|
958
958
|
readFileInputSchema = z11.object({
|
|
@@ -1206,12 +1206,12 @@ var init_schema13 = __esm(() => {
|
|
|
1206
1206
|
|
|
1207
1207
|
// packages/ai/src/web-fetch/schema.ts
|
|
1208
1208
|
import { z as z19 } from "zod";
|
|
1209
|
-
var webFetchDescription = "Fetch a URL and return normalized, bounded text with source metadata. Requires network permission.", webFetchInputSchema, webFetchProviderInputSchema, webFetchErrorSchema, webFetchOutputSchema;
|
|
1209
|
+
var webFetchDescription = "Fetch a URL and return normalized, bounded text with source metadata. Keep maxChars modest and request more only if the answer wasn't in the returned text. Requires network permission.", webFetchInputSchema, webFetchProviderInputSchema, webFetchErrorSchema, webFetchOutputSchema;
|
|
1210
1210
|
var init_schema14 = __esm(() => {
|
|
1211
1211
|
init_base_schemas();
|
|
1212
1212
|
webFetchInputSchema = z19.object({
|
|
1213
1213
|
url: z19.string().url().max(4096),
|
|
1214
|
-
maxChars: integerRangeSchema(200, MAX_TOOL_TEXT_OUTPUT_CHARS, "maxChars").optional().default(
|
|
1214
|
+
maxChars: integerRangeSchema(200, MAX_TOOL_TEXT_OUTPUT_CHARS, "maxChars").optional().default(DEFAULT_TOOL_TEXT_OUTPUT_CHARS),
|
|
1215
1215
|
timeoutMs: integerRangeSchema(1000, 30000, "timeoutMs").optional().default(1e4)
|
|
1216
1216
|
});
|
|
1217
1217
|
webFetchProviderInputSchema = z19.object({
|
|
@@ -1549,7 +1549,8 @@ var init_agent_tools = __esm(() => {
|
|
|
1549
1549
|
permissionMode: permissionModeSchema.optional(),
|
|
1550
1550
|
allowedTools: z21.array(codingAgentToolNameSchema).optional(),
|
|
1551
1551
|
permissionRules: permissionRulesSchema.optional(),
|
|
1552
|
-
sandbox: sandboxConfigSchema.optional()
|
|
1552
|
+
sandbox: sandboxConfigSchema.optional(),
|
|
1553
|
+
environmentContext: z21.string().optional()
|
|
1553
1554
|
});
|
|
1554
1555
|
});
|
|
1555
1556
|
|
|
@@ -1883,7 +1884,8 @@ function buildCodingAgentSystemPrompt({
|
|
|
1883
1884
|
cwd,
|
|
1884
1885
|
override,
|
|
1885
1886
|
mode = defaultCodingAgentMode,
|
|
1886
|
-
includeToolDiscipline = false
|
|
1887
|
+
includeToolDiscipline = false,
|
|
1888
|
+
environmentContext
|
|
1887
1889
|
}) {
|
|
1888
1890
|
const promptTemplate = override?.trim() ? override : defaultCodingAgentSystemPrompt;
|
|
1889
1891
|
const normalizedPrompt = normalizePromptTemplate(promptTemplate, cwd);
|
|
@@ -1894,16 +1896,18 @@ function buildCodingAgentSystemPrompt({
|
|
|
1894
1896
|
` + `Mode purpose: ${modeDefinition.purpose}
|
|
1895
1897
|
` + `${modeDefinition.instructions}` + (includeToolDiscipline ? `
|
|
1896
1898
|
|
|
1897
|
-
${toolCallingDisciplineAddendum}` : "")
|
|
1899
|
+
${toolCallingDisciplineAddendum}` : "") + (environmentContext?.trim() ? `
|
|
1900
|
+
|
|
1901
|
+
${environmentContext.trim()}` : "");
|
|
1898
1902
|
}
|
|
1899
1903
|
var cwdPromptPlaceholder = "{cwd}", defaultCodingAgentSystemPrompt, toolCallingDisciplineAddendum;
|
|
1900
1904
|
var init_coding_agent_prompt = __esm(() => {
|
|
1901
1905
|
init_coding_agent_modes();
|
|
1902
|
-
defaultCodingAgentSystemPrompt = "You are a
|
|
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. " + `Use todo_write to keep multi-step work visible and current, inspect context before risky operations, and emit a brief, natural progress note before major tool actions and after important findings.
|
|
1903
1907
|
|
|
1904
|
-
` +
|
|
1908
|
+
` + `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.
|
|
1905
1909
|
|
|
1906
|
-
` + "Long-running processes
|
|
1910
|
+
` + "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.";
|
|
1907
1911
|
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.";
|
|
1908
1912
|
});
|
|
1909
1913
|
|
|
@@ -2222,6 +2226,17 @@ function selectCodingAgentIntentTools({
|
|
|
2222
2226
|
return [];
|
|
2223
2227
|
}
|
|
2224
2228
|
const selectedTools = [];
|
|
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
|
+
]);
|
|
2225
2240
|
const hasCodeIntent = includesAny(normalizedText, [
|
|
2226
2241
|
"file",
|
|
2227
2242
|
"folder",
|
|
@@ -2268,7 +2283,44 @@ function selectCodingAgentIntentTools({
|
|
|
2268
2283
|
"analyze",
|
|
2269
2284
|
"check"
|
|
2270
2285
|
]);
|
|
2271
|
-
const
|
|
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, [
|
|
2272
2324
|
"write",
|
|
2273
2325
|
"edit",
|
|
2274
2326
|
"change",
|
|
@@ -2279,8 +2331,8 @@ function selectCodingAgentIntentTools({
|
|
|
2279
2331
|
"delete",
|
|
2280
2332
|
"remove",
|
|
2281
2333
|
"rename"
|
|
2282
|
-
]);
|
|
2283
|
-
const hasShellIntent = mode === "build" && includesAny(normalizedText, [
|
|
2334
|
+
]));
|
|
2335
|
+
const hasShellIntent = mode === "build" && (hasContinuationIntent || includesAny(normalizedText, [
|
|
2284
2336
|
"run",
|
|
2285
2337
|
"command",
|
|
2286
2338
|
"terminal",
|
|
@@ -2292,7 +2344,7 @@ function selectCodingAgentIntentTools({
|
|
|
2292
2344
|
"run test",
|
|
2293
2345
|
"run tests",
|
|
2294
2346
|
"typecheck"
|
|
2295
|
-
]);
|
|
2347
|
+
]));
|
|
2296
2348
|
const hasGitIntent = includesAny(normalizedText, [
|
|
2297
2349
|
"git",
|
|
2298
2350
|
"diff",
|
|
@@ -2340,7 +2392,7 @@ function selectCodingAgentIntentTools({
|
|
|
2340
2392
|
"mcp tool",
|
|
2341
2393
|
"mcp server"
|
|
2342
2394
|
]);
|
|
2343
|
-
if (hasCodeIntent || hasReadIntent && !hasGitIntent && !hasWebIntent && !explicitToolSearch) {
|
|
2395
|
+
if (hasCodeIntent || hasContinuationIntent || hasReviewIntent || hasReadIntent && !hasGitIntent && !hasWebIntent && !explicitToolSearch) {
|
|
2344
2396
|
for (const toolName of baseReadTools) {
|
|
2345
2397
|
pushUniqueTool(selectedTools, toolName);
|
|
2346
2398
|
}
|
|
@@ -2594,7 +2646,8 @@ function createCodingAgent({
|
|
|
2594
2646
|
cwd: options.cwd,
|
|
2595
2647
|
override: promptOverride,
|
|
2596
2648
|
mode,
|
|
2597
|
-
includeToolDiscipline
|
|
2649
|
+
includeToolDiscipline,
|
|
2650
|
+
environmentContext: options.environmentContext
|
|
2598
2651
|
})
|
|
2599
2652
|
};
|
|
2600
2653
|
}
|
|
@@ -2674,6 +2727,10 @@ var init_config3 = __esm(() => {
|
|
|
2674
2727
|
preserveRecentMessages: z26.number().int().min(2).max(100).optional(),
|
|
2675
2728
|
contextWindowOverride: z26.number().int().min(8000).max(1e7).optional(),
|
|
2676
2729
|
summaryMaxChars: z26.number().int().min(200).max(50000).optional(),
|
|
2730
|
+
aggressivePruneWhenUncached: z26.boolean().optional(),
|
|
2731
|
+
uncachedPruneAtFraction: z26.number().min(0.2).max(0.9).optional(),
|
|
2732
|
+
uncachedPruneMinOutputChars: z26.number().int().min(200).max(12000).optional(),
|
|
2733
|
+
uncachedQuantizeUserTurns: z26.number().int().min(1).max(20).optional(),
|
|
2677
2734
|
maxInputTokens: z26.number().int().min(1).max(1e7).optional()
|
|
2678
2735
|
}).strict();
|
|
2679
2736
|
resolvedContextOptimizerConfigSchema = z26.object({
|
|
@@ -2682,15 +2739,23 @@ var init_config3 = __esm(() => {
|
|
|
2682
2739
|
pruneAtFraction: z26.number().min(0.2).max(0.9),
|
|
2683
2740
|
preserveRecentMessages: z26.number().int().min(2).max(100),
|
|
2684
2741
|
contextWindowOverride: z26.number().int().min(8000).max(1e7).nullable(),
|
|
2685
|
-
summaryMaxChars: z26.number().int().min(200).max(50000)
|
|
2742
|
+
summaryMaxChars: z26.number().int().min(200).max(50000),
|
|
2743
|
+
aggressivePruneWhenUncached: z26.boolean(),
|
|
2744
|
+
uncachedPruneAtFraction: z26.number().min(0.2).max(0.9),
|
|
2745
|
+
uncachedPruneMinOutputChars: z26.number().int().min(200).max(12000),
|
|
2746
|
+
uncachedQuantizeUserTurns: z26.number().int().min(1).max(20)
|
|
2686
2747
|
});
|
|
2687
2748
|
defaultContextOptimizerConfig = {
|
|
2688
2749
|
autoCompact: true,
|
|
2689
|
-
compactAtFraction: 0.
|
|
2750
|
+
compactAtFraction: 0.7,
|
|
2690
2751
|
pruneAtFraction: 0.6,
|
|
2691
2752
|
preserveRecentMessages: 6,
|
|
2692
2753
|
contextWindowOverride: null,
|
|
2693
|
-
summaryMaxChars: 4000
|
|
2754
|
+
summaryMaxChars: 4000,
|
|
2755
|
+
aggressivePruneWhenUncached: true,
|
|
2756
|
+
uncachedPruneAtFraction: 0.45,
|
|
2757
|
+
uncachedPruneMinOutputChars: 600,
|
|
2758
|
+
uncachedQuantizeUserTurns: 1
|
|
2694
2759
|
};
|
|
2695
2760
|
});
|
|
2696
2761
|
|
|
@@ -3156,9 +3221,13 @@ function buildProviderView(options) {
|
|
|
3156
3221
|
}
|
|
3157
3222
|
let estimate = estimateContextTokens(providerMessages);
|
|
3158
3223
|
let tier1 = null;
|
|
3159
|
-
|
|
3224
|
+
const uncachedActive = options.cacheActive === false && config.aggressivePruneWhenUncached;
|
|
3225
|
+
const pruneFraction = uncachedActive ? config.uncachedPruneAtFraction : config.pruneAtFraction;
|
|
3226
|
+
if (estimate.tokens > pruneFraction * contextWindow) {
|
|
3160
3227
|
tier1 = pruneToolOutputs(providerMessages, {
|
|
3161
|
-
preserveRecentMessages: config.preserveRecentMessages
|
|
3228
|
+
preserveRecentMessages: config.preserveRecentMessages,
|
|
3229
|
+
minOutputChars: uncachedActive ? config.uncachedPruneMinOutputChars : undefined,
|
|
3230
|
+
quantizeUserTurns: uncachedActive ? config.uncachedQuantizeUserTurns : undefined
|
|
3162
3231
|
});
|
|
3163
3232
|
if (tier1.savedChars > 0) {
|
|
3164
3233
|
providerMessages = tier1.messages;
|
|
@@ -3339,7 +3408,11 @@ function readEnvConfig(env) {
|
|
|
3339
3408
|
contextWindowOverride: parseNumberEnv(env.LIGHTCODE_CONTEXT_WINDOW, "LIGHTCODE_CONTEXT_WINDOW"),
|
|
3340
3409
|
maxInputTokens: parseNumberEnv(env.LIGHTCODE_CONTEXT_MAX_INPUT_TOKENS, "LIGHTCODE_CONTEXT_MAX_INPUT_TOKENS"),
|
|
3341
3410
|
preserveRecentMessages: parseNumberEnv(env.LIGHTCODE_CONTEXT_PRESERVE_RECENT_MESSAGES, "LIGHTCODE_CONTEXT_PRESERVE_RECENT_MESSAGES"),
|
|
3342
|
-
summaryMaxChars: parseNumberEnv(env.LIGHTCODE_CONTEXT_SUMMARY_MAX_CHARS, "LIGHTCODE_CONTEXT_SUMMARY_MAX_CHARS")
|
|
3411
|
+
summaryMaxChars: parseNumberEnv(env.LIGHTCODE_CONTEXT_SUMMARY_MAX_CHARS, "LIGHTCODE_CONTEXT_SUMMARY_MAX_CHARS"),
|
|
3412
|
+
aggressivePruneWhenUncached: parseBooleanEnv(env.LIGHTCODE_CONTEXT_AGGRESSIVE_PRUNE_UNCACHED, "LIGHTCODE_CONTEXT_AGGRESSIVE_PRUNE_UNCACHED"),
|
|
3413
|
+
uncachedPruneAtFraction: parseNumberEnv(env.LIGHTCODE_CONTEXT_UNCACHED_PRUNE_AT_FRACTION, "LIGHTCODE_CONTEXT_UNCACHED_PRUNE_AT_FRACTION"),
|
|
3414
|
+
uncachedPruneMinOutputChars: parseNumberEnv(env.LIGHTCODE_CONTEXT_UNCACHED_PRUNE_MIN_OUTPUT_CHARS, "LIGHTCODE_CONTEXT_UNCACHED_PRUNE_MIN_OUTPUT_CHARS"),
|
|
3415
|
+
uncachedQuantizeUserTurns: parseNumberEnv(env.LIGHTCODE_CONTEXT_UNCACHED_QUANTIZE_USER_TURNS, "LIGHTCODE_CONTEXT_UNCACHED_QUANTIZE_USER_TURNS")
|
|
3343
3416
|
};
|
|
3344
3417
|
const compactContextConfig = Object.fromEntries(Object.entries(contextConfig).filter(([, value]) => value !== undefined));
|
|
3345
3418
|
const headroomConfig = {
|
|
@@ -3466,8 +3539,8 @@ var init_lightcode_config = __esm(() => {
|
|
|
3466
3539
|
defaultAutoContinueConfig = {
|
|
3467
3540
|
enabled: true,
|
|
3468
3541
|
maxAutoContinues: 50,
|
|
3469
|
-
maxErrorRetries:
|
|
3470
|
-
stallTimeoutSeconds:
|
|
3542
|
+
maxErrorRetries: 8,
|
|
3543
|
+
stallTimeoutSeconds: 300
|
|
3471
3544
|
};
|
|
3472
3545
|
headroomConfigSchema = z28.object({
|
|
3473
3546
|
enabled: z28.boolean().optional(),
|
|
@@ -6149,6 +6222,112 @@ var init_runtime8 = __esm(() => {
|
|
|
6149
6222
|
// packages/ai/src/git/runtime.ts
|
|
6150
6223
|
import { execFile as execFile2 } from "child_process";
|
|
6151
6224
|
import { promisify as promisify3 } from "util";
|
|
6225
|
+
function isRecord7(value) {
|
|
6226
|
+
return typeof value === "object" && value !== null;
|
|
6227
|
+
}
|
|
6228
|
+
function getStringProperty(record, key) {
|
|
6229
|
+
const value = record[key];
|
|
6230
|
+
return typeof value === "string" ? value : "";
|
|
6231
|
+
}
|
|
6232
|
+
function getExitCode(error) {
|
|
6233
|
+
const code = error.code;
|
|
6234
|
+
return typeof code === "number" ? code : null;
|
|
6235
|
+
}
|
|
6236
|
+
async function runGit(args, workspaceContext, maxBuffer = 10 * 1024 * 1024) {
|
|
6237
|
+
try {
|
|
6238
|
+
const result = await execFileAsync2("git", args, {
|
|
6239
|
+
cwd: workspaceContext.root,
|
|
6240
|
+
maxBuffer,
|
|
6241
|
+
windowsHide: true
|
|
6242
|
+
});
|
|
6243
|
+
return {
|
|
6244
|
+
ok: true,
|
|
6245
|
+
stdout: result.stdout,
|
|
6246
|
+
stderr: result.stderr,
|
|
6247
|
+
exitCode: 0
|
|
6248
|
+
};
|
|
6249
|
+
} catch (error) {
|
|
6250
|
+
const record = isRecord7(error) ? error : {};
|
|
6251
|
+
const code = record.code;
|
|
6252
|
+
const codeText = typeof code === "string" ? code : "git_failed";
|
|
6253
|
+
const message = getStringProperty(record, "message") || "Git command failed.";
|
|
6254
|
+
return {
|
|
6255
|
+
ok: false,
|
|
6256
|
+
error: {
|
|
6257
|
+
code: codeText,
|
|
6258
|
+
message,
|
|
6259
|
+
exitCode: getExitCode(record),
|
|
6260
|
+
stdout: getStringProperty(record, "stdout"),
|
|
6261
|
+
stderr: getStringProperty(record, "stderr")
|
|
6262
|
+
}
|
|
6263
|
+
};
|
|
6264
|
+
}
|
|
6265
|
+
}
|
|
6266
|
+
function parseBranchLine(line) {
|
|
6267
|
+
const branchLine = line.replace(/^##\s*/, "");
|
|
6268
|
+
const [branchSegment, trackingSegment] = branchLine.split("...");
|
|
6269
|
+
const branch = branchSegment && branchSegment !== "No commits yet on " ? branchSegment.replace(/^No commits yet on\s+/, "") : null;
|
|
6270
|
+
const trackingMatch = trackingSegment?.match(/^([^\s]+)(?:\s+\[(.*)\])?/);
|
|
6271
|
+
const upstream = trackingMatch?.[1] ?? null;
|
|
6272
|
+
const trackingDetails = trackingMatch?.[2] ?? "";
|
|
6273
|
+
const aheadMatch = trackingDetails.match(/ahead\s+(\d+)/);
|
|
6274
|
+
const behindMatch = trackingDetails.match(/behind\s+(\d+)/);
|
|
6275
|
+
return {
|
|
6276
|
+
branch,
|
|
6277
|
+
upstream,
|
|
6278
|
+
ahead: aheadMatch ? Number(aheadMatch[1]) : 0,
|
|
6279
|
+
behind: behindMatch ? Number(behindMatch[1]) : 0
|
|
6280
|
+
};
|
|
6281
|
+
}
|
|
6282
|
+
function parseStatusEntries(stdout, maxEntries) {
|
|
6283
|
+
const lines = stdout.split(/\r?\n/).filter((line) => line.length > 0);
|
|
6284
|
+
const branchInfo = lines[0]?.startsWith("##") ? parseBranchLine(lines[0]) : { branch: null, upstream: null, ahead: 0, behind: 0 };
|
|
6285
|
+
const statusLines = lines.filter((line) => !line.startsWith("##"));
|
|
6286
|
+
const entries = statusLines.slice(0, maxEntries).map((line) => {
|
|
6287
|
+
const indexStatus = line[0] === " " ? "" : line[0] ?? "";
|
|
6288
|
+
const workingTreeStatus = line[1] === " " ? "" : line[1] ?? "";
|
|
6289
|
+
const rawPath = line.slice(3);
|
|
6290
|
+
const renameParts = rawPath.split(" -> ");
|
|
6291
|
+
const originalPath = renameParts.length > 1 ? renameParts[0] : null;
|
|
6292
|
+
const filePath = renameParts.length > 1 ? renameParts[1] : rawPath;
|
|
6293
|
+
return {
|
|
6294
|
+
path: filePath,
|
|
6295
|
+
indexStatus,
|
|
6296
|
+
workingTreeStatus,
|
|
6297
|
+
originalPath
|
|
6298
|
+
};
|
|
6299
|
+
});
|
|
6300
|
+
return {
|
|
6301
|
+
...branchInfo,
|
|
6302
|
+
entries,
|
|
6303
|
+
totalEntries: statusLines.length,
|
|
6304
|
+
truncated: statusLines.length > entries.length
|
|
6305
|
+
};
|
|
6306
|
+
}
|
|
6307
|
+
async function executeGitStatus(input, workspaceContext = getDefaultWorkspaceContext()) {
|
|
6308
|
+
const parsedInput = gitStatusInputSchema.parse(input);
|
|
6309
|
+
const result = await runGit(["status", "--porcelain=v1", "-b", "--untracked-files=all", "--", "."], workspaceContext);
|
|
6310
|
+
if (!result.ok) {
|
|
6311
|
+
return gitStatusOutputSchema.parse({
|
|
6312
|
+
ok: false,
|
|
6313
|
+
cwd: workspaceContext.root,
|
|
6314
|
+
branch: null,
|
|
6315
|
+
upstream: null,
|
|
6316
|
+
ahead: 0,
|
|
6317
|
+
behind: 0,
|
|
6318
|
+
entries: [],
|
|
6319
|
+
totalEntries: 0,
|
|
6320
|
+
truncated: false,
|
|
6321
|
+
error: result.error
|
|
6322
|
+
});
|
|
6323
|
+
}
|
|
6324
|
+
return gitStatusOutputSchema.parse({
|
|
6325
|
+
ok: true,
|
|
6326
|
+
cwd: workspaceContext.root,
|
|
6327
|
+
...parseStatusEntries(result.stdout, parsedInput.maxEntries),
|
|
6328
|
+
error: null
|
|
6329
|
+
});
|
|
6330
|
+
}
|
|
6152
6331
|
var execFileAsync2;
|
|
6153
6332
|
var init_runtime9 = __esm(() => {
|
|
6154
6333
|
init_resolve_within_workspace();
|
|
@@ -6919,6 +7098,7 @@ var init_provider_registry = __esm(() => {
|
|
|
6919
7098
|
var exports_runtime_config = {};
|
|
6920
7099
|
__export(exports_runtime_config, {
|
|
6921
7100
|
resolvedProviderModel: () => resolvedProviderModel,
|
|
7101
|
+
reloadRuntimeConfig: () => reloadRuntimeConfig,
|
|
6922
7102
|
lightcodeConfigResult: () => lightcodeConfigResult,
|
|
6923
7103
|
initializeModelCapabilities: () => initializeModelCapabilities,
|
|
6924
7104
|
disableHeadroomRouting: () => disableHeadroomRouting,
|
|
@@ -6979,6 +7159,31 @@ function applyModelSelection({
|
|
|
6979
7159
|
});
|
|
6980
7160
|
return configStatus;
|
|
6981
7161
|
}
|
|
7162
|
+
function reloadRuntimeConfig() {
|
|
7163
|
+
const nextConfigResult = loadLightcodeConfig({
|
|
7164
|
+
cwd: process.cwd(),
|
|
7165
|
+
env: Bun.env
|
|
7166
|
+
});
|
|
7167
|
+
const nextResolved = resolveConfiguredProviderModel({
|
|
7168
|
+
config: nextConfigResult.config,
|
|
7169
|
+
env: Bun.env
|
|
7170
|
+
});
|
|
7171
|
+
const nextAgent = buildCodingAgent(nextConfigResult.config, nextResolved);
|
|
7172
|
+
lightcodeConfigResult = nextConfigResult;
|
|
7173
|
+
resolvedProviderModel = nextResolved;
|
|
7174
|
+
chatModelId = nextResolved.resolvedModelId;
|
|
7175
|
+
codingAgent = nextAgent;
|
|
7176
|
+
configStatus = createConfigStatus({
|
|
7177
|
+
config: nextConfigResult.config,
|
|
7178
|
+
loadedFiles: nextConfigResult.loadedFiles,
|
|
7179
|
+
resolvedProviderModel: nextResolved
|
|
7180
|
+
});
|
|
7181
|
+
logger5.info("runtime_config_reloaded", {
|
|
7182
|
+
provider: nextConfigResult.config.provider,
|
|
7183
|
+
model: nextConfigResult.config.model
|
|
7184
|
+
});
|
|
7185
|
+
return configStatus;
|
|
7186
|
+
}
|
|
6982
7187
|
function disableHeadroomRouting() {
|
|
6983
7188
|
if (!lightcodeConfigResult.config.headroom?.enabled) {
|
|
6984
7189
|
return;
|
|
@@ -7114,11 +7319,122 @@ var init_session_auto_title = __esm(() => {
|
|
|
7114
7319
|
logger6 = createLogger("session-auto-title");
|
|
7115
7320
|
});
|
|
7116
7321
|
|
|
7322
|
+
// apps/server/src/lib/workspace-context.ts
|
|
7323
|
+
import { readFile as readFile2, readdir } from "fs/promises";
|
|
7324
|
+
import { platform as platform3 } from "os";
|
|
7325
|
+
import path11 from "path";
|
|
7326
|
+
function describePlatform() {
|
|
7327
|
+
const map = {
|
|
7328
|
+
darwin: "macOS",
|
|
7329
|
+
win32: "Windows",
|
|
7330
|
+
linux: "Linux"
|
|
7331
|
+
};
|
|
7332
|
+
return map[platform3()] ?? platform3();
|
|
7333
|
+
}
|
|
7334
|
+
async function buildDirectoryListing(cwd) {
|
|
7335
|
+
try {
|
|
7336
|
+
const dirents = await readdir(cwd, { withFileTypes: true });
|
|
7337
|
+
const names = dirents.filter((entry) => entry.name !== ".git").map((entry) => entry.isDirectory() ? `${entry.name}/` : entry.name).sort((a, b) => a.localeCompare(b));
|
|
7338
|
+
const shown = names.slice(0, maxListedEntries);
|
|
7339
|
+
const suffix = names.length > shown.length ? `
|
|
7340
|
+
\u2026 and ${names.length - shown.length} more` : "";
|
|
7341
|
+
return shown.length > 0 ? shown.join(`
|
|
7342
|
+
`) + suffix : "(empty directory)";
|
|
7343
|
+
} catch (error) {
|
|
7344
|
+
logger7.warn("workspace_listing_failed", { cwd, error: getErrorMessage(error) });
|
|
7345
|
+
return "(unable to list directory)";
|
|
7346
|
+
}
|
|
7347
|
+
}
|
|
7348
|
+
async function buildGitSummary(cwd) {
|
|
7349
|
+
try {
|
|
7350
|
+
const context2 = createWorkspaceContext(cwd);
|
|
7351
|
+
const status = await executeGitStatus({}, context2);
|
|
7352
|
+
if (!status.ok) {
|
|
7353
|
+
return "Not a git repository.";
|
|
7354
|
+
}
|
|
7355
|
+
const header = [
|
|
7356
|
+
`branch: ${status.branch ?? "(detached)"}`,
|
|
7357
|
+
status.ahead || status.behind ? `ahead ${status.ahead}, behind ${status.behind}` : null
|
|
7358
|
+
].filter(Boolean).join(" | ");
|
|
7359
|
+
if (status.totalEntries === 0) {
|
|
7360
|
+
return `${header}
|
|
7361
|
+
working tree clean`;
|
|
7362
|
+
}
|
|
7363
|
+
const lines = status.entries.slice(0, maxListedEntries).map((entry) => `${entry.indexStatus}${entry.workingTreeStatus} ${entry.path}`.trim());
|
|
7364
|
+
const more = status.totalEntries > lines.length ? `
|
|
7365
|
+
\u2026 and ${status.totalEntries - lines.length} more` : "";
|
|
7366
|
+
return `${header}
|
|
7367
|
+
changes:
|
|
7368
|
+
${lines.join(`
|
|
7369
|
+
`)}${more}`;
|
|
7370
|
+
} catch (error) {
|
|
7371
|
+
logger7.warn("workspace_git_summary_failed", { cwd, error: getErrorMessage(error) });
|
|
7372
|
+
return "Not a git repository.";
|
|
7373
|
+
}
|
|
7374
|
+
}
|
|
7375
|
+
async function buildProjectDocs(cwd) {
|
|
7376
|
+
const sections = [];
|
|
7377
|
+
for (const name of projectDocNames) {
|
|
7378
|
+
try {
|
|
7379
|
+
const content = (await readFile2(path11.join(cwd, name), "utf8")).trim();
|
|
7380
|
+
if (!content) {
|
|
7381
|
+
continue;
|
|
7382
|
+
}
|
|
7383
|
+
const truncated = content.length > maxDocChars ? `${content.slice(0, maxDocChars)}
|
|
7384
|
+
\u2026 (truncated)` : content;
|
|
7385
|
+
sections.push(`### ${name}
|
|
7386
|
+
${truncated}`);
|
|
7387
|
+
} catch {}
|
|
7388
|
+
}
|
|
7389
|
+
return sections.join(`
|
|
7390
|
+
|
|
7391
|
+
`);
|
|
7392
|
+
}
|
|
7393
|
+
async function buildWorkspaceContext({
|
|
7394
|
+
cwd
|
|
7395
|
+
}) {
|
|
7396
|
+
const [listing, git, docs] = await Promise.all([
|
|
7397
|
+
buildDirectoryListing(cwd),
|
|
7398
|
+
buildGitSummary(cwd),
|
|
7399
|
+
buildProjectDocs(cwd)
|
|
7400
|
+
]);
|
|
7401
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
7402
|
+
const parts = [
|
|
7403
|
+
"<environment>",
|
|
7404
|
+
`Working directory: ${cwd}`,
|
|
7405
|
+
`Platform: ${describePlatform()}`,
|
|
7406
|
+
`Date: ${today}`,
|
|
7407
|
+
"",
|
|
7408
|
+
"Git:",
|
|
7409
|
+
git,
|
|
7410
|
+
"",
|
|
7411
|
+
"Top-level entries:",
|
|
7412
|
+
listing,
|
|
7413
|
+
docs ? `
|
|
7414
|
+
Project instructions:
|
|
7415
|
+
${docs}` : "",
|
|
7416
|
+
"</environment>"
|
|
7417
|
+
];
|
|
7418
|
+
const block = parts.join(`
|
|
7419
|
+
`);
|
|
7420
|
+
return block.length > maxBlockChars ? `${block.slice(0, maxBlockChars)}
|
|
7421
|
+
\u2026 (environment truncated)
|
|
7422
|
+
</environment>` : block;
|
|
7423
|
+
}
|
|
7424
|
+
var logger7, projectDocNames, maxListedEntries = 40, maxDocChars = 1500, maxBlockChars = 8000;
|
|
7425
|
+
var init_workspace_context = __esm(() => {
|
|
7426
|
+
init_runtime17();
|
|
7427
|
+
init_src();
|
|
7428
|
+
logger7 = createLogger("workspace-context");
|
|
7429
|
+
projectDocNames = ["AGENTS.md", "CLAUDE.md", "README.md"];
|
|
7430
|
+
});
|
|
7431
|
+
|
|
7117
7432
|
// apps/server/src/lib/chat-stream.ts
|
|
7118
7433
|
import {
|
|
7119
7434
|
createAgentUIStreamResponse,
|
|
7120
7435
|
consumeStream,
|
|
7121
7436
|
generateId as generateId3,
|
|
7437
|
+
isToolUIPart as isToolUIPart2,
|
|
7122
7438
|
safeValidateUIMessages as safeValidateUIMessages2,
|
|
7123
7439
|
streamText
|
|
7124
7440
|
} from "ai";
|
|
@@ -7138,6 +7454,9 @@ function buildFastChatModelMessages(messages) {
|
|
|
7138
7454
|
content: collectMessageText(message)
|
|
7139
7455
|
})).filter((message) => message.content.length > 0).slice(-fastChatRecentMessageCount);
|
|
7140
7456
|
}
|
|
7457
|
+
function messagesHaveToolActivity(messages) {
|
|
7458
|
+
return messages.some((message) => message.parts?.some((part) => isToolUIPart2(part)));
|
|
7459
|
+
}
|
|
7141
7460
|
function shouldUseFastChatPath({
|
|
7142
7461
|
messages,
|
|
7143
7462
|
mode,
|
|
@@ -7146,6 +7465,9 @@ function shouldUseFastChatPath({
|
|
|
7146
7465
|
if (allowedTools && allowedTools.length > 0) {
|
|
7147
7466
|
return false;
|
|
7148
7467
|
}
|
|
7468
|
+
if (messagesHaveToolActivity(messages)) {
|
|
7469
|
+
return false;
|
|
7470
|
+
}
|
|
7149
7471
|
return selectCodingAgentIntentTools({
|
|
7150
7472
|
mode,
|
|
7151
7473
|
prompt: "",
|
|
@@ -7160,7 +7482,7 @@ async function countPendingChatInteractions(sessionId) {
|
|
|
7160
7482
|
});
|
|
7161
7483
|
return pendingInteractions.interactions.length;
|
|
7162
7484
|
} catch (error) {
|
|
7163
|
-
|
|
7485
|
+
logger8.warn("context_compaction_pending_interactions_unavailable", {
|
|
7164
7486
|
sessionId,
|
|
7165
7487
|
error: getErrorMessage(error)
|
|
7166
7488
|
});
|
|
@@ -7171,7 +7493,7 @@ async function loadSessionContextStateSafe(sessionId) {
|
|
|
7171
7493
|
try {
|
|
7172
7494
|
return await getSessionContextState(sessionId);
|
|
7173
7495
|
} catch (error) {
|
|
7174
|
-
|
|
7496
|
+
logger8.warn("context_state_unavailable", {
|
|
7175
7497
|
sessionId,
|
|
7176
7498
|
error: getErrorMessage(error)
|
|
7177
7499
|
});
|
|
@@ -7220,7 +7542,7 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7220
7542
|
staleSkip: false
|
|
7221
7543
|
});
|
|
7222
7544
|
} catch (error) {
|
|
7223
|
-
|
|
7545
|
+
logger8.error("chat_pre_stream_persist_failed", {
|
|
7224
7546
|
sessionId,
|
|
7225
7547
|
error: getErrorMessage(error)
|
|
7226
7548
|
});
|
|
@@ -7230,6 +7552,7 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7230
7552
|
}, 500);
|
|
7231
7553
|
}
|
|
7232
7554
|
const contextConfig = lightcodeConfigResult.config.context;
|
|
7555
|
+
const cacheActive = resolvedProviderModel.provider === "anthropic";
|
|
7233
7556
|
const pendingInteractionCount = await countPendingChatInteractions(sessionId);
|
|
7234
7557
|
let contextState = await loadSessionContextStateSafe(sessionId);
|
|
7235
7558
|
let view = buildProviderView({
|
|
@@ -7237,7 +7560,8 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7237
7560
|
contextState,
|
|
7238
7561
|
config: contextConfig,
|
|
7239
7562
|
modelContextWindow: resolvedProviderModel.contextWindow,
|
|
7240
|
-
pendingInteractionCount
|
|
7563
|
+
pendingInteractionCount,
|
|
7564
|
+
cacheActive
|
|
7241
7565
|
});
|
|
7242
7566
|
if (view.needsCompaction && contextConfig.autoCompact) {
|
|
7243
7567
|
try {
|
|
@@ -7258,9 +7582,10 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7258
7582
|
contextState,
|
|
7259
7583
|
config: contextConfig,
|
|
7260
7584
|
modelContextWindow: resolvedProviderModel.contextWindow,
|
|
7261
|
-
pendingInteractionCount
|
|
7585
|
+
pendingInteractionCount,
|
|
7586
|
+
cacheActive
|
|
7262
7587
|
});
|
|
7263
|
-
|
|
7588
|
+
logger8.info("context_auto_compacted", {
|
|
7264
7589
|
sessionId,
|
|
7265
7590
|
tier: compaction.state.tier,
|
|
7266
7591
|
usedFallback: compaction.usedFallback,
|
|
@@ -7269,7 +7594,7 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7269
7594
|
estimatedTokensAfter: view.estimate.tokens
|
|
7270
7595
|
});
|
|
7271
7596
|
} catch (error) {
|
|
7272
|
-
|
|
7597
|
+
logger8.error("context_compaction_failed", {
|
|
7273
7598
|
sessionId,
|
|
7274
7599
|
error: getErrorMessage(error)
|
|
7275
7600
|
});
|
|
@@ -7287,7 +7612,8 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7287
7612
|
contextState,
|
|
7288
7613
|
config: overflowConfig,
|
|
7289
7614
|
modelContextWindow: resolvedProviderModel.contextWindow,
|
|
7290
|
-
pendingInteractionCount: 0
|
|
7615
|
+
pendingInteractionCount: 0,
|
|
7616
|
+
cacheActive
|
|
7291
7617
|
});
|
|
7292
7618
|
if (overflowView.coveredMessages.length > 0) {
|
|
7293
7619
|
const compaction = await compactSessionContext({
|
|
@@ -7307,9 +7633,10 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7307
7633
|
contextState,
|
|
7308
7634
|
config: overflowConfig,
|
|
7309
7635
|
modelContextWindow: resolvedProviderModel.contextWindow,
|
|
7310
|
-
pendingInteractionCount: 0
|
|
7636
|
+
pendingInteractionCount: 0,
|
|
7637
|
+
cacheActive
|
|
7311
7638
|
});
|
|
7312
|
-
|
|
7639
|
+
logger8.info("context_overflow_recovery", {
|
|
7313
7640
|
sessionId,
|
|
7314
7641
|
preserveRecentMessages: overflowConfig.preserveRecentMessages,
|
|
7315
7642
|
estimatedTokens: view.estimate.tokens,
|
|
@@ -7318,7 +7645,7 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7318
7645
|
compactedMessages: view.coveredMessages.length
|
|
7319
7646
|
});
|
|
7320
7647
|
} catch (error) {
|
|
7321
|
-
|
|
7648
|
+
logger8.error("context_overflow_recovery_failed", {
|
|
7322
7649
|
sessionId,
|
|
7323
7650
|
error: getErrorMessage(error)
|
|
7324
7651
|
});
|
|
@@ -7354,14 +7681,14 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7354
7681
|
});
|
|
7355
7682
|
const normalizedMessages = removeTrailingEmptyAssistantMessages(fullHistory);
|
|
7356
7683
|
if (normalizedMessages.length !== fullHistory.length) {
|
|
7357
|
-
|
|
7684
|
+
logger8.warn("chat_finish_empty_assistant_skipped", { sessionId });
|
|
7358
7685
|
return;
|
|
7359
7686
|
}
|
|
7360
7687
|
const validatedFinishResult = await safeValidateUIMessages2({
|
|
7361
7688
|
messages: normalizedMessages
|
|
7362
7689
|
});
|
|
7363
7690
|
if (!validatedFinishResult.success) {
|
|
7364
|
-
|
|
7691
|
+
logger8.warn("chat_invalid_finish_payload", {
|
|
7365
7692
|
sessionId,
|
|
7366
7693
|
message: validatedFinishResult.error.message
|
|
7367
7694
|
});
|
|
@@ -7395,7 +7722,7 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7395
7722
|
messages: validatedFinishResult.data
|
|
7396
7723
|
});
|
|
7397
7724
|
} catch (error) {
|
|
7398
|
-
|
|
7725
|
+
logger8.error("chat_finish_persist_failed", {
|
|
7399
7726
|
sessionId,
|
|
7400
7727
|
error: getErrorMessage(error)
|
|
7401
7728
|
});
|
|
@@ -7421,19 +7748,22 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7421
7748
|
finishReason: part.finishReason
|
|
7422
7749
|
};
|
|
7423
7750
|
};
|
|
7751
|
+
const environmentContext = await buildWorkspaceContext({ cwd });
|
|
7424
7752
|
try {
|
|
7425
7753
|
if (shouldUseFastChatPath({
|
|
7426
7754
|
messages: providerMessages,
|
|
7427
7755
|
mode,
|
|
7428
7756
|
allowedTools
|
|
7429
7757
|
})) {
|
|
7430
|
-
|
|
7758
|
+
logger8.info("chat_fast_path", {
|
|
7431
7759
|
sessionId,
|
|
7432
7760
|
model: chatModelId
|
|
7433
7761
|
});
|
|
7434
7762
|
const result = streamText({
|
|
7435
7763
|
model: resolvedProviderModel.model,
|
|
7436
|
-
system: fastChatSystemPrompt
|
|
7764
|
+
system: `${fastChatSystemPrompt}
|
|
7765
|
+
|
|
7766
|
+
${environmentContext}`,
|
|
7437
7767
|
messages: buildFastChatModelMessages(providerMessages),
|
|
7438
7768
|
maxOutputTokens: Math.min(lightcodeConfigResult.config.maxOutputTokens, fastChatMaxOutputTokens),
|
|
7439
7769
|
maxRetries: lightcodeConfigResult.config.maxRetries,
|
|
@@ -7503,7 +7833,8 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7503
7833
|
permissionMode,
|
|
7504
7834
|
allowedTools,
|
|
7505
7835
|
permissionRules,
|
|
7506
|
-
sandbox
|
|
7836
|
+
sandbox,
|
|
7837
|
+
environmentContext
|
|
7507
7838
|
},
|
|
7508
7839
|
abortSignal: c.req.raw.signal,
|
|
7509
7840
|
generateMessageId: generateId3,
|
|
@@ -7563,7 +7894,7 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7563
7894
|
}, 500);
|
|
7564
7895
|
}
|
|
7565
7896
|
}
|
|
7566
|
-
var
|
|
7897
|
+
var logger8, recoverableDisconnectMessage = "Connection interrupted. Please retry or regenerate your last message.", providerBillingOrQuotaMessage, fastChatSystemPrompt, fastChatMaxOutputTokens = 512, fastChatRecentMessageCount = 6;
|
|
7567
7898
|
var init_chat_stream = __esm(() => {
|
|
7568
7899
|
init_src2();
|
|
7569
7900
|
init_src();
|
|
@@ -7575,10 +7906,11 @@ var init_chat_stream = __esm(() => {
|
|
|
7575
7906
|
init_sse_heartbeat();
|
|
7576
7907
|
init_context_state_store();
|
|
7577
7908
|
init_session_auto_title();
|
|
7909
|
+
init_workspace_context();
|
|
7578
7910
|
init_runtime_config();
|
|
7579
|
-
|
|
7911
|
+
logger8 = createLogger("chat-stream");
|
|
7580
7912
|
providerBillingOrQuotaMessage = "The configured model provider rejected this request due to billing or quota limits. " + "Update provider credits/quota and retry.";
|
|
7581
|
-
fastChatSystemPrompt = "You are Lightcode's
|
|
7913
|
+
fastChatSystemPrompt = "You are Lightcode's coding assistant, running inside the user's project. " + "For casual conversation, reply briefly and naturally. " + "If the user asks about or wants work on their code, do not ask them to paste it \u2014 you can read the project files directly; offer to look and proceed.";
|
|
7582
7914
|
});
|
|
7583
7915
|
|
|
7584
7916
|
// apps/server/src/routes/route-helpers.ts
|
|
@@ -7587,16 +7919,16 @@ function internalErrorResponse(c, {
|
|
|
7587
7919
|
message,
|
|
7588
7920
|
error
|
|
7589
7921
|
}) {
|
|
7590
|
-
|
|
7922
|
+
logger9.error(event, { error: getErrorMessage(error) });
|
|
7591
7923
|
return c.json({
|
|
7592
7924
|
error: message,
|
|
7593
7925
|
details: Bun.env.NODE_ENV === "production" ? undefined : getErrorMessage(error)
|
|
7594
7926
|
}, 500);
|
|
7595
7927
|
}
|
|
7596
|
-
var
|
|
7928
|
+
var logger9;
|
|
7597
7929
|
var init_route_helpers = __esm(() => {
|
|
7598
7930
|
init_src();
|
|
7599
|
-
|
|
7931
|
+
logger9 = createLogger("routes");
|
|
7600
7932
|
});
|
|
7601
7933
|
|
|
7602
7934
|
// apps/server/src/routes/chat-routes.ts
|
|
@@ -7891,7 +8223,7 @@ var init_config_routes = __esm(() => {
|
|
|
7891
8223
|
provider: lightcodeProviderSchema,
|
|
7892
8224
|
model: z35.string().min(1).max(240)
|
|
7893
8225
|
});
|
|
7894
|
-
configRoutes = new Hono2().get("/status", (c) => c.json(configStatus)).get("/models", async (c) => {
|
|
8226
|
+
configRoutes = new Hono2().get("/status", (c) => c.json(configStatus)).post("/reload", (c) => c.json({ ok: true, configStatus: reloadRuntimeConfig() })).get("/models", async (c) => {
|
|
7895
8227
|
const provider = c.req.query("provider") ?? "openrouter";
|
|
7896
8228
|
if (provider !== "openrouter") {
|
|
7897
8229
|
return c.json({ error: `Model listing is not supported for provider "${provider}".` }, 400);
|
|
@@ -7962,7 +8294,7 @@ var init_extension_runtime = __esm(() => {
|
|
|
7962
8294
|
|
|
7963
8295
|
// apps/server/src/routes/diagnostics-routes.ts
|
|
7964
8296
|
import { existsSync as existsSync9, readdirSync as readdirSync4 } from "fs";
|
|
7965
|
-
import
|
|
8297
|
+
import path12 from "path";
|
|
7966
8298
|
import { Hono as Hono3 } from "hono";
|
|
7967
8299
|
function combineStatuses(statuses) {
|
|
7968
8300
|
if (statuses.includes("error")) {
|
|
@@ -8284,7 +8616,7 @@ async function buildLogsTailPayload(requestedLines) {
|
|
|
8284
8616
|
lines: []
|
|
8285
8617
|
};
|
|
8286
8618
|
}
|
|
8287
|
-
const filePath =
|
|
8619
|
+
const filePath = path12.join(directory, latestFile);
|
|
8288
8620
|
const content = await Bun.file(filePath).text();
|
|
8289
8621
|
const lines = content.split(`
|
|
8290
8622
|
`).filter((line) => line.length > 0);
|
|
@@ -8407,36 +8739,36 @@ async function ensureHeadroomRouting() {
|
|
|
8407
8739
|
health = await checkHeadroomHealth(headroom.proxyUrl);
|
|
8408
8740
|
} catch (error) {
|
|
8409
8741
|
health = "down";
|
|
8410
|
-
|
|
8742
|
+
logger10.warn("headroom_health_probe_failed", {
|
|
8411
8743
|
proxyUrl: headroom.proxyUrl,
|
|
8412
8744
|
error: getErrorMessage(error)
|
|
8413
8745
|
});
|
|
8414
8746
|
}
|
|
8415
8747
|
if (health === "healthy") {
|
|
8416
|
-
|
|
8748
|
+
logger10.info("headroom_routing_active", { proxyUrl: headroom.proxyUrl });
|
|
8417
8749
|
return;
|
|
8418
8750
|
}
|
|
8419
8751
|
if (headroom.failOpen) {
|
|
8420
|
-
|
|
8752
|
+
logger10.warn("headroom_unreachable_failing_open", {
|
|
8421
8753
|
proxyUrl: headroom.proxyUrl
|
|
8422
8754
|
});
|
|
8423
8755
|
disableHeadroomRouting();
|
|
8424
8756
|
return;
|
|
8425
8757
|
}
|
|
8426
|
-
|
|
8758
|
+
logger10.error("headroom_unreachable_fail_closed", {
|
|
8427
8759
|
proxyUrl: headroom.proxyUrl
|
|
8428
8760
|
});
|
|
8429
8761
|
}
|
|
8430
|
-
var
|
|
8762
|
+
var logger10, healthCheckTimeoutMs = 1500;
|
|
8431
8763
|
var init_headroom_proxy = __esm(() => {
|
|
8432
8764
|
init_src();
|
|
8433
8765
|
init_runtime_config();
|
|
8434
|
-
|
|
8766
|
+
logger10 = createLogger("headroom-proxy");
|
|
8435
8767
|
});
|
|
8436
8768
|
|
|
8437
8769
|
// apps/server/src/index.ts
|
|
8438
8770
|
init_src();
|
|
8439
|
-
var
|
|
8771
|
+
var logger11 = createLogger("server");
|
|
8440
8772
|
var httpIdleTimeoutSeconds = 30;
|
|
8441
8773
|
var shutdownForceExitMs = 5000;
|
|
8442
8774
|
function isChatStreamingRoute(request) {
|
|
@@ -8449,7 +8781,7 @@ function isChatStreamingRoute(request) {
|
|
|
8449
8781
|
async function startServer() {
|
|
8450
8782
|
const logDirectory = enableFileLogSink(Bun.env);
|
|
8451
8783
|
if (logDirectory) {
|
|
8452
|
-
|
|
8784
|
+
logger11.info("file_log_sink_enabled", { directory: logDirectory });
|
|
8453
8785
|
}
|
|
8454
8786
|
const port = Number(Bun.env.PORT ?? 4983);
|
|
8455
8787
|
const hostname = Bun.env.LIGHTCODE_HOST ?? "127.0.0.1";
|
|
@@ -8470,12 +8802,12 @@ async function startServer() {
|
|
|
8470
8802
|
process.exit(1);
|
|
8471
8803
|
}
|
|
8472
8804
|
Promise.resolve().then(() => (init_runtime_config(), exports_runtime_config)).then((runtime19) => runtime19.initializeModelCapabilities()).catch((error) => {
|
|
8473
|
-
|
|
8805
|
+
logger11.warn("model_capability_init_failed", {
|
|
8474
8806
|
error: getErrorMessage(error)
|
|
8475
8807
|
});
|
|
8476
8808
|
});
|
|
8477
8809
|
Promise.resolve().then(() => (init_headroom_proxy(), exports_headroom_proxy)).then((headroom) => headroom.ensureHeadroomRouting()).catch((error) => {
|
|
8478
|
-
|
|
8810
|
+
logger11.warn("headroom_init_failed", {
|
|
8479
8811
|
error: getErrorMessage(error)
|
|
8480
8812
|
});
|
|
8481
8813
|
});
|
|
@@ -8511,7 +8843,7 @@ async function startServer() {
|
|
|
8511
8843
|
return;
|
|
8512
8844
|
}
|
|
8513
8845
|
shuttingDown = true;
|
|
8514
|
-
|
|
8846
|
+
logger11.info("server_shutdown", { signal });
|
|
8515
8847
|
const forceExitTimer = setTimeout(() => {
|
|
8516
8848
|
server.stop(true);
|
|
8517
8849
|
process.exit(0);
|
|
@@ -8526,6 +8858,17 @@ async function startServer() {
|
|
|
8526
8858
|
};
|
|
8527
8859
|
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
8528
8860
|
process.on("SIGTERM", () => void shutdown("SIGTERM"));
|
|
8861
|
+
const parentPid = Number(Bun.env.LIGHTCODE_PARENT_PID);
|
|
8862
|
+
if (Number.isInteger(parentPid) && parentPid > 0) {
|
|
8863
|
+
const parentWatch = setInterval(() => {
|
|
8864
|
+
try {
|
|
8865
|
+
process.kill(parentPid, 0);
|
|
8866
|
+
} catch {
|
|
8867
|
+
shutdown("parent-exit");
|
|
8868
|
+
}
|
|
8869
|
+
}, 2000);
|
|
8870
|
+
parentWatch.unref();
|
|
8871
|
+
}
|
|
8529
8872
|
console.log(`Server listening on http://${hostname}:${port}`);
|
|
8530
8873
|
}
|
|
8531
8874
|
if (import.meta.main) {
|