@trydecember/cli 0.4.3 → 0.4.4

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 (2) hide show
  1. package/dist/december.js +1469 -254
  2. package/package.json +1 -1
package/dist/december.js CHANGED
@@ -65,7 +65,7 @@ var package_default;
65
65
  var init_package = __esm(() => {
66
66
  package_default = {
67
67
  name: "@trydecember/cli",
68
- version: "0.4.3",
68
+ version: "0.4.4",
69
69
  description: "a coding agent for terminal and cloud.",
70
70
  homepage: "https://trydecember.com",
71
71
  repository: {
@@ -11072,10 +11072,13 @@ async function providerFetch(url, init) {
11072
11072
  if (!res.ok) {
11073
11073
  const text = await res.text();
11074
11074
  const normalized = normalizeErrorResponse(text);
11075
+ const headers = new Headers(res.headers);
11076
+ headers.delete("retry-after");
11077
+ headers.delete("retry-after-ms");
11075
11078
  return new Response(normalized, {
11076
11079
  status: res.status,
11077
11080
  statusText: res.statusText,
11078
- headers: res.headers
11081
+ headers
11079
11082
  });
11080
11083
  }
11081
11084
  return res;
@@ -11085,7 +11088,8 @@ function openaiProvider(baseURL, apiKey, defaultHeaders, customClient) {
11085
11088
  baseURL,
11086
11089
  apiKey: apiKey || process.env.OPENAI_API_KEY || "dummy-key",
11087
11090
  defaultHeaders,
11088
- fetch: providerFetch
11091
+ fetch: providerFetch,
11092
+ maxRetries: 0
11089
11093
  });
11090
11094
  return createProvider({
11091
11095
  id: "openai",
@@ -11184,6 +11188,11 @@ function openaiProvider(baseURL, apiKey, defaultHeaders, customClient) {
11184
11188
  }
11185
11189
  const activeToolCalls = new Map;
11186
11190
  for await (const chunk of stream) {
11191
+ if (chunk.error) {
11192
+ const errObj = chunk.error;
11193
+ const msg = typeof errObj === "string" ? errObj : errObj.message || JSON.stringify(errObj);
11194
+ throw new Error(msg);
11195
+ }
11187
11196
  if (chunk.usage) {
11188
11197
  const cachedTokens = chunk.usage.prompt_tokens_details?.cached_tokens;
11189
11198
  yield {
@@ -66472,15 +66481,23 @@ function anthropicProvider(baseURL, apiKey, customClientOrDefaultHeaders, custom
66472
66481
  antMessages.push({ role: "assistant", content });
66473
66482
  }
66474
66483
  } else {
66475
- antMessages.push({
66476
- role: msg.role,
66477
- content: [
66478
- {
66479
- type: "text",
66480
- text: msg.content
66481
- }
66482
- ]
66483
- });
66484
+ const prev = antMessages[antMessages.length - 1];
66485
+ if (prev && prev.role === "user" && Array.isArray(prev.content)) {
66486
+ prev.content.push({
66487
+ type: "text",
66488
+ text: msg.content
66489
+ });
66490
+ } else {
66491
+ antMessages.push({
66492
+ role: "user",
66493
+ content: [
66494
+ {
66495
+ type: "text",
66496
+ text: msg.content
66497
+ }
66498
+ ]
66499
+ });
66500
+ }
66484
66501
  }
66485
66502
  }
66486
66503
  if (antMessages.length >= 4) {
@@ -109444,6 +109461,14 @@ var LIVE_MODEL_CACHE_TTL_MS, liveModelCache, diskCacheLoaded = false, getCurated
109444
109461
  case "aurikoai":
109445
109462
  case "auriko-ai":
109446
109463
  return [
109464
+ {
109465
+ label: "DeepSeek V4 Flash",
109466
+ value: "deepseek-v4-flash"
109467
+ },
109468
+ {
109469
+ label: "Gemini 2.5 Flash",
109470
+ value: "gemini-2.5-flash"
109471
+ },
109447
109472
  {
109448
109473
  label: "Claude Sonnet 4.6",
109449
109474
  value: "claude-sonnet-4-6"
@@ -109456,18 +109481,10 @@ var LIVE_MODEL_CACHE_TTL_MS, liveModelCache, diskCacheLoaded = false, getCurated
109456
109481
  label: "Claude Opus 4.7",
109457
109482
  value: "claude-opus-4-7"
109458
109483
  },
109459
- {
109460
- label: "DeepSeek V4 Flash",
109461
- value: "deepseek-v4-flash"
109462
- },
109463
109484
  {
109464
109485
  label: "DeepSeek V4 Pro",
109465
109486
  value: "deepseek-v4-pro"
109466
109487
  },
109467
- {
109468
- label: "Gemini 2.5 Flash",
109469
- value: "gemini-2.5-flash"
109470
- },
109471
109488
  {
109472
109489
  label: "Gemini 2.5 Pro",
109473
109490
  value: "gemini-2.5-pro"
@@ -111284,9 +111301,15 @@ var LIVE_MODEL_CACHE_TTL_MS, liveModelCache, diskCacheLoaded = false, getCurated
111284
111301
  if (normalized === "anyapi") {
111285
111302
  return "anthropic/claude-sonnet-4-6";
111286
111303
  }
111304
+ if (normalized === "auriko" || normalized === "aurikoai" || normalized === "auriko-ai") {
111305
+ return "deepseek-v4-flash";
111306
+ }
111287
111307
  if (normalized === "bailing") {
111288
111308
  return "Ling-1T";
111289
111309
  }
111310
+ if (normalized === "baseten" || normalized === "basetenco" || normalized === "baseten-co") {
111311
+ return "deepseek-ai/DeepSeek-V4.1-Flash";
111312
+ }
111290
111313
  if (normalized === "berget") {
111291
111314
  return "mistralai/Mistral-Small-3.2-24B-Instruct-2506";
111292
111315
  }
@@ -119421,6 +119444,190 @@ var init_browser = __esm(() => {
119421
119444
  };
119422
119445
  });
119423
119446
 
119447
+ // ../../packages/tools/src/code_intelligence.ts
119448
+ function formatDiagnostics(diags, path) {
119449
+ const target = path ? ` for ${path}` : "";
119450
+ if (!diags) {
119451
+ return `No diagnostic errors found${target}.`;
119452
+ }
119453
+ if (typeof diags === "string") {
119454
+ const trimmed = diags.trim();
119455
+ if (!trimmed) {
119456
+ return `No diagnostic errors found${target}.`;
119457
+ }
119458
+ return `Diagnostic errors found${target}:
119459
+ ${trimmed}`;
119460
+ }
119461
+ if (Array.isArray(diags)) {
119462
+ if (diags.length === 0) {
119463
+ return `No diagnostic errors found${target}.`;
119464
+ }
119465
+ const formatted = diags.map((d) => {
119466
+ const loc = d.line ? `:${d.line}${d.column ? `:${d.column}` : ""}` : "";
119467
+ return `${d.filePath}${loc} - [${d.severity ?? "error"}] ${d.message}${d.source ? ` (${d.source})` : ""}`;
119468
+ }).join(`
119469
+ `);
119470
+ return `Diagnostic errors found${target}:
119471
+ ${formatted}`;
119472
+ }
119473
+ return `No diagnostic errors found${target}.`;
119474
+ }
119475
+ var codeIntelligenceSchema, CodeIntelligenceTool;
119476
+ var init_code_intelligence = __esm(() => {
119477
+ init_esm2();
119478
+ codeIntelligenceSchema = Type2.Object({
119479
+ action: Type2.Union([
119480
+ Type2.Literal("diagnostics"),
119481
+ Type2.Literal("definition"),
119482
+ Type2.Literal("references"),
119483
+ Type2.Literal("outline")
119484
+ ], {
119485
+ description: 'The code intelligence action to perform: "diagnostics", "definition", "references", or "outline".'
119486
+ }),
119487
+ path: Type2.Optional(Type2.String({
119488
+ description: 'Relative or absolute file path or directory to query. Required for "definition", "references", and "outline".'
119489
+ })),
119490
+ line: Type2.Optional(Type2.Integer({
119491
+ description: "1-indexed line number of the symbol for definition or references lookup."
119492
+ })),
119493
+ column: Type2.Optional(Type2.Integer({
119494
+ description: "1-indexed column number of the symbol for definition or references lookup."
119495
+ })),
119496
+ symbol: Type2.Optional(Type2.String({
119497
+ description: "Optional symbol name to search for in the file if line and column are not specified."
119498
+ }))
119499
+ });
119500
+ CodeIntelligenceTool = {
119501
+ name: "code_intelligence",
119502
+ description: "Query on-demand code intelligence, symbol definitions, call site references, document outline, and compiler diagnostics across the workspace or for a specific file.",
119503
+ inputSchema: codeIntelligenceSchema,
119504
+ execute: async ({ action, path, line, column, symbol }, context) => {
119505
+ if (action !== "diagnostics" && action !== "definition" && action !== "references" && action !== "outline") {
119506
+ return `Unsupported action: "${action}". Supported actions: "diagnostics", "definition", "references", "outline".`;
119507
+ }
119508
+ if (action === "diagnostics") {
119509
+ const diagFn = context.operations.diagnostics?.getDiagnostics || context.operations.lsp?.getDiagnostics;
119510
+ if (!diagFn) {
119511
+ return "Error: Diagnostics operations are not supported in the current environment.";
119512
+ }
119513
+ try {
119514
+ const diags = await diagFn(path ?? "");
119515
+ return formatDiagnostics(diags, path);
119516
+ } catch (err) {
119517
+ const message = err instanceof Error ? err.message : String(err);
119518
+ return `Error retrieving diagnostics: ${message}`;
119519
+ }
119520
+ }
119521
+ if (action === "outline") {
119522
+ if (!path) {
119523
+ return 'Error: "path" parameter is required for "outline" action.';
119524
+ }
119525
+ if (!context.operations.lsp?.getOutline) {
119526
+ return "Error: LSP operations are not supported in the current environment.";
119527
+ }
119528
+ try {
119529
+ const symbols = await context.operations.lsp.getOutline(path);
119530
+ if (!symbols || symbols.length === 0) {
119531
+ return `No document symbols found for ${path}.`;
119532
+ }
119533
+ const lines = symbols.map((s) => `- [${s.kind}] ${s.name} (${path}:${s.line}:${s.column})${s.containerName ? ` (in ${s.containerName})` : ""}`);
119534
+ return `Document outline for ${path}:
119535
+ ${lines.join(`
119536
+ `)}`;
119537
+ } catch (err) {
119538
+ const message = err instanceof Error ? err.message : String(err);
119539
+ return `Error retrieving outline: ${message}`;
119540
+ }
119541
+ }
119542
+ if (!path) {
119543
+ return `Error: "path" parameter is required for "${action}" action.`;
119544
+ }
119545
+ let targetLine = line;
119546
+ let targetColumn = column;
119547
+ if ((targetLine === undefined || targetColumn === undefined) && symbol) {
119548
+ try {
119549
+ const content = await context.operations.fs.readFile(path);
119550
+ const fileLines = content.split(`
119551
+ `);
119552
+ let fallbackLine;
119553
+ let fallbackCol;
119554
+ for (let i = 0;i < fileLines.length; i++) {
119555
+ const lineStr = fileLines[i];
119556
+ const idx = lineStr.indexOf(symbol);
119557
+ if (idx !== -1) {
119558
+ if (/^\s*import\b/.test(lineStr)) {
119559
+ if (fallbackLine === undefined) {
119560
+ fallbackLine = i + 1;
119561
+ fallbackCol = idx + 1;
119562
+ }
119563
+ } else {
119564
+ targetLine = i + 1;
119565
+ targetColumn = idx + 1;
119566
+ break;
119567
+ }
119568
+ }
119569
+ }
119570
+ if (targetLine === undefined) {
119571
+ targetLine = fallbackLine;
119572
+ targetColumn = fallbackCol;
119573
+ }
119574
+ if (targetLine === undefined || targetColumn === undefined) {
119575
+ return `Symbol "${symbol}" not found in ${path}.`;
119576
+ }
119577
+ } catch (err) {
119578
+ const message = err instanceof Error ? err.message : String(err);
119579
+ return `Error reading file ${path}: ${message}`;
119580
+ }
119581
+ }
119582
+ if (targetLine === undefined || targetColumn === undefined) {
119583
+ return `Error: "line" and "column" (or "symbol") are required for "${action}" action.`;
119584
+ }
119585
+ if (action === "definition") {
119586
+ if (!context.operations.lsp?.getDefinition) {
119587
+ return "Error: LSP operations are not supported in the current environment.";
119588
+ }
119589
+ try {
119590
+ const raw = await context.operations.lsp.getDefinition(path, targetLine, targetColumn);
119591
+ const locs = Array.isArray(raw) ? raw : raw ? [raw] : [];
119592
+ if (locs.length === 0) {
119593
+ return `No definition found for symbol at ${path}:${targetLine}:${targetColumn}.`;
119594
+ }
119595
+ if (locs.length === 1) {
119596
+ const loc = locs[0];
119597
+ return `Definition found at ${loc.filePath}:${loc.line}:${loc.column}`;
119598
+ }
119599
+ const list = locs.map((l) => `- ${l.filePath}:${l.line}:${l.column}`).join(`
119600
+ `);
119601
+ return `Definition(s) found:
119602
+ ${list}`;
119603
+ } catch (err) {
119604
+ const message = err instanceof Error ? err.message : String(err);
119605
+ return `Error retrieving definition: ${message}`;
119606
+ }
119607
+ }
119608
+ if (action === "references") {
119609
+ if (!context.operations.lsp?.getReferences) {
119610
+ return "Error: LSP operations are not supported in the current environment.";
119611
+ }
119612
+ try {
119613
+ const refs = await context.operations.lsp.getReferences(path, targetLine, targetColumn);
119614
+ if (!refs || refs.length === 0) {
119615
+ return `No references found for symbol at ${path}:${targetLine}:${targetColumn}.`;
119616
+ }
119617
+ const list = refs.map((r) => `- ${r.filePath}:${r.line}:${r.column}`).join(`
119618
+ `);
119619
+ return `Found ${refs.length} reference(s) for symbol at ${path}:${targetLine}:${targetColumn}:
119620
+ ${list}`;
119621
+ } catch (err) {
119622
+ const message = err instanceof Error ? err.message : String(err);
119623
+ return `Error retrieving references: ${message}`;
119624
+ }
119625
+ }
119626
+ return `Unsupported action: "${action}".`;
119627
+ }
119628
+ };
119629
+ });
119630
+
119424
119631
  // ../../packages/tools/src/deferred-registry.ts
119425
119632
  class DeferredToolRegistry {
119426
119633
  tools = new Map;
@@ -121274,6 +121481,264 @@ var init_ls = __esm(() => {
121274
121481
  };
121275
121482
  });
121276
121483
 
121484
+ // ../../packages/tools/src/manage_memory.ts
121485
+ import path20 from "node:path";
121486
+ function resolveWriteTarget(cwd, target, category) {
121487
+ const isRules = target === "rules" || target === "RULES.md" || !target && category === "rules";
121488
+ if (isRules) {
121489
+ if (target === "RULES.md") {
121490
+ return { relativePath: "RULES.md", isRules: true };
121491
+ }
121492
+ return { relativePath: "RULES.md", isRules: true };
121493
+ }
121494
+ if (target === "MEMORY.md") {
121495
+ return { relativePath: "MEMORY.md", isRules: false };
121496
+ }
121497
+ return { relativePath: path20.join(".december", "memory.md"), isRules: false };
121498
+ }
121499
+ function resolveReadCandidates(target) {
121500
+ if (target === "rules" || target === "RULES.md") {
121501
+ return ["RULES.md", path20.join(".december", "rules.md"), path20.join(".december", "RULES.md")];
121502
+ }
121503
+ if (target === "memory" || target === "MEMORY.md") {
121504
+ return [
121505
+ path20.join(".december", "memory.md"),
121506
+ "MEMORY.md",
121507
+ path20.join(".december", "MEMORY.md")
121508
+ ];
121509
+ }
121510
+ return [
121511
+ path20.join(".december", "memory.md"),
121512
+ "MEMORY.md",
121513
+ "RULES.md",
121514
+ path20.join(".december", "rules.md")
121515
+ ];
121516
+ }
121517
+ function normalizeEntry(text) {
121518
+ return text.replace(/^[-*•]\s+/, "").trim();
121519
+ }
121520
+ var manageMemorySchema, ManageMemoryTool;
121521
+ var init_manage_memory = __esm(() => {
121522
+ init_esm2();
121523
+ manageMemorySchema = Type2.Object({
121524
+ action: Type2.Union([Type2.Literal("record"), Type2.Literal("read"), Type2.Literal("forget")], {
121525
+ description: 'The action to perform: "record" to store an entry, "read" to inspect entries, or "forget" to prune an entry.'
121526
+ }),
121527
+ entry: Type2.Optional(Type2.String({
121528
+ description: "The memory or rule entry text to record or forget."
121529
+ })),
121530
+ category: Type2.Optional(Type2.Union([
121531
+ Type2.Literal("build_and_test"),
121532
+ Type2.Literal("conventions"),
121533
+ Type2.Literal("architecture"),
121534
+ Type2.Literal("quirks"),
121535
+ Type2.Literal("rules"),
121536
+ Type2.Literal("general")
121537
+ ], {
121538
+ description: "Category header: build_and_test, conventions, architecture, quirks, rules, or general."
121539
+ })),
121540
+ target: Type2.Optional(Type2.Union([
121541
+ Type2.Literal("memory"),
121542
+ Type2.Literal("rules"),
121543
+ Type2.Literal("MEMORY.md"),
121544
+ Type2.Literal("RULES.md")
121545
+ ], {
121546
+ description: 'Target file: "memory" (.december/memory.md or MEMORY.md) or "rules" (RULES.md or .december/rules.md). Defaults to memory for general/memory entries, or rules if category is "rules".'
121547
+ })),
121548
+ query: Type2.Optional(Type2.String({
121549
+ description: "Optional query keyword to filter entries when reading."
121550
+ }))
121551
+ });
121552
+ ManageMemoryTool = {
121553
+ name: "manage_memory",
121554
+ description: "Use this tool to record, read, or forget persistent notes, conventions, build/test commands, quirks, and rules across sessions in workspace memory (.december/memory.md, MEMORY.md, RULES.md).",
121555
+ inputSchema: manageMemorySchema,
121556
+ execute: async ({ action, entry, category, target, query }, context) => {
121557
+ const cwd = context.operations.env.cwd();
121558
+ switch (action) {
121559
+ case "record": {
121560
+ if (!entry || !entry.trim()) {
121561
+ return 'Error: "entry" parameter is required for action "record".';
121562
+ }
121563
+ const cleanEntry = normalizeEntry(entry);
121564
+ if (!cleanEntry) {
121565
+ return 'Error: "entry" parameter cannot be empty.';
121566
+ }
121567
+ const { relativePath, isRules } = resolveWriteTarget(cwd, target, category);
121568
+ const fullPath = path20.isAbsolute(relativePath) ? relativePath : path20.join(cwd, relativePath);
121569
+ const cat = category || (isRules ? "rules" : "conventions");
121570
+ let existingContent = "";
121571
+ try {
121572
+ existingContent = await context.operations.fs.readFile(fullPath);
121573
+ } catch {}
121574
+ const lines = existingContent.split(`
121575
+ `);
121576
+ for (const line of lines) {
121577
+ const lineContent = normalizeEntry(line);
121578
+ if (lineContent && lineContent.toLowerCase() === cleanEntry.toLowerCase()) {
121579
+ return `Entry already exists in ${relativePath} under "${cat}". Skipped duplicate.`;
121580
+ }
121581
+ }
121582
+ const headerRegex = new RegExp(`^##\\s+${cat}\\b`, "im");
121583
+ const headerMatch = existingContent.match(headerRegex);
121584
+ let updatedContent = "";
121585
+ if (headerMatch && headerMatch.index !== undefined) {
121586
+ const headerIndex = headerMatch.index;
121587
+ const rest = existingContent.slice(headerIndex);
121588
+ const nextHeaderMatch = rest.slice(headerMatch[0].length).match(/\n##\s+/i);
121589
+ if (nextHeaderMatch && nextHeaderMatch.index !== undefined) {
121590
+ const insertPos = headerIndex + headerMatch[0].length + nextHeaderMatch.index;
121591
+ updatedContent = `${existingContent.slice(0, insertPos)}
121592
+ - ${cleanEntry}${existingContent.slice(insertPos)}`;
121593
+ } else {
121594
+ updatedContent = `${existingContent.trimEnd()}
121595
+ - ${cleanEntry}
121596
+ `;
121597
+ }
121598
+ } else {
121599
+ const prefix = existingContent.trim() ? `${existingContent.trimEnd()}
121600
+
121601
+ ` : "";
121602
+ updatedContent = `${prefix}## ${cat}
121603
+ - ${cleanEntry}
121604
+ `;
121605
+ }
121606
+ try {
121607
+ await context.operations.fs.writeFile(fullPath, updatedContent);
121608
+ return `Recorded entry under "${cat}" in ${relativePath}.`;
121609
+ } catch (err) {
121610
+ return `Failed to write memory file: ${err.message}`;
121611
+ }
121612
+ }
121613
+ case "read": {
121614
+ const candidates = resolveReadCandidates(target);
121615
+ const contents = [];
121616
+ for (const candidate of candidates) {
121617
+ const fullPath = path20.isAbsolute(candidate) ? candidate : path20.join(cwd, candidate);
121618
+ try {
121619
+ const text = await context.operations.fs.readFile(fullPath);
121620
+ if (text && text.trim()) {
121621
+ contents.push({ file: candidate, text: text.trim() });
121622
+ }
121623
+ } catch {}
121624
+ }
121625
+ if (contents.length === 0) {
121626
+ return "No memory or rules entries found.";
121627
+ }
121628
+ if (category) {
121629
+ const matchedSections = [];
121630
+ const catLower = category.toLowerCase();
121631
+ for (const item of contents) {
121632
+ const lines = item.text.split(`
121633
+ `);
121634
+ let inCategory = false;
121635
+ const sectionLines = [];
121636
+ for (const line of lines) {
121637
+ if (line.startsWith("## ")) {
121638
+ const headerName = line.slice(3).trim().toLowerCase();
121639
+ if (headerName === catLower) {
121640
+ inCategory = true;
121641
+ sectionLines.push(line);
121642
+ } else {
121643
+ inCategory = false;
121644
+ }
121645
+ } else if (inCategory) {
121646
+ sectionLines.push(line);
121647
+ }
121648
+ }
121649
+ if (sectionLines.length > 0) {
121650
+ matchedSections.push(`### Source: ${item.file}
121651
+ ${sectionLines.join(`
121652
+ `).trim()}`);
121653
+ }
121654
+ }
121655
+ if (matchedSections.length === 0) {
121656
+ return `No entries found under category "${category}".`;
121657
+ }
121658
+ return matchedSections.join(`
121659
+
121660
+ `);
121661
+ }
121662
+ if (query) {
121663
+ const queryLower = query.toLowerCase();
121664
+ const matchedLines = [];
121665
+ for (const item of contents) {
121666
+ const lines = item.text.split(`
121667
+ `);
121668
+ let currentHeader = "";
121669
+ const fileMatches = [];
121670
+ for (const line of lines) {
121671
+ if (line.startsWith("## ")) {
121672
+ currentHeader = line;
121673
+ } else if (line.toLowerCase().includes(queryLower)) {
121674
+ if (currentHeader && !fileMatches.includes(currentHeader)) {
121675
+ fileMatches.push(currentHeader);
121676
+ }
121677
+ fileMatches.push(line);
121678
+ }
121679
+ }
121680
+ if (fileMatches.length > 0) {
121681
+ matchedLines.push(`### Source: ${item.file}
121682
+ ${fileMatches.join(`
121683
+ `)}`);
121684
+ }
121685
+ }
121686
+ if (matchedLines.length === 0) {
121687
+ return `No memory entries found matching "${query}".`;
121688
+ }
121689
+ return matchedLines.join(`
121690
+
121691
+ `);
121692
+ }
121693
+ return contents.map((c) => `### Source: ${c.file}
121694
+ ${c.text}`).join(`
121695
+
121696
+ `);
121697
+ }
121698
+ case "forget": {
121699
+ if (!entry || !entry.trim()) {
121700
+ return 'Error: "entry" parameter is required for action "forget".';
121701
+ }
121702
+ const targetClean = normalizeEntry(entry).toLowerCase();
121703
+ const candidates = resolveReadCandidates(target);
121704
+ let removed = false;
121705
+ for (const candidate of candidates) {
121706
+ const fullPath = path20.isAbsolute(candidate) ? candidate : path20.join(cwd, candidate);
121707
+ try {
121708
+ const text = await context.operations.fs.readFile(fullPath);
121709
+ const lines = text.split(`
121710
+ `);
121711
+ const newLines = [];
121712
+ let fileModified = false;
121713
+ for (const line of lines) {
121714
+ const lineClean = normalizeEntry(line).toLowerCase();
121715
+ if (lineClean && (lineClean === targetClean || lineClean.includes(targetClean))) {
121716
+ fileModified = true;
121717
+ removed = true;
121718
+ continue;
121719
+ }
121720
+ newLines.push(line);
121721
+ }
121722
+ if (fileModified) {
121723
+ await context.operations.fs.writeFile(fullPath, newLines.join(`
121724
+ `).trimEnd() + `
121725
+ `);
121726
+ }
121727
+ } catch {}
121728
+ }
121729
+ if (removed) {
121730
+ return "Removed entry from memory.";
121731
+ }
121732
+ return "Entry not found in memory.";
121733
+ }
121734
+ default: {
121735
+ return "Invalid action. Supported actions: record, read, forget.";
121736
+ }
121737
+ }
121738
+ }
121739
+ };
121740
+ });
121741
+
121277
121742
  // ../../packages/tools/src/manage_task.ts
121278
121743
  var manageTaskSchema, ManageTaskTool;
121279
121744
  var init_manage_task = __esm(() => {
@@ -162242,7 +162707,7 @@ ${formatted}`;
162242
162707
 
162243
162708
  // ../../packages/tools/src/bash-operations.ts
162244
162709
  import { spawn as spawn2 } from "node:child_process";
162245
- import path20 from "node:path";
162710
+ import path21 from "node:path";
162246
162711
  function trackDetachedChildPid(pid) {
162247
162712
  trackedDetachedChildPids.add(pid);
162248
162713
  }
@@ -162252,7 +162717,7 @@ function untrackDetachedChildPid(pid) {
162252
162717
  function killProcessGroup(pid) {
162253
162718
  if (process.platform === "win32") {
162254
162719
  try {
162255
- const child = spawn2(path20.join(process.env.SystemRoot ?? "C:\\Windows", "System32", "taskkill.exe"), ["/F", "/T", "/PID", String(pid)], {
162720
+ const child = spawn2(path21.join(process.env.SystemRoot ?? "C:\\Windows", "System32", "taskkill.exe"), ["/F", "/T", "/PID", String(pid)], {
162256
162721
  stdio: "ignore",
162257
162722
  detached: true,
162258
162723
  windowsHide: true
@@ -162370,12 +162835,14 @@ var init_src4 = __esm(() => {
162370
162835
  init_ask_question();
162371
162836
  init_bash();
162372
162837
  init_browser();
162838
+ init_code_intelligence();
162373
162839
  init_deferred_registry();
162374
162840
  init_edit();
162375
162841
  init_edit_diff();
162376
162842
  init_find();
162377
162843
  init_grep();
162378
162844
  init_ls();
162845
+ init_manage_memory();
162379
162846
  init_manage_task();
162380
162847
  init_read();
162381
162848
  init_search_tools();
@@ -162393,11 +162860,13 @@ var init_src4 = __esm(() => {
162393
162860
  init_grep();
162394
162861
  init_ask_question();
162395
162862
  init_manage_task();
162863
+ init_manage_memory();
162396
162864
  init_browser();
162397
162865
  init_web_search();
162398
162866
  init_diff_preview();
162399
162867
  init_deferred_registry();
162400
162868
  init_search_tools();
162869
+ init_code_intelligence();
162401
162870
  CORE_TOOLS = [
162402
162871
  BashTool,
162403
162872
  ReadFileTool,
@@ -162408,7 +162877,9 @@ var init_src4 = __esm(() => {
162408
162877
  FindFilesTool,
162409
162878
  GrepSearchTool,
162410
162879
  AskQuestionTool,
162411
- SearchToolsTool
162880
+ SearchToolsTool,
162881
+ CodeIntelligenceTool,
162882
+ ManageMemoryTool
162412
162883
  ];
162413
162884
  DEFERRED_TOOLS = [ManageTaskTool, BrowserTool, WebSearchTool];
162414
162885
  defaultDeferredRegistry.register(BrowserTool, {
@@ -162423,6 +162894,10 @@ var init_src4 = __esm(() => {
162423
162894
  category: "task",
162424
162895
  keywords: ["task", "background", "kill", "status", "process", "pid", "manage_task"]
162425
162896
  });
162897
+ defaultDeferredRegistry.register(ManageMemoryTool, {
162898
+ category: "memory",
162899
+ keywords: ["memory", "rules", "conventions", "quirks", "manage_memory", "remember", "forget"]
162900
+ });
162426
162901
  });
162427
162902
 
162428
162903
  // ../../node_modules/.bun/uuid@14.0.1/node_modules/uuid/dist-node/rng.js
@@ -162627,6 +163102,64 @@ function estimateTokens(messages) {
162627
163102
  return acc + Math.ceil((text.length + toolInput.length) / 4);
162628
163103
  }, 0);
162629
163104
  }
163105
+ function isSafeCompactionBoundary(messages, splitIndex, startIndex = 0) {
163106
+ if (splitIndex <= startIndex || splitIndex >= messages.length) {
163107
+ return false;
163108
+ }
163109
+ if (messages[splitIndex]?.role === "tool") {
163110
+ return false;
163111
+ }
163112
+ const preCallIds = new Set;
163113
+ for (let i = startIndex;i < splitIndex; i++) {
163114
+ const msg = messages[i];
163115
+ if (msg?.toolCalls) {
163116
+ for (const tc of msg.toolCalls) {
163117
+ if (tc.id)
163118
+ preCallIds.add(tc.id);
163119
+ }
163120
+ }
163121
+ }
163122
+ for (let i = splitIndex;i < messages.length; i++) {
163123
+ const msg = messages[i];
163124
+ if (msg?.role === "tool" && msg.toolCallId && preCallIds.has(msg.toolCallId)) {
163125
+ return false;
163126
+ }
163127
+ }
163128
+ const postCallIds = new Set;
163129
+ for (let i = splitIndex;i < messages.length; i++) {
163130
+ const msg = messages[i];
163131
+ if (msg?.toolCalls) {
163132
+ for (const tc of msg.toolCalls) {
163133
+ if (tc.id)
163134
+ postCallIds.add(tc.id);
163135
+ }
163136
+ }
163137
+ }
163138
+ for (let i = startIndex;i < splitIndex; i++) {
163139
+ const msg = messages[i];
163140
+ if (msg?.role === "tool" && msg.toolCallId && postCallIds.has(msg.toolCallId)) {
163141
+ return false;
163142
+ }
163143
+ }
163144
+ return true;
163145
+ }
163146
+ function findSafeCompactionBoundary(messages, targetIndex, startIndex = 1) {
163147
+ if (isSafeCompactionBoundary(messages, targetIndex, startIndex)) {
163148
+ return targetIndex;
163149
+ }
163150
+ const maxDelta = messages.length;
163151
+ for (let delta = 1;delta < maxDelta; delta++) {
163152
+ const left = targetIndex - delta;
163153
+ if (left > startIndex && isSafeCompactionBoundary(messages, left, startIndex)) {
163154
+ return left;
163155
+ }
163156
+ const right = targetIndex + delta;
163157
+ if (right < messages.length && isSafeCompactionBoundary(messages, right, startIndex)) {
163158
+ return right;
163159
+ }
163160
+ }
163161
+ return targetIndex;
163162
+ }
162630
163163
  async function compactContextIfNeeded(messages, llm, maxTokens, modelOptions, signal) {
162631
163164
  let limit = maxTokens;
162632
163165
  if (!limit) {
@@ -162645,10 +163178,17 @@ async function compactContextIfNeeded(messages, llm, maxTokens, modelOptions, si
162645
163178
  if (messages.length <= PROTECTED_TAIL + 1) {
162646
163179
  return messages;
162647
163180
  }
162648
- const systemPrompt = messages[0];
162649
- const middleHistory = messages.slice(1, messages.length - PROTECTED_TAIL);
162650
- const recentHistory = messages.slice(messages.length - PROTECTED_TAIL);
162651
- const hasPreviousSummary = middleHistory.length > 0 && middleHistory[0].role === "system" && middleHistory[0].content.includes("[COMPACTED HISTORY SUMMARY]");
163181
+ const hasSystemPrompt = messages[0]?.role === "system";
163182
+ const systemPrompt = hasSystemPrompt ? messages[0] : undefined;
163183
+ const historyStartIndex = hasSystemPrompt ? 1 : 0;
163184
+ const targetIndex = messages.length - PROTECTED_TAIL;
163185
+ const safeSplitIndex = findSafeCompactionBoundary(messages, targetIndex, historyStartIndex);
163186
+ if (safeSplitIndex <= historyStartIndex || safeSplitIndex >= messages.length) {
163187
+ return messages;
163188
+ }
163189
+ const middleHistory = messages.slice(historyStartIndex, safeSplitIndex);
163190
+ const recentHistory = messages.slice(safeSplitIndex);
163191
+ const hasPreviousSummary = middleHistory.length > 0 && (middleHistory[0].role === "system" || middleHistory[0].role === "user") && middleHistory[0].content.includes("[COMPACTED HISTORY SUMMARY]");
162652
163192
  let previousSummaryText = "";
162653
163193
  let messagesToSummarize = middleHistory;
162654
163194
  if (hasPreviousSummary) {
@@ -162656,6 +163196,9 @@ async function compactContextIfNeeded(messages, llm, maxTokens, modelOptions, si
162656
163196
  `, "");
162657
163197
  messagesToSummarize = middleHistory.slice(1);
162658
163198
  }
163199
+ if (messagesToSummarize.length === 0) {
163200
+ return messages;
163201
+ }
162659
163202
  const fileManifests = extractFileManifests(messagesToSummarize, previousSummaryText);
162660
163203
  const historyText = messagesToSummarize.map((m) => {
162661
163204
  let txt = `[${m.role.toUpperCase()}]: ${m.content}`;
@@ -162793,7 +163336,7 @@ ${fileManifests.modifiedFiles.join(`
162793
163336
  content: `[COMPACTED HISTORY SUMMARY]
162794
163337
  ${finalSummary}`
162795
163338
  };
162796
- return [systemPrompt, summaryMessage, ...recentHistory];
163339
+ return systemPrompt ? [systemPrompt, summaryMessage, ...recentHistory] : [summaryMessage, ...recentHistory];
162797
163340
  }
162798
163341
  var DEFAULT_MAX_TOKENS = 32000, PRUNE_PROTECT_TOKENS = 40000, PRUNE_MINIMUM_SAVINGS = 15000, PRUNE_PROTECT_STEPS = 3, PRUNE_PROTECTED_TOOLS;
162799
163342
  var init_compaction = __esm(() => {
@@ -162802,10 +163345,144 @@ var init_compaction = __esm(() => {
162802
163345
  });
162803
163346
 
162804
163347
  // ../../packages/agent/src/conversation-manager.ts
163348
+ import fs19 from "node:fs";
163349
+ import path22 from "node:path";
163350
+
162805
163351
  class ConversationManager {
162806
163352
  _messages = [];
162807
- constructor(initialMessages = []) {
163353
+ workspaceRoot;
163354
+ constructor(initialMessagesOrOptions, options) {
163355
+ let initialMessages = [];
163356
+ let opts;
163357
+ if (Array.isArray(initialMessagesOrOptions)) {
163358
+ initialMessages = initialMessagesOrOptions;
163359
+ opts = options;
163360
+ } else if (initialMessagesOrOptions && typeof initialMessagesOrOptions === "object") {
163361
+ opts = initialMessagesOrOptions;
163362
+ }
162808
163363
  this._messages = initialMessages;
163364
+ if (opts?.workspaceRoot) {
163365
+ this.workspaceRoot = opts.workspaceRoot;
163366
+ this.initSession({
163367
+ workspaceRoot: opts.workspaceRoot,
163368
+ systemPrompt: opts.systemPrompt
163369
+ });
163370
+ }
163371
+ }
163372
+ initSession(optionsOrWorkspaceRoot, systemPromptArg) {
163373
+ let workspaceRoot;
163374
+ let systemPrompt;
163375
+ if (typeof optionsOrWorkspaceRoot === "string") {
163376
+ workspaceRoot = optionsOrWorkspaceRoot;
163377
+ systemPrompt = systemPromptArg;
163378
+ } else if (optionsOrWorkspaceRoot && typeof optionsOrWorkspaceRoot === "object") {
163379
+ workspaceRoot = optionsOrWorkspaceRoot.workspaceRoot;
163380
+ systemPrompt = optionsOrWorkspaceRoot.systemPrompt;
163381
+ } else {
163382
+ workspaceRoot = this.workspaceRoot;
163383
+ systemPrompt = systemPromptArg;
163384
+ }
163385
+ if (workspaceRoot) {
163386
+ this.workspaceRoot = workspaceRoot;
163387
+ }
163388
+ const existingSystemIndex = this._messages.findIndex((m) => m.role === "system");
163389
+ let prompt = systemPrompt;
163390
+ if (prompt === undefined) {
163391
+ if (existingSystemIndex !== -1) {
163392
+ prompt = this._messages[existingSystemIndex].content;
163393
+ } else {
163394
+ prompt = "";
163395
+ }
163396
+ }
163397
+ const memoryContent = this.readWorkspaceMemory(workspaceRoot);
163398
+ let finalPrompt = prompt;
163399
+ if (memoryContent) {
163400
+ finalPrompt = this.injectMemoryBlock(prompt, memoryContent);
163401
+ }
163402
+ if (existingSystemIndex !== -1) {
163403
+ this._messages[existingSystemIndex].content = finalPrompt;
163404
+ } else if (finalPrompt) {
163405
+ this._messages.unshift({
163406
+ role: "system",
163407
+ content: finalPrompt,
163408
+ id: v4_default2(),
163409
+ timestamp: Date.now()
163410
+ });
163411
+ }
163412
+ return finalPrompt;
163413
+ }
163414
+ readWorkspaceMemory(workspaceRoot) {
163415
+ if (!workspaceRoot)
163416
+ return "";
163417
+ const memoryCandidates = [
163418
+ path22.join(workspaceRoot, ".december", "memory.md"),
163419
+ path22.join(workspaceRoot, "MEMORY.md"),
163420
+ path22.join(workspaceRoot, ".december", "MEMORY.md"),
163421
+ path22.join(workspaceRoot, "memory.md")
163422
+ ];
163423
+ const rulesCandidates = [
163424
+ path22.join(workspaceRoot, "RULES.md"),
163425
+ path22.join(workspaceRoot, ".december", "rules.md"),
163426
+ path22.join(workspaceRoot, ".december", "RULES.md"),
163427
+ path22.join(workspaceRoot, "rules.md")
163428
+ ];
163429
+ let memoryText = "";
163430
+ for (const candidate of memoryCandidates) {
163431
+ try {
163432
+ if (fs19.existsSync(candidate) && fs19.statSync(candidate).isFile()) {
163433
+ const content = fs19.readFileSync(candidate, "utf8").trim();
163434
+ if (content) {
163435
+ memoryText = content;
163436
+ break;
163437
+ }
163438
+ }
163439
+ } catch {}
163440
+ }
163441
+ let rulesText = "";
163442
+ for (const candidate of rulesCandidates) {
163443
+ try {
163444
+ if (fs19.existsSync(candidate) && fs19.statSync(candidate).isFile()) {
163445
+ const content = fs19.readFileSync(candidate, "utf8").trim();
163446
+ if (content) {
163447
+ rulesText = content;
163448
+ break;
163449
+ }
163450
+ }
163451
+ } catch {}
163452
+ }
163453
+ const parts = [];
163454
+ if (memoryText)
163455
+ parts.push(memoryText);
163456
+ if (rulesText && rulesText !== memoryText)
163457
+ parts.push(rulesText);
163458
+ return parts.join(`
163459
+
163460
+ `).trim();
163461
+ }
163462
+ injectMemoryBlock(prompt, memoryContent) {
163463
+ if (!memoryContent.trim())
163464
+ return prompt;
163465
+ if (prompt.includes("<project_memory>"))
163466
+ return prompt;
163467
+ const block = `<project_memory>
163468
+ ${memoryContent.trim()}
163469
+ </project_memory>`;
163470
+ if (!prompt)
163471
+ return block;
163472
+ if (prompt.includes(`
163473
+
163474
+ Current date:`)) {
163475
+ return prompt.replace(`
163476
+
163477
+ Current date:`, `
163478
+
163479
+ ${block}
163480
+
163481
+ Current date:`);
163482
+ }
163483
+ return `${prompt}
163484
+
163485
+ ${block}`;
162809
163486
  }
162810
163487
  get messages() {
162811
163488
  return this._messages;
@@ -163560,10 +164237,10 @@ class LangfuseMedia {
163560
164237
  this._contentType = contentType;
163561
164238
  this._source = "bytes";
163562
164239
  } else if (filePath && contentType) {
163563
- if (!fs19) {
164240
+ if (!fs20) {
163564
164241
  throw new Error("File system support is not available in this environment");
163565
164242
  }
163566
- if (!fs19.existsSync(filePath)) {
164243
+ if (!fs20.existsSync(filePath)) {
163567
164244
  throw new Error(`File at path ${filePath} does not exist`);
163568
164245
  }
163569
164246
  this._contentBytes = this.readFile(filePath);
@@ -163575,10 +164252,10 @@ class LangfuseMedia {
163575
164252
  }
163576
164253
  readFile(filePath) {
163577
164254
  try {
163578
- if (!fs19) {
164255
+ if (!fs20) {
163579
164256
  throw new Error("File system support is not available in this environment");
163580
164257
  }
163581
- return fs19.readFileSync(filePath);
164258
+ return fs20.readFileSync(filePath);
163582
164259
  } catch (error) {
163583
164260
  console.error(`Error reading file at path ${filePath}`, error);
163584
164261
  return;
@@ -164700,7 +165377,7 @@ var DEFAULT_PROMPT_CACHE_TTL_SECONDS = 60, LangfusePersistedProperty, ChatMessag
164700
165377
  }
164701
165378
  });
164702
165379
  return queryParams.toString();
164703
- }, utils, common_release_envs, fs19 = null, cryptoModule = null, dynamicImport = (module) => {
165380
+ }, utils, common_release_envs, fs20 = null, cryptoModule = null, dynamicImport = (module) => {
164704
165381
  return import(module);
164705
165382
  }, MAX_EVENT_SIZE_BYTES, MAX_BATCH_SIZE_BYTES, ENVIRONMENT_PATTERN, WHITELISTED_LANGFUSE_INTERNAL_ENVIRONMENTS, LangfuseFetchHttpError, LangfuseFetchNetworkError, SUPPORT_URL = "https://langfuse.com/support", API_DOCS_URL = "https://api.reference.langfuse.com", RBAC_DOCS_URL = "https://langfuse.com/docs/rbac", INSTALLATION_DOCS_URL = "https://langfuse.com/docs/sdk/typescript/guide", RATE_LIMITS_URL = "https://langfuse.com/faq/all/api-limits", NPM_PACKAGE_URL = "https://www.npmjs.com/package/langfuse", updatePromptResponse, defaultServerErrorPrompt, defaultErrorResponse, errorResponseByCode, LangfuseCore, LangfuseTraceClient, LangfuseObservationClient, LangfuseSpanClient, LangfuseGenerationClient, LangfuseEventClient;
164706
165383
  var init_lib = __esm(() => {
@@ -164879,12 +165556,12 @@ var init_lib = __esm(() => {
164879
165556
  ];
164880
165557
  if (typeof globalThis.Deno !== "undefined") {
164881
165558
  Promise.all([dynamicImport("node:fs"), dynamicImport("node:crypto")]).then(([importedFs, importedCrypto]) => {
164882
- fs19 = importedFs;
165559
+ fs20 = importedFs;
164883
165560
  cryptoModule = importedCrypto;
164884
165561
  }).catch();
164885
165562
  } else if (typeof process !== "undefined" && process.versions?.node) {
164886
165563
  Promise.all([dynamicImport("fs"), dynamicImport("crypto")]).then(([importedFs, importedCrypto]) => {
164887
- fs19 = importedFs;
165564
+ fs20 = importedFs;
164888
165565
  cryptoModule = importedCrypto;
164889
165566
  }).catch();
164890
165567
  } else if (typeof crypto !== "undefined") {
@@ -166387,29 +167064,73 @@ var init_tracer_factory = __esm(() => {
166387
167064
  });
166388
167065
 
166389
167066
  // ../../packages/agent/src/utils/evaporation.ts
166390
- function evaporateStaleToolOutputs(messages, preserveRecentTurns = 3) {
167067
+ function isErrorToolOutput(content) {
167068
+ const lower = content.toLowerCase();
167069
+ return content.includes("[Tool Error]") || lower.includes("error:") || lower.includes("command failed") || lower.includes("exit code 1") || lower.includes("exit code 2") || lower.includes("failed:") || lower.includes("traceback (most recent call last)") || lower.includes("exception:") || lower.includes("fatal:") || lower.includes("panic:") || lower.includes("syntaxerror");
167070
+ }
167071
+ function truncateLargeToolOutput(rawContent, maxBytes = 6 * 1024, headLines = 20, tailLines = 20) {
167072
+ const byteLength = Buffer.byteLength(rawContent, "utf8");
167073
+ if (byteLength <= maxBytes) {
167074
+ return rawContent;
167075
+ }
167076
+ if (isErrorToolOutput(rawContent)) {
167077
+ return rawContent;
167078
+ }
167079
+ const lines = rawContent.split(`
167080
+ `);
167081
+ if (lines.length <= headLines + tailLines) {
167082
+ return rawContent;
167083
+ }
167084
+ const head = lines.slice(0, headLines).join(`
167085
+ `);
167086
+ const tail = lines.slice(lines.length - tailLines).join(`
167087
+ `);
167088
+ const omittedLines = lines.length - headLines - tailLines;
167089
+ const omittedBytes = byteLength - Buffer.byteLength(head, "utf8") - Buffer.byteLength(tail, "utf8");
167090
+ const omittedKb = (omittedBytes / 1024).toFixed(1);
167091
+ const banner = `
167092
+
167093
+ [... ${omittedLines} lines truncated (${omittedKb} KB) ...]
167094
+
167095
+ `;
167096
+ return head + banner + tail;
167097
+ }
167098
+ function evaporateStaleToolOutputs(messages, preserveRecentTurnsOrOptions = 3, preserveRecentStepsArg) {
166391
167099
  if (!messages || messages.length === 0)
166392
167100
  return [];
167101
+ let preserveRecentTurns = 3;
167102
+ let preserveRecentSteps = 2;
167103
+ let maxOutputSize = 6 * 1024;
167104
+ let headLines = 20;
167105
+ let tailLines = 20;
167106
+ if (typeof preserveRecentTurnsOrOptions === "number") {
167107
+ preserveRecentTurns = preserveRecentTurnsOrOptions;
167108
+ if (preserveRecentStepsArg !== undefined) {
167109
+ preserveRecentSteps = preserveRecentStepsArg;
167110
+ }
167111
+ } else if (typeof preserveRecentTurnsOrOptions === "object") {
167112
+ preserveRecentTurns = preserveRecentTurnsOrOptions.preserveRecentTurns ?? 3;
167113
+ preserveRecentSteps = preserveRecentTurnsOrOptions.preserveRecentSteps ?? 2;
167114
+ maxOutputSize = preserveRecentTurnsOrOptions.maxOutputSize ?? 6 * 1024;
167115
+ headLines = preserveRecentTurnsOrOptions.headLines ?? 20;
167116
+ tailLines = preserveRecentTurnsOrOptions.tailLines ?? 20;
167117
+ }
166393
167118
  let userTurnsSeen = 0;
166394
- let cutoffIndex = 0;
167119
+ let agentStepsSeen = 0;
167120
+ const isStale = new Array(messages.length).fill(false);
166395
167121
  for (let i = messages.length - 1;i >= 0; i--) {
166396
167122
  const msg = messages[i];
167123
+ if (!msg)
167124
+ continue;
166397
167125
  if (msg.role === "user" && !msg.isUI) {
166398
167126
  userTurnsSeen++;
166399
- if (userTurnsSeen >= preserveRecentTurns) {
166400
- cutoffIndex = i;
166401
- break;
166402
- }
166403
167127
  }
166404
- }
166405
- if (userTurnsSeen < preserveRecentTurns || cutoffIndex <= 0) {
166406
- return messages.map((m) => ({
166407
- role: m.role,
166408
- content: m.content,
166409
- thinking: m.thinking,
166410
- toolCalls: m.toolCalls,
166411
- toolCallId: m.toolCallId
166412
- }));
167128
+ if (msg.role === "assistant" && (msg.toolCalls?.length || 0) > 0) {
167129
+ agentStepsSeen++;
167130
+ }
167131
+ if (userTurnsSeen >= preserveRecentTurns || agentStepsSeen >= preserveRecentSteps) {
167132
+ isStale[i] = true;
167133
+ }
166413
167134
  }
166414
167135
  return messages.map((msg, idx) => {
166415
167136
  const baseMsg = {
@@ -166419,27 +167140,37 @@ function evaporateStaleToolOutputs(messages, preserveRecentTurns = 3) {
166419
167140
  toolCalls: msg.toolCalls,
166420
167141
  toolCallId: msg.toolCallId
166421
167142
  };
166422
- if (idx >= cutoffIndex) {
167143
+ if (msg.role !== "tool") {
166423
167144
  return baseMsg;
166424
167145
  }
166425
- if (msg.role === "tool") {
166426
- const rawContent = typeof msg.content === "string" ? msg.content : "";
166427
- if (rawContent.length < 200) {
166428
- return baseMsg;
166429
- }
166430
- if (rawContent.includes("[Tool Error]") || rawContent.toLowerCase().includes("error:") || rawContent.toLowerCase().includes("command failed")) {
166431
- return baseMsg;
166432
- }
167146
+ const rawContent = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
167147
+ if (rawContent.length < 200) {
167148
+ return baseMsg;
167149
+ }
167150
+ if (isErrorToolOutput(rawContent)) {
167151
+ return baseMsg;
167152
+ }
167153
+ if (rawContent.startsWith("[Tool Output Evaporated:") || rawContent.startsWith("[tool output microcompacted:") || rawContent.startsWith("[Old tool result content cleared]")) {
167154
+ return baseMsg;
167155
+ }
167156
+ if (isStale[idx]) {
166433
167157
  const lines = rawContent.split(`
166434
167158
  `);
166435
167159
  const lineCount = lines.length;
166436
167160
  const byteSize = (Buffer.byteLength(rawContent, "utf8") / 1024).toFixed(1);
166437
- const tombstone = `[Tool Output Evaporated: ${lineCount} lines (${byteSize} KB). Content previously processed by assistant.]`;
167161
+ const tombstone = `[Tool Output Evaporated: ${lineCount} lines (${byteSize} KB). exit code 0. Content previously processed by assistant.]`;
166438
167162
  return {
166439
167163
  ...baseMsg,
166440
167164
  content: tombstone
166441
167165
  };
166442
167166
  }
167167
+ const truncated = truncateLargeToolOutput(rawContent, maxOutputSize, headLines, tailLines);
167168
+ if (truncated !== rawContent) {
167169
+ return {
167170
+ ...baseMsg,
167171
+ content: truncated
167172
+ };
167173
+ }
166443
167174
  return baseMsg;
166444
167175
  });
166445
167176
  }
@@ -166504,8 +167235,14 @@ class Agent2 {
166504
167235
  this.convertToLlm = config.convertToLlm || this.defaultConvertToLlm;
166505
167236
  this.steeringQueue = new PendingMessageQueue(config.steeringMode || "all");
166506
167237
  this.followUpQueue = new PendingMessageQueue(config.followUpMode || "all");
166507
- this.conversation = new ConversationManager;
166508
167238
  this.workspaceDir = config.workspaceDir;
167239
+ this.conversation = new ConversationManager([], {
167240
+ workspaceRoot: config.workspaceDir
167241
+ });
167242
+ this.systemPrompt = this.conversation.initSession({
167243
+ workspaceRoot: config.workspaceDir,
167244
+ systemPrompt: this.systemPrompt
167245
+ });
166509
167246
  this.logsDir = config.logsDir;
166510
167247
  this.disableLogging = config.disableLogging || false;
166511
167248
  this.deferredRegistry = config.deferredRegistry || new DeferredToolRegistry;
@@ -166523,10 +167260,12 @@ class Agent2 {
166523
167260
  this.deferredRegistry.register(tool);
166524
167261
  }
166525
167262
  }
166526
- this.conversation.addMessage({
166527
- role: "system",
166528
- content: this.systemPrompt
166529
- });
167263
+ if (this.conversation.messages.length === 0) {
167264
+ this.conversation.addMessage({
167265
+ role: "system",
167266
+ content: this.systemPrompt
167267
+ });
167268
+ }
166530
167269
  }
166531
167270
  get messages() {
166532
167271
  return this.conversation.messages;
@@ -166536,16 +167275,26 @@ class Agent2 {
166536
167275
  }
166537
167276
  defaultConvertToLlm(messages) {
166538
167277
  const filtered = messages.filter((m) => !m.isUI);
166539
- const evaporated = evaporateStaleToolOutputs(filtered, 3);
167278
+ const evaporated = evaporateStaleToolOutputs(filtered, {
167279
+ preserveRecentTurns: 3,
167280
+ preserveRecentSteps: 2,
167281
+ maxOutputSize: 6 * 1024
167282
+ });
166540
167283
  const merged = [];
166541
- for (const msg of evaporated) {
167284
+ for (let i = 0;i < evaporated.length; i++) {
167285
+ const msg = evaporated[i];
167286
+ let role = msg.role;
167287
+ const content = msg.content;
167288
+ if (role === "system" && i > 0 && content.includes("[COMPACTED HISTORY SUMMARY]")) {
167289
+ role = "user";
167290
+ }
166542
167291
  const prev = merged[merged.length - 1];
166543
- if (prev && prev.role === "user" && msg.role === "user" && !prev.toolCalls && !msg.toolCalls) {
167292
+ if (prev && prev.role === "user" && role === "user" && !prev.toolCalls && !msg.toolCalls) {
166544
167293
  prev.content = `${prev.content}
166545
167294
 
166546
- ${msg.content}`;
167295
+ ${content}`;
166547
167296
  } else {
166548
- merged.push({ ...msg });
167297
+ merged.push({ ...msg, role, content });
166549
167298
  }
166550
167299
  }
166551
167300
  return merged;
@@ -166859,9 +167608,9 @@ var init_p_retry = __esm(() => {
166859
167608
  });
166860
167609
 
166861
167610
  // ../../packages/agent/src/utils/request-logger.ts
166862
- import fs20 from "node:fs";
167611
+ import fs21 from "node:fs";
166863
167612
  import os11 from "node:os";
166864
- import path21 from "node:path";
167613
+ import path23 from "node:path";
166865
167614
  function createRequestLogEntry(params) {
166866
167615
  const {
166867
167616
  turn,
@@ -166914,11 +167663,11 @@ function getGlobalConfigDir() {
166914
167663
  const home = process.env.HOME || os11.homedir();
166915
167664
  const isTestEnv = !!process.env.VITEST;
166916
167665
  if (isTestEnv && home === os11.homedir()) {
166917
- return path21.join(os11.tmpdir(), "december-test-config", ".december");
167666
+ return path23.join(os11.tmpdir(), "december-test-config", ".december");
166918
167667
  }
166919
- const canonicalDir = path21.join(home, ".december");
166920
- const legacyDir = path21.join(home, ".config", "december");
166921
- if (fs20.existsSync(canonicalDir) || !fs20.existsSync(legacyDir)) {
167668
+ const canonicalDir = path23.join(home, ".december");
167669
+ const legacyDir = path23.join(home, ".config", "december");
167670
+ if (fs21.existsSync(canonicalDir) || !fs21.existsSync(legacyDir)) {
166922
167671
  return canonicalDir;
166923
167672
  }
166924
167673
  return legacyDir;
@@ -166928,7 +167677,7 @@ function getLogsDir(customDir) {
166928
167677
  return customDir;
166929
167678
  if (process.env.DECEMBER_LOGS_DIR)
166930
167679
  return process.env.DECEMBER_LOGS_DIR;
166931
- return path21.join(getGlobalConfigDir(), "logs");
167680
+ return path23.join(getGlobalConfigDir(), "logs");
166932
167681
  }
166933
167682
  function getSessionLogPath(sessionIdOrLogsDir, logsDirOrSessionId) {
166934
167683
  if (!sessionIdOrLogsDir && !logsDirOrSessionId)
@@ -166936,7 +167685,7 @@ function getSessionLogPath(sessionIdOrLogsDir, logsDirOrSessionId) {
166936
167685
  let sessionId;
166937
167686
  let customDir;
166938
167687
  if (logsDirOrSessionId) {
166939
- if (sessionIdOrLogsDir.startsWith("session-") || !sessionIdOrLogsDir.includes(path21.sep) && logsDirOrSessionId.includes(path21.sep)) {
167688
+ if (sessionIdOrLogsDir.startsWith("session-") || !sessionIdOrLogsDir.includes(path23.sep) && logsDirOrSessionId.includes(path23.sep)) {
166940
167689
  sessionId = sessionIdOrLogsDir;
166941
167690
  customDir = logsDirOrSessionId;
166942
167691
  } else {
@@ -166947,7 +167696,7 @@ function getSessionLogPath(sessionIdOrLogsDir, logsDirOrSessionId) {
166947
167696
  sessionId = sessionIdOrLogsDir;
166948
167697
  }
166949
167698
  const cleanSessionId = sessionId.startsWith("session-") ? sessionId : `session-${sessionId}`;
166950
- return path21.join(getLogsDir(customDir), `${cleanSessionId}.jsonl`);
167699
+ return path23.join(getLogsDir(customDir), `${cleanSessionId}.jsonl`);
166951
167700
  }
166952
167701
  async function appendTurnLog(arg1, arg2, arg3) {
166953
167702
  try {
@@ -166970,11 +167719,11 @@ async function appendTurnLog(arg1, arg2, arg3) {
166970
167719
  const logFile = getSessionLogPath(sessionId, logsDir);
166971
167720
  if (!logFile)
166972
167721
  return;
166973
- const dir = path21.dirname(logFile);
166974
- await fs20.promises.mkdir(dir, { recursive: true });
167722
+ const dir = path23.dirname(logFile);
167723
+ await fs21.promises.mkdir(dir, { recursive: true });
166975
167724
  const line = JSON.stringify(entry) + `
166976
167725
  `;
166977
- await fs20.promises.appendFile(logFile, line, "utf8");
167726
+ await fs21.promises.appendFile(logFile, line, "utf8");
166978
167727
  } catch {}
166979
167728
  }
166980
167729
  var init_request_logger = __esm(() => {
@@ -167753,17 +168502,17 @@ var init_agent_loop = __esm(() => {
167753
168502
  ]);
167754
168503
  });
167755
168504
  // ../../packages/agent/src/skills/skill-subagent.ts
167756
- import fs21 from "node:fs";
168505
+ import fs22 from "node:fs";
167757
168506
  async function executeSkillInSubagent(options) {
167758
168507
  const { skill, args = [], task, parentAgent, signal, onEvent } = options;
167759
168508
  let rawBody = skill.content || "";
167760
- if (!rawBody && skill.entryFilePath && fs21.existsSync(skill.entryFilePath)) {
168509
+ if (!rawBody && skill.entryFilePath && fs22.existsSync(skill.entryFilePath)) {
167761
168510
  try {
167762
168511
  const parsed = parseSkillFile(skill.entryFilePath);
167763
168512
  rawBody = parsed.body;
167764
168513
  } catch {
167765
168514
  try {
167766
- rawBody = fs21.readFileSync(skill.entryFilePath, "utf8");
168515
+ rawBody = fs22.readFileSync(skill.entryFilePath, "utf8");
167767
168516
  } catch (err) {
167768
168517
  return {
167769
168518
  success: false,
@@ -167911,9 +168660,9 @@ var init_run_skill_tool = __esm(() => {
167911
168660
  });
167912
168661
 
167913
168662
  // ../../packages/agent/src/harness/agent-harness.ts
167914
- import fs22 from "node:fs";
168663
+ import fs23 from "node:fs";
167915
168664
  import os12 from "node:os";
167916
- import path22 from "node:path";
168665
+ import path24 from "node:path";
167917
168666
  function isReasoningModel(model, thinkingLevel) {
167918
168667
  if (thinkingLevel === "off") {
167919
168668
  return false;
@@ -167975,22 +168724,22 @@ function discoverProjectRules(startDir, rootBoundary) {
167975
168724
  "AGENTS.override.md",
167976
168725
  "AGENTS.md",
167977
168726
  "CLAUDE.md",
167978
- path22.join(".december", "rules.md"),
167979
- path22.join(".december", "AGENTS.md")
168727
+ path24.join(".december", "rules.md"),
168728
+ path24.join(".december", "AGENTS.md")
167980
168729
  ];
167981
168730
  const seen = new Set;
167982
- let current = path22.resolve(startDir);
167983
- const boundary = rootBoundary ? path22.resolve(rootBoundary) : path22.parse(current).root;
168731
+ let current = path24.resolve(startDir);
168732
+ const boundary = rootBoundary ? path24.resolve(rootBoundary) : path24.parse(current).root;
167984
168733
  const collectedFiles = [];
167985
168734
  while (true) {
167986
168735
  for (const name of candidateNames) {
167987
- const candidatePath = path22.join(current, name);
167988
- if (fs22.existsSync(candidatePath)) {
168736
+ const candidatePath = path24.join(current, name);
168737
+ if (fs23.existsSync(candidatePath)) {
167989
168738
  try {
167990
- if (fs22.statSync(candidatePath).isFile()) {
168739
+ if (fs23.statSync(candidatePath).isFile()) {
167991
168740
  if (!seen.has(candidatePath)) {
167992
168741
  seen.add(candidatePath);
167993
- const content = fs22.readFileSync(candidatePath, "utf8").trim();
168742
+ const content = fs23.readFileSync(candidatePath, "utf8").trim();
167994
168743
  if (content) {
167995
168744
  collectedFiles.unshift({ path: candidatePath, content });
167996
168745
  }
@@ -168001,7 +168750,7 @@ function discoverProjectRules(startDir, rootBoundary) {
168001
168750
  }
168002
168751
  if (current === boundary)
168003
168752
  break;
168004
- const parent = path22.dirname(current);
168753
+ const parent = path24.dirname(current);
168005
168754
  if (parent === current)
168006
168755
  break;
168007
168756
  current = parent;
@@ -168009,17 +168758,17 @@ function discoverProjectRules(startDir, rootBoundary) {
168009
168758
  const home = process.env.HOME || process.env.USERPROFILE || os12.homedir();
168010
168759
  const globalCandidateDirs = [
168011
168760
  process.env.DECEMBER_CONFIG_DIR,
168012
- path22.join(home, ".december"),
168013
- path22.join(home, ".config", "december")
168761
+ path24.join(home, ".december"),
168762
+ path24.join(home, ".config", "december")
168014
168763
  ].filter((dir) => !!dir);
168015
168764
  for (const globalDir of globalCandidateDirs) {
168016
168765
  for (const candidate of ["rules.md", "AGENTS.md"]) {
168017
- const globalPath = path22.join(globalDir, candidate);
168018
- if (fs22.existsSync(globalPath) && !seen.has(globalPath)) {
168766
+ const globalPath = path24.join(globalDir, candidate);
168767
+ if (fs23.existsSync(globalPath) && !seen.has(globalPath)) {
168019
168768
  try {
168020
- if (fs22.statSync(globalPath).isFile()) {
168769
+ if (fs23.statSync(globalPath).isFile()) {
168021
168770
  seen.add(globalPath);
168022
- const content = fs22.readFileSync(globalPath, "utf8").trim();
168771
+ const content = fs23.readFileSync(globalPath, "utf8").trim();
168023
168772
  if (content) {
168024
168773
  collectedFiles.unshift({ path: globalPath, content });
168025
168774
  }
@@ -168275,6 +169024,12 @@ var init_agent_harness = __esm(() => {
168275
169024
  guidelines: [
168276
169025
  "Use 'run_skill' to delegate complex procedural workflows or specialized domain tasks to an isolated subagent, keeping the main conversation context clean."
168277
169026
  ]
169027
+ },
169028
+ manage_memory: {
169029
+ snippet: "Record, read, or prune persistent workspace rules and memories",
169030
+ guidelines: [
169031
+ "Use 'manage_memory' to record learned conventions, build/test commands, architecture notes, and quirks into persistent workspace memory."
169032
+ ]
168278
169033
  }
168279
169034
  };
168280
169035
  });
@@ -168289,10 +169044,383 @@ var init_telemetry = __esm(() => {
168289
169044
  init_tracer_factory();
168290
169045
  });
168291
169046
 
169047
+ // ../../packages/agent/src/lsp-client.ts
169048
+ import { spawn as spawn3 } from "node:child_process";
169049
+ import fs24 from "node:fs";
169050
+ import path25 from "node:path";
169051
+ import { fileURLToPath, pathToFileURL } from "node:url";
169052
+
169053
+ class LspClient {
169054
+ workspaceRoot;
169055
+ binaryPath;
169056
+ requestTimeoutMs;
169057
+ child = null;
169058
+ nextId = 1;
169059
+ pendingRequests = new Map;
169060
+ buffer = Buffer.alloc(0);
169061
+ diagnosticsByUri = new Map;
169062
+ openedDocuments = new Set;
169063
+ initPromise = null;
169064
+ isShutdown = false;
169065
+ constructor(options) {
169066
+ this.workspaceRoot = path25.resolve(options.workspaceRoot);
169067
+ this.binaryPath = options.binaryPath;
169068
+ this.requestTimeoutMs = options.requestTimeoutMs ?? 15000;
169069
+ }
169070
+ async start() {
169071
+ if (this.initPromise) {
169072
+ return this.initPromise;
169073
+ }
169074
+ this.initPromise = this.initInternal();
169075
+ return this.initPromise;
169076
+ }
169077
+ findBinary() {
169078
+ if (this.binaryPath && fs24.existsSync(this.binaryPath)) {
169079
+ return this.binaryPath;
169080
+ }
169081
+ const candidatePaths = [
169082
+ path25.resolve(this.workspaceRoot, "node_modules/.bin/typescript-language-server"),
169083
+ path25.resolve(process.cwd(), "node_modules/.bin/typescript-language-server"),
169084
+ path25.resolve(process.cwd(), "node_modules/typescript-language-server/lib/cli.mjs")
169085
+ ];
169086
+ for (const candidate of candidatePaths) {
169087
+ if (fs24.existsSync(candidate)) {
169088
+ return candidate;
169089
+ }
169090
+ }
169091
+ return "typescript-language-server";
169092
+ }
169093
+ async initInternal() {
169094
+ const bin = this.findBinary();
169095
+ this.child = spawn3(bin, ["--stdio"], {
169096
+ cwd: this.workspaceRoot,
169097
+ env: { ...process.env },
169098
+ stdio: ["pipe", "pipe", "pipe"]
169099
+ });
169100
+ this.child.stdout?.on("data", (chunk) => {
169101
+ this.handleStdout(chunk);
169102
+ });
169103
+ this.child.stderr?.on("data", (_chunk) => {});
169104
+ this.child.on("error", (err) => {
169105
+ this.rejectAllPending(new Error(`LSP process error: ${err.message}`));
169106
+ });
169107
+ this.child.on("exit", (code) => {
169108
+ this.rejectAllPending(new Error(`LSP process exited with code ${code}`));
169109
+ this.child = null;
169110
+ this.initPromise = null;
169111
+ });
169112
+ const rootUri = pathToFileURL(this.workspaceRoot).toString();
169113
+ await this.sendRequest("initialize", {
169114
+ processId: process.pid,
169115
+ rootUri,
169116
+ rootPath: this.workspaceRoot,
169117
+ capabilities: {
169118
+ textDocument: {
169119
+ definition: { dynamicRegistration: false, linkSupport: true },
169120
+ references: { dynamicRegistration: false },
169121
+ documentSymbol: {
169122
+ dynamicRegistration: false,
169123
+ hierarchicalDocumentSymbolSupport: true
169124
+ },
169125
+ synchronization: {
169126
+ dynamicRegistration: false,
169127
+ willSave: false,
169128
+ didSave: true
169129
+ }
169130
+ }
169131
+ }
169132
+ });
169133
+ this.sendNotification("initialized", {});
169134
+ }
169135
+ handleStdout(chunk) {
169136
+ this.buffer = Buffer.concat([this.buffer, chunk]);
169137
+ while (true) {
169138
+ const headerEnd = this.buffer.indexOf(`\r
169139
+ \r
169140
+ `);
169141
+ if (headerEnd === -1)
169142
+ break;
169143
+ const headerStr = this.buffer.subarray(0, headerEnd).toString("utf8");
169144
+ const match = headerStr.match(/Content-Length:\s*(\d+)/i);
169145
+ if (!match) {
169146
+ this.buffer = this.buffer.subarray(headerEnd + 4);
169147
+ continue;
169148
+ }
169149
+ const contentLength = parseInt(match[1], 10);
169150
+ const totalMsgLength = headerEnd + 4 + contentLength;
169151
+ if (this.buffer.length < totalMsgLength)
169152
+ break;
169153
+ const body = this.buffer.subarray(headerEnd + 4, totalMsgLength).toString("utf8");
169154
+ this.buffer = this.buffer.subarray(totalMsgLength);
169155
+ try {
169156
+ const msg = JSON.parse(body);
169157
+ if (msg.id !== undefined && this.pendingRequests.has(msg.id)) {
169158
+ const pending = this.pendingRequests.get(msg.id);
169159
+ clearTimeout(pending.timeout);
169160
+ this.pendingRequests.delete(msg.id);
169161
+ if (msg.error) {
169162
+ pending.reject(new Error(msg.error.message || "LSP error"));
169163
+ } else {
169164
+ pending.resolve(msg.result);
169165
+ }
169166
+ } else if (msg.method === "textDocument/publishDiagnostics") {
169167
+ this.handlePublishDiagnostics(msg.params);
169168
+ }
169169
+ } catch {}
169170
+ }
169171
+ }
169172
+ handlePublishDiagnostics(params) {
169173
+ if (!params || !params.uri || !Array.isArray(params.diagnostics))
169174
+ return;
169175
+ const uri = params.uri;
169176
+ const filePath = fileURLToPath(uri);
169177
+ const items = params.diagnostics.map((d) => {
169178
+ const sevNum = d.severity ?? 1;
169179
+ const severity = sevNum === 1 ? "error" : sevNum === 2 ? "warning" : "info";
169180
+ return {
169181
+ filePath,
169182
+ line: (d.range?.start?.line ?? 0) + 1,
169183
+ column: (d.range?.start?.character ?? 0) + 1,
169184
+ message: d.message || "",
169185
+ severity,
169186
+ source: d.source || "typescript"
169187
+ };
169188
+ });
169189
+ this.diagnosticsByUri.set(uri, items);
169190
+ }
169191
+ sendRequest(method, params) {
169192
+ if (!this.child || !this.child.stdin || this.isShutdown) {
169193
+ return Promise.reject(new Error("LSP client is not running"));
169194
+ }
169195
+ const id = this.nextId++;
169196
+ return new Promise((resolve, reject) => {
169197
+ const timeout = setTimeout(() => {
169198
+ this.pendingRequests.delete(id);
169199
+ reject(new Error(`LSP request "${method}" timed out after ${this.requestTimeoutMs}ms`));
169200
+ }, this.requestTimeoutMs);
169201
+ this.pendingRequests.set(id, { resolve, reject, timeout });
169202
+ const payload = JSON.stringify({ jsonrpc: "2.0", id, method, params });
169203
+ const header = `Content-Length: ${Buffer.byteLength(payload, "utf8")}\r
169204
+ \r
169205
+ `;
169206
+ this.child?.stdin?.write(header + payload);
169207
+ });
169208
+ }
169209
+ sendNotification(method, params) {
169210
+ if (!this.child || !this.child.stdin || this.isShutdown)
169211
+ return;
169212
+ const payload = JSON.stringify({ jsonrpc: "2.0", method, params });
169213
+ const header = `Content-Length: ${Buffer.byteLength(payload, "utf8")}\r
169214
+ \r
169215
+ `;
169216
+ this.child.stdin.write(header + payload);
169217
+ }
169218
+ async openDocument(filePath, content) {
169219
+ const absPath = path25.isAbsolute(filePath) ? filePath : path25.resolve(this.workspaceRoot, filePath);
169220
+ const uri = pathToFileURL(absPath).toString();
169221
+ if (this.openedDocuments.has(uri)) {
169222
+ return uri;
169223
+ }
169224
+ let fileContent = content;
169225
+ if (fileContent === undefined) {
169226
+ try {
169227
+ fileContent = fs24.readFileSync(absPath, "utf8");
169228
+ } catch {
169229
+ fileContent = "";
169230
+ }
169231
+ }
169232
+ this.sendNotification("textDocument/didOpen", {
169233
+ textDocument: {
169234
+ uri,
169235
+ languageId: "typescript",
169236
+ version: 1,
169237
+ text: fileContent
169238
+ }
169239
+ });
169240
+ this.openedDocuments.add(uri);
169241
+ return uri;
169242
+ }
169243
+ async getDefinition(filePath, line, column) {
169244
+ const queryRawDefinition = async (targetFile, targetLine, targetCol) => {
169245
+ await this.start();
169246
+ const uri = await this.openDocument(targetFile);
169247
+ const raw = await this.sendRequest("textDocument/definition", {
169248
+ textDocument: { uri },
169249
+ position: {
169250
+ line: Math.max(0, targetLine - 1),
169251
+ character: Math.max(0, targetCol - 1)
169252
+ }
169253
+ });
169254
+ if (!raw)
169255
+ return [];
169256
+ const array = Array.isArray(raw) ? raw : [raw];
169257
+ const locations = [];
169258
+ for (const loc of array) {
169259
+ const targetUri = loc.targetUri || loc.uri;
169260
+ if (!targetUri)
169261
+ continue;
169262
+ const targetRange = loc.targetSelectionRange || loc.targetRange || loc.range;
169263
+ const targetFilePath = fileURLToPath(targetUri);
169264
+ locations.push({
169265
+ filePath: targetFilePath,
169266
+ line: (targetRange?.start?.line ?? 0) + 1,
169267
+ column: (targetRange?.start?.character ?? 0) + 1
169268
+ });
169269
+ }
169270
+ return locations;
169271
+ };
169272
+ const initial = await queryRawDefinition(filePath, line, column);
169273
+ const resolved = [];
169274
+ for (const loc of initial) {
169275
+ try {
169276
+ const content = fs24.readFileSync(loc.filePath, "utf8");
169277
+ const lines = content.split(`
169278
+ `);
169279
+ const lineContent = lines[loc.line - 1] || "";
169280
+ if (/^\s*import\b/.test(lineContent)) {
169281
+ const secondHop = await queryRawDefinition(loc.filePath, loc.line, loc.column);
169282
+ if (secondHop.length > 0 && !(secondHop.length === 1 && secondHop[0].filePath === loc.filePath && secondHop[0].line === loc.line)) {
169283
+ resolved.push(...secondHop);
169284
+ continue;
169285
+ }
169286
+ }
169287
+ } catch {}
169288
+ resolved.push(loc);
169289
+ }
169290
+ return resolved;
169291
+ }
169292
+ async getReferences(filePath, line, column) {
169293
+ await this.start();
169294
+ const uri = await this.openDocument(filePath);
169295
+ const raw = await this.sendRequest("textDocument/references", {
169296
+ textDocument: { uri },
169297
+ position: {
169298
+ line: Math.max(0, line - 1),
169299
+ character: Math.max(0, column - 1)
169300
+ },
169301
+ context: { includeDeclaration: true }
169302
+ });
169303
+ if (!raw || !Array.isArray(raw))
169304
+ return [];
169305
+ const locations = [];
169306
+ for (const loc of raw) {
169307
+ if (!loc.uri)
169308
+ continue;
169309
+ const targetFilePath = fileURLToPath(loc.uri);
169310
+ locations.push({
169311
+ filePath: targetFilePath,
169312
+ line: (loc.range?.start?.line ?? 0) + 1,
169313
+ column: (loc.range?.start?.character ?? 0) + 1
169314
+ });
169315
+ }
169316
+ return locations;
169317
+ }
169318
+ async getOutline(filePath) {
169319
+ await this.start();
169320
+ const uri = await this.openDocument(filePath);
169321
+ const raw = await this.sendRequest("textDocument/documentSymbol", {
169322
+ textDocument: { uri }
169323
+ });
169324
+ if (!raw || !Array.isArray(raw))
169325
+ return [];
169326
+ const items = [];
169327
+ const extractSymbols = (symbols, containerName) => {
169328
+ for (const sym of symbols) {
169329
+ const kindStr = SYMBOL_KINDS[sym.kind] || "symbol";
169330
+ const range = sym.range || sym.location?.range;
169331
+ const line = (range?.start?.line ?? 0) + 1;
169332
+ const column = (range?.start?.character ?? 0) + 1;
169333
+ items.push({
169334
+ name: sym.name || "anonymous",
169335
+ kind: kindStr,
169336
+ line,
169337
+ column,
169338
+ containerName: sym.containerName || containerName
169339
+ });
169340
+ if (Array.isArray(sym.children) && sym.children.length > 0) {
169341
+ extractSymbols(sym.children, sym.name);
169342
+ }
169343
+ }
169344
+ };
169345
+ extractSymbols(raw);
169346
+ return items;
169347
+ }
169348
+ getDiagnostics(filePath) {
169349
+ if (filePath) {
169350
+ const absPath = path25.isAbsolute(filePath) ? filePath : path25.resolve(this.workspaceRoot, filePath);
169351
+ const uri = pathToFileURL(absPath).toString();
169352
+ return this.diagnosticsByUri.get(uri) || [];
169353
+ }
169354
+ const all = [];
169355
+ for (const items of this.diagnosticsByUri.values()) {
169356
+ all.push(...items);
169357
+ }
169358
+ return all;
169359
+ }
169360
+ async shutdown() {
169361
+ if (this.isShutdown)
169362
+ return;
169363
+ this.isShutdown = true;
169364
+ if (this.child) {
169365
+ try {
169366
+ await Promise.race([
169367
+ this.sendRequest("shutdown", {}),
169368
+ new Promise((r) => setTimeout(r, 500))
169369
+ ]);
169370
+ this.sendNotification("exit", {});
169371
+ } catch {}
169372
+ try {
169373
+ this.child.kill("SIGTERM");
169374
+ } catch {}
169375
+ this.child = null;
169376
+ }
169377
+ this.rejectAllPending(new Error("LSP client shutdown"));
169378
+ }
169379
+ rejectAllPending(err) {
169380
+ for (const [id, pending] of this.pendingRequests.entries()) {
169381
+ clearTimeout(pending.timeout);
169382
+ pending.reject(err);
169383
+ this.pendingRequests.delete(id);
169384
+ }
169385
+ }
169386
+ }
169387
+ var SYMBOL_KINDS;
169388
+ var init_lsp_client = __esm(() => {
169389
+ SYMBOL_KINDS = {
169390
+ 1: "file",
169391
+ 2: "module",
169392
+ 3: "namespace",
169393
+ 4: "package",
169394
+ 5: "class",
169395
+ 6: "method",
169396
+ 7: "property",
169397
+ 8: "field",
169398
+ 9: "constructor",
169399
+ 10: "enum",
169400
+ 11: "interface",
169401
+ 12: "function",
169402
+ 13: "variable",
169403
+ 14: "constant",
169404
+ 15: "string",
169405
+ 16: "number",
169406
+ 17: "boolean",
169407
+ 18: "array",
169408
+ 19: "object",
169409
+ 20: "key",
169410
+ 21: "null",
169411
+ 22: "enumMember",
169412
+ 23: "struct",
169413
+ 24: "event",
169414
+ 25: "operator",
169415
+ 26: "typeParameter"
169416
+ };
169417
+ });
169418
+
168292
169419
  // ../../packages/agent/src/index.ts
168293
169420
  var exports_src3 = {};
168294
169421
  __export(exports_src3, {
168295
169422
  AgentHarness: () => AgentHarness,
169423
+ LspClient: () => LspClient,
168296
169424
  runAgentLoop: () => runAgentLoop
168297
169425
  });
168298
169426
  var init_src5 = __esm(() => {
@@ -168305,6 +169433,7 @@ var init_src5 = __esm(() => {
168305
169433
  init_telemetry();
168306
169434
  init_skill_subagent();
168307
169435
  init_run_skill_tool();
169436
+ init_lsp_client();
168308
169437
  });
168309
169438
 
168310
169439
  // src/headless-runner.ts
@@ -168582,8 +169711,8 @@ var exports_file_session_repository = {};
168582
169711
  __export(exports_file_session_repository, {
168583
169712
  FileSessionRepository: () => FileSessionRepository
168584
169713
  });
168585
- import fs23 from "node:fs/promises";
168586
- import path23 from "node:path";
169714
+ import fs25 from "node:fs/promises";
169715
+ import path26 from "node:path";
168587
169716
 
168588
169717
  class FileSessionRepository {
168589
169718
  sessionDir;
@@ -168593,22 +169722,22 @@ class FileSessionRepository {
168593
169722
  if (sessionDir) {
168594
169723
  this.sessionDir = sessionDir;
168595
169724
  } else {
168596
- this.sessionDir = path23.join(getConfigDir(), "sessions");
168597
- const legacyDir = path23.join(getLegacyConfigDir(), "sessions");
169725
+ this.sessionDir = path26.join(getConfigDir(), "sessions");
169726
+ const legacyDir = path26.join(getLegacyConfigDir(), "sessions");
168598
169727
  if (legacyDir !== this.sessionDir) {
168599
169728
  this.legacySessionDir = legacyDir;
168600
169729
  }
168601
169730
  }
168602
169731
  }
168603
169732
  async saveContext(sessionId, messages) {
168604
- await fs23.mkdir(this.sessionDir, { recursive: true });
168605
- const historyPath = path23.join(this.sessionDir, `${sessionId}.jsonl`);
169733
+ await fs25.mkdir(this.sessionDir, { recursive: true });
169734
+ const historyPath = path26.join(this.sessionDir, `${sessionId}.jsonl`);
168606
169735
  let existing = this.sessionCache[sessionId];
168607
169736
  if (!existing) {
168608
169737
  existing = {};
168609
169738
  this.sessionCache[sessionId] = existing;
168610
169739
  try {
168611
- const data = await fs23.readFile(historyPath, "utf-8");
169740
+ const data = await fs25.readFile(historyPath, "utf-8");
168612
169741
  const lines = data.split(`
168613
169742
  `).filter((l) => l.trim().length > 0);
168614
169743
  for (const l of lines) {
@@ -168632,19 +169761,19 @@ class FileSessionRepository {
168632
169761
  }
168633
169762
  }
168634
169763
  if (appendContent) {
168635
- await fs23.appendFile(historyPath, appendContent, "utf-8");
169764
+ await fs25.appendFile(historyPath, appendContent, "utf-8");
168636
169765
  }
168637
169766
  }
168638
169767
  async loadContext(sessionId) {
168639
- let historyPath = path23.join(this.sessionDir, `${sessionId}.jsonl`);
169768
+ let historyPath = path26.join(this.sessionDir, `${sessionId}.jsonl`);
168640
169769
  try {
168641
169770
  let data;
168642
169771
  try {
168643
- data = await fs23.readFile(historyPath, "utf-8");
169772
+ data = await fs25.readFile(historyPath, "utf-8");
168644
169773
  } catch (err) {
168645
169774
  if (this.legacySessionDir) {
168646
- const legacyPath = path23.join(this.legacySessionDir, `${sessionId}.jsonl`);
168647
- data = await fs23.readFile(legacyPath, "utf-8");
169775
+ const legacyPath = path26.join(this.legacySessionDir, `${sessionId}.jsonl`);
169776
+ data = await fs25.readFile(legacyPath, "utf-8");
168648
169777
  historyPath = legacyPath;
168649
169778
  } else {
168650
169779
  throw err;
@@ -168685,7 +169814,7 @@ class FileSessionRepository {
168685
169814
  if (msgs.length < lines.length) {
168686
169815
  const compacted = msgs.map((m) => JSON.stringify(m)).join(`
168687
169816
  `);
168688
- await fs23.writeFile(historyPath, compacted, "utf-8").catch(() => {});
169817
+ await fs25.writeFile(historyPath, compacted, "utf-8").catch(() => {});
168689
169818
  }
168690
169819
  const cacheObj = {};
168691
169820
  for (const msg of msgs) {
@@ -168699,12 +169828,12 @@ class FileSessionRepository {
168699
169828
  }
168700
169829
  async listSessions() {
168701
169830
  try {
168702
- await fs23.mkdir(this.sessionDir, { recursive: true });
169831
+ await fs25.mkdir(this.sessionDir, { recursive: true });
168703
169832
  const sessions = [];
168704
169833
  const seenIds = new Set;
168705
169834
  const processDirectory = async (dirPath) => {
168706
169835
  try {
168707
- const dirFiles = await fs23.readdir(dirPath);
169836
+ const dirFiles = await fs25.readdir(dirPath);
168708
169837
  for (const file of dirFiles) {
168709
169838
  if (!file.endsWith(".jsonl"))
168710
169839
  continue;
@@ -168712,10 +169841,10 @@ class FileSessionRepository {
168712
169841
  if (seenIds.has(id))
168713
169842
  continue;
168714
169843
  seenIds.add(id);
168715
- const filePath = path23.join(dirPath, file);
169844
+ const filePath = path26.join(dirPath, file);
168716
169845
  try {
168717
- const stat = await fs23.stat(filePath);
168718
- const data = await fs23.readFile(filePath, "utf-8");
169846
+ const stat = await fs25.stat(filePath);
169847
+ const data = await fs25.readFile(filePath, "utf-8");
168719
169848
  const lines = data.split(`
168720
169849
  `).filter((l) => l.trim().length > 0);
168721
169850
  let preview = "";
@@ -168749,20 +169878,20 @@ class FileSessionRepository {
168749
169878
  }
168750
169879
  }
168751
169880
  async deleteSession(sessionId) {
168752
- const historyPath = path23.join(this.sessionDir, `${sessionId}.jsonl`);
169881
+ const historyPath = path26.join(this.sessionDir, `${sessionId}.jsonl`);
168753
169882
  try {
168754
- await fs23.unlink(historyPath);
169883
+ await fs25.unlink(historyPath);
168755
169884
  } catch (e) {}
168756
169885
  if (this.legacySessionDir) {
168757
169886
  try {
168758
- await fs23.unlink(path23.join(this.legacySessionDir, `${sessionId}.jsonl`));
169887
+ await fs25.unlink(path26.join(this.legacySessionDir, `${sessionId}.jsonl`));
168759
169888
  } catch {}
168760
169889
  }
168761
169890
  }
168762
169891
  async renameSession(oldSessionId, newSessionId) {
168763
- const oldPath = path23.join(this.sessionDir, `${oldSessionId}.jsonl`);
168764
- const newPath = path23.join(this.sessionDir, `${newSessionId}.jsonl`);
168765
- await fs23.rename(oldPath, newPath);
169892
+ const oldPath = path26.join(this.sessionDir, `${oldSessionId}.jsonl`);
169893
+ const newPath = path26.join(this.sessionDir, `${newSessionId}.jsonl`);
169894
+ await fs25.rename(oldPath, newPath);
168766
169895
  }
168767
169896
  }
168768
169897
  var init_file_session_repository = __esm(() => {
@@ -174396,21 +175525,53 @@ __export(exports_local_operations, {
174396
175525
  localOperations: () => localOperations,
174397
175526
  setActiveScopeDir: () => setActiveScopeDir
174398
175527
  });
174399
- import { exec as exec4, spawn as spawn3 } from "node:child_process";
174400
- import fs24 from "node:fs/promises";
174401
- import path24 from "node:path";
175528
+ import { exec as exec4, spawn as spawn4 } from "node:child_process";
175529
+ import nodeFs from "node:fs";
175530
+ import fs26 from "node:fs/promises";
175531
+ import path27 from "node:path";
174402
175532
  import { promisify as promisify4 } from "node:util";
174403
175533
  function setActiveScopeDir(dir) {
174404
175534
  activeScopeDir = dir;
174405
175535
  }
174406
175536
  function getScopedCwd() {
174407
175537
  if (activeScopeDir) {
174408
- return path24.resolve(process.cwd(), activeScopeDir);
175538
+ return path27.resolve(process.cwd(), activeScopeDir);
174409
175539
  }
174410
175540
  return process.cwd();
174411
175541
  }
174412
- var import_fast_glob, execAsync2, localBashOps, activeScopeDir, SERVER_READY_REGEX, DEFAULT_BG_TIMEOUT_MS = 20000, DEFAULT_SERVER_READY_DELAY_MS = 2500, localOperations;
175542
+ function isTypeScriptProject(workspaceDir) {
175543
+ return nodeFs.existsSync(path27.join(workspaceDir, "tsconfig.json")) || nodeFs.existsSync(path27.join(workspaceDir, "package.json"));
175544
+ }
175545
+ async function getOrLaunchLspClient(customWorkspaceDir) {
175546
+ const workspaceDir = customWorkspaceDir || getScopedCwd();
175547
+ if (activeLspClient && activeLspWorkspace === workspaceDir) {
175548
+ return activeLspClient;
175549
+ }
175550
+ if (!isTypeScriptProject(workspaceDir)) {
175551
+ return null;
175552
+ }
175553
+ if (activeLspClient) {
175554
+ try {
175555
+ await activeLspClient.shutdown();
175556
+ } catch {}
175557
+ activeLspClient = null;
175558
+ activeLspWorkspace = null;
175559
+ }
175560
+ const client = new LspClient({ workspaceRoot: workspaceDir });
175561
+ activeLspClient = client;
175562
+ activeLspWorkspace = workspaceDir;
175563
+ try {
175564
+ await client.start();
175565
+ } catch {
175566
+ activeLspClient = null;
175567
+ activeLspWorkspace = null;
175568
+ return null;
175569
+ }
175570
+ return activeLspClient;
175571
+ }
175572
+ var import_fast_glob, execAsync2, localBashOps, activeScopeDir, SERVER_READY_REGEX, DEFAULT_BG_TIMEOUT_MS = 20000, DEFAULT_SERVER_READY_DELAY_MS = 2500, localOperations, activeLspClient = null, activeLspWorkspace = null;
174413
175573
  var init_local_operations = __esm(() => {
175574
+ init_src5();
174414
175575
  init_src();
174415
175576
  init_src4();
174416
175577
  init_task_manager();
@@ -174426,7 +175587,7 @@ var init_local_operations = __esm(() => {
174426
175587
  const fallbackTimeoutMs = actualOptions.waitMsBeforeAsync ?? DEFAULT_BG_TIMEOUT_MS;
174427
175588
  const serverDelayMs = actualOptions.serverReadyDelayMs ?? DEFAULT_SERVER_READY_DELAY_MS;
174428
175589
  return new Promise((resolve, reject) => {
174429
- const child = spawn3(command, {
175590
+ const child = spawn4(command, {
174430
175591
  cwd: targetCwd,
174431
175592
  detached: process.platform !== "win32",
174432
175593
  env: actualOptions.env ?? process.env,
@@ -174556,19 +175717,19 @@ Failed to start process: ${err?.message || err}
174556
175717
  fs: {
174557
175718
  readFile: async (filepath) => {
174558
175719
  const root = getScopedCwd();
174559
- const absolutePath = path24.isAbsolute(filepath) ? filepath : path24.resolve(root, filepath);
174560
- return fs24.readFile(absolutePath, "utf8");
175720
+ const absolutePath = path27.isAbsolute(filepath) ? filepath : path27.resolve(root, filepath);
175721
+ return fs26.readFile(absolutePath, "utf8");
174561
175722
  },
174562
175723
  writeFile: async (filepath, content) => {
174563
175724
  const root = getScopedCwd();
174564
- const absolutePath = path24.isAbsolute(filepath) ? filepath : path24.resolve(root, filepath);
174565
- await fs24.mkdir(path24.dirname(absolutePath), { recursive: true });
174566
- await fs24.writeFile(absolutePath, content, "utf8");
175725
+ const absolutePath = path27.isAbsolute(filepath) ? filepath : path27.resolve(root, filepath);
175726
+ await fs26.mkdir(path27.dirname(absolutePath), { recursive: true });
175727
+ await fs26.writeFile(absolutePath, content, "utf8");
174567
175728
  },
174568
175729
  readdir: async (dirPath) => {
174569
175730
  const root = getScopedCwd();
174570
- const absolutePath = path24.isAbsolute(dirPath) ? dirPath : path24.resolve(root, dirPath);
174571
- const entries = await fs24.readdir(absolutePath, { withFileTypes: true });
175731
+ const absolutePath = path27.isAbsolute(dirPath) ? dirPath : path27.resolve(root, dirPath);
175732
+ const entries = await fs26.readdir(absolutePath, { withFileTypes: true });
174572
175733
  const ignores = getWorkspaceIgnores(root);
174573
175734
  return entries.filter((entry) => !isPathIgnored(entry.name, ignores)).map((entry) => {
174574
175735
  const type = entry.isDirectory() ? "DIR " : "FILE";
@@ -174577,14 +175738,14 @@ Failed to start process: ${err?.message || err}
174577
175738
  },
174578
175739
  mkdir: async (dirPath, options) => {
174579
175740
  const root = getScopedCwd();
174580
- const absolutePath = path24.isAbsolute(dirPath) ? dirPath : path24.resolve(root, dirPath);
174581
- await fs24.mkdir(absolutePath, options);
175741
+ const absolutePath = path27.isAbsolute(dirPath) ? dirPath : path27.resolve(root, dirPath);
175742
+ await fs26.mkdir(absolutePath, options);
174582
175743
  },
174583
175744
  exists: async (filepath) => {
174584
175745
  const root = getScopedCwd();
174585
- const absolutePath = path24.isAbsolute(filepath) ? filepath : path24.resolve(root, filepath);
175746
+ const absolutePath = path27.isAbsolute(filepath) ? filepath : path27.resolve(root, filepath);
174586
175747
  try {
174587
- await fs24.access(absolutePath);
175748
+ await fs26.access(absolutePath);
174588
175749
  return true;
174589
175750
  } catch {
174590
175751
  return false;
@@ -174594,7 +175755,7 @@ Failed to start process: ${err?.message || err}
174594
175755
  search: {
174595
175756
  find: async (dirPath, query) => {
174596
175757
  const root = getScopedCwd();
174597
- const targetDir = path24.isAbsolute(dirPath) ? dirPath : path24.resolve(root, dirPath);
175758
+ const targetDir = path27.isAbsolute(dirPath) ? dirPath : path27.resolve(root, dirPath);
174598
175759
  const ignores = getWorkspaceIgnores(root);
174599
175760
  const files = await import_fast_glob.default([query], {
174600
175761
  cwd: targetDir,
@@ -174606,7 +175767,7 @@ Failed to start process: ${err?.message || err}
174606
175767
  },
174607
175768
  grep: async (dirPath, query) => {
174608
175769
  const root = getScopedCwd();
174609
- const targetDir = path24.isAbsolute(dirPath) ? dirPath : path24.resolve(root, dirPath);
175770
+ const targetDir = path27.isAbsolute(dirPath) ? dirPath : path27.resolve(root, dirPath);
174610
175771
  const ignores = getWorkspaceIgnores(root);
174611
175772
  try {
174612
175773
  const escapedQuery = query.replace(/"/g, "\\\"");
@@ -174620,7 +175781,7 @@ Failed to start process: ${err?.message || err}
174620
175781
  if (!line.trim())
174621
175782
  return false;
174622
175783
  const rawPath = line.split(":")[0];
174623
- const relPath = path24.isAbsolute(rawPath) ? path24.relative(root, rawPath) : rawPath;
175784
+ const relPath = path27.isAbsolute(rawPath) ? path27.relative(root, rawPath) : rawPath;
174624
175785
  return !isPathIgnored(relPath, ignores);
174625
175786
  });
174626
175787
  return filtered.join(`
@@ -174662,8 +175823,56 @@ Failed to start process: ${err?.message || err}
174662
175823
  return { text: "", error: error.message };
174663
175824
  }
174664
175825
  }
175826
+ },
175827
+ diagnostics: {
175828
+ getDiagnostics: async (filePath) => {
175829
+ const client = await getOrLaunchLspClient();
175830
+ if (!client)
175831
+ return [];
175832
+ return client.getDiagnostics(filePath);
175833
+ }
175834
+ },
175835
+ lsp: {
175836
+ getDefinition: async (filePath, line, column) => {
175837
+ const client = await getOrLaunchLspClient();
175838
+ if (!client)
175839
+ return [];
175840
+ return client.getDefinition(filePath, line, column);
175841
+ },
175842
+ getReferences: async (filePath, line, column) => {
175843
+ const client = await getOrLaunchLspClient();
175844
+ if (!client)
175845
+ return [];
175846
+ return client.getReferences(filePath, line, column);
175847
+ },
175848
+ getOutline: async (filePath) => {
175849
+ const client = await getOrLaunchLspClient();
175850
+ if (!client)
175851
+ return [];
175852
+ return client.getOutline(filePath);
175853
+ },
175854
+ getDiagnostics: async (filePath) => {
175855
+ const client = await getOrLaunchLspClient();
175856
+ if (!client)
175857
+ return [];
175858
+ return client.getDiagnostics(filePath);
175859
+ },
175860
+ shutdown: async () => {
175861
+ if (activeLspClient) {
175862
+ await activeLspClient.shutdown();
175863
+ activeLspClient = null;
175864
+ activeLspWorkspace = null;
175865
+ }
175866
+ }
174665
175867
  }
174666
175868
  };
175869
+ process.on("exit", () => {
175870
+ if (activeLspClient) {
175871
+ try {
175872
+ activeLspClient.shutdown();
175873
+ } catch {}
175874
+ }
175875
+ });
174667
175876
  });
174668
175877
 
174669
175878
  // src/utils/provider-factory.ts
@@ -179015,7 +180224,7 @@ var init_wrap_ansi = __esm(() => {
179015
180224
  // ../../node_modules/.bun/terminal-size@4.0.1/node_modules/terminal-size/index.js
179016
180225
  import process3 from "node:process";
179017
180226
  import { execFileSync } from "node:child_process";
179018
- import fs25 from "node:fs";
180227
+ import fs27 from "node:fs";
179019
180228
  import tty from "node:tty";
179020
180229
  function terminalSize() {
179021
180230
  const { env, stdout, stderr } = process3;
@@ -179063,7 +180272,7 @@ var defaultColumns = 80, defaultRows = 24, exec6 = (command, arguments_, { shell
179063
180272
  return true;
179064
180273
  }
179065
180274
  try {
179066
- const statContents = fs25.readFileSync("/proc/self/stat", "utf8");
180275
+ const statContents = fs27.readFileSync("/proc/self/stat", "utf8");
179067
180276
  const closingParenthesisIndex = statContents.lastIndexOf(") ");
179068
180277
  if (closingParenthesisIndex === -1) {
179069
180278
  return false;
@@ -179083,8 +180292,8 @@ var defaultColumns = 80, defaultRows = 24, exec6 = (command, arguments_, { shell
179083
180292
  }
179084
180293
  }, devTty = () => {
179085
180294
  try {
179086
- const flags = process3.platform === "darwin" ? fs25.constants.O_EVTONLY | fs25.constants.O_NONBLOCK : fs25.constants.O_NONBLOCK;
179087
- const { columns, rows } = tty.WriteStream(fs25.openSync("/dev/tty", flags));
180295
+ const flags = process3.platform === "darwin" ? fs27.constants.O_EVTONLY | fs27.constants.O_NONBLOCK : fs27.constants.O_NONBLOCK;
180296
+ const { columns, rows } = tty.WriteStream(fs27.openSync("/dev/tty", flags));
179088
180297
  return { columns, rows };
179089
180298
  } catch {}
179090
180299
  }, tput = () => {
@@ -211286,7 +212495,7 @@ var init_Text = __esm(() => {
211286
212495
  });
211287
212496
 
211288
212497
  // ../../node_modules/.bun/ink@7.1.0+90ffd46201e3176f/node_modules/ink/build/components/ErrorOverview.js
211289
- import * as fs26 from "node:fs";
212498
+ import * as fs28 from "node:fs";
211290
212499
  import { cwd } from "node:process";
211291
212500
  function ErrorOverview({ error }) {
211292
212501
  const stack = error.stack ? error.stack.split(`
@@ -211296,8 +212505,8 @@ function ErrorOverview({ error }) {
211296
212505
  let excerpt;
211297
212506
  let lineWidth = 0;
211298
212507
  const stackLineCounts = new Map;
211299
- if (filePath && origin?.line && fs26.existsSync(filePath)) {
211300
- const sourceCode = fs26.readFileSync(filePath, "utf8");
212508
+ if (filePath && origin?.line && fs28.existsSync(filePath)) {
212509
+ const sourceCode = fs28.readFileSync(filePath, "utf8");
211301
212510
  excerpt = dist_default4(sourceCode, origin.line);
211302
212511
  if (excerpt) {
211303
212512
  for (const { line } of excerpt) {
@@ -212728,7 +213937,7 @@ var init_render_to_string = __esm(async () => {
212728
213937
  });
212729
213938
 
212730
213939
  // ../../node_modules/.bun/ink@7.1.0+90ffd46201e3176f/node_modules/ink/build/components/Static.js
212731
- function Static13(props) {
213940
+ function Static15(props) {
212732
213941
  const { items, children: render, style: customStyle } = props;
212733
213942
  const [index, setIndex] = import_react17.useState(0);
212734
213943
  const itemsToRender = import_react17.useMemo(() => {
@@ -213971,9 +215180,9 @@ var init_paste_manager = __esm(() => {
213971
215180
  });
213972
215181
 
213973
215182
  // ../../packages/tui/src/utils/prompt-history.ts
213974
- import fs27 from "node:fs";
215183
+ import fs29 from "node:fs";
213975
215184
  import os15 from "node:os";
213976
- import path25 from "node:path";
215185
+ import path28 from "node:path";
213977
215186
 
213978
215187
  class PromptHistory {
213979
215188
  historyPath;
@@ -213985,16 +215194,16 @@ class PromptHistory {
213985
215194
  this.historyPath = customPath;
213986
215195
  } else {
213987
215196
  const home = process.env.HOME || process.env.USERPROFILE || os15.homedir();
213988
- const canonicalPath = path25.join(home, ".december", "history");
213989
- const legacyPath = path25.join(home, ".config", "december", "history");
213990
- this.historyPath = fs27.existsSync(canonicalPath) || !fs27.existsSync(legacyPath) ? canonicalPath : legacyPath;
215197
+ const canonicalPath = path28.join(home, ".december", "history");
215198
+ const legacyPath = path28.join(home, ".config", "december", "history");
215199
+ this.historyPath = fs29.existsSync(canonicalPath) || !fs29.existsSync(legacyPath) ? canonicalPath : legacyPath;
213991
215200
  }
213992
215201
  this.load();
213993
215202
  }
213994
215203
  load() {
213995
215204
  try {
213996
- if (fs27.existsSync(this.historyPath)) {
213997
- const raw = fs27.readFileSync(this.historyPath, "utf8");
215205
+ if (fs29.existsSync(this.historyPath)) {
215206
+ const raw = fs29.readFileSync(this.historyPath, "utf8");
213998
215207
  this.entries = raw.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
213999
215208
  }
214000
215209
  } catch {
@@ -214010,11 +215219,11 @@ class PromptHistory {
214010
215219
  this.cursor = this.entries.length;
214011
215220
  this.currentDraft = "";
214012
215221
  try {
214013
- const dir = path25.dirname(this.historyPath);
214014
- if (!fs27.existsSync(dir)) {
214015
- fs27.mkdirSync(dir, { recursive: true });
215222
+ const dir = path28.dirname(this.historyPath);
215223
+ if (!fs29.existsSync(dir)) {
215224
+ fs29.mkdirSync(dir, { recursive: true });
214016
215225
  }
214017
- fs27.appendFileSync(this.historyPath, trimmed + `
215226
+ fs29.appendFileSync(this.historyPath, trimmed + `
214018
215227
  `, "utf8");
214019
215228
  } catch {}
214020
215229
  }
@@ -214078,16 +215287,16 @@ var init_clipboard = () => {};
214078
215287
 
214079
215288
  // ../../packages/tui/src/utils/handoff.ts
214080
215289
  import { exec as exec7 } from "node:child_process";
214081
- import fs28 from "node:fs";
214082
- import path26 from "node:path";
215290
+ import fs30 from "node:fs";
215291
+ import path29 from "node:path";
214083
215292
  import { promisify as promisify5 } from "node:util";
214084
215293
  function getHandoffExcludes(baseDir = process.cwd()) {
214085
215294
  const excludes = [...MANDATORY_HANDOFF_EXCLUDES];
214086
215295
  const readIgnoreFile = (fileName) => {
214087
- const filePath = path26.join(baseDir, fileName);
215296
+ const filePath = path29.join(baseDir, fileName);
214088
215297
  try {
214089
- if (fs28.existsSync(filePath)) {
214090
- const lines = fs28.readFileSync(filePath, "utf8").split(`
215298
+ if (fs30.existsSync(filePath)) {
215299
+ const lines = fs30.readFileSync(filePath, "utf8").split(`
214091
215300
  `);
214092
215301
  for (const line of lines) {
214093
215302
  const trimmed = line.trim();
@@ -214156,9 +215365,9 @@ var init_handoff = __esm(() => {
214156
215365
  });
214157
215366
 
214158
215367
  // ../../packages/tui/src/components/command-menu/commands.tsx
214159
- import fs29 from "node:fs";
215368
+ import fs31 from "node:fs";
214160
215369
  import os17 from "node:os";
214161
- import path27 from "node:path";
215370
+ import path30 from "node:path";
214162
215371
  var COMMANDS;
214163
215372
  var init_commands2 = __esm(() => {
214164
215373
  init_clipboard();
@@ -214262,13 +215471,13 @@ var init_commands2 = __esm(() => {
214262
215471
  const archivePath = ".december-handoff.tar.gz";
214263
215472
  try {
214264
215473
  const home = process.env.HOME || process.env.USERPROFILE || os17.homedir();
214265
- const canonicalConfigPath = path27.join(home, ".december", "config.json");
214266
- const legacyConfigPath = path27.join(home, ".config", "december", "config.json");
214267
- const configPath = fs29.existsSync(canonicalConfigPath) || !fs29.existsSync(legacyConfigPath) ? canonicalConfigPath : legacyConfigPath;
215474
+ const canonicalConfigPath = path30.join(home, ".december", "config.json");
215475
+ const legacyConfigPath = path30.join(home, ".config", "december", "config.json");
215476
+ const configPath = fs31.existsSync(canonicalConfigPath) || !fs31.existsSync(legacyConfigPath) ? canonicalConfigPath : legacyConfigPath;
214268
215477
  let config = {};
214269
215478
  try {
214270
- if (fs29.existsSync(configPath)) {
214271
- config = JSON.parse(fs29.readFileSync(configPath, "utf8"));
215479
+ if (fs31.existsSync(configPath)) {
215480
+ config = JSON.parse(fs31.readFileSync(configPath, "utf8"));
214272
215481
  }
214273
215482
  } catch {}
214274
215483
  if (!config.decemberToken) {
@@ -214303,7 +215512,7 @@ var init_commands2 = __esm(() => {
214303
215512
  headers: { Authorization: `Bearer ${config.decemberToken}` }
214304
215513
  });
214305
215514
  if (!urlRes.ok) {
214306
- await fs29.promises.unlink(archivePath).catch(() => {});
215515
+ await fs31.promises.unlink(archivePath).catch(() => {});
214307
215516
  if (urlRes.status === 401) {
214308
215517
  ctx.toast.show({
214309
215518
  variant: "error",
@@ -214324,7 +215533,7 @@ var init_commands2 = __esm(() => {
214324
215533
  const urlJson = await urlRes.json();
214325
215534
  const { uploadUrl, objectKey } = urlJson.data || urlJson;
214326
215535
  ctx.toast.show({ variant: "info", message: "Uploading to MinIO..." });
214327
- const fileData = await fs29.promises.readFile(archivePath);
215536
+ const fileData = await fs31.promises.readFile(archivePath);
214328
215537
  const uploadRes = await fetch(uploadUrl, {
214329
215538
  method: "PUT",
214330
215539
  body: fileData
@@ -214348,11 +215557,11 @@ var init_commands2 = __esm(() => {
214348
215557
  const errJson = await sessionRes.json().catch(() => ({}));
214349
215558
  throw new Error(errJson.message || await sessionRes.text());
214350
215559
  }
214351
- await fs29.promises.unlink(archivePath).catch(() => {});
215560
+ await fs31.promises.unlink(archivePath).catch(() => {});
214352
215561
  ctx.toast.show({ variant: "success", message: "Handoff complete! Exiting in 3s." });
214353
215562
  setTimeout(() => ctx.exit(), 3000);
214354
215563
  } catch (e) {
214355
- await fs29.promises.unlink(archivePath).catch(() => {});
215564
+ await fs31.promises.unlink(archivePath).catch(() => {});
214356
215565
  ctx.toast.show({ variant: "error", message: `Handoff failed: ${e.message}` });
214357
215566
  }
214358
215567
  }
@@ -216045,7 +217254,7 @@ var init_scroll_viewport = __esm(async () => {
216045
217254
  });
216046
217255
 
216047
217256
  // ../../packages/tui/src/utils/terminal-link.ts
216048
- import path28 from "node:path";
217257
+ import path31 from "node:path";
216049
217258
  function terminalLink(text, uri) {
216050
217259
  if (!uri)
216051
217260
  return text;
@@ -216054,7 +217263,7 @@ function terminalLink(text, uri) {
216054
217263
  function fileLink(displayPath, targetPath, line, col) {
216055
217264
  if (!targetPath)
216056
217265
  return displayPath;
216057
- const absPath = path28.isAbsolute(targetPath) ? targetPath : path28.resolve(process.cwd(), targetPath);
217266
+ const absPath = path31.isAbsolute(targetPath) ? targetPath : path31.resolve(process.cwd(), targetPath);
216058
217267
  const lineFragment = line ? `#L${line}${col ? `:${col}` : ""}` : "";
216059
217268
  const uri = `file://${absPath}${lineFragment}`;
216060
217269
  return terminalLink(displayPath, uri);
@@ -216063,9 +217272,9 @@ function toRelativePath(filePath, cwd = process.cwd()) {
216063
217272
  if (!filePath)
216064
217273
  return "";
216065
217274
  const trimmed = filePath.trim();
216066
- if (!path28.isAbsolute(trimmed))
217275
+ if (!path31.isAbsolute(trimmed))
216067
217276
  return trimmed;
216068
- const rel = path28.relative(cwd, trimmed);
217277
+ const rel = path31.relative(cwd, trimmed);
216069
217278
  if (!rel || rel.startsWith("..")) {
216070
217279
  return trimmed;
216071
217280
  }
@@ -262433,27 +263642,27 @@ ${r}`;
262433
263642
  });
262434
263643
 
262435
263644
  // ../../packages/tui/src/components/header.tsx
262436
- import fs30 from "node:fs";
263645
+ import fs32 from "node:fs";
262437
263646
  import { homedir } from "node:os";
262438
- import path29 from "node:path";
263647
+ import path32 from "node:path";
262439
263648
  function getGitBranch(workspaceRoot = process.cwd()) {
262440
263649
  if (cachedBranch !== undefined) {
262441
263650
  return cachedBranch;
262442
263651
  }
262443
263652
  try {
262444
- const gitPath = path29.resolve(workspaceRoot, ".git");
262445
- if (fs30.existsSync(gitPath)) {
262446
- const stat = fs30.statSync(gitPath);
262447
- let headPath = path29.resolve(gitPath, "HEAD");
263653
+ const gitPath = path32.resolve(workspaceRoot, ".git");
263654
+ if (fs32.existsSync(gitPath)) {
263655
+ const stat = fs32.statSync(gitPath);
263656
+ let headPath = path32.resolve(gitPath, "HEAD");
262448
263657
  if (stat.isFile()) {
262449
- const content = fs30.readFileSync(gitPath, "utf-8").trim();
263658
+ const content = fs32.readFileSync(gitPath, "utf-8").trim();
262450
263659
  if (content.startsWith("gitdir:")) {
262451
263660
  const gitDir = content.slice(7).trim();
262452
- headPath = path29.resolve(workspaceRoot, gitDir, "HEAD");
263661
+ headPath = path32.resolve(workspaceRoot, gitDir, "HEAD");
262453
263662
  }
262454
263663
  }
262455
- if (fs30.existsSync(headPath)) {
262456
- const headContent = fs30.readFileSync(headPath, "utf-8").trim();
263664
+ if (fs32.existsSync(headPath)) {
263665
+ const headContent = fs32.readFileSync(headPath, "utf-8").trim();
262457
263666
  if (headContent.startsWith("ref: refs/heads/")) {
262458
263667
  cachedBranch = headContent.replace("ref: refs/heads/", "").trim();
262459
263668
  return cachedBranch;
@@ -262727,7 +263936,7 @@ var init_message_list = __esm(async () => {
262727
263936
  flexDirection: "column",
262728
263937
  width: "100%",
262729
263938
  children: [
262730
- staticMessages.length > 0 && /* @__PURE__ */ jsx_runtime20.jsx(Static13, {
263939
+ staticMessages.length > 0 && /* @__PURE__ */ jsx_runtime20.jsx(Static15, {
262731
263940
  items: staticMessages,
262732
263941
  style: { width: "100%" },
262733
263942
  children: (msg, index) => renderSingleMessage(msg, index, staticMessages, cliVersion, latestVersion, userEmail, expandCommands, announcement)
@@ -267816,17 +269025,17 @@ var init_global_shortcuts = __esm(async () => {
267816
269025
 
267817
269026
  // ../../packages/tui/src/utils/pager.ts
267818
269027
  import * as cp3 from "node:child_process";
267819
- import fs31 from "node:fs";
269028
+ import fs33 from "node:fs";
267820
269029
  import os18 from "node:os";
267821
- import path30 from "node:path";
269030
+ import path33 from "node:path";
267822
269031
  function openPlanInPager(planText, title = "december-plan") {
267823
269032
  if (Boolean(process.env.BUN_TEST) || Boolean(process.env.VITEST)) {
267824
269033
  return Promise.resolve();
267825
269034
  }
267826
269035
  return new Promise((resolve) => {
267827
269036
  try {
267828
- const tmpFile = path30.join(os18.tmpdir(), `${title}-${Date.now()}.md`);
267829
- fs31.writeFileSync(tmpFile, planText, "utf8");
269037
+ const tmpFile = path33.join(os18.tmpdir(), `${title}-${Date.now()}.md`);
269038
+ fs33.writeFileSync(tmpFile, planText, "utf8");
267830
269039
  const pager = process.env.PAGER || process.env.EDITOR || (os18.platform() === "win32" ? "notepad" : "less -R");
267831
269040
  const child = cp3.spawn(pager, [tmpFile], {
267832
269041
  stdio: "inherit",
@@ -267834,7 +269043,7 @@ function openPlanInPager(planText, title = "december-plan") {
267834
269043
  });
267835
269044
  child.on("exit", () => {
267836
269045
  try {
267837
- fs31.unlinkSync(tmpFile);
269046
+ fs33.unlinkSync(tmpFile);
267838
269047
  } catch {}
267839
269048
  resolve();
267840
269049
  });
@@ -268404,8 +269613,8 @@ var init_store = __esm(() => {
268404
269613
 
268405
269614
  // src/store/interceptors.ts
268406
269615
  import { execSync as execSync2 } from "node:child_process";
268407
- import fs32 from "node:fs/promises";
268408
- import path31 from "node:path";
269616
+ import fs34 from "node:fs/promises";
269617
+ import path34 from "node:path";
268409
269618
  async function computeToolCallDiff(toolCall) {
268410
269619
  if (toolCall.input?.diff) {
268411
269620
  return toolCall.input.diff;
@@ -268414,11 +269623,11 @@ async function computeToolCallDiff(toolCall) {
268414
269623
  if (!targetPath)
268415
269624
  return;
268416
269625
  const cwd = process.cwd();
268417
- const resolved = path31.resolve(cwd, targetPath);
268418
- const relative = path31.relative(cwd, resolved);
269626
+ const resolved = path34.resolve(cwd, targetPath);
269627
+ const relative = path34.relative(cwd, resolved);
268419
269628
  let oldContent = "";
268420
269629
  try {
268421
- oldContent = await fs32.readFile(resolved, "utf-8");
269630
+ oldContent = await fs34.readFile(resolved, "utf-8");
268422
269631
  } catch {}
268423
269632
  if (toolCall.name === "write_to_file" || toolCall.name === "write") {
268424
269633
  const newContent = toolCall.input?.CodeContent ?? toolCall.input?.content ?? "";
@@ -268482,9 +269691,9 @@ function setupAgentInterceptors(agent, storeState) {
268482
269691
  ];
268483
269692
  if (fileTools.includes(toolCall.name)) {
268484
269693
  const cwd = process.cwd();
268485
- const resolved = path31.resolve(cwd, targetPath);
268486
- const relative = path31.relative(cwd, resolved);
268487
- const isOutside = relative.startsWith("..") || path31.isAbsolute(relative);
269694
+ const resolved = path34.resolve(cwd, targetPath);
269695
+ const relative = path34.relative(cwd, resolved);
269696
+ const isOutside = relative.startsWith("..") || path34.isAbsolute(relative);
268488
269697
  if (!config.nonWorkspaceAccess && isOutside) {
268489
269698
  return {
268490
269699
  block: true,
@@ -268548,8 +269757,8 @@ function setupAgentInterceptors(agent, storeState) {
268548
269757
  const targetPath = toolCall.input?.TargetFile || toolCall.input?.AbsolutePath || toolCall.input?.filePath || toolCall.input?.path;
268549
269758
  if (targetPath) {
268550
269759
  const cwd = process.cwd();
268551
- const resolved = path31.resolve(cwd, targetPath);
268552
- const relative = path31.relative(cwd, resolved);
269760
+ const resolved = path34.resolve(cwd, targetPath);
269761
+ const relative = path34.relative(cwd, resolved);
268553
269762
  try {
268554
269763
  execSync2(`git ls-files --error-unmatch "${relative}"`, {
268555
269764
  cwd,
@@ -268576,7 +269785,7 @@ var init_interceptors = __esm(() => {
268576
269785
  });
268577
269786
 
268578
269787
  // src/utils/direct-shell.ts
268579
- import { spawn as spawn5 } from "node:child_process";
269788
+ import { spawn as spawn6 } from "node:child_process";
268580
269789
  function startDirectCommand(command, options = {}) {
268581
269790
  const { timeoutMs = 20000, serverReadyDelayMs = 2500, onData, onBackground } = options;
268582
269791
  let output = "";
@@ -268586,7 +269795,7 @@ function startDirectCommand(command, options = {}) {
268586
269795
  const isWindows = process.platform === "win32";
268587
269796
  const shellCmd = isWindows ? "cmd.exe" : "/bin/bash";
268588
269797
  const shellArgs = isWindows ? ["/d", "/s", "/c", command] : ["-c", command];
268589
- const child = spawn5(shellCmd, shellArgs, {
269798
+ const child = spawn6(shellCmd, shellArgs, {
268590
269799
  cwd: process.cwd(),
268591
269800
  env: process.env,
268592
269801
  stdio: ["pipe", "pipe", "pipe"],
@@ -268738,15 +269947,15 @@ var init_direct_shell = __esm(() => {
268738
269947
  });
268739
269948
 
268740
269949
  // src/utils/usage-tracker.ts
268741
- import fs33 from "node:fs/promises";
268742
- import path32 from "node:path";
269950
+ import fs35 from "node:fs/promises";
269951
+ import path35 from "node:path";
268743
269952
  function getUsageFilePath() {
268744
- return path32.join(getConfigDir(), "usage.json");
269953
+ return path35.join(getConfigDir(), "usage.json");
268745
269954
  }
268746
269955
  async function loadUsageLedger() {
268747
269956
  const filePath = getUsageFilePath();
268748
269957
  try {
268749
- const raw = await fs33.readFile(filePath, "utf-8");
269958
+ const raw = await fs35.readFile(filePath, "utf-8");
268750
269959
  const parsed = JSON.parse(raw);
268751
269960
  if (parsed && typeof parsed === "object" && parsed.version === 1 && parsed.days) {
268752
269961
  return parsed;
@@ -268759,8 +269968,8 @@ async function loadUsageLedger() {
268759
269968
  }
268760
269969
  async function saveUsageLedger(ledger) {
268761
269970
  const configDir = getConfigDir();
268762
- const filePath = path32.join(configDir, "usage.json");
268763
- const tmpPath = path32.join(configDir, `usage.json.tmp.${Date.now()}`);
269971
+ const filePath = path35.join(configDir, "usage.json");
269972
+ const tmpPath = path35.join(configDir, `usage.json.tmp.${Date.now()}`);
268764
269973
  const cutoffDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
268765
269974
  const prunedDays = {};
268766
269975
  for (const [dateKey, dayRecord] of Object.entries(ledger.days)) {
@@ -268770,12 +269979,12 @@ async function saveUsageLedger(ledger) {
268770
269979
  }
268771
269980
  ledger.days = prunedDays;
268772
269981
  try {
268773
- await fs33.mkdir(configDir, { recursive: true });
268774
- await fs33.writeFile(tmpPath, JSON.stringify(ledger, null, 2), "utf-8");
268775
- await fs33.rename(tmpPath, filePath);
269982
+ await fs35.mkdir(configDir, { recursive: true });
269983
+ await fs35.writeFile(tmpPath, JSON.stringify(ledger, null, 2), "utf-8");
269984
+ await fs35.rename(tmpPath, filePath);
268776
269985
  } catch {
268777
269986
  try {
268778
- await fs33.unlink(tmpPath);
269987
+ await fs35.unlink(tmpPath);
268779
269988
  } catch {}
268780
269989
  }
268781
269990
  }
@@ -268973,7 +270182,7 @@ var init_usage_rates2 = __esm(() => {
268973
270182
  // src/utils/error-parser.ts
268974
270183
  import util5 from "util";
268975
270184
  function isCreditOrBalanceError(combined) {
268976
- return combined.includes("insufficient balance") || combined.includes("insufficient credits") || combined.includes("insufficient_balance") || combined.includes("insufficient_credits") || combined.includes("insufficient_quota") || combined.includes("balance is insufficient") || combined.includes("balance insufficient") || combined.includes("balance exhausted") || combined.includes("out of credits") || combined.includes("credit balance is too low") || combined.includes("credits exhausted") || combined.includes("requires more credits") || combined.includes("can only afford") || combined.includes("payment required") || combined.includes("quota exceeded") || combined.includes("quota_exhausted") || combined.includes("402") || combined.includes("1008") || combined.includes("20009") || (combined.includes("credits") || combined.includes("balance")) && (combined.includes("insufficient") || combined.includes("exhausted") || combined.includes("empty") || combined.includes("zero") || combined.includes("low") || combined.includes("top up") || combined.includes("recharge") || combined.includes("afford") || combined.includes("add credits"));
270185
+ return combined.includes("insufficient balance") || combined.includes("insufficient credits") || combined.includes("insufficient_balance") || combined.includes("insufficient_credits") || combined.includes("insufficient_quota") || combined.includes("balance is insufficient") || combined.includes("balance insufficient") || combined.includes("balance exhausted") || combined.includes("out of credits") || combined.includes("credit balance is too low") || combined.includes("credits exhausted") || combined.includes("requires more credits") || combined.includes("can only afford") || combined.includes("payment required") || combined.includes("quota exceeded") || combined.includes("quota_exhausted") || combined.includes("budget_exhausted") || combined.includes("budget exhausted") || combined.includes("budget exceeded") || combined.includes("budget_exceeded") || combined.includes("402") || combined.includes("1008") || combined.includes("20009") || (combined.includes("credits") || combined.includes("balance")) && (combined.includes("insufficient") || combined.includes("exhausted") || combined.includes("empty") || combined.includes("zero") || combined.includes("low") || combined.includes("top up") || combined.includes("recharge") || combined.includes("afford") || combined.includes("add credits"));
268977
270186
  }
268978
270187
  function normalizeProviderAlias(provider) {
268979
270188
  if (!provider)
@@ -269357,7 +270566,8 @@ function parseErrorMessage(err, context) {
269357
270566
  }
269358
270567
  const contextStr = `${context?.provider || ""} ${context?.model || ""}`.toLowerCase().trim();
269359
270568
  const lowerStr = (errMsg + " " + (finalResult || "") + (contextStr ? " " + contextStr : "")).toLowerCase();
269360
- const isRateLimit = lowerStr.includes("429") || lowerStr.includes("quota") || lowerStr.includes("rate limit") || lowerStr.includes("rate_limit") || lowerStr.includes("resource_exhausted") || lowerStr.includes("generativelanguage.googleapis.com");
270569
+ const isBudgetExhausted = lowerStr.includes("budget_exhausted") || lowerStr.includes("budget exhausted") || lowerStr.includes("budget_exceeded") || lowerStr.includes("budget exceeded");
270570
+ const isRateLimit = !isBudgetExhausted && (lowerStr.includes("429") || lowerStr.includes("quota") || lowerStr.includes("rate limit") || lowerStr.includes("rate_limit") || lowerStr.includes("resource_exhausted") || lowerStr.includes("generativelanguage.googleapis.com"));
269361
270571
  if (isRateLimit && !finalResult.includes("Rate limit or quota exhausted")) {
269362
270572
  const rateDetails = resolveRateLimitDetails(lowerStr, context);
269363
270573
  return rateDetails.notice + finalResult;
@@ -269398,7 +270608,8 @@ function parseError(err, context) {
269398
270608
  const contextStr = `${context?.provider || ""} ${context?.model || ""}`.toLowerCase().trim();
269399
270609
  const fullMessage = parseErrorMessage(err, context);
269400
270610
  const combined = `${fullMessage} ${explicitCause || ""} ${contextStr ? " " + contextStr : ""}`.toLowerCase();
269401
- const isRateLimit = combined.includes("429") || combined.includes("quota") || combined.includes("rate limit") || combined.includes("rate_limit") || combined.includes("resource_exhausted") || combined.includes("generativelanguage.googleapis.com");
270611
+ const isBudgetExhausted = combined.includes("budget_exhausted") || combined.includes("budget exhausted") || combined.includes("budget_exceeded") || combined.includes("budget exceeded");
270612
+ const isRateLimit = !isBudgetExhausted && (combined.includes("429") || combined.includes("quota") || combined.includes("rate limit") || combined.includes("rate_limit") || combined.includes("resource_exhausted") || combined.includes("generativelanguage.googleapis.com"));
269402
270613
  if (isRateLimit) {
269403
270614
  const parts = fullMessage.split(`
269404
270615
  `);
@@ -270040,7 +271251,7 @@ var init_error_parser = __esm(() => {
270040
271251
  {
270041
271252
  id: "auriko",
270042
271253
  name: "Auriko",
270043
- match: (str) => (str.includes("auriko.ai") || str.includes("api.auriko.ai") || str.includes("auriko")) && (isCreditOrBalanceError(str) || str.includes("credits") || str.includes("402")),
271254
+ match: (str) => (str.includes("auriko.ai") || str.includes("api.auriko.ai") || str.includes("auriko")) && (isCreditOrBalanceError(str) || str.includes("credits") || str.includes("budget") || str.includes("402")),
270044
271255
  notice: `Insufficient credits in your Auriko account. Please check your account at https://www.auriko.ai/dashboard
270045
271256
  `,
270046
271257
  message: "Insufficient credits in your Auriko account.",
@@ -270210,14 +271421,14 @@ function extractJsonArray(rawText) {
270210
271421
  }
270211
271422
 
270212
271423
  // src/utils/project-context.ts
270213
- import fs34 from "node:fs";
270214
- import path33 from "node:path";
271424
+ import fs36 from "node:fs";
271425
+ import path36 from "node:path";
270215
271426
  function getProjectContext(workspaceDir = process.cwd()) {
270216
271427
  const parts = [];
270217
271428
  try {
270218
- const pkgPath = path33.join(workspaceDir, "package.json");
270219
- if (fs34.existsSync(pkgPath)) {
270220
- const pkg = JSON.parse(fs34.readFileSync(pkgPath, "utf8"));
271429
+ const pkgPath = path36.join(workspaceDir, "package.json");
271430
+ if (fs36.existsSync(pkgPath)) {
271431
+ const pkg = JSON.parse(fs36.readFileSync(pkgPath, "utf8"));
270221
271432
  const deps = Object.keys(pkg.dependencies || {}).slice(0, 15).join(", ");
270222
271433
  const devDeps = Object.keys(pkg.devDependencies || {}).slice(0, 10).join(", ");
270223
271434
  parts.push(`Project: ${pkg.name || "Unnamed"}`);
@@ -270226,13 +271437,13 @@ function getProjectContext(workspaceDir = process.cwd()) {
270226
271437
  if (devDeps)
270227
271438
  parts.push(`DevDependencies: ${devDeps}`);
270228
271439
  }
270229
- const agentsPath = path33.join(workspaceDir, "AGENTS.md");
270230
- if (fs34.existsSync(agentsPath)) {
270231
- const content = fs34.readFileSync(agentsPath, "utf8");
271440
+ const agentsPath = path36.join(workspaceDir, "AGENTS.md");
271441
+ if (fs36.existsSync(agentsPath)) {
271442
+ const content = fs36.readFileSync(agentsPath, "utf8");
270232
271443
  parts.push(`Guidelines (from AGENTS.md):
270233
271444
  ${content.slice(0, 500)}`);
270234
271445
  }
270235
- const entries = fs34.readdirSync(workspaceDir, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.isDirectory() ? `${e.name}/` : e.name).slice(0, 20);
271446
+ const entries = fs36.readdirSync(workspaceDir, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.isDirectory() ? `${e.name}/` : e.name).slice(0, 20);
270236
271447
  if (entries.length > 0) {
270237
271448
  parts.push(`Workspace layout: ${entries.join(", ")}`);
270238
271449
  }
@@ -270387,14 +271598,14 @@ async function fetchProviderBalance(provider, apiKey, timeoutMs = 2000) {
270387
271598
  }
270388
271599
 
270389
271600
  // src/utils/formatters.ts
270390
- import path34 from "node:path";
271601
+ import path37 from "node:path";
270391
271602
  function toRelativePath2(filePath, cwd = process.cwd()) {
270392
271603
  if (!filePath)
270393
271604
  return "";
270394
271605
  const trimmed = filePath.trim();
270395
- if (!path34.isAbsolute(trimmed))
271606
+ if (!path37.isAbsolute(trimmed))
270396
271607
  return trimmed;
270397
- const rel = path34.relative(cwd, trimmed);
271608
+ const rel = path37.relative(cwd, trimmed);
270398
271609
  if (!rel || rel.startsWith("..")) {
270399
271610
  return trimmed;
270400
271611
  }
@@ -271134,7 +272345,7 @@ ${formatSubscriptionCard(bundle, targetModel)}`
271134
272345
  const probeAbortController = new AbortController;
271135
272346
  const probeTimeout = setTimeout(() => {
271136
272347
  probeAbortController.abort(new Error("Connection timed out while verifying API key."));
271137
- }, 1e4);
272348
+ }, 8000);
271138
272349
  try {
271139
272350
  const stream = testProvider.stream([{ role: "user", content: "Hi" }], undefined, undefined, {
271140
272351
  model: testModel,
@@ -271151,15 +272362,19 @@ ${formatSubscriptionCard(bundle, targetModel)}`
271151
272362
  }
271152
272363
  } catch (probeErr) {
271153
272364
  lastProbeErr = probeErr;
271154
- const errText = (probeErr?.message || String(probeErr)).toLowerCase();
272365
+ const errText = `${probeErr?.message || ""} ${probeErr?.code || ""} ${probeErr?.error?.code || ""} ${String(probeErr)}`.toLowerCase();
271155
272366
  if (errText.includes("401") || errText.includes("unauthorized") || errText.includes("invalid api key") || errText.includes("incorrect api key")) {
271156
272367
  throw probeErr;
271157
272368
  }
271158
- if (errText.includes("402") || errText.includes("credits") || errText.includes("payment") || errText.includes("afford") || errText.includes("insufficient_quota")) {
272369
+ if (errText.includes("402") || errText.includes("credits") || errText.includes("payment") || errText.includes("afford") || errText.includes("budget") || errText.includes("balance") || errText.includes("insufficient_quota")) {
271159
272370
  if (!lowCreditErr) {
271160
272371
  lowCreditErr = probeErr;
271161
272372
  lowCreditModel = testModel;
271162
272373
  }
272374
+ break;
272375
+ }
272376
+ if (errText.includes("timed out") || errText.includes("timeout") || probeErr?.name === "AbortError") {
272377
+ break;
271163
272378
  }
271164
272379
  }
271165
272380
  }
@@ -271199,10 +272414,10 @@ ${formatSubscriptionCard(bundle, targetModel)}`
271199
272414
  setApiKey("");
271200
272415
  addToast(MESSAGES.AUTH.API_KEY_SAVED(selectedProvider), "success");
271201
272416
  } catch (err) {
271202
- const errStr = (err?.message || JSON.stringify(err) || String(err)).toLowerCase();
272417
+ const errStr = `${err?.message || ""} ${err?.code || ""} ${err?.error?.code || ""} ${String(err)}`.toLowerCase();
271203
272418
  const isTimeout = err?.name === "AbortError" || errStr.includes("aborted") || errStr.includes("timed out") || errStr.includes("timeout");
271204
- const isLowCredit = errStr.includes("402") || errStr.includes("credits") || errStr.includes("payment") || errStr.includes("afford");
271205
- if (errStr.includes("429") || errStr.includes("quota") || errStr.includes("rate limit") || errStr.includes("404") || errStr.includes("not found") || errStr.includes("503") || errStr.includes("无可用渠道") || errStr.includes("no available channel") || isLowCredit) {
272419
+ const isLowCredit = errStr.includes("402") || errStr.includes("credits") || errStr.includes("payment") || errStr.includes("afford") || errStr.includes("budget") || errStr.includes("balance") || errStr.includes("insufficient_quota");
272420
+ if (errStr.includes("429") || errStr.includes("quota") || errStr.includes("rate limit") || errStr.includes("404") || errStr.includes("not found") || errStr.includes("503") || errStr.includes("无可用渠道") || errStr.includes("no available channel") || errStr.includes("platform_keys_unavailable") || errStr.includes("byok_keys_required") || errStr.includes("tier_opt_in_required") || errStr.includes("no_provider_available") || isLowCredit) {
271206
272421
  await Promise.resolve().then(() => init_models5());
271207
272422
  const finalModel = testModel || getDefaultModelForProvider(selectedProvider);
271208
272423
  const config = await loadConfig();
@@ -273793,7 +275008,7 @@ var init_use_agent_session = __esm(async () => {
273793
275008
 
273794
275009
  // src/index.ts
273795
275010
  init_package();
273796
- import path35 from "node:path";
275011
+ import path38 from "node:path";
273797
275012
 
273798
275013
  // src/args.ts
273799
275014
  import { parseArgs } from "node:util";
@@ -273931,12 +275146,12 @@ Options:
273931
275146
  }
273932
275147
  // src/constants/release-highlights.json
273933
275148
  var release_highlights_default = {
273934
- version: "0.4.3",
273935
- title: "What's New in v0.4.3",
275149
+ version: "0.4.4",
275150
+ title: "What's New in v0.4.4",
273936
275151
  bullets: [
273937
- "Remove search mode and add prompt focus text animation",
273938
- "*(tui)* Eagerly update scroll state ref to support rapid mouse wheel scroll events",
273939
- "*(tui)* Prevent layout overflow and ghosting on terminal resize and small heights"
275152
+ "*(agent)* Passive workspace memory ingestion and manage_memory tool",
275153
+ "*(agent)* Lsp client integration for definition and reference navigation",
275154
+ "*(tools)* Prefer non-import lines when resolving symbol definition in code_intelligence"
273940
275155
  ],
273941
275156
  url: "https://trydecember.com/docs/changelog"
273942
275157
  };
@@ -274137,7 +275352,7 @@ To define project-specific instructions, create an AGENTS.md file in your projec
274137
275352
  sessionRepository,
274138
275353
  sessionId,
274139
275354
  runtime: "cli",
274140
- workspaceDir: parsedArgs.scope ? path35.resolve(process.cwd(), parsedArgs.scope) : process.cwd(),
275355
+ workspaceDir: parsedArgs.scope ? path38.resolve(process.cwd(), parsedArgs.scope) : process.cwd(),
274141
275356
  thinkingLevel: config.thinkingLevel || "auto",
274142
275357
  steeringMode: config.steeringMode || "all",
274143
275358
  followUpMode: config.followUpMode || "all"
@@ -274241,7 +275456,7 @@ Executing Headless Task: "${parsedArgs.prompt}"
274241
275456
  sessionRepository,
274242
275457
  sessionId,
274243
275458
  runtime: "cli",
274244
- workspaceDir: parsedArgs.scope ? path35.resolve(process.cwd(), parsedArgs.scope) : process.cwd(),
275459
+ workspaceDir: parsedArgs.scope ? path38.resolve(process.cwd(), parsedArgs.scope) : process.cwd(),
274245
275460
  thinkingLevel: config.thinkingLevel || "auto",
274246
275461
  steeringMode: config.steeringMode || "all",
274247
275462
  followUpMode: config.followUpMode || "all"