@kmugalkhod/lightcode 0.3.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.
Files changed (3) hide show
  1. package/cli.js +55 -25
  2. package/package.json +1 -1
  3. package/server.js +46 -21
package/cli.js CHANGED
@@ -124,7 +124,7 @@ var init_logger = __esm(() => {
124
124
  });
125
125
 
126
126
  // packages/shared/src/version.ts
127
- var lightcodeVersion = "0.3.0";
127
+ var lightcodeVersion = "0.4.0";
128
128
 
129
129
  // packages/shared/src/index.ts
130
130
  var productName = "Lightcode";
@@ -998,7 +998,7 @@ var init_slash_page_menu = __esm(() => {
998
998
  });
999
999
 
1000
1000
  // packages/ai/src/constants.ts
1001
- var MAX_TOOL_TEXT_OUTPUT_CHARS = 12000, MAX_TOOL_LIST_ENTRIES = 200, MAX_TOOL_SEARCH_RESULTS = 200, ANTHROPIC_TOOL_OPTIONAL_PARAMETER_BUDGET = 24;
1001
+ 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;
1002
1002
 
1003
1003
  // packages/ai/src/coding-agent-modes.ts
1004
1004
  import { z } from "zod";
@@ -1594,13 +1594,13 @@ var init_base_schemas = __esm(() => {
1594
1594
 
1595
1595
  // packages/ai/src/bash/schema.ts
1596
1596
  import { z as z5 } from "zod";
1597
- var bashDescription = "Run a shell command from workspace root with a timeout. Commands are classified before execution and may require approval.", bashInputSchema, bashProviderInputSchema, bashOutputSchema;
1597
+ 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;
1598
1598
  var init_schema = __esm(() => {
1599
1599
  init_base_schemas();
1600
1600
  bashInputSchema = z5.object({
1601
1601
  command: z5.string().min(1).max(4000),
1602
1602
  timeoutMs: integerRangeSchema(1000, 120000, "timeoutMs").optional().default(30000),
1603
- maxOutputChars: integerRangeSchema(200, MAX_TOOL_TEXT_OUTPUT_CHARS, "maxOutputChars").optional().default(MAX_TOOL_TEXT_OUTPUT_CHARS)
1603
+ maxOutputChars: integerRangeSchema(200, MAX_TOOL_TEXT_OUTPUT_CHARS, "maxOutputChars").optional().default(DEFAULT_TOOL_TEXT_OUTPUT_CHARS)
1604
1604
  });
1605
1605
  bashProviderInputSchema = z5.object({
1606
1606
  command: z5.string().min(1).max(4000)
@@ -1710,7 +1710,7 @@ var init_schema5 = __esm(() => {
1710
1710
  path: boundedPathSchema.optional(),
1711
1711
  staged: z9.boolean().optional().default(false),
1712
1712
  statOnly: z9.boolean().optional().default(false),
1713
- maxChars: integerRangeSchema(200, MAX_TOOL_TEXT_OUTPUT_CHARS, "maxChars").optional().default(MAX_TOOL_TEXT_OUTPUT_CHARS)
1713
+ maxChars: integerRangeSchema(200, MAX_TOOL_TEXT_OUTPUT_CHARS, "maxChars").optional().default(DEFAULT_TOOL_TEXT_OUTPUT_CHARS)
1714
1714
  });
1715
1715
  gitDiffProviderInputSchema = z9.object({
1716
1716
  path: boundedPathSchema.optional(),
@@ -1725,7 +1725,7 @@ var init_schema5 = __esm(() => {
1725
1725
  gitShowInputSchema = z9.object({
1726
1726
  revision: z9.string().min(1).max(240),
1727
1727
  path: boundedPathSchema.optional(),
1728
- maxChars: integerRangeSchema(200, MAX_TOOL_TEXT_OUTPUT_CHARS, "maxChars").optional().default(MAX_TOOL_TEXT_OUTPUT_CHARS)
1728
+ maxChars: integerRangeSchema(200, MAX_TOOL_TEXT_OUTPUT_CHARS, "maxChars").optional().default(DEFAULT_TOOL_TEXT_OUTPUT_CHARS)
1729
1729
  });
1730
1730
  gitShowProviderInputSchema = z9.object({
1731
1731
  revision: z9.string().min(1).max(240),
@@ -1822,7 +1822,7 @@ var init_schema6 = __esm(() => {
1822
1822
 
1823
1823
  // packages/ai/src/read-file/schema.ts
1824
1824
  import { z as z11 } from "zod";
1825
- var readFileDescription = "Read file content from the working directory with optional line range limits.", readFileInputSchema, readFileProviderInputSchema, readFileOutputSchema;
1825
+ 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;
1826
1826
  var init_schema7 = __esm(() => {
1827
1827
  init_base_schemas();
1828
1828
  readFileInputSchema = z11.object({
@@ -2076,12 +2076,12 @@ var init_schema13 = __esm(() => {
2076
2076
 
2077
2077
  // packages/ai/src/web-fetch/schema.ts
2078
2078
  import { z as z19 } from "zod";
2079
- var webFetchDescription = "Fetch a URL and return normalized, bounded text with source metadata. Requires network permission.", webFetchInputSchema, webFetchProviderInputSchema, webFetchErrorSchema, webFetchOutputSchema;
2079
+ 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;
2080
2080
  var init_schema14 = __esm(() => {
2081
2081
  init_base_schemas();
2082
2082
  webFetchInputSchema = z19.object({
2083
2083
  url: z19.string().url().max(4096),
2084
- maxChars: integerRangeSchema(200, MAX_TOOL_TEXT_OUTPUT_CHARS, "maxChars").optional().default(MAX_TOOL_TEXT_OUTPUT_CHARS),
2084
+ maxChars: integerRangeSchema(200, MAX_TOOL_TEXT_OUTPUT_CHARS, "maxChars").optional().default(DEFAULT_TOOL_TEXT_OUTPUT_CHARS),
2085
2085
  timeoutMs: integerRangeSchema(1000, 30000, "timeoutMs").optional().default(1e4)
2086
2086
  });
2087
2087
  webFetchProviderInputSchema = z19.object({
@@ -2857,11 +2857,11 @@ ${toolCallingDisciplineAddendum}` : "");
2857
2857
  var cwdPromptPlaceholder = "{cwd}", defaultCodingAgentSystemPrompt, toolCallingDisciplineAddendum;
2858
2858
  var init_coding_agent_prompt = __esm(() => {
2859
2859
  init_coding_agent_modes();
2860
- defaultCodingAgentSystemPrompt = "You are a basic coding agent. Use tools for filesystem and codebase tasks instead of guessing. " + "Respect the user's intent, explain changes clearly, and prefer incremental, auditable actions. " + "You can only interact with files under this working directory: {cwd}. " + "Use glob_search for path discovery, grep for text search, structured git tools for repository inspection, and bash only when dedicated tools are not enough. " + "Use todo_write to keep multi-step implementation work visible and current. " + "For risky or uncertain operations, inspect context first and be explicit about assumptions. " + "While working, emit brief progress notes in natural language before major tool actions and after important findings. " + `Keep them short, human, and concrete. Vary wording naturally and avoid repetitive templates or rigid labels.
2860
+ 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.
2861
2861
 
2862
- ` + "You are an agent \u2014 keep working until the user's request is fully resolved before ending your turn. " + "Never stop with a partial answer. If you say you will do something, do it in the same turn by calling a tool. " + "Use todo_write to track multi-step work and do not finish while any todo is pending or in_progress. " + `Only end your turn when the task is complete or you are blocked on input that only the user can provide.
2862
+ ` + `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.
2863
2863
 
2864
- ` + "Long-running processes: never run dev servers, watchers, or other non-exiting commands as plain foreground bash \u2014 " + "the bash tool enforces a timeout and the command will be killed. Start them detached with output redirected to a " + 'log file (e.g. on Windows: start /B cmd /c "npm run dev > dev.log 2>&1"; on Unix: nohup npm run dev > dev.log 2>&1 &), ' + "wait briefly, then verify by reading the log file or with a bounded probe like curl --max-time 5. " + "Dev servers do not always get their default port \u2014 read the actual port from the startup log instead of assuming it. " + "Never kill processes or free ports you did not open yourself; other tools (including this agent's own backend) may be using them.";
2864
+ ` + "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.";
2865
2865
  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.";
2866
2866
  });
2867
2867
 
@@ -3646,6 +3646,10 @@ var init_config3 = __esm(() => {
3646
3646
  preserveRecentMessages: z26.number().int().min(2).max(100).optional(),
3647
3647
  contextWindowOverride: z26.number().int().min(8000).max(1e7).optional(),
3648
3648
  summaryMaxChars: z26.number().int().min(200).max(50000).optional(),
3649
+ aggressivePruneWhenUncached: z26.boolean().optional(),
3650
+ uncachedPruneAtFraction: z26.number().min(0.2).max(0.9).optional(),
3651
+ uncachedPruneMinOutputChars: z26.number().int().min(200).max(12000).optional(),
3652
+ uncachedQuantizeUserTurns: z26.number().int().min(1).max(20).optional(),
3649
3653
  maxInputTokens: z26.number().int().min(1).max(1e7).optional()
3650
3654
  }).strict();
3651
3655
  resolvedContextOptimizerConfigSchema = z26.object({
@@ -3654,15 +3658,23 @@ var init_config3 = __esm(() => {
3654
3658
  pruneAtFraction: z26.number().min(0.2).max(0.9),
3655
3659
  preserveRecentMessages: z26.number().int().min(2).max(100),
3656
3660
  contextWindowOverride: z26.number().int().min(8000).max(1e7).nullable(),
3657
- summaryMaxChars: z26.number().int().min(200).max(50000)
3661
+ summaryMaxChars: z26.number().int().min(200).max(50000),
3662
+ aggressivePruneWhenUncached: z26.boolean(),
3663
+ uncachedPruneAtFraction: z26.number().min(0.2).max(0.9),
3664
+ uncachedPruneMinOutputChars: z26.number().int().min(200).max(12000),
3665
+ uncachedQuantizeUserTurns: z26.number().int().min(1).max(20)
3658
3666
  });
3659
3667
  defaultContextOptimizerConfig = {
3660
3668
  autoCompact: true,
3661
- compactAtFraction: 0.8,
3669
+ compactAtFraction: 0.7,
3662
3670
  pruneAtFraction: 0.6,
3663
3671
  preserveRecentMessages: 6,
3664
3672
  contextWindowOverride: null,
3665
- summaryMaxChars: 4000
3673
+ summaryMaxChars: 4000,
3674
+ aggressivePruneWhenUncached: true,
3675
+ uncachedPruneAtFraction: 0.45,
3676
+ uncachedPruneMinOutputChars: 600,
3677
+ uncachedQuantizeUserTurns: 1
3666
3678
  };
3667
3679
  });
3668
3680
 
@@ -4128,9 +4140,13 @@ function buildProviderView(options) {
4128
4140
  }
4129
4141
  let estimate = estimateContextTokens(providerMessages);
4130
4142
  let tier1 = null;
4131
- if (estimate.tokens > config.pruneAtFraction * contextWindow) {
4143
+ const uncachedActive = options.cacheActive === false && config.aggressivePruneWhenUncached;
4144
+ const pruneFraction = uncachedActive ? config.uncachedPruneAtFraction : config.pruneAtFraction;
4145
+ if (estimate.tokens > pruneFraction * contextWindow) {
4132
4146
  tier1 = pruneToolOutputs(providerMessages, {
4133
- preserveRecentMessages: config.preserveRecentMessages
4147
+ preserveRecentMessages: config.preserveRecentMessages,
4148
+ minOutputChars: uncachedActive ? config.uncachedPruneMinOutputChars : undefined,
4149
+ quantizeUserTurns: uncachedActive ? config.uncachedQuantizeUserTurns : undefined
4134
4150
  });
4135
4151
  if (tier1.savedChars > 0) {
4136
4152
  providerMessages = tier1.messages;
@@ -4314,7 +4330,11 @@ function readEnvConfig(env) {
4314
4330
  contextWindowOverride: parseNumberEnv(env.LIGHTCODE_CONTEXT_WINDOW, "LIGHTCODE_CONTEXT_WINDOW"),
4315
4331
  maxInputTokens: parseNumberEnv(env.LIGHTCODE_CONTEXT_MAX_INPUT_TOKENS, "LIGHTCODE_CONTEXT_MAX_INPUT_TOKENS"),
4316
4332
  preserveRecentMessages: parseNumberEnv(env.LIGHTCODE_CONTEXT_PRESERVE_RECENT_MESSAGES, "LIGHTCODE_CONTEXT_PRESERVE_RECENT_MESSAGES"),
4317
- summaryMaxChars: parseNumberEnv(env.LIGHTCODE_CONTEXT_SUMMARY_MAX_CHARS, "LIGHTCODE_CONTEXT_SUMMARY_MAX_CHARS")
4333
+ summaryMaxChars: parseNumberEnv(env.LIGHTCODE_CONTEXT_SUMMARY_MAX_CHARS, "LIGHTCODE_CONTEXT_SUMMARY_MAX_CHARS"),
4334
+ aggressivePruneWhenUncached: parseBooleanEnv(env.LIGHTCODE_CONTEXT_AGGRESSIVE_PRUNE_UNCACHED, "LIGHTCODE_CONTEXT_AGGRESSIVE_PRUNE_UNCACHED"),
4335
+ uncachedPruneAtFraction: parseNumberEnv(env.LIGHTCODE_CONTEXT_UNCACHED_PRUNE_AT_FRACTION, "LIGHTCODE_CONTEXT_UNCACHED_PRUNE_AT_FRACTION"),
4336
+ uncachedPruneMinOutputChars: parseNumberEnv(env.LIGHTCODE_CONTEXT_UNCACHED_PRUNE_MIN_OUTPUT_CHARS, "LIGHTCODE_CONTEXT_UNCACHED_PRUNE_MIN_OUTPUT_CHARS"),
4337
+ uncachedQuantizeUserTurns: parseNumberEnv(env.LIGHTCODE_CONTEXT_UNCACHED_QUANTIZE_USER_TURNS, "LIGHTCODE_CONTEXT_UNCACHED_QUANTIZE_USER_TURNS")
4318
4338
  };
4319
4339
  const compactContextConfig = Object.fromEntries(Object.entries(contextConfig).filter(([, value]) => value !== undefined));
4320
4340
  const headroomConfig = {
@@ -5949,19 +5969,29 @@ var init_resolve_within_workspace = __esm(() => {
5949
5969
  });
5950
5970
 
5951
5971
  // packages/ai/src/common/output-utils.ts
5952
- function truncateText(text, maxChars = MAX_TOOL_TEXT_OUTPUT_CHARS) {
5972
+ function omissionMarker(omitted) {
5973
+ return `
5974
+
5975
+ ... [${omitted} characters omitted; re-read a line range with read_file or raise maxChars] ...
5976
+
5977
+ `;
5978
+ }
5979
+ function truncateText(text, maxChars = DEFAULT_TOOL_TEXT_OUTPUT_CHARS) {
5953
5980
  if (text.length <= maxChars) {
5954
5981
  return {
5955
5982
  text,
5956
5983
  truncated: false
5957
5984
  };
5958
5985
  }
5959
- const suffix = `
5960
-
5961
- ... output truncated to ${maxChars} characters ...`;
5962
- const allowedLength = Math.max(0, maxChars - suffix.length);
5986
+ const markerReserve = omissionMarker(text.length).length;
5987
+ const usable = Math.max(0, maxChars - markerReserve);
5988
+ const headChars = Math.floor(usable * 0.7);
5989
+ const tailChars = usable - headChars;
5990
+ const head = text.slice(0, headChars);
5991
+ const tail = tailChars > 0 ? text.slice(text.length - tailChars) : "";
5992
+ const omitted = text.length - head.length - tail.length;
5963
5993
  return {
5964
- text: `${text.slice(0, allowedLength)}${suffix}`,
5994
+ text: `${head}${omissionMarker(omitted)}${tail}`,
5965
5995
  truncated: true
5966
5996
  };
5967
5997
  }
@@ -6971,7 +7001,7 @@ async function executeReadFile(input, workspaceContext = getDefaultWorkspaceCont
6971
7001
  const { start, end } = clampLineRange(lines.length, parsedInput.startLine, parsedInput.endLine);
6972
7002
  const selectedContent = lines.slice(start - 1, end).join(`
6973
7003
  `);
6974
- const maxChars = parsedInput.maxChars ?? MAX_TOOL_TEXT_OUTPUT_CHARS;
7004
+ const maxChars = parsedInput.maxChars ?? DEFAULT_TOOL_TEXT_OUTPUT_CHARS;
6975
7005
  const truncated = truncateText(selectedContent, maxChars);
6976
7006
  return readFileOutputSchema.parse({
6977
7007
  path: relativePath,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kmugalkhod/lightcode",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Lightcode — a personal AI coding agent with a terminal UI and local session storage.",
5
5
  "type": "module",
6
6
  "license": "MIT",
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(MAX_TOOL_TEXT_OUTPUT_CHARS)
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(MAX_TOOL_TEXT_OUTPUT_CHARS)
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(MAX_TOOL_TEXT_OUTPUT_CHARS)
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 with optional line range limits.", readFileInputSchema, readFileProviderInputSchema, readFileOutputSchema;
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(MAX_TOOL_TEXT_OUTPUT_CHARS),
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 basic coding agent. Use tools for filesystem and codebase tasks instead of guessing. " + "Respect the user's intent, explain changes clearly, and prefer incremental, auditable actions. " + "You can only interact with files under this working directory: {cwd}. " + "Use glob_search for path discovery, grep for text search, structured git tools for repository inspection, and bash only when dedicated tools are not enough. " + "Use todo_write to keep multi-step implementation work visible and current. " + "For risky or uncertain operations, inspect context first and be explicit about assumptions. " + "While working, emit brief progress notes in natural language before major tool actions and after important findings. " + `Keep them short, human, and concrete. Vary wording naturally and avoid repetitive templates or rigid labels.
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
- ` + "You are an agent \u2014 keep working until the user's request is fully resolved before ending your turn. " + "Never stop with a partial answer. If you say you will do something, do it in the same turn by calling a tool. " + "Use todo_write to track multi-step work and do not finish while any todo is pending or in_progress. " + `Only end your turn when the task is complete or you are blocked on input that only the user can provide.
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: never run dev servers, watchers, or other non-exiting commands as plain foreground bash \u2014 " + "the bash tool enforces a timeout and the command will be killed. Start them detached with output redirected to a " + 'log file (e.g. on Windows: start /B cmd /c "npm run dev > dev.log 2>&1"; on Unix: nohup npm run dev > dev.log 2>&1 &), ' + "wait briefly, then verify by reading the log file or with a bounded probe like curl --max-time 5. " + "Dev servers do not always get their default port \u2014 read the actual port from the startup log instead of assuming it. " + "Never kill processes or free ports you did not open yourself; other tools (including this agent's own backend) may be using them.";
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.8,
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
- if (estimate.tokens > config.pruneAtFraction * contextWindow) {
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;
@@ -3339,7 +3355,11 @@ function readEnvConfig(env) {
3339
3355
  contextWindowOverride: parseNumberEnv(env.LIGHTCODE_CONTEXT_WINDOW, "LIGHTCODE_CONTEXT_WINDOW"),
3340
3356
  maxInputTokens: parseNumberEnv(env.LIGHTCODE_CONTEXT_MAX_INPUT_TOKENS, "LIGHTCODE_CONTEXT_MAX_INPUT_TOKENS"),
3341
3357
  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")
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")
3343
3363
  };
3344
3364
  const compactContextConfig = Object.fromEntries(Object.entries(contextConfig).filter(([, value]) => value !== undefined));
3345
3365
  const headroomConfig = {
@@ -7230,6 +7250,7 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
7230
7250
  }, 500);
7231
7251
  }
7232
7252
  const contextConfig = lightcodeConfigResult.config.context;
7253
+ const cacheActive = resolvedProviderModel.provider === "anthropic";
7233
7254
  const pendingInteractionCount = await countPendingChatInteractions(sessionId);
7234
7255
  let contextState = await loadSessionContextStateSafe(sessionId);
7235
7256
  let view = buildProviderView({
@@ -7237,7 +7258,8 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
7237
7258
  contextState,
7238
7259
  config: contextConfig,
7239
7260
  modelContextWindow: resolvedProviderModel.contextWindow,
7240
- pendingInteractionCount
7261
+ pendingInteractionCount,
7262
+ cacheActive
7241
7263
  });
7242
7264
  if (view.needsCompaction && contextConfig.autoCompact) {
7243
7265
  try {
@@ -7258,7 +7280,8 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
7258
7280
  contextState,
7259
7281
  config: contextConfig,
7260
7282
  modelContextWindow: resolvedProviderModel.contextWindow,
7261
- pendingInteractionCount
7283
+ pendingInteractionCount,
7284
+ cacheActive
7262
7285
  });
7263
7286
  logger7.info("context_auto_compacted", {
7264
7287
  sessionId,
@@ -7287,7 +7310,8 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
7287
7310
  contextState,
7288
7311
  config: overflowConfig,
7289
7312
  modelContextWindow: resolvedProviderModel.contextWindow,
7290
- pendingInteractionCount: 0
7313
+ pendingInteractionCount: 0,
7314
+ cacheActive
7291
7315
  });
7292
7316
  if (overflowView.coveredMessages.length > 0) {
7293
7317
  const compaction = await compactSessionContext({
@@ -7307,7 +7331,8 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
7307
7331
  contextState,
7308
7332
  config: overflowConfig,
7309
7333
  modelContextWindow: resolvedProviderModel.contextWindow,
7310
- pendingInteractionCount: 0
7334
+ pendingInteractionCount: 0,
7335
+ cacheActive
7311
7336
  });
7312
7337
  logger7.info("context_overflow_recovery", {
7313
7338
  sessionId,