@kmugalkhod/lightcode 0.2.0 → 0.4.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/cli.js +261 -80
- package/package.json +1 -1
- package/server.js +276 -40
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({
|
|
@@ -1899,11 +1899,11 @@ ${toolCallingDisciplineAddendum}` : "");
|
|
|
1899
1899
|
var cwdPromptPlaceholder = "{cwd}", defaultCodingAgentSystemPrompt, toolCallingDisciplineAddendum;
|
|
1900
1900
|
var init_coding_agent_prompt = __esm(() => {
|
|
1901
1901
|
init_coding_agent_modes();
|
|
1902
|
-
defaultCodingAgentSystemPrompt = "You are a
|
|
1902
|
+
defaultCodingAgentSystemPrompt = "You are a coding agent. Use tools for filesystem and codebase tasks instead of guessing, and only interact with files under this working directory: {cwd}. " + "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
1903
|
|
|
1904
|
-
` +
|
|
1904
|
+
` + `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
1905
|
|
|
1906
|
-
` + "Long-running processes
|
|
1906
|
+
` + "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
1907
|
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
1908
|
});
|
|
1909
1909
|
|
|
@@ -2674,6 +2674,10 @@ var init_config3 = __esm(() => {
|
|
|
2674
2674
|
preserveRecentMessages: z26.number().int().min(2).max(100).optional(),
|
|
2675
2675
|
contextWindowOverride: z26.number().int().min(8000).max(1e7).optional(),
|
|
2676
2676
|
summaryMaxChars: z26.number().int().min(200).max(50000).optional(),
|
|
2677
|
+
aggressivePruneWhenUncached: z26.boolean().optional(),
|
|
2678
|
+
uncachedPruneAtFraction: z26.number().min(0.2).max(0.9).optional(),
|
|
2679
|
+
uncachedPruneMinOutputChars: z26.number().int().min(200).max(12000).optional(),
|
|
2680
|
+
uncachedQuantizeUserTurns: z26.number().int().min(1).max(20).optional(),
|
|
2677
2681
|
maxInputTokens: z26.number().int().min(1).max(1e7).optional()
|
|
2678
2682
|
}).strict();
|
|
2679
2683
|
resolvedContextOptimizerConfigSchema = z26.object({
|
|
@@ -2682,15 +2686,23 @@ var init_config3 = __esm(() => {
|
|
|
2682
2686
|
pruneAtFraction: z26.number().min(0.2).max(0.9),
|
|
2683
2687
|
preserveRecentMessages: z26.number().int().min(2).max(100),
|
|
2684
2688
|
contextWindowOverride: z26.number().int().min(8000).max(1e7).nullable(),
|
|
2685
|
-
summaryMaxChars: z26.number().int().min(200).max(50000)
|
|
2689
|
+
summaryMaxChars: z26.number().int().min(200).max(50000),
|
|
2690
|
+
aggressivePruneWhenUncached: z26.boolean(),
|
|
2691
|
+
uncachedPruneAtFraction: z26.number().min(0.2).max(0.9),
|
|
2692
|
+
uncachedPruneMinOutputChars: z26.number().int().min(200).max(12000),
|
|
2693
|
+
uncachedQuantizeUserTurns: z26.number().int().min(1).max(20)
|
|
2686
2694
|
});
|
|
2687
2695
|
defaultContextOptimizerConfig = {
|
|
2688
2696
|
autoCompact: true,
|
|
2689
|
-
compactAtFraction: 0.
|
|
2697
|
+
compactAtFraction: 0.7,
|
|
2690
2698
|
pruneAtFraction: 0.6,
|
|
2691
2699
|
preserveRecentMessages: 6,
|
|
2692
2700
|
contextWindowOverride: null,
|
|
2693
|
-
summaryMaxChars: 4000
|
|
2701
|
+
summaryMaxChars: 4000,
|
|
2702
|
+
aggressivePruneWhenUncached: true,
|
|
2703
|
+
uncachedPruneAtFraction: 0.45,
|
|
2704
|
+
uncachedPruneMinOutputChars: 600,
|
|
2705
|
+
uncachedQuantizeUserTurns: 1
|
|
2694
2706
|
};
|
|
2695
2707
|
});
|
|
2696
2708
|
|
|
@@ -3156,9 +3168,13 @@ function buildProviderView(options) {
|
|
|
3156
3168
|
}
|
|
3157
3169
|
let estimate = estimateContextTokens(providerMessages);
|
|
3158
3170
|
let tier1 = null;
|
|
3159
|
-
|
|
3171
|
+
const uncachedActive = options.cacheActive === false && config.aggressivePruneWhenUncached;
|
|
3172
|
+
const pruneFraction = uncachedActive ? config.uncachedPruneAtFraction : config.pruneAtFraction;
|
|
3173
|
+
if (estimate.tokens > pruneFraction * contextWindow) {
|
|
3160
3174
|
tier1 = pruneToolOutputs(providerMessages, {
|
|
3161
|
-
preserveRecentMessages: config.preserveRecentMessages
|
|
3175
|
+
preserveRecentMessages: config.preserveRecentMessages,
|
|
3176
|
+
minOutputChars: uncachedActive ? config.uncachedPruneMinOutputChars : undefined,
|
|
3177
|
+
quantizeUserTurns: uncachedActive ? config.uncachedQuantizeUserTurns : undefined
|
|
3162
3178
|
});
|
|
3163
3179
|
if (tier1.savedChars > 0) {
|
|
3164
3180
|
providerMessages = tier1.messages;
|
|
@@ -3201,6 +3217,40 @@ var init_build_provider_view = __esm(() => {
|
|
|
3201
3217
|
init_tier1_prune();
|
|
3202
3218
|
});
|
|
3203
3219
|
|
|
3220
|
+
// packages/ai/src/context/normalize-provider-messages.ts
|
|
3221
|
+
import { isToolUIPart } from "ai";
|
|
3222
|
+
function resolveDanglingToolParts(messages, errorText = interruptedToolErrorText) {
|
|
3223
|
+
return messages.map((message) => {
|
|
3224
|
+
if (message.role !== "assistant") {
|
|
3225
|
+
return message;
|
|
3226
|
+
}
|
|
3227
|
+
let changed = false;
|
|
3228
|
+
const parts = message.parts.map((part) => {
|
|
3229
|
+
if (!isToolUIPart(part) || terminalToolStates.has(part.state)) {
|
|
3230
|
+
return part;
|
|
3231
|
+
}
|
|
3232
|
+
changed = true;
|
|
3233
|
+
return {
|
|
3234
|
+
...part,
|
|
3235
|
+
state: "output-error",
|
|
3236
|
+
errorText
|
|
3237
|
+
};
|
|
3238
|
+
});
|
|
3239
|
+
return changed ? { ...message, parts } : message;
|
|
3240
|
+
});
|
|
3241
|
+
}
|
|
3242
|
+
function normalizeProviderMessages(messages) {
|
|
3243
|
+
return resolveDanglingToolParts(messages);
|
|
3244
|
+
}
|
|
3245
|
+
var terminalToolStates, interruptedToolErrorText = "Tool call was interrupted by a connection retry. Re-issue the tool call if it is still needed.";
|
|
3246
|
+
var init_normalize_provider_messages = __esm(() => {
|
|
3247
|
+
terminalToolStates = new Set([
|
|
3248
|
+
"output-available",
|
|
3249
|
+
"output-error",
|
|
3250
|
+
"output-denied"
|
|
3251
|
+
]);
|
|
3252
|
+
});
|
|
3253
|
+
|
|
3204
3254
|
// packages/ai/src/context/index.ts
|
|
3205
3255
|
var init_context = __esm(() => {
|
|
3206
3256
|
init_build_provider_view();
|
|
@@ -3209,6 +3259,7 @@ var init_context = __esm(() => {
|
|
|
3209
3259
|
init_estimate();
|
|
3210
3260
|
init_heuristic_summary();
|
|
3211
3261
|
init_message_parts();
|
|
3262
|
+
init_normalize_provider_messages();
|
|
3212
3263
|
init_tier1_prune();
|
|
3213
3264
|
});
|
|
3214
3265
|
|
|
@@ -3304,9 +3355,19 @@ function readEnvConfig(env) {
|
|
|
3304
3355
|
contextWindowOverride: parseNumberEnv(env.LIGHTCODE_CONTEXT_WINDOW, "LIGHTCODE_CONTEXT_WINDOW"),
|
|
3305
3356
|
maxInputTokens: parseNumberEnv(env.LIGHTCODE_CONTEXT_MAX_INPUT_TOKENS, "LIGHTCODE_CONTEXT_MAX_INPUT_TOKENS"),
|
|
3306
3357
|
preserveRecentMessages: parseNumberEnv(env.LIGHTCODE_CONTEXT_PRESERVE_RECENT_MESSAGES, "LIGHTCODE_CONTEXT_PRESERVE_RECENT_MESSAGES"),
|
|
3307
|
-
summaryMaxChars: parseNumberEnv(env.LIGHTCODE_CONTEXT_SUMMARY_MAX_CHARS, "LIGHTCODE_CONTEXT_SUMMARY_MAX_CHARS")
|
|
3358
|
+
summaryMaxChars: parseNumberEnv(env.LIGHTCODE_CONTEXT_SUMMARY_MAX_CHARS, "LIGHTCODE_CONTEXT_SUMMARY_MAX_CHARS"),
|
|
3359
|
+
aggressivePruneWhenUncached: parseBooleanEnv(env.LIGHTCODE_CONTEXT_AGGRESSIVE_PRUNE_UNCACHED, "LIGHTCODE_CONTEXT_AGGRESSIVE_PRUNE_UNCACHED"),
|
|
3360
|
+
uncachedPruneAtFraction: parseNumberEnv(env.LIGHTCODE_CONTEXT_UNCACHED_PRUNE_AT_FRACTION, "LIGHTCODE_CONTEXT_UNCACHED_PRUNE_AT_FRACTION"),
|
|
3361
|
+
uncachedPruneMinOutputChars: parseNumberEnv(env.LIGHTCODE_CONTEXT_UNCACHED_PRUNE_MIN_OUTPUT_CHARS, "LIGHTCODE_CONTEXT_UNCACHED_PRUNE_MIN_OUTPUT_CHARS"),
|
|
3362
|
+
uncachedQuantizeUserTurns: parseNumberEnv(env.LIGHTCODE_CONTEXT_UNCACHED_QUANTIZE_USER_TURNS, "LIGHTCODE_CONTEXT_UNCACHED_QUANTIZE_USER_TURNS")
|
|
3308
3363
|
};
|
|
3309
3364
|
const compactContextConfig = Object.fromEntries(Object.entries(contextConfig).filter(([, value]) => value !== undefined));
|
|
3365
|
+
const headroomConfig = {
|
|
3366
|
+
enabled: parseBooleanEnv(env.LIGHTCODE_HEADROOM_ENABLED, "LIGHTCODE_HEADROOM_ENABLED"),
|
|
3367
|
+
proxyUrl: env.LIGHTCODE_HEADROOM_PROXY_URL?.trim() || undefined,
|
|
3368
|
+
failOpen: parseBooleanEnv(env.LIGHTCODE_HEADROOM_FAIL_OPEN, "LIGHTCODE_HEADROOM_FAIL_OPEN")
|
|
3369
|
+
};
|
|
3370
|
+
const compactHeadroomConfig = Object.fromEntries(Object.entries(headroomConfig).filter(([, value]) => value !== undefined));
|
|
3310
3371
|
const rawConfig = {
|
|
3311
3372
|
provider: env.LIGHTCODE_PROVIDER,
|
|
3312
3373
|
model: env.LIGHTCODE_CHAT_MODEL,
|
|
@@ -3316,7 +3377,8 @@ function readEnvConfig(env) {
|
|
|
3316
3377
|
maxOutputTokens: parseNumberEnv(env.LIGHTCODE_MAX_OUTPUT_TOKENS, "LIGHTCODE_MAX_OUTPUT_TOKENS"),
|
|
3317
3378
|
maxSteps: parseNumberEnv(env.LIGHTCODE_MAX_STEPS, "LIGHTCODE_MAX_STEPS"),
|
|
3318
3379
|
maxRetries: parseNumberEnv(env.LIGHTCODE_MAX_RETRIES, "LIGHTCODE_MAX_RETRIES"),
|
|
3319
|
-
context: Object.keys(compactContextConfig).length > 0 ? compactContextConfig : undefined
|
|
3380
|
+
context: Object.keys(compactContextConfig).length > 0 ? compactContextConfig : undefined,
|
|
3381
|
+
headroom: Object.keys(compactHeadroomConfig).length > 0 ? compactHeadroomConfig : undefined
|
|
3320
3382
|
};
|
|
3321
3383
|
const compactConfig = Object.fromEntries(Object.entries(rawConfig).filter(([, value]) => value !== undefined));
|
|
3322
3384
|
const parsedConfig = lightcodeConfigSchema.safeParse(compactConfig);
|
|
@@ -3340,6 +3402,10 @@ function mergeConfig(...configs) {
|
|
|
3340
3402
|
...mergedConfig.autoContinue,
|
|
3341
3403
|
...config2.autoContinue
|
|
3342
3404
|
} : mergedConfig.autoContinue,
|
|
3405
|
+
headroom: config2.headroom ? {
|
|
3406
|
+
...mergedConfig.headroom,
|
|
3407
|
+
...config2.headroom
|
|
3408
|
+
} : mergedConfig.headroom,
|
|
3343
3409
|
allowedTools: config2.allowedTools ?? mergedConfig.allowedTools
|
|
3344
3410
|
}), {});
|
|
3345
3411
|
}
|
|
@@ -3380,6 +3446,10 @@ function loadLightcodeConfig({
|
|
|
3380
3446
|
autoContinue: {
|
|
3381
3447
|
...defaultAutoContinueConfig,
|
|
3382
3448
|
...mergedConfig.autoContinue
|
|
3449
|
+
},
|
|
3450
|
+
headroom: {
|
|
3451
|
+
...defaultHeadroomConfig,
|
|
3452
|
+
...mergedConfig.headroom
|
|
3383
3453
|
}
|
|
3384
3454
|
};
|
|
3385
3455
|
return lightcodeConfigLoadResultSchema.parse({
|
|
@@ -3387,7 +3457,7 @@ function loadLightcodeConfig({
|
|
|
3387
3457
|
loadedFiles: [userConfig.file, projectConfig.file]
|
|
3388
3458
|
});
|
|
3389
3459
|
}
|
|
3390
|
-
var lightcodeProviderSchema, autoContinueConfigSchema, resolvedAutoContinueConfigSchema, defaultAutoContinueConfig, lightcodeConfigSchema, lightcodeConfigDefaults, loadedConfigFileSchema, lightcodeResolvedConfigSchema, lightcodeConfigLoadResultSchema, lightcodeConfigStatusSchema, LightcodeConfigError;
|
|
3460
|
+
var lightcodeProviderSchema, autoContinueConfigSchema, resolvedAutoContinueConfigSchema, defaultAutoContinueConfig, headroomConfigSchema, resolvedHeadroomConfigSchema, defaultHeadroomConfig, lightcodeConfigSchema, lightcodeConfigDefaults, loadedConfigFileSchema, lightcodeResolvedConfigSchema, lightcodeConfigLoadResultSchema, lightcodeConfigStatusSchema, LightcodeConfigError;
|
|
3391
3461
|
var init_lightcode_config = __esm(() => {
|
|
3392
3462
|
init_coding_agent_modes();
|
|
3393
3463
|
init_coding_agent_modes();
|
|
@@ -3419,6 +3489,24 @@ var init_lightcode_config = __esm(() => {
|
|
|
3419
3489
|
maxErrorRetries: 5,
|
|
3420
3490
|
stallTimeoutSeconds: 180
|
|
3421
3491
|
};
|
|
3492
|
+
headroomConfigSchema = z28.object({
|
|
3493
|
+
enabled: z28.boolean().optional(),
|
|
3494
|
+
proxyUrl: z28.string().min(1).max(4096).optional(),
|
|
3495
|
+
providers: z28.array(lightcodeProviderSchema).optional(),
|
|
3496
|
+
failOpen: z28.boolean().optional()
|
|
3497
|
+
}).strict();
|
|
3498
|
+
resolvedHeadroomConfigSchema = z28.object({
|
|
3499
|
+
enabled: z28.boolean(),
|
|
3500
|
+
proxyUrl: z28.string().min(1).max(4096),
|
|
3501
|
+
providers: z28.array(lightcodeProviderSchema).nullable(),
|
|
3502
|
+
failOpen: z28.boolean()
|
|
3503
|
+
});
|
|
3504
|
+
defaultHeadroomConfig = {
|
|
3505
|
+
enabled: false,
|
|
3506
|
+
proxyUrl: "http://127.0.0.1:8787",
|
|
3507
|
+
providers: null,
|
|
3508
|
+
failOpen: true
|
|
3509
|
+
};
|
|
3422
3510
|
lightcodeConfigSchema = z28.object({
|
|
3423
3511
|
provider: lightcodeProviderSchema.optional(),
|
|
3424
3512
|
model: z28.string().min(1).max(240).optional(),
|
|
@@ -3430,10 +3518,11 @@ var init_lightcode_config = __esm(() => {
|
|
|
3430
3518
|
sandbox: sandboxConfigSchema.optional(),
|
|
3431
3519
|
mcp: mcpConfigSchema.optional(),
|
|
3432
3520
|
context: contextOptimizerConfigSchema.optional(),
|
|
3433
|
-
maxOutputTokens: z28.number().int().min(1).max(
|
|
3521
|
+
maxOutputTokens: z28.number().int().min(1).max(1e6).optional(),
|
|
3434
3522
|
maxSteps: z28.number().int().min(1).max(100).optional(),
|
|
3435
3523
|
maxRetries: z28.number().int().min(0).max(10).optional(),
|
|
3436
|
-
autoContinue: autoContinueConfigSchema.optional()
|
|
3524
|
+
autoContinue: autoContinueConfigSchema.optional(),
|
|
3525
|
+
headroom: headroomConfigSchema.optional()
|
|
3437
3526
|
}).strict();
|
|
3438
3527
|
lightcodeConfigDefaults = {
|
|
3439
3528
|
provider: "anthropic",
|
|
@@ -3442,7 +3531,8 @@ var init_lightcode_config = __esm(() => {
|
|
|
3442
3531
|
maxOutputTokens: 32768,
|
|
3443
3532
|
maxSteps: 30,
|
|
3444
3533
|
maxRetries: 5,
|
|
3445
|
-
autoContinue: defaultAutoContinueConfig
|
|
3534
|
+
autoContinue: defaultAutoContinueConfig,
|
|
3535
|
+
headroom: defaultHeadroomConfig
|
|
3446
3536
|
};
|
|
3447
3537
|
loadedConfigFileSchema = z28.object({
|
|
3448
3538
|
scope: z28.enum(["user", "project"]),
|
|
@@ -3454,10 +3544,11 @@ var init_lightcode_config = __esm(() => {
|
|
|
3454
3544
|
provider: lightcodeProviderSchema,
|
|
3455
3545
|
defaultMode: codingAgentModeSchema,
|
|
3456
3546
|
context: resolvedContextOptimizerConfigSchema,
|
|
3457
|
-
maxOutputTokens: z28.number().int().min(1).max(
|
|
3547
|
+
maxOutputTokens: z28.number().int().min(1).max(1e6),
|
|
3458
3548
|
maxSteps: z28.number().int().min(1).max(100),
|
|
3459
3549
|
maxRetries: z28.number().int().min(0).max(10),
|
|
3460
|
-
autoContinue: resolvedAutoContinueConfigSchema
|
|
3550
|
+
autoContinue: resolvedAutoContinueConfigSchema,
|
|
3551
|
+
headroom: resolvedHeadroomConfigSchema.optional()
|
|
3461
3552
|
});
|
|
3462
3553
|
lightcodeConfigLoadResultSchema = z28.object({
|
|
3463
3554
|
config: lightcodeResolvedConfigSchema,
|
|
@@ -3471,10 +3562,15 @@ var init_lightcode_config = __esm(() => {
|
|
|
3471
3562
|
baseUrl: z28.string().nullable(),
|
|
3472
3563
|
defaultMode: codingAgentModeSchema,
|
|
3473
3564
|
permissionMode: permissionModeSchema.nullable(),
|
|
3474
|
-
maxOutputTokens: z28.number().int().min(1).max(
|
|
3565
|
+
maxOutputTokens: z28.number().int().min(1).max(1e6),
|
|
3475
3566
|
maxSteps: z28.number().int().min(1).max(100),
|
|
3476
3567
|
autoContinue: resolvedAutoContinueConfigSchema,
|
|
3477
3568
|
contextWindow: z28.number().int().positive(),
|
|
3569
|
+
headroom: z28.object({
|
|
3570
|
+
enabled: z28.boolean(),
|
|
3571
|
+
proxyUrl: z28.string().nullable(),
|
|
3572
|
+
routed: z28.boolean()
|
|
3573
|
+
}),
|
|
3478
3574
|
missingCredentialHints: z28.array(z28.string())
|
|
3479
3575
|
});
|
|
3480
3576
|
LightcodeConfigError = class LightcodeConfigError extends Error {
|
|
@@ -3714,6 +3810,21 @@ var init_runtime = __esm(() => {
|
|
|
3714
3810
|
init_schema13();
|
|
3715
3811
|
});
|
|
3716
3812
|
|
|
3813
|
+
// packages/ai/src/providers/clamp-output-tokens.ts
|
|
3814
|
+
function clampMaxOutputTokens(configured, modelMaxCompletionTokens) {
|
|
3815
|
+
if (typeof modelMaxCompletionTokens === "number" && Number.isFinite(modelMaxCompletionTokens) && modelMaxCompletionTokens > 0) {
|
|
3816
|
+
return Math.min(configured, modelMaxCompletionTokens);
|
|
3817
|
+
}
|
|
3818
|
+
return configured;
|
|
3819
|
+
}
|
|
3820
|
+
function resolveMaxOutputTokens(configured, defaultValue, modelMaxCompletionTokens) {
|
|
3821
|
+
const hasModelCap = typeof modelMaxCompletionTokens === "number" && Number.isFinite(modelMaxCompletionTokens) && modelMaxCompletionTokens > 0;
|
|
3822
|
+
if (configured === defaultValue) {
|
|
3823
|
+
return hasModelCap ? modelMaxCompletionTokens : configured;
|
|
3824
|
+
}
|
|
3825
|
+
return clampMaxOutputTokens(configured, modelMaxCompletionTokens);
|
|
3826
|
+
}
|
|
3827
|
+
|
|
3717
3828
|
// packages/ai/src/providers/model-metadata.ts
|
|
3718
3829
|
function getModelContextWindow(provider, modelId) {
|
|
3719
3830
|
if (provider === "anthropic") {
|
|
@@ -6551,6 +6662,9 @@ function summarizeRawModel(raw2) {
|
|
|
6551
6662
|
}
|
|
6552
6663
|
const name = Reflect.get(raw2, "name");
|
|
6553
6664
|
const contextLength = Reflect.get(raw2, "context_length");
|
|
6665
|
+
const topProvider = Reflect.get(raw2, "top_provider");
|
|
6666
|
+
const topProviderMaxCompletion = typeof topProvider === "object" && topProvider !== null ? Reflect.get(topProvider, "max_completion_tokens") : undefined;
|
|
6667
|
+
const rawMaxCompletion = typeof topProviderMaxCompletion === "number" ? topProviderMaxCompletion : Reflect.get(raw2, "max_completion_tokens");
|
|
6554
6668
|
const supportedParameters = Reflect.get(raw2, "supported_parameters");
|
|
6555
6669
|
const parameterList = Array.isArray(supportedParameters) ? supportedParameters.filter((entry) => typeof entry === "string") : [];
|
|
6556
6670
|
const pricingRaw = Reflect.get(raw2, "pricing");
|
|
@@ -6562,6 +6676,7 @@ function summarizeRawModel(raw2) {
|
|
|
6562
6676
|
id,
|
|
6563
6677
|
name: typeof name === "string" && name.length > 0 ? name : id,
|
|
6564
6678
|
contextLength: typeof contextLength === "number" && contextLength > 0 ? Math.round(contextLength) : null,
|
|
6679
|
+
maxCompletionTokens: typeof rawMaxCompletion === "number" && rawMaxCompletion > 0 ? Math.round(rawMaxCompletion) : null,
|
|
6565
6680
|
supportsTools: parameterList.includes("tools"),
|
|
6566
6681
|
supportsReasoning: parameterList.includes("reasoning"),
|
|
6567
6682
|
pricing
|
|
@@ -6611,7 +6726,8 @@ async function getOpenRouterModelCapabilities(modelId) {
|
|
|
6611
6726
|
}
|
|
6612
6727
|
return {
|
|
6613
6728
|
supportsNativeTools: model.supportsTools,
|
|
6614
|
-
contextLength: model.contextLength ?? undefined
|
|
6729
|
+
contextLength: model.contextLength ?? undefined,
|
|
6730
|
+
maxCompletionTokens: model.maxCompletionTokens
|
|
6615
6731
|
};
|
|
6616
6732
|
} catch {
|
|
6617
6733
|
return;
|
|
@@ -6626,6 +6742,7 @@ var init_openrouter_models = __esm(() => {
|
|
|
6626
6742
|
id: z34.string(),
|
|
6627
6743
|
name: z34.string(),
|
|
6628
6744
|
contextLength: z34.number().int().positive().nullable(),
|
|
6745
|
+
maxCompletionTokens: z34.number().int().positive().nullable().default(null),
|
|
6629
6746
|
supportsTools: z34.boolean(),
|
|
6630
6747
|
supportsReasoning: z34.boolean(),
|
|
6631
6748
|
pricing: z34.object({
|
|
@@ -6671,6 +6788,16 @@ function getAnthropicProviderOptions(modelId) {
|
|
|
6671
6788
|
}
|
|
6672
6789
|
};
|
|
6673
6790
|
}
|
|
6791
|
+
function resolveHeadroomRouting(config5, realBaseUrl) {
|
|
6792
|
+
const headroom = config5.headroom;
|
|
6793
|
+
if (!headroom?.enabled) {
|
|
6794
|
+
return { effectiveBaseUrl: realBaseUrl, headroomRouted: false };
|
|
6795
|
+
}
|
|
6796
|
+
if (headroom.providers && !headroom.providers.includes(config5.provider)) {
|
|
6797
|
+
return { effectiveBaseUrl: realBaseUrl, headroomRouted: false };
|
|
6798
|
+
}
|
|
6799
|
+
return { effectiveBaseUrl: headroom.proxyUrl, headroomRouted: true };
|
|
6800
|
+
}
|
|
6674
6801
|
function resolveAnthropicModel({
|
|
6675
6802
|
config: config5,
|
|
6676
6803
|
env,
|
|
@@ -6681,19 +6808,22 @@ function resolveAnthropicModel({
|
|
|
6681
6808
|
const apiKey = getEnvValue(env, "ANTHROPIC_API_KEY") ?? storedCredentials.anthropicApiKey;
|
|
6682
6809
|
const authToken = getEnvValue(env, "ANTHROPIC_AUTH_TOKEN");
|
|
6683
6810
|
const baseUrl = config5.baseUrl ?? getEnvValue(env, "ANTHROPIC_BASE_URL") ?? null;
|
|
6811
|
+
const { effectiveBaseUrl, headroomRouted } = resolveHeadroomRouting(config5, baseUrl);
|
|
6684
6812
|
const missingCredentialHints = apiKey || authToken ? [] : [
|
|
6685
6813
|
"Set ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN to call Anthropic models."
|
|
6686
6814
|
];
|
|
6687
6815
|
const provider = createAnthropic({
|
|
6688
6816
|
apiKey,
|
|
6689
6817
|
authToken,
|
|
6690
|
-
baseURL:
|
|
6818
|
+
baseURL: effectiveBaseUrl ?? undefined
|
|
6691
6819
|
});
|
|
6692
6820
|
return {
|
|
6693
6821
|
provider: "anthropic",
|
|
6694
6822
|
configuredModel,
|
|
6695
6823
|
resolvedModelId: modelId,
|
|
6696
6824
|
baseUrl,
|
|
6825
|
+
effectiveBaseUrl,
|
|
6826
|
+
headroomRouted,
|
|
6697
6827
|
model: provider(modelId),
|
|
6698
6828
|
providerOptions: getAnthropicProviderOptions(modelId),
|
|
6699
6829
|
contextWindow: getModelContextWindow("anthropic", modelId),
|
|
@@ -6716,6 +6846,7 @@ function resolveOpenAITransportModel({
|
|
|
6716
6846
|
if (!baseUrl) {
|
|
6717
6847
|
throw new Error('OpenAI-compatible provider requires "baseUrl" in config or LIGHTCODE_OPENAI_COMPATIBLE_BASE_URL.');
|
|
6718
6848
|
}
|
|
6849
|
+
const { effectiveBaseUrl, headroomRouted } = resolveHeadroomRouting(config5, baseUrl);
|
|
6719
6850
|
const apiKey = isOpenCodeZen ? getEnvValue(env, "OPENCODE_API_KEY", "LIGHTCODE_OPENAI_COMPATIBLE_API_KEY", "OPENAI_API_KEY") ?? storedCredentials.opencodeApiKey : isOpenRouter ? getEnvValue(env, "OPENROUTER_API_KEY", "LIGHTCODE_OPENAI_COMPATIBLE_API_KEY", "OPENAI_API_KEY") ?? storedCredentials.openrouterApiKey : getEnvValue(env, "LIGHTCODE_OPENAI_COMPATIBLE_API_KEY", "OPENAI_API_KEY") ?? storedCredentials.openaiCompatibleApiKey;
|
|
6720
6851
|
const headers = {};
|
|
6721
6852
|
if (isOpenRouter) {
|
|
@@ -6730,7 +6861,7 @@ function resolveOpenAITransportModel({
|
|
|
6730
6861
|
}
|
|
6731
6862
|
const provider = createOpenAICompatible({
|
|
6732
6863
|
name: config5.provider,
|
|
6733
|
-
baseURL: baseUrl,
|
|
6864
|
+
baseURL: effectiveBaseUrl ?? baseUrl,
|
|
6734
6865
|
apiKey,
|
|
6735
6866
|
headers
|
|
6736
6867
|
});
|
|
@@ -6739,12 +6870,15 @@ function resolveOpenAITransportModel({
|
|
|
6739
6870
|
configuredModel,
|
|
6740
6871
|
resolvedModelId: configuredModel,
|
|
6741
6872
|
baseUrl,
|
|
6873
|
+
effectiveBaseUrl,
|
|
6874
|
+
headroomRouted,
|
|
6742
6875
|
model: withXmlToolCallSupport(provider(configuredModel)),
|
|
6743
6876
|
needsToolCallDiscipline: true,
|
|
6744
6877
|
contextWindow: capabilities?.contextLength ?? getModelContextWindow(config5.provider, configuredModel),
|
|
6745
6878
|
missingCredentialHints: apiKey ? [] : isOpenCodeZen ? ["Set OPENCODE_API_KEY to call OpenCode Zen models."] : isOpenRouter ? ["Set OPENROUTER_API_KEY to call OpenRouter models."] : [
|
|
6746
6879
|
"Set LIGHTCODE_OPENAI_COMPATIBLE_API_KEY or OPENAI_API_KEY if your OpenAI-compatible endpoint requires authentication."
|
|
6747
|
-
]
|
|
6880
|
+
],
|
|
6881
|
+
maxCompletionTokens: capabilities?.maxCompletionTokens ?? null
|
|
6748
6882
|
};
|
|
6749
6883
|
}
|
|
6750
6884
|
function resolveConfiguredProviderModel({
|
|
@@ -6776,13 +6910,18 @@ function createConfigStatus({
|
|
|
6776
6910
|
baseUrl: resolvedProviderModel.baseUrl,
|
|
6777
6911
|
defaultMode: config5.defaultMode,
|
|
6778
6912
|
permissionMode: config5.permissionMode ?? null,
|
|
6779
|
-
maxOutputTokens: config5.maxOutputTokens,
|
|
6913
|
+
maxOutputTokens: resolveMaxOutputTokens(config5.maxOutputTokens, lightcodeConfigDefaults.maxOutputTokens, resolvedProviderModel.maxCompletionTokens),
|
|
6780
6914
|
maxSteps: config5.maxSteps,
|
|
6781
6915
|
autoContinue: config5.autoContinue,
|
|
6782
6916
|
contextWindow: resolveContextWindowTokens({
|
|
6783
6917
|
config: config5.context,
|
|
6784
6918
|
modelContextWindow: resolvedProviderModel.contextWindow
|
|
6785
6919
|
}),
|
|
6920
|
+
headroom: {
|
|
6921
|
+
enabled: config5.headroom?.enabled ?? false,
|
|
6922
|
+
proxyUrl: config5.headroom?.proxyUrl ?? null,
|
|
6923
|
+
routed: resolvedProviderModel.headroomRouted
|
|
6924
|
+
},
|
|
6786
6925
|
missingCredentialHints: resolvedProviderModel.missingCredentialHints
|
|
6787
6926
|
});
|
|
6788
6927
|
}
|
|
@@ -6802,6 +6941,7 @@ __export(exports_runtime_config, {
|
|
|
6802
6941
|
resolvedProviderModel: () => resolvedProviderModel,
|
|
6803
6942
|
lightcodeConfigResult: () => lightcodeConfigResult,
|
|
6804
6943
|
initializeModelCapabilities: () => initializeModelCapabilities,
|
|
6944
|
+
disableHeadroomRouting: () => disableHeadroomRouting,
|
|
6805
6945
|
configStatus: () => configStatus,
|
|
6806
6946
|
codingAgent: () => codingAgent,
|
|
6807
6947
|
chatModelId: () => chatModelId,
|
|
@@ -6814,7 +6954,7 @@ function buildCodingAgent(config5, model) {
|
|
|
6814
6954
|
providerOptions: model.providerOptions,
|
|
6815
6955
|
maxSteps: config5.maxSteps,
|
|
6816
6956
|
maxRetries: config5.maxRetries,
|
|
6817
|
-
maxOutputTokens: config5.maxOutputTokens,
|
|
6957
|
+
maxOutputTokens: resolveMaxOutputTokens(config5.maxOutputTokens, lightcodeConfigDefaults.maxOutputTokens, model.maxCompletionTokens),
|
|
6818
6958
|
includeToolDiscipline: model.needsToolCallDiscipline ?? false
|
|
6819
6959
|
});
|
|
6820
6960
|
}
|
|
@@ -6859,6 +6999,39 @@ function applyModelSelection({
|
|
|
6859
6999
|
});
|
|
6860
7000
|
return configStatus;
|
|
6861
7001
|
}
|
|
7002
|
+
function disableHeadroomRouting() {
|
|
7003
|
+
if (!lightcodeConfigResult.config.headroom?.enabled) {
|
|
7004
|
+
return;
|
|
7005
|
+
}
|
|
7006
|
+
const nextConfig = {
|
|
7007
|
+
...lightcodeConfigResult.config,
|
|
7008
|
+
headroom: {
|
|
7009
|
+
...lightcodeConfigResult.config.headroom ?? lightcodeConfigDefaults.headroom,
|
|
7010
|
+
enabled: false
|
|
7011
|
+
}
|
|
7012
|
+
};
|
|
7013
|
+
const nextResolved = resolveConfiguredProviderModel({
|
|
7014
|
+
config: nextConfig,
|
|
7015
|
+
env: Bun.env
|
|
7016
|
+
});
|
|
7017
|
+
const nextAgent = buildCodingAgent(nextConfig, nextResolved);
|
|
7018
|
+
lightcodeConfigResult = {
|
|
7019
|
+
...lightcodeConfigResult,
|
|
7020
|
+
config: nextConfig
|
|
7021
|
+
};
|
|
7022
|
+
resolvedProviderModel = nextResolved;
|
|
7023
|
+
chatModelId = nextResolved.resolvedModelId;
|
|
7024
|
+
codingAgent = nextAgent;
|
|
7025
|
+
configStatus = createConfigStatus({
|
|
7026
|
+
config: nextConfig,
|
|
7027
|
+
loadedFiles: lightcodeConfigResult.loadedFiles,
|
|
7028
|
+
resolvedProviderModel: nextResolved
|
|
7029
|
+
});
|
|
7030
|
+
logger5.info("headroom_routing_disabled", {
|
|
7031
|
+
provider: nextResolved.provider,
|
|
7032
|
+
model: nextResolved.resolvedModelId
|
|
7033
|
+
});
|
|
7034
|
+
}
|
|
6862
7035
|
async function initializeModelCapabilities() {
|
|
6863
7036
|
if (lightcodeConfigResult.config.provider !== "openrouter") {
|
|
6864
7037
|
return;
|
|
@@ -7077,6 +7250,7 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7077
7250
|
}, 500);
|
|
7078
7251
|
}
|
|
7079
7252
|
const contextConfig = lightcodeConfigResult.config.context;
|
|
7253
|
+
const cacheActive = resolvedProviderModel.provider === "anthropic";
|
|
7080
7254
|
const pendingInteractionCount = await countPendingChatInteractions(sessionId);
|
|
7081
7255
|
let contextState = await loadSessionContextStateSafe(sessionId);
|
|
7082
7256
|
let view = buildProviderView({
|
|
@@ -7084,7 +7258,8 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7084
7258
|
contextState,
|
|
7085
7259
|
config: contextConfig,
|
|
7086
7260
|
modelContextWindow: resolvedProviderModel.contextWindow,
|
|
7087
|
-
pendingInteractionCount
|
|
7261
|
+
pendingInteractionCount,
|
|
7262
|
+
cacheActive
|
|
7088
7263
|
});
|
|
7089
7264
|
if (view.needsCompaction && contextConfig.autoCompact) {
|
|
7090
7265
|
try {
|
|
@@ -7105,7 +7280,8 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7105
7280
|
contextState,
|
|
7106
7281
|
config: contextConfig,
|
|
7107
7282
|
modelContextWindow: resolvedProviderModel.contextWindow,
|
|
7108
|
-
pendingInteractionCount
|
|
7283
|
+
pendingInteractionCount,
|
|
7284
|
+
cacheActive
|
|
7109
7285
|
});
|
|
7110
7286
|
logger7.info("context_auto_compacted", {
|
|
7111
7287
|
sessionId,
|
|
@@ -7134,7 +7310,8 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7134
7310
|
contextState,
|
|
7135
7311
|
config: overflowConfig,
|
|
7136
7312
|
modelContextWindow: resolvedProviderModel.contextWindow,
|
|
7137
|
-
pendingInteractionCount: 0
|
|
7313
|
+
pendingInteractionCount: 0,
|
|
7314
|
+
cacheActive
|
|
7138
7315
|
});
|
|
7139
7316
|
if (overflowView.coveredMessages.length > 0) {
|
|
7140
7317
|
const compaction = await compactSessionContext({
|
|
@@ -7154,7 +7331,8 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7154
7331
|
contextState,
|
|
7155
7332
|
config: overflowConfig,
|
|
7156
7333
|
modelContextWindow: resolvedProviderModel.contextWindow,
|
|
7157
|
-
pendingInteractionCount: 0
|
|
7334
|
+
pendingInteractionCount: 0,
|
|
7335
|
+
cacheActive
|
|
7158
7336
|
});
|
|
7159
7337
|
logger7.info("context_overflow_recovery", {
|
|
7160
7338
|
sessionId,
|
|
@@ -7171,7 +7349,7 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
|
|
|
7171
7349
|
});
|
|
7172
7350
|
}
|
|
7173
7351
|
}
|
|
7174
|
-
const providerMessages = view.providerMessages;
|
|
7352
|
+
const providerMessages = normalizeProviderMessages(view.providerMessages);
|
|
7175
7353
|
let overflowNotedThisRequest = false;
|
|
7176
7354
|
const noteOverflowForSession = () => {
|
|
7177
7355
|
if (overflowNotedThisRequest) {
|
|
@@ -8228,9 +8406,62 @@ var init_app = __esm(() => {
|
|
|
8228
8406
|
})).route("/", rootRoutes).route("/config", configRoutes).route("/diagnostics", diagnosticsRoutes).route("/extensions", extensionRoutes).route("/sessions", sessionRoutes);
|
|
8229
8407
|
});
|
|
8230
8408
|
|
|
8409
|
+
// apps/server/src/lib/headroom-proxy.ts
|
|
8410
|
+
var exports_headroom_proxy = {};
|
|
8411
|
+
__export(exports_headroom_proxy, {
|
|
8412
|
+
ensureHeadroomRouting: () => ensureHeadroomRouting,
|
|
8413
|
+
checkHeadroomHealth: () => checkHeadroomHealth
|
|
8414
|
+
});
|
|
8415
|
+
async function checkHeadroomHealth(proxyUrl) {
|
|
8416
|
+
try {
|
|
8417
|
+
await fetch(new URL("/health", proxyUrl), {
|
|
8418
|
+
signal: AbortSignal.timeout(healthCheckTimeoutMs)
|
|
8419
|
+
});
|
|
8420
|
+
return "healthy";
|
|
8421
|
+
} catch {
|
|
8422
|
+
return "down";
|
|
8423
|
+
}
|
|
8424
|
+
}
|
|
8425
|
+
async function ensureHeadroomRouting() {
|
|
8426
|
+
const headroom = lightcodeConfigResult.config.headroom;
|
|
8427
|
+
if (!headroom?.enabled) {
|
|
8428
|
+
return;
|
|
8429
|
+
}
|
|
8430
|
+
let health;
|
|
8431
|
+
try {
|
|
8432
|
+
health = await checkHeadroomHealth(headroom.proxyUrl);
|
|
8433
|
+
} catch (error) {
|
|
8434
|
+
health = "down";
|
|
8435
|
+
logger9.warn("headroom_health_probe_failed", {
|
|
8436
|
+
proxyUrl: headroom.proxyUrl,
|
|
8437
|
+
error: getErrorMessage(error)
|
|
8438
|
+
});
|
|
8439
|
+
}
|
|
8440
|
+
if (health === "healthy") {
|
|
8441
|
+
logger9.info("headroom_routing_active", { proxyUrl: headroom.proxyUrl });
|
|
8442
|
+
return;
|
|
8443
|
+
}
|
|
8444
|
+
if (headroom.failOpen) {
|
|
8445
|
+
logger9.warn("headroom_unreachable_failing_open", {
|
|
8446
|
+
proxyUrl: headroom.proxyUrl
|
|
8447
|
+
});
|
|
8448
|
+
disableHeadroomRouting();
|
|
8449
|
+
return;
|
|
8450
|
+
}
|
|
8451
|
+
logger9.error("headroom_unreachable_fail_closed", {
|
|
8452
|
+
proxyUrl: headroom.proxyUrl
|
|
8453
|
+
});
|
|
8454
|
+
}
|
|
8455
|
+
var logger9, healthCheckTimeoutMs = 1500;
|
|
8456
|
+
var init_headroom_proxy = __esm(() => {
|
|
8457
|
+
init_src();
|
|
8458
|
+
init_runtime_config();
|
|
8459
|
+
logger9 = createLogger("headroom-proxy");
|
|
8460
|
+
});
|
|
8461
|
+
|
|
8231
8462
|
// apps/server/src/index.ts
|
|
8232
8463
|
init_src();
|
|
8233
|
-
var
|
|
8464
|
+
var logger10 = createLogger("server");
|
|
8234
8465
|
var httpIdleTimeoutSeconds = 30;
|
|
8235
8466
|
var shutdownForceExitMs = 5000;
|
|
8236
8467
|
function isChatStreamingRoute(request) {
|
|
@@ -8243,7 +8474,7 @@ function isChatStreamingRoute(request) {
|
|
|
8243
8474
|
async function startServer() {
|
|
8244
8475
|
const logDirectory = enableFileLogSink(Bun.env);
|
|
8245
8476
|
if (logDirectory) {
|
|
8246
|
-
|
|
8477
|
+
logger10.info("file_log_sink_enabled", { directory: logDirectory });
|
|
8247
8478
|
}
|
|
8248
8479
|
const port = Number(Bun.env.PORT ?? 4983);
|
|
8249
8480
|
const hostname = Bun.env.LIGHTCODE_HOST ?? "127.0.0.1";
|
|
@@ -8264,7 +8495,12 @@ async function startServer() {
|
|
|
8264
8495
|
process.exit(1);
|
|
8265
8496
|
}
|
|
8266
8497
|
Promise.resolve().then(() => (init_runtime_config(), exports_runtime_config)).then((runtime19) => runtime19.initializeModelCapabilities()).catch((error) => {
|
|
8267
|
-
|
|
8498
|
+
logger10.warn("model_capability_init_failed", {
|
|
8499
|
+
error: getErrorMessage(error)
|
|
8500
|
+
});
|
|
8501
|
+
});
|
|
8502
|
+
Promise.resolve().then(() => (init_headroom_proxy(), exports_headroom_proxy)).then((headroom) => headroom.ensureHeadroomRouting()).catch((error) => {
|
|
8503
|
+
logger10.warn("headroom_init_failed", {
|
|
8268
8504
|
error: getErrorMessage(error)
|
|
8269
8505
|
});
|
|
8270
8506
|
});
|
|
@@ -8300,7 +8536,7 @@ async function startServer() {
|
|
|
8300
8536
|
return;
|
|
8301
8537
|
}
|
|
8302
8538
|
shuttingDown = true;
|
|
8303
|
-
|
|
8539
|
+
logger10.info("server_shutdown", { signal });
|
|
8304
8540
|
const forceExitTimer = setTimeout(() => {
|
|
8305
8541
|
server.stop(true);
|
|
8306
8542
|
process.exit(0);
|