@creative-dswork/dscode 0.2.5 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/dscode.mjs CHANGED
@@ -115,6 +115,33 @@ var init_qwen = __esm({
115
115
  }
116
116
  });
117
117
 
118
+ // src/models/kimi.ts
119
+ var KIMI_BASE_URL, KIMI_MODELS;
120
+ var init_kimi = __esm({
121
+ "src/models/kimi.ts"() {
122
+ "use strict";
123
+ KIMI_BASE_URL = "https://api.moonshot.cn/v1";
124
+ KIMI_MODELS = {
125
+ "kimi-k3": {
126
+ api: "openai-completions",
127
+ provider: "kimi",
128
+ baseUrl: KIMI_BASE_URL,
129
+ reasoning: true,
130
+ thinkingLevelMap: { low: null, medium: null, high: null, max: "max" },
131
+ input: ["text", "image"],
132
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
133
+ contextWindow: 1048576,
134
+ maxTokens: 131072,
135
+ compat: {
136
+ supportsDeveloperRole: false,
137
+ thinkingFormat: "deepseek",
138
+ deferredToolsMode: "kimi"
139
+ }
140
+ }
141
+ };
142
+ }
143
+ });
144
+
118
145
  // src/models/registry.ts
119
146
  import { builtinModels } from "@earendil-works/pi-ai/providers/all";
120
147
  import { createProvider, lazyApi, envApiKeyAuth } from "@earendil-works/pi-ai";
@@ -155,8 +182,13 @@ function resolveModel(provider, modelId) {
155
182
  function getThinkingLevel(provider, modelId) {
156
183
  try {
157
184
  const model = resolveModel(provider, modelId);
158
- if (model.reasoning) return "high";
159
- return "off";
185
+ if (!model.reasoning) return "off";
186
+ if (model.thinkingLevelMap) {
187
+ const supportedLevels = Object.entries(model.thinkingLevelMap).filter(([, v]) => v !== null);
188
+ if (supportedLevels.length === 0) return "off";
189
+ if (supportedLevels.length === 1 && supportedLevels[0][0] === "max") return "max";
190
+ }
191
+ return "high";
160
192
  } catch {
161
193
  }
162
194
  if (modelId.includes("thinking")) return "high";
@@ -179,11 +211,12 @@ function getEnvApiKey(provider) {
179
211
  }
180
212
  return void 0;
181
213
  }
182
- var providerFactories, customModelDefs, models, qwenProvider, API_KEY_ENV_VARS;
214
+ var providerFactories, customModelDefs, models, qwenProvider, kimiProvider, API_KEY_ENV_VARS;
183
215
  var init_registry = __esm({
184
216
  "src/models/registry.ts"() {
185
217
  "use strict";
186
218
  init_qwen();
219
+ init_kimi();
187
220
  providerFactories = /* @__PURE__ */ new Map();
188
221
  customModelDefs = /* @__PURE__ */ new Map();
189
222
  models = builtinModels();
@@ -195,6 +228,15 @@ var init_registry = __esm({
195
228
  models: Object.entries(QWEN_MODELS).map(([id, def]) => ({ id, name: `Qwen: ${id}`, ...def })),
196
229
  api: lazyApi(() => import("@earendil-works/pi-ai/api/openai-completions"))
197
230
  });
231
+ kimiProvider = createProvider({
232
+ id: "kimi",
233
+ name: "Kimi (Moonshot)",
234
+ baseUrl: KIMI_BASE_URL,
235
+ auth: { apiKey: envApiKeyAuth("Kimi API key", ["KIMI_API_KEY", "MOONSHOT_API_KEY"]) },
236
+ models: Object.entries(KIMI_MODELS).map(([id, def]) => ({ id, name: `Kimi ${id}`, ...def })),
237
+ api: lazyApi(() => import("@earendil-works/pi-ai/api/openai-completions"))
238
+ });
239
+ models.setProvider(kimiProvider);
198
240
  models.setProvider(qwenProvider);
199
241
  API_KEY_ENV_VARS = {
200
242
  deepseek: "DEEPSEEK_API_KEY",
@@ -207,6 +249,7 @@ var init_registry = __esm({
207
249
  openrouter: "OPENROUTER_API_KEY",
208
250
  "vercel-ai-gateway": "AI_GATEWAY_API_KEY",
209
251
  zai: "ZAI_API_KEY",
252
+ "kimi": "KIMI_API_KEY",
210
253
  "zai-coding-cn": "ZAI_CODING_CN_API_KEY",
211
254
  mistral: "MISTRAL_API_KEY",
212
255
  minimax: "MINIMAX_API_KEY",
@@ -453,7 +496,7 @@ function loadConfig(cliCwd) {
453
496
  const userCommandsDir = join(configDir, "commands");
454
497
  const projectCommandsDir = join(projectPath, ".dscode", "commands");
455
498
  const defaultThinkingLevel = getThinkingLevel(provider, modelId);
456
- const validThinkingLevels = /* @__PURE__ */ new Set(["off", "minimal", "low", "medium", "high", "xhigh"]);
499
+ const validThinkingLevels = /* @__PURE__ */ new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
457
500
  const rawThinkingLevel = process.env.AGENT_THINKING_LEVEL ?? userConfig.thinkingLevel ?? merged.thinkingLevel;
458
501
  const thinkingLevel = rawThinkingLevel !== void 0 && validThinkingLevels.has(rawThinkingLevel) ? rawThinkingLevel : defaultThinkingLevel;
459
502
  const retryFromEnv = {
@@ -531,6 +574,7 @@ var init_config = __esm({
531
574
  PROVIDER_ENV_VARS = {
532
575
  deepseek: "DEEPSEEK_API_KEY",
533
576
  "kimi-coding": "KIMI_API_KEY",
577
+ "kimi": "KIMI_API_KEY",
534
578
  openai: "OPENAI_API_KEY",
535
579
  anthropic: "ANTHROPIC_API_KEY",
536
580
  google: "GEMINI_API_KEY",
@@ -538,7 +582,8 @@ var init_config = __esm({
538
582
  groq: "GROQ_API_KEY",
539
583
  openrouter: "OPENROUTER_API_KEY",
540
584
  mistral: "MISTRAL_API_KEY",
541
- qwen: "DASHSCOPE_API_KEY"
585
+ qwen: "DASHSCOPE_API_KEY",
586
+ "moonshotai-cn": "MOONSHOT_API_KEY"
542
587
  };
543
588
  }
544
589
  });
@@ -547,17 +592,17 @@ var init_config = __esm({
547
592
  import { appendFileSync, mkdirSync as mkdirSync2, existsSync as existsSync2, writeFileSync as writeFileSync2 } from "node:fs";
548
593
  import { join as join2 } from "node:path";
549
594
  import { homedir as homedir2 } from "node:os";
550
- function formatLine(level, channel, agentType, agentId, tag, message) {
595
+ function formatLine(level, agentType, agentId, tag, message) {
551
596
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
552
- return `[${ts}] [${level.toUpperCase()}] [${channel}] [${agentType}/${agentId}] [${tag}] ${message}`;
597
+ return `[${ts}] [${level.toUpperCase()}] [${agentType}/${agentId}] [${tag}] ${message}`;
553
598
  }
554
599
  function ensureDir() {
555
600
  if (!existsSync2(LOG_DIR)) {
556
601
  mkdirSync2(LOG_DIR, { recursive: true });
557
602
  }
558
603
  }
559
- function channelPath(channel) {
560
- return join2(LOG_DIR, `${channel}.log`);
604
+ function filePath() {
605
+ return join2(LOG_DIR, "dscode.log");
561
606
  }
562
607
  var LEVEL_RANK, Logger, LOG_DIR;
563
608
  var init_logger = __esm({
@@ -579,32 +624,32 @@ var init_logger = __esm({
579
624
  this.minLevel = LEVEL_RANK[options.level ?? "debug"];
580
625
  }
581
626
  // ── Public API ──
582
- debug(channel, tag, message) {
583
- this.write("debug", channel, tag, message);
627
+ debug(tag, message) {
628
+ this.write("debug", tag, message);
584
629
  }
585
- info(channel, tag, message) {
586
- this.write("info", channel, tag, message);
630
+ info(tag, message) {
631
+ this.write("info", tag, message);
587
632
  }
588
- warn(channel, tag, message) {
589
- this.write("warn", channel, tag, message);
633
+ warn(tag, message) {
634
+ this.write("warn", tag, message);
590
635
  }
591
- error(channel, tag, message) {
592
- this.write("error", channel, tag, message);
636
+ error(tag, message) {
637
+ this.write("error", tag, message);
593
638
  }
594
- clear(channel) {
639
+ clear() {
595
640
  try {
596
641
  ensureDir();
597
- writeFileSync2(channelPath(channel), "", "utf-8");
642
+ writeFileSync2(filePath(), "", "utf-8");
598
643
  } catch {
599
644
  }
600
645
  }
601
646
  // ── Internal ──
602
- write(level, channel, tag, message) {
647
+ write(level, tag, message) {
603
648
  if (LEVEL_RANK[level] < this.minLevel) return;
604
- const line = formatLine(level, channel, this.type, this.id, tag, message);
649
+ const line = formatLine(level, this.type, this.id, tag, message);
605
650
  try {
606
651
  ensureDir();
607
- appendFileSync(channelPath(channel), line + "\n", "utf8");
652
+ appendFileSync(filePath(), line + "\n", "utf8");
608
653
  } catch {
609
654
  }
610
655
  }
@@ -626,7 +671,7 @@ async function getSharp() {
626
671
  return sharpInstance;
627
672
  } catch {
628
673
  sharpAvailable = false;
629
- _cacheLogger.warn("tool", "ImageCache", "sharp not available, images will be stored uncompressed");
674
+ _cacheLogger.warn("ImageCache", "sharp not available, images will be stored uncompressed");
630
675
  return null;
631
676
  }
632
677
  }
@@ -685,7 +730,7 @@ var init_cache = __esm({
685
730
  await pipeline.png().toFile(filepath);
686
731
  return { type: "image_ref", hash: filename, mimeType: "image/png" };
687
732
  } catch (err) {
688
- _cacheLogger.warn("tool", "ImageCache", `sharp processing failed, storing original: ${String(err)}`);
733
+ _cacheLogger.warn("ImageCache", `sharp processing failed, storing original: ${String(err)}`);
689
734
  }
690
735
  }
691
736
  if (!existsSync3(filepath)) {
@@ -830,20 +875,20 @@ var init_store = __esm({
830
875
  const projPath = join4(this.projectDir, `${id}.json`);
831
876
  const globalPath = join4(this.globalDir, `${id}.json`);
832
877
  let raw;
833
- let filePath;
878
+ let filePath2;
834
879
  if (existsSync4(projPath)) {
835
- filePath = projPath;
880
+ filePath2 = projPath;
836
881
  } else if (existsSync4(globalPath)) {
837
- filePath = globalPath;
882
+ filePath2 = globalPath;
838
883
  } else {
839
884
  throw new SessionValidateError(`Session not found: ${id}`);
840
885
  }
841
- const stat4 = existsSync4(filePath);
886
+ const stat4 = existsSync4(filePath2);
842
887
  if (!stat4) {
843
888
  throw new SessionValidateError(`Session not found: ${id}`);
844
889
  }
845
890
  try {
846
- raw = JSON.parse(readFileSync3(filePath, "utf8"));
891
+ raw = JSON.parse(readFileSync3(filePath2, "utf8"));
847
892
  } catch {
848
893
  throw new SessionValidateError("Session file is corrupted: invalid JSON");
849
894
  }
@@ -853,16 +898,16 @@ var init_store = __esm({
853
898
  async loadSessionFile(sessionId) {
854
899
  const projPath = join4(this.projectDir, `${sessionId}.json`);
855
900
  const globalPath = join4(this.globalDir, `${sessionId}.json`);
856
- let filePath;
901
+ let filePath2;
857
902
  if (existsSync4(projPath)) {
858
- filePath = projPath;
903
+ filePath2 = projPath;
859
904
  } else if (existsSync4(globalPath)) {
860
- filePath = globalPath;
905
+ filePath2 = globalPath;
861
906
  } else {
862
907
  return null;
863
908
  }
864
909
  try {
865
- const raw = JSON.parse(readFileSync3(filePath, "utf8"));
910
+ const raw = JSON.parse(readFileSync3(filePath2, "utf8"));
866
911
  return validateSession(raw, sessionId);
867
912
  } catch {
868
913
  return null;
@@ -1228,7 +1273,7 @@ var init_manager = __esm({
1228
1273
  try {
1229
1274
  this.saveSession(agent, pendingPermission);
1230
1275
  } catch (err) {
1231
- this.logger.error("session", "Save", `trySaveSession failed: ${String(err)}`);
1276
+ this.logger.error("Save", `trySaveSession failed: ${String(err)}`);
1232
1277
  }
1233
1278
  }
1234
1279
  async loadSession(id, agent) {
@@ -9124,11 +9169,11 @@ ${lanes.join("\n")}
9124
9169
  const toCanonicalName = createGetCanonicalFileName(useCaseSensitiveFileNames2);
9125
9170
  return nonPollingWatchFile;
9126
9171
  function nonPollingWatchFile(fileName, callback, _pollingInterval, fallbackOptions) {
9127
- const filePath = toCanonicalName(fileName);
9128
- if (fileWatcherCallbacks.add(filePath, callback).length === 1 && fileTimestamps) {
9129
- fileTimestamps.set(filePath, getModifiedTime3(fileName) || missingFileModifiedTime);
9172
+ const filePath2 = toCanonicalName(fileName);
9173
+ if (fileWatcherCallbacks.add(filePath2, callback).length === 1 && fileTimestamps) {
9174
+ fileTimestamps.set(filePath2, getModifiedTime3(fileName) || missingFileModifiedTime);
9130
9175
  }
9131
- const dirPath = getDirectoryPath(filePath) || ".";
9176
+ const dirPath = getDirectoryPath(filePath2) || ".";
9132
9177
  const watcher = dirWatchers.get(dirPath) || createDirectoryWatcher(getDirectoryPath(fileName) || ".", dirPath, fallbackOptions);
9133
9178
  watcher.referenceCount++;
9134
9179
  return {
@@ -9139,7 +9184,7 @@ ${lanes.join("\n")}
9139
9184
  } else {
9140
9185
  watcher.referenceCount--;
9141
9186
  }
9142
- fileWatcherCallbacks.remove(filePath, callback);
9187
+ fileWatcherCallbacks.remove(filePath2, callback);
9143
9188
  }
9144
9189
  };
9145
9190
  }
@@ -9150,19 +9195,19 @@ ${lanes.join("\n")}
9150
9195
  (eventName, relativeFileName) => {
9151
9196
  if (!isString2(relativeFileName)) return;
9152
9197
  const fileName = getNormalizedAbsolutePath(relativeFileName, dirName);
9153
- const filePath = toCanonicalName(fileName);
9154
- const callbacks = fileName && fileWatcherCallbacks.get(filePath);
9198
+ const filePath2 = toCanonicalName(fileName);
9199
+ const callbacks = fileName && fileWatcherCallbacks.get(filePath2);
9155
9200
  if (callbacks) {
9156
9201
  let currentModifiedTime;
9157
9202
  let eventKind = 1;
9158
9203
  if (fileTimestamps) {
9159
- const existingTime = fileTimestamps.get(filePath);
9204
+ const existingTime = fileTimestamps.get(filePath2);
9160
9205
  if (eventName === "change") {
9161
9206
  currentModifiedTime = getModifiedTime3(fileName) || missingFileModifiedTime;
9162
9207
  if (currentModifiedTime.getTime() === existingTime.getTime()) return;
9163
9208
  }
9164
9209
  currentModifiedTime || (currentModifiedTime = getModifiedTime3(fileName) || missingFileModifiedTime);
9165
- fileTimestamps.set(filePath, currentModifiedTime);
9210
+ fileTimestamps.set(filePath2, currentModifiedTime);
9166
9211
  if (existingTime === missingFileModifiedTime) eventKind = 0;
9167
9212
  else if (currentModifiedTime === missingFileModifiedTime) eventKind = 2;
9168
9213
  }
@@ -9981,7 +10026,7 @@ ${lanes.join("\n")}
9981
10026
  return process.memoryUsage().heapUsed;
9982
10027
  },
9983
10028
  getFileSize(path) {
9984
- const stat4 = statSync5(path);
10029
+ const stat4 = statSync6(path);
9985
10030
  if (stat4 == null ? void 0 : stat4.isFile()) {
9986
10031
  return stat4.size;
9987
10032
  }
@@ -10025,7 +10070,7 @@ ${lanes.join("\n")}
10025
10070
  }
10026
10071
  };
10027
10072
  return nodeSystem;
10028
- function statSync5(path) {
10073
+ function statSync6(path) {
10029
10074
  try {
10030
10075
  return _fs.statSync(path, statSyncOptions);
10031
10076
  } catch {
@@ -10084,7 +10129,7 @@ ${lanes.join("\n")}
10084
10129
  activeSession.post("Profiler.stop", (err, { profile }) => {
10085
10130
  var _a;
10086
10131
  if (!err) {
10087
- if ((_a = statSync5(profilePath)) == null ? void 0 : _a.isDirectory()) {
10132
+ if ((_a = statSync6(profilePath)) == null ? void 0 : _a.isDirectory()) {
10088
10133
  profilePath = _path.join(profilePath, `${(/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-")}+P${process.pid}.cpuprofile`);
10089
10134
  }
10090
10135
  try {
@@ -10204,7 +10249,7 @@ ${lanes.join("\n")}
10204
10249
  let stat4;
10205
10250
  if (typeof dirent === "string" || dirent.isSymbolicLink()) {
10206
10251
  const name = combinePaths(path, entry);
10207
- stat4 = statSync5(name);
10252
+ stat4 = statSync6(name);
10208
10253
  if (!stat4) {
10209
10254
  continue;
10210
10255
  }
@@ -10228,7 +10273,7 @@ ${lanes.join("\n")}
10228
10273
  return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames2, process.cwd(), depth, getAccessibleFileSystemEntries, realpath);
10229
10274
  }
10230
10275
  function fileSystemEntryExists(path, entryKind) {
10231
- const stat4 = statSync5(path);
10276
+ const stat4 = statSync6(path);
10232
10277
  if (!stat4) {
10233
10278
  return false;
10234
10279
  }
@@ -10270,7 +10315,7 @@ ${lanes.join("\n")}
10270
10315
  }
10271
10316
  function getModifiedTime3(path) {
10272
10317
  var _a;
10273
- return (_a = statSync5(path)) == null ? void 0 : _a.mtime;
10318
+ return (_a = statSync6(path)) == null ? void 0 : _a.mtime;
10274
10319
  }
10275
10320
  function setModifiedTime(path, time) {
10276
10321
  try {
@@ -16645,20 +16690,20 @@ ${lanes.join("\n")}
16645
16690
  function trySetLanguageAndTerritory(language2, territory2, errors2) {
16646
16691
  const compilerFilePath = normalizePath2(sys2.getExecutingFilePath());
16647
16692
  const containingDirectoryPath = getDirectoryPath(compilerFilePath);
16648
- let filePath = combinePaths(containingDirectoryPath, language2);
16693
+ let filePath2 = combinePaths(containingDirectoryPath, language2);
16649
16694
  if (territory2) {
16650
- filePath = filePath + "-" + territory2;
16695
+ filePath2 = filePath2 + "-" + territory2;
16651
16696
  }
16652
- filePath = sys2.resolvePath(combinePaths(filePath, "diagnosticMessages.generated.json"));
16653
- if (!sys2.fileExists(filePath)) {
16697
+ filePath2 = sys2.resolvePath(combinePaths(filePath2, "diagnosticMessages.generated.json"));
16698
+ if (!sys2.fileExists(filePath2)) {
16654
16699
  return false;
16655
16700
  }
16656
16701
  let fileContents = "";
16657
16702
  try {
16658
- fileContents = sys2.readFile(filePath);
16703
+ fileContents = sys2.readFile(filePath2);
16659
16704
  } catch {
16660
16705
  if (errors2) {
16661
- errors2.push(createCompilerDiagnostic(Diagnostics.Unable_to_open_file_0, filePath));
16706
+ errors2.push(createCompilerDiagnostic(Diagnostics.Unable_to_open_file_0, filePath2));
16662
16707
  }
16663
16708
  return false;
16664
16709
  }
@@ -16666,7 +16711,7 @@ ${lanes.join("\n")}
16666
16711
  setLocalizedDiagnosticMessages(JSON.parse(fileContents));
16667
16712
  } catch {
16668
16713
  if (errors2) {
16669
- errors2.push(createCompilerDiagnostic(Diagnostics.Corrupted_locale_file_0, filePath));
16714
+ errors2.push(createCompilerDiagnostic(Diagnostics.Corrupted_locale_file_0, filePath2));
16670
16715
  }
16671
16716
  return false;
16672
16717
  }
@@ -22118,10 +22163,10 @@ ${lanes.join("\n")}
22118
22163
  function getExternalModuleNameFromPath(host, fileName, referencePath) {
22119
22164
  const getCanonicalFileName = (f) => host.getCanonicalFileName(f);
22120
22165
  const dir = toPath(referencePath ? getDirectoryPath(referencePath) : host.getCommonSourceDirectory(), host.getCurrentDirectory(), getCanonicalFileName);
22121
- const filePath = getNormalizedAbsolutePath(fileName, host.getCurrentDirectory());
22166
+ const filePath2 = getNormalizedAbsolutePath(fileName, host.getCurrentDirectory());
22122
22167
  const relativePath = getRelativePathToDirectoryOrUrl(
22123
22168
  dir,
22124
- filePath,
22169
+ filePath2,
22125
22170
  dir,
22126
22171
  getCanonicalFileName,
22127
22172
  /*isAbsolutePathAnUrl*/
@@ -22196,11 +22241,11 @@ ${lanes.join("\n")}
22196
22241
  /* Js */
22197
22242
  ];
22198
22243
  }
22199
- function getPossibleOriginalInputPathWithoutChangingExt(filePath, ignoreCase, outputDir, getCommonSourceDirectory2) {
22244
+ function getPossibleOriginalInputPathWithoutChangingExt(filePath2, ignoreCase, outputDir, getCommonSourceDirectory2) {
22200
22245
  return outputDir ? resolvePath2(
22201
22246
  getCommonSourceDirectory2(),
22202
- getRelativePathFromDirectory(outputDir, filePath, ignoreCase)
22203
- ) : filePath;
22247
+ getRelativePathFromDirectory(outputDir, filePath2, ignoreCase)
22248
+ ) : filePath2;
22204
22249
  }
22205
22250
  function getPathsBasePath(options, host) {
22206
22251
  var _a;
@@ -131244,7 +131289,7 @@ ${lanes.join("\n")}
131244
131289
  const sourceRoot = normalizeSlashes(mapOptions.sourceRoot || "");
131245
131290
  return sourceRoot ? ensureTrailingDirectorySeparator(sourceRoot) : sourceRoot;
131246
131291
  }
131247
- function getSourceMapDirectory(mapOptions, filePath, sourceFile) {
131292
+ function getSourceMapDirectory(mapOptions, filePath2, sourceFile) {
131248
131293
  if (mapOptions.sourceRoot) return host.getCommonSourceDirectory();
131249
131294
  if (mapOptions.mapRoot) {
131250
131295
  let sourceMapDir = normalizeSlashes(mapOptions.mapRoot);
@@ -131256,9 +131301,9 @@ ${lanes.join("\n")}
131256
131301
  }
131257
131302
  return sourceMapDir;
131258
131303
  }
131259
- return getDirectoryPath(normalizePath2(filePath));
131304
+ return getDirectoryPath(normalizePath2(filePath2));
131260
131305
  }
131261
- function getSourceMappingURL(mapOptions, sourceMapGenerator, filePath, sourceMapFilePath, sourceFile) {
131306
+ function getSourceMappingURL(mapOptions, sourceMapGenerator, filePath2, sourceMapFilePath, sourceFile) {
131262
131307
  if (mapOptions.inlineSourceMap) {
131263
131308
  const sourceMapText = sourceMapGenerator.toString();
131264
131309
  const base64SourceMapText = base64encode(sys, sourceMapText);
@@ -131274,7 +131319,7 @@ ${lanes.join("\n")}
131274
131319
  sourceMapDir = combinePaths(host.getCommonSourceDirectory(), sourceMapDir);
131275
131320
  return encodeURI(
131276
131321
  getRelativePathToDirectoryOrUrl(
131277
- getDirectoryPath(normalizePath2(filePath)),
131322
+ getDirectoryPath(normalizePath2(filePath2)),
131278
131323
  // get the relative sourceMapDir path based on jsFilePath
131279
131324
  combinePaths(sourceMapDir, sourceMapFile),
131280
131325
  // this is where user expects to see sourceMap
@@ -136253,11 +136298,11 @@ ${lanes.join("\n")}
136253
136298
  }
136254
136299
  return fsQueryResult;
136255
136300
  }
136256
- function addOrDeleteFile(fileName, filePath, eventKind) {
136301
+ function addOrDeleteFile(fileName, filePath2, eventKind) {
136257
136302
  if (eventKind === 1) {
136258
136303
  return;
136259
136304
  }
136260
- const parentResult = getCachedFileSystemEntriesForBaseDir(filePath);
136305
+ const parentResult = getCachedFileSystemEntriesForBaseDir(filePath2);
136261
136306
  if (parentResult) {
136262
136307
  updateFilesOfFileSystemEntry(
136263
136308
  parentResult,
@@ -136266,7 +136311,7 @@ ${lanes.join("\n")}
136266
136311
  /* Created */
136267
136312
  );
136268
136313
  } else {
136269
- clearFirstAncestorEntry(filePath);
136314
+ clearFirstAncestorEntry(filePath2);
136270
136315
  }
136271
136316
  }
136272
136317
  function updateFilesOfFileSystemEntry(parentResult, baseName, fileExists22) {
@@ -137800,7 +137845,7 @@ ${lanes.join("\n")}
137800
137845
  function forEachResolution(resolutionCache, callback, file) {
137801
137846
  var _a2;
137802
137847
  if (file) (_a2 = resolutionCache == null ? void 0 : resolutionCache.get(file.path)) == null ? void 0 : _a2.forEach((resolution, name, mode) => callback(resolution, name, mode, file.path));
137803
- else resolutionCache == null ? void 0 : resolutionCache.forEach((resolutions, filePath) => resolutions.forEach((resolution, name, mode) => callback(resolution, name, mode, filePath)));
137848
+ else resolutionCache == null ? void 0 : resolutionCache.forEach((resolutions, filePath2) => resolutions.forEach((resolution, name, mode) => callback(resolution, name, mode, filePath2)));
137804
137849
  }
137805
137850
  function getPackagesMap() {
137806
137851
  if (packageMap) return packageMap;
@@ -140139,26 +140184,26 @@ ${lanes.join("\n")}
140139
140184
  if (options.noEmit) {
140140
140185
  return false;
140141
140186
  }
140142
- const filePath = toPath3(file);
140143
- if (getSourceFileByPath(filePath)) {
140187
+ const filePath2 = toPath3(file);
140188
+ if (getSourceFileByPath(filePath2)) {
140144
140189
  return false;
140145
140190
  }
140146
140191
  const out = options.outFile;
140147
140192
  if (out) {
140148
- return isSameFile(filePath, out) || isSameFile(
140149
- filePath,
140193
+ return isSameFile(filePath2, out) || isSameFile(
140194
+ filePath2,
140150
140195
  removeFileExtension(out) + ".d.ts"
140151
140196
  /* Dts */
140152
140197
  );
140153
140198
  }
140154
- if (options.declarationDir && containsPath(options.declarationDir, filePath, currentDirectory, !host.useCaseSensitiveFileNames())) {
140199
+ if (options.declarationDir && containsPath(options.declarationDir, filePath2, currentDirectory, !host.useCaseSensitiveFileNames())) {
140155
140200
  return true;
140156
140201
  }
140157
140202
  if (options.outDir) {
140158
- return containsPath(options.outDir, filePath, currentDirectory, !host.useCaseSensitiveFileNames());
140203
+ return containsPath(options.outDir, filePath2, currentDirectory, !host.useCaseSensitiveFileNames());
140159
140204
  }
140160
- if (fileExtensionIsOneOf(filePath, supportedJSExtensionsFlat) || isDeclarationFileName(filePath)) {
140161
- const filePathWithoutExtension = removeFileExtension(filePath);
140205
+ if (fileExtensionIsOneOf(filePath2, supportedJSExtensionsFlat) || isDeclarationFileName(filePath2)) {
140206
+ const filePathWithoutExtension = removeFileExtension(filePath2);
140162
140207
  return !!getSourceFileByPath(
140163
140208
  filePathWithoutExtension + ".ts"
140164
140209
  /* Ts */
@@ -141570,9 +141615,9 @@ ${lanes.join("\n")}
141570
141615
  const newSignature = Debug.checkDefined(state.fileInfos.get(path)).signature;
141571
141616
  return newSignature !== oldSignature;
141572
141617
  }
141573
- function handleDtsMayChangeOfGlobalScope(state, filePath, invalidateJsFiles, cancellationToken, host) {
141618
+ function handleDtsMayChangeOfGlobalScope(state, filePath2, invalidateJsFiles, cancellationToken, host) {
141574
141619
  var _a;
141575
- if (!((_a = state.fileInfos.get(filePath)) == null ? void 0 : _a.affectsGlobalScope)) return false;
141620
+ if (!((_a = state.fileInfos.get(filePath2)) == null ? void 0 : _a.affectsGlobalScope)) return false;
141576
141621
  BuilderState.getAllFilesExcludingDefaultLibraryFile(
141577
141622
  state,
141578
141623
  state.program,
@@ -141638,9 +141683,9 @@ ${lanes.join("\n")}
141638
141683
  (_b = state.referencedMap.getKeys(affectedFile.resolvedPath)) == null ? void 0 : _b.forEach((exportedFromPath) => {
141639
141684
  if (handleDtsMayChangeOfGlobalScope(state, exportedFromPath, invalidateJsFiles, cancellationToken, host)) return true;
141640
141685
  const references = state.referencedMap.getKeys(exportedFromPath);
141641
- return references && forEachKey(references, (filePath) => handleDtsMayChangeOfFileAndExportsOfFile(
141686
+ return references && forEachKey(references, (filePath2) => handleDtsMayChangeOfFileAndExportsOfFile(
141642
141687
  state,
141643
- filePath,
141688
+ filePath2,
141644
141689
  invalidateJsFiles,
141645
141690
  seenFileAndExportsOfFile,
141646
141691
  cancellationToken,
@@ -141648,12 +141693,12 @@ ${lanes.join("\n")}
141648
141693
  ));
141649
141694
  });
141650
141695
  }
141651
- function handleDtsMayChangeOfFileAndExportsOfFile(state, filePath, invalidateJsFiles, seenFileAndExportsOfFile, cancellationToken, host) {
141696
+ function handleDtsMayChangeOfFileAndExportsOfFile(state, filePath2, invalidateJsFiles, seenFileAndExportsOfFile, cancellationToken, host) {
141652
141697
  var _a;
141653
- if (!tryAddToSet(seenFileAndExportsOfFile, filePath)) return void 0;
141654
- if (handleDtsMayChangeOfGlobalScope(state, filePath, invalidateJsFiles, cancellationToken, host)) return true;
141655
- handleDtsMayChangeOf(state, filePath, invalidateJsFiles, cancellationToken, host);
141656
- (_a = state.referencedMap.getKeys(filePath)) == null ? void 0 : _a.forEach(
141698
+ if (!tryAddToSet(seenFileAndExportsOfFile, filePath2)) return void 0;
141699
+ if (handleDtsMayChangeOfGlobalScope(state, filePath2, invalidateJsFiles, cancellationToken, host)) return true;
141700
+ handleDtsMayChangeOf(state, filePath2, invalidateJsFiles, cancellationToken, host);
141701
+ (_a = state.referencedMap.getKeys(filePath2)) == null ? void 0 : _a.forEach(
141657
141702
  (referencingFilePath) => handleDtsMayChangeOfFileAndExportsOfFile(
141658
141703
  state,
141659
141704
  referencingFilePath,
@@ -142297,10 +142342,10 @@ ${lanes.join("\n")}
142297
142342
  }
142298
142343
  }
142299
142344
  if (state.compilerOptions.composite) {
142300
- const filePath = sourceFiles[0].resolvedPath;
142301
- emitSignature = handleNewSignature((_c = state.emitSignatures) == null ? void 0 : _c.get(filePath), emitSignature);
142345
+ const filePath2 = sourceFiles[0].resolvedPath;
142346
+ emitSignature = handleNewSignature((_c = state.emitSignatures) == null ? void 0 : _c.get(filePath2), emitSignature);
142302
142347
  if (!emitSignature) return data.skippedDtsWrite = true;
142303
- (state.emitSignatures ?? (state.emitSignatures = /* @__PURE__ */ new Map())).set(filePath, emitSignature);
142348
+ (state.emitSignatures ?? (state.emitSignatures = /* @__PURE__ */ new Map())).set(filePath2, emitSignature);
142304
142349
  }
142305
142350
  } else if (state.compilerOptions.composite) {
142306
142351
  const newSignature = handleNewSignature(
@@ -142783,8 +142828,8 @@ ${lanes.join("\n")}
142783
142828
  function canWatchAffectedPackageJsonOrNodeModulesOfAtTypes(fileOrDirPath) {
142784
142829
  return canWatchDirectoryOrFilePath(fileOrDirPath);
142785
142830
  }
142786
- function canWatchAffectingLocation(filePath) {
142787
- return canWatchAffectedPackageJsonOrNodeModulesOfAtTypes(filePath);
142831
+ function canWatchAffectingLocation(filePath2) {
142832
+ return canWatchAffectedPackageJsonOrNodeModulesOfAtTypes(filePath2);
142788
142833
  }
142789
142834
  function getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath, rootDir, rootPath, rootPathComponents, isRootWatchable, getCurrentDirectory, preferNonRecursiveWatch) {
142790
142835
  const failedLookupPathComponents = getPathComponents(failedLookupLocationPath);
@@ -143366,8 +143411,8 @@ ${lanes.join("\n")}
143366
143411
  function isNodeModulesAtTypesDirectory(dirPath) {
143367
143412
  return endsWith(dirPath, "/node_modules/@types");
143368
143413
  }
143369
- function watchFailedLookupLocationsOfExternalModuleResolutions(name, resolution, filePath, getResolutionWithResolvedFileName, deferWatchingNonRelativeResolution) {
143370
- (resolution.files ?? (resolution.files = /* @__PURE__ */ new Set())).add(filePath);
143414
+ function watchFailedLookupLocationsOfExternalModuleResolutions(name, resolution, filePath2, getResolutionWithResolvedFileName, deferWatchingNonRelativeResolution) {
143415
+ (resolution.files ?? (resolution.files = /* @__PURE__ */ new Set())).add(filePath2);
143371
143416
  if (resolution.files.size !== 1) return;
143372
143417
  if (!deferWatchingNonRelativeResolution || isExternalModuleNameRelative(name)) {
143373
143418
  watchFailedLookupLocationOfResolution(resolution);
@@ -143609,8 +143654,8 @@ ${lanes.join("\n")}
143609
143654
  }
143610
143655
  return removeAtRoot;
143611
143656
  }
143612
- function stopWatchFailedLookupLocationOfResolution(resolution, filePath, getResolutionWithResolvedFileName) {
143613
- Debug.checkDefined(resolution.files).delete(filePath);
143657
+ function stopWatchFailedLookupLocationOfResolution(resolution, filePath2, getResolutionWithResolvedFileName) {
143658
+ Debug.checkDefined(resolution.files).delete(filePath2);
143614
143659
  if (resolution.files.size) return;
143615
143660
  resolution.files = void 0;
143616
143661
  const resolved = getResolutionWithResolvedFileName(resolution);
@@ -143657,34 +143702,34 @@ ${lanes.join("\n")}
143657
143702
  /* Recursive */
143658
143703
  );
143659
143704
  }
143660
- function removeResolutionsOfFileFromCache(cache, filePath, getResolutionWithResolvedFileName) {
143661
- const resolutions = cache.get(filePath);
143705
+ function removeResolutionsOfFileFromCache(cache, filePath2, getResolutionWithResolvedFileName) {
143706
+ const resolutions = cache.get(filePath2);
143662
143707
  if (resolutions) {
143663
143708
  resolutions.forEach(
143664
143709
  (resolution) => stopWatchFailedLookupLocationOfResolution(
143665
143710
  resolution,
143666
- filePath,
143711
+ filePath2,
143667
143712
  getResolutionWithResolvedFileName
143668
143713
  )
143669
143714
  );
143670
- cache.delete(filePath);
143715
+ cache.delete(filePath2);
143671
143716
  }
143672
143717
  }
143673
- function removeResolutionsFromProjectReferenceRedirects(filePath) {
143718
+ function removeResolutionsFromProjectReferenceRedirects(filePath2) {
143674
143719
  if (!fileExtensionIs(
143675
- filePath,
143720
+ filePath2,
143676
143721
  ".json"
143677
143722
  /* Json */
143678
143723
  )) return;
143679
143724
  const program = resolutionHost.getCurrentProgram();
143680
143725
  if (!program) return;
143681
- const resolvedProjectReference = program.getResolvedProjectReferenceByPath(filePath);
143726
+ const resolvedProjectReference = program.getResolvedProjectReferenceByPath(filePath2);
143682
143727
  if (!resolvedProjectReference) return;
143683
143728
  resolvedProjectReference.commandLine.fileNames.forEach((f) => removeResolutionsOfFile(resolutionHost.toPath(f)));
143684
143729
  }
143685
- function removeResolutionsOfFile(filePath) {
143686
- removeResolutionsOfFileFromCache(resolvedModuleNames, filePath, getResolvedModuleFromResolution);
143687
- removeResolutionsOfFileFromCache(resolvedTypeReferenceDirectives, filePath, getResolvedTypeReferenceDirectiveFromResolution);
143730
+ function removeResolutionsOfFile(filePath2) {
143731
+ removeResolutionsOfFileFromCache(resolvedModuleNames, filePath2, getResolvedModuleFromResolution);
143732
+ removeResolutionsOfFileFromCache(resolvedTypeReferenceDirectives, filePath2, getResolvedTypeReferenceDirectiveFromResolution);
143688
143733
  }
143689
143734
  function invalidateResolutions(resolutions, canInvalidate) {
143690
143735
  if (!resolutions) return false;
@@ -143699,10 +143744,10 @@ ${lanes.join("\n")}
143699
143744
  });
143700
143745
  return invalidated;
143701
143746
  }
143702
- function invalidateResolutionOfFile(filePath) {
143703
- removeResolutionsOfFile(filePath);
143747
+ function invalidateResolutionOfFile(filePath2) {
143748
+ removeResolutionsOfFile(filePath2);
143704
143749
  const prevHasChangedAutomaticTypeDirectiveNames = hasChangedAutomaticTypeDirectiveNames;
143705
- if (invalidateResolutions(resolvedFileToResolution.get(filePath), returnTrue) && hasChangedAutomaticTypeDirectiveNames && !prevHasChangedAutomaticTypeDirectiveNames) {
143750
+ if (invalidateResolutions(resolvedFileToResolution.get(filePath2), returnTrue) && hasChangedAutomaticTypeDirectiveNames && !prevHasChangedAutomaticTypeDirectiveNames) {
143706
143751
  resolutionHost.onChangedAutomaticTypeDirectiveNames();
143707
143752
  }
143708
143753
  }
@@ -144096,9 +144141,9 @@ ${lanes.join("\n")}
144096
144141
  var _a;
144097
144142
  const configFile = program.getCompilerOptions().configFile;
144098
144143
  if (!((_a = configFile == null ? void 0 : configFile.configFileSpecs) == null ? void 0 : _a.validatedFilesSpec)) return void 0;
144099
- const filePath = program.getCanonicalFileName(fileName);
144144
+ const filePath2 = program.getCanonicalFileName(fileName);
144100
144145
  const basePath = getDirectoryPath(getNormalizedAbsolutePath(configFile.fileName, program.getCurrentDirectory()));
144101
- const index = findIndex(configFile.configFileSpecs.validatedFilesSpec, (fileSpec) => program.getCanonicalFileName(getNormalizedAbsolutePath(fileSpec, basePath)) === filePath);
144146
+ const index = findIndex(configFile.configFileSpecs.validatedFilesSpec, (fileSpec) => program.getCanonicalFileName(getNormalizedAbsolutePath(fileSpec, basePath)) === filePath2);
144102
144147
  return index !== -1 ? configFile.configFileSpecs.validatedFilesSpecBeforeSubstitution[index] : void 0;
144103
144148
  }
144104
144149
  function getMatchedIncludeSpec(program, fileName) {
@@ -183289,13 +183334,13 @@ ${newComment.split("\n").map((c2) => ` * ${c2}`).join("\n")}
183289
183334
  ["./*"]
183290
183335
  );
183291
183336
  if (files) {
183292
- for (let filePath of files) {
183293
- filePath = normalizePath2(filePath);
183294
- if (exclude && comparePaths(filePath, exclude, scriptDirectory, ignoreCase) === 0) {
183337
+ for (let filePath2 of files) {
183338
+ filePath2 = normalizePath2(filePath2);
183339
+ if (exclude && comparePaths(filePath2, exclude, scriptDirectory, ignoreCase) === 0) {
183295
183340
  continue;
183296
183341
  }
183297
183342
  const { name, extension } = getFilenameWithExtensionOption(
183298
- getBaseFileName(filePath),
183343
+ getBaseFileName(filePath2),
183299
183344
  program,
183300
183345
  extensionOptions,
183301
183346
  /*isExportsOrImportsWildcard*/
@@ -189595,8 +189640,8 @@ ${content}
189595
189640
  }
189596
189641
  return void 0;
189597
189642
  }
189598
- function getPackagePathComponents(filePath) {
189599
- const components = getPathComponents(filePath);
189643
+ function getPackagePathComponents(filePath2) {
189644
+ const components = getPathComponents(filePath2);
189600
189645
  const nodeModulesIdx = components.lastIndexOf("node_modules");
189601
189646
  if (nodeModulesIdx === -1) {
189602
189647
  return void 0;
@@ -201069,8 +201114,8 @@ ${options.prefix}` : "\n" : options.prefix
201069
201114
  this.projectService.delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles(this);
201070
201115
  }
201071
201116
  /** @internal */
201072
- fileIsOpen(filePath) {
201073
- return this.projectService.openFiles.has(filePath);
201117
+ fileIsOpen(filePath2) {
201118
+ return this.projectService.openFiles.has(filePath2);
201074
201119
  }
201075
201120
  /** @internal */
201076
201121
  writeLog(s) {
@@ -215423,15 +215468,15 @@ function generateDashboard(result, outputPath) {
215423
215468
  writeFileSync10(outputPath, html, "utf8");
215424
215469
  return outputPath;
215425
215470
  }
215426
- function openDashboard(filePath) {
215471
+ function openDashboard(filePath2) {
215427
215472
  const platform = process.platform;
215428
215473
  let cmd;
215429
215474
  if (platform === "darwin") {
215430
- cmd = `open "${filePath}"`;
215475
+ cmd = `open "${filePath2}"`;
215431
215476
  } else if (platform === "linux") {
215432
- cmd = `xdg-open "${filePath}"`;
215477
+ cmd = `xdg-open "${filePath2}"`;
215433
215478
  } else if (platform === "win32") {
215434
- cmd = `start "" "${filePath}"`;
215479
+ cmd = `start "" "${filePath2}"`;
215435
215480
  } else {
215436
215481
  return;
215437
215482
  }
@@ -215723,11 +215768,11 @@ function safeJsonParse(json, stepName, validator, logger) {
215723
215768
  parsed = JSON.parse(json);
215724
215769
  } catch (err) {
215725
215770
  const msg = err instanceof Error ? err.message : String(err);
215726
- if (logger) logger.warn("analysis", stepName, `JSON parse: ${msg}`);
215771
+ if (logger) logger.warn(stepName, `JSON parse: ${msg}`);
215727
215772
  }
215728
215773
  const result = validator(parsed);
215729
215774
  if (!result.ok) {
215730
- if (logger) logger.warn("analysis", stepName, `validation: ${result.errors.join("; ")}`);
215775
+ if (logger) logger.warn(stepName, `validation: ${result.errors.join("; ")}`);
215731
215776
  if (result.partial !== void 0) return result.partial;
215732
215777
  return null;
215733
215778
  }
@@ -216742,13 +216787,13 @@ async function attributeWithLLM(data, steps, stats, meta, graphStore, attributio
216742
216787
  statsSummary,
216743
216788
  recoveryArcs
216744
216789
  );
216745
- if (logger) logger.info("analysis", "RuleAttribution", `Prompt size: ${prompt.length} chars, graph ${graphSnapshot ? `${graphSnapshot.dataFlows.length} dataFlows, ${graphSnapshot.subtasks.length} subtasks` : "none"}`);
216790
+ if (logger) logger.info("RuleAttribution", `Prompt size: ${prompt.length} chars, graph ${graphSnapshot ? `${graphSnapshot.dataFlows.length} dataFlows, ${graphSnapshot.subtasks.length} subtasks` : "none"}`);
216746
216791
  let rawOutput;
216747
216792
  let rulesOutput = [];
216748
216793
  try {
216749
216794
  rawOutput = await callLLM(RULE_ATTRIBUTION_SYSTEM, prompt, harness, 16384);
216750
216795
  } catch (err) {
216751
- if (logger) logger.warn("analysis", "RuleAttribution", `LLM call failed: ${err.message}`);
216796
+ if (logger) logger.warn("RuleAttribution", `LLM call failed: ${err.message}`);
216752
216797
  return [];
216753
216798
  }
216754
216799
  for (let attempt = 0; attempt < 2; attempt++) {
@@ -216760,11 +216805,11 @@ async function attributeWithLLM(data, steps, stats, meta, graphStore, attributio
216760
216805
  rawOutput = await callLLM(RULE_ATTRIBUTION_SYSTEM, retryPrompt, harness, 16384);
216761
216806
  continue;
216762
216807
  } catch {
216763
- if (logger) logger.warn("analysis", "RuleAttribution", "Retry LLM call failed");
216808
+ if (logger) logger.warn("RuleAttribution", "Retry LLM call failed");
216764
216809
  return [];
216765
216810
  }
216766
216811
  }
216767
- if (logger) logger.warn("analysis", "RuleAttribution", "No JSON found in LLM response after retry");
216812
+ if (logger) logger.warn("RuleAttribution", "No JSON found in LLM response after retry");
216768
216813
  return [];
216769
216814
  }
216770
216815
  let parsed;
@@ -216802,7 +216847,7 @@ Output the FULL corrected JSON array.`;
216802
216847
  rawOutput = await callLLM(RULE_ATTRIBUTION_SYSTEM, retryPrompt, harness, 16384);
216803
216848
  continue;
216804
216849
  } catch {
216805
- if (logger) logger.warn("analysis", "RuleAttribution", "Validation retry failed");
216850
+ if (logger) logger.warn("RuleAttribution", "Validation retry failed");
216806
216851
  break;
216807
216852
  }
216808
216853
  }
@@ -216816,11 +216861,11 @@ Output the FULL corrected JSON array.`;
216816
216861
  rawOutput = await callLLM(RULE_ATTRIBUTION_SYSTEM, retryPrompt, harness, 16384);
216817
216862
  continue;
216818
216863
  } catch {
216819
- if (logger) logger.warn("analysis", "RuleAttribution", "Retry LLM call failed");
216864
+ if (logger) logger.warn("RuleAttribution", "Retry LLM call failed");
216820
216865
  return [];
216821
216866
  }
216822
216867
  }
216823
- if (logger) logger.warn("analysis", "RuleAttribution", "No JSON found in LLM response after retry");
216868
+ if (logger) logger.warn("RuleAttribution", "No JSON found in LLM response after retry");
216824
216869
  return [];
216825
216870
  }
216826
216871
  const parsed = safeJsonParse(json, "rule-attribution", validateHarnessRuleOutputs);
@@ -216836,14 +216881,14 @@ Output the FULL corrected JSON array.`;
216836
216881
  rawOutput = await callLLM(RULE_ATTRIBUTION_SYSTEM, retryPrompt, harness, 16384);
216837
216882
  continue;
216838
216883
  } catch {
216839
- if (logger) logger.warn("analysis", "RuleAttribution", "Validation retry failed");
216884
+ if (logger) logger.warn("RuleAttribution", "Validation retry failed");
216840
216885
  return [];
216841
216886
  }
216842
216887
  }
216843
216888
  }
216844
- if (logger) logger.info("analysis", "RuleAttribution", `Generated ${rulesOutput.length} rules`);
216889
+ if (logger) logger.info("RuleAttribution", `Generated ${rulesOutput.length} rules`);
216845
216890
  if (rulesOutput.length === 0) {
216846
- if (logger) logger.info("analysis", "RuleAttribution", `Raw LLM response (first 500): ${rawOutput.slice(0, 500)}`);
216891
+ if (logger) logger.info("RuleAttribution", `Raw LLM response (first 500): ${rawOutput.slice(0, 500)}`);
216847
216892
  }
216848
216893
  return rulesOutput.map((r) => ({
216849
216894
  id: r.id,
@@ -218393,9 +218438,9 @@ function writeLibraryMetadata(skeleton, workspacePath) {
218393
218438
  `| Screenshots Taken | ${stats.screenshotsTaken} |`,
218394
218439
  ""
218395
218440
  ].join("\n");
218396
- const filePath = join24(workspacePath, "library", "meta.md");
218397
- writeFileSync12(filePath, content, "utf-8");
218398
- return filePath;
218441
+ const filePath2 = join24(workspacePath, "library", "meta.md");
218442
+ writeFileSync12(filePath2, content, "utf-8");
218443
+ return filePath2;
218399
218444
  }
218400
218445
  function writeLibrarySkeleton(skeleton, workspacePath) {
218401
218446
  const lines = [];
@@ -218458,9 +218503,9 @@ function writeLibrarySkeleton(skeleton, workspacePath) {
218458
218503
  }
218459
218504
  lines.push(hints.join("\n"));
218460
218505
  lines.push("");
218461
- const filePath = join24(workspacePath, "library", "skeleton.md");
218462
- writeFileSync12(filePath, lines.join("\n"), "utf-8");
218463
- return filePath;
218506
+ const filePath2 = join24(workspacePath, "library", "skeleton.md");
218507
+ writeFileSync12(filePath2, lines.join("\n"), "utf-8");
218508
+ return filePath2;
218464
218509
  }
218465
218510
  function writeLibrarySignals(skeleton, workspacePath) {
218466
218511
  const typeOrder = [
@@ -218504,9 +218549,9 @@ function writeLibrarySignals(skeleton, workspacePath) {
218504
218549
  lines.push("_No signal anchors detected._");
218505
218550
  lines.push("");
218506
218551
  }
218507
- const filePath = join24(workspacePath, "library", "signals.md");
218508
- writeFileSync12(filePath, lines.join("\n"), "utf-8");
218509
- return filePath;
218552
+ const filePath2 = join24(workspacePath, "library", "signals.md");
218553
+ writeFileSync12(filePath2, lines.join("\n"), "utf-8");
218554
+ return filePath2;
218510
218555
  }
218511
218556
  function writeLibraryDataItems(skeleton, workspacePath) {
218512
218557
  const sorted = [...skeleton.dataItems].sort((a, b) => b.operationCount - a.operationCount);
@@ -218527,9 +218572,9 @@ function writeLibraryDataItems(skeleton, workspacePath) {
218527
218572
  lines.push("");
218528
218573
  }
218529
218574
  }
218530
- const filePath = join24(workspacePath, "library", "data-items.md");
218531
- writeFileSync12(filePath, lines.join("\n"), "utf-8");
218532
- return filePath;
218575
+ const filePath2 = join24(workspacePath, "library", "data-items.md");
218576
+ writeFileSync12(filePath2, lines.join("\n"), "utf-8");
218577
+ return filePath2;
218533
218578
  }
218534
218579
  function writeLibrarySteps(steps, phases, workspacePath) {
218535
218580
  const MAX_STEPS_PER_FILE = 150;
@@ -218570,9 +218615,9 @@ function writeLibrarySteps(steps, phases, workspacePath) {
218570
218615
  }
218571
218616
  lines.push("");
218572
218617
  }
218573
- const filePath = join24(workspacePath, "library", "steps", fileName);
218574
- writeFileSync12(filePath, lines.join("\n"), "utf-8");
218575
- written.push(filePath);
218618
+ const filePath2 = join24(workspacePath, "library", "steps", fileName);
218619
+ writeFileSync12(filePath2, lines.join("\n"), "utf-8");
218620
+ written.push(filePath2);
218576
218621
  }
218577
218622
  }
218578
218623
  return written;
@@ -218654,9 +218699,9 @@ function writeLibraryReadme(phase, workspacePath) {
218654
218699
  "- `glob(pattern)` \u2014 list files matching a pattern in library/ or notebook/",
218655
218700
  ""
218656
218701
  ].join("\n");
218657
- const filePath = join24(workspacePath, "library", "README.md");
218658
- writeFileSync12(filePath, content, "utf-8");
218659
- return filePath;
218702
+ const filePath2 = join24(workspacePath, "library", "README.md");
218703
+ writeFileSync12(filePath2, content, "utf-8");
218704
+ return filePath2;
218660
218705
  }
218661
218706
  function writeLibrary(skeleton, steps, ruleResult, phase, workspacePath) {
218662
218707
  let fileCount = 0;
@@ -218750,7 +218795,7 @@ var init_progress = __esm({
218750
218795
  this.logProgressBar();
218751
218796
  }
218752
218797
  if (this.logger) {
218753
- this.logger.info("analysis", "Progress", label);
218798
+ this.logger.info("Progress", label);
218754
218799
  }
218755
218800
  }
218756
218801
  onPhaseProgress(phaseIndex, event, subZoneId) {
@@ -218789,7 +218834,7 @@ var init_progress = __esm({
218789
218834
  this.emitWebEvent("phaseDone", { phaseIndex, summary, durationMs });
218790
218835
  if (summary) {
218791
218836
  if (this.onLog) this.onLog(`\u2713 ${summary}`);
218792
- if (this.logger) this.logger.info("analysis", "Progress", `Phase ${phaseIndex} done: ${summary}`);
218837
+ if (this.logger) this.logger.info("Progress", `Phase ${phaseIndex} done: ${summary}`);
218793
218838
  }
218794
218839
  this.logProgressBar();
218795
218840
  }
@@ -218814,7 +218859,7 @@ var init_progress = __esm({
218814
218859
  this.logProgressBar();
218815
218860
  }
218816
218861
  if (this.logger) {
218817
- this.logger.info("analysis", "Progress", msg);
218862
+ this.logger.info("Progress", msg);
218818
218863
  }
218819
218864
  }
218820
218865
  dispose() {
@@ -218916,7 +218961,7 @@ async function runFocusPipeline(data, harness, sessionStats, onLog, logger) {
218916
218961
  }
218917
218962
  const pipelineStart = Date.now();
218918
218963
  let totalLLMCalls = 0;
218919
- if (logger) logger.info("analysis", "FocusPipeline", `Start: ${steps.length} steps, session ${sessionId.slice(0, 8)}`);
218964
+ if (logger) logger.info("FocusPipeline", `Start: ${steps.length} steps, session ${sessionId.slice(0, 8)}`);
218920
218965
  const progress = new ProgressDisplay({ onLog, logger });
218921
218966
  progress.onPhaseStart(0);
218922
218967
  await yieldTui();
@@ -218936,7 +218981,7 @@ async function runFocusPipeline(data, harness, sessionStats, onLog, logger) {
218936
218981
  const workspacePath = createWorkspace(sessionId);
218937
218982
  const { fileCount } = writeLibrary(skeleton, steps, ruleResult, "SCAN", workspacePath);
218938
218983
  cleanOldWorkspaces(10);
218939
- if (logger) logger.info("analysis", "Phase0", `Library: ${fileCount} files \u2192 ${workspacePath}`);
218984
+ if (logger) logger.info("Phase0", `Library: ${fileCount} files \u2192 ${workspacePath}`);
218940
218985
  progress.onPhaseDone(0, `\u5199\u5165 ${fileCount} \u4E2A\u6587\u4EF6`, Date.now() - pipelineStart);
218941
218986
  await yieldTui();
218942
218987
  progress.onPhaseStart(1);
@@ -218944,7 +218989,7 @@ async function runFocusPipeline(data, harness, sessionStats, onLog, logger) {
218944
218989
  const scanResult = await scanSession(skeleton, harness, workspacePath, sessionId, progress);
218945
218990
  totalLLMCalls++;
218946
218991
  const scanSummary = scanResult.noIssuesDetected ? "\u672A\u68C0\u6D4B\u5230\u95EE\u9898" : `\u8BC6\u522B\u5230 ${scanResult.zones.length} \u4E2A attention zones: ${scanResult.zones.map((z) => z.id).join(", ")}`;
218947
- if (logger) logger.info("analysis", "SCAN", scanSummary);
218992
+ if (logger) logger.info("SCAN", scanSummary);
218948
218993
  progress.onPhaseDone(1, scanSummary, Date.now() - pipelineStart);
218949
218994
  await yieldTui();
218950
218995
  if (scanResult.noIssuesDetected || scanResult.zones.length === 0) {
@@ -218961,7 +219006,7 @@ async function runFocusPipeline(data, harness, sessionStats, onLog, logger) {
218961
219006
  Date.now(),
218962
219007
  logger
218963
219008
  );
218964
- if (logger) logger.info("analysis", "FocusPipeline", "Early exit: no issues detected");
219009
+ if (logger) logger.info("FocusPipeline", "Early exit: no issues detected");
218965
219010
  return { ...sessionStats, phases: [], deviations: [], rootCauses: [], rules: rules2, timeline: [], causalGraph: null, attribution: null, rulesApplied: [] };
218966
219011
  }
218967
219012
  progress.onPhaseStart(2);
@@ -218980,7 +219025,7 @@ async function runFocusPipeline(data, harness, sessionStats, onLog, logger) {
218980
219025
  zoneAnalyses.push({ zoneId: zone.id, subtasks: [], subtaskEdges: [], agentNodes: [], agentEdges: [], stepDataFlows: [], candidates: [], topCandidate: null, zoneGraphComplete: false });
218981
219026
  }
218982
219027
  }
218983
- if (logger) logger.info("analysis", "ZOOM", `${zoneAnalyses.length} zones, ${zoneAnalyses.filter((z) => z.zoneGraphComplete).length} complete`);
219028
+ if (logger) logger.info("ZOOM", `${zoneAnalyses.length} zones, ${zoneAnalyses.filter((z) => z.zoneGraphComplete).length} complete`);
218984
219029
  progress.onPhaseDone(2, `${zoneAnalyses.length} zones \u5206\u6790\u5B8C\u6210`, Date.now() - pipelineStart);
218985
219030
  await yieldTui();
218986
219031
  progress.onPhaseStart(3);
@@ -218996,7 +219041,7 @@ async function runFocusPipeline(data, harness, sessionStats, onLog, logger) {
218996
219041
  );
218997
219042
  totalLLMCalls++;
218998
219043
  const synthSummary = attribution.mistakeStep > 0 ? `\u6839\u56E0: ${attribution.mistakeAgent}@Step ${attribution.mistakeStep}` : "\u5F52\u56E0\u5B8C\u6210";
218999
- if (logger) logger.info("analysis", "SYNTH", synthSummary);
219044
+ if (logger) logger.info("SYNTH", synthSummary);
219000
219045
  progress.onPhaseDone(3, synthSummary, Date.now() - pipelineStart);
219001
219046
  await yieldTui();
219002
219047
  progress.onPhaseStart(4);
@@ -219017,11 +219062,11 @@ async function runFocusPipeline(data, harness, sessionStats, onLog, logger) {
219017
219062
  sessionStats.metadata.sessionId,
219018
219063
  Date.now()
219019
219064
  );
219020
- if (logger) logger.info("analysis", "Phase4", `${rules.length} rules extracted`);
219065
+ if (logger) logger.info("Phase4", `${rules.length} rules extracted`);
219021
219066
  progress.onPhaseDone(4, `${rules.length} \u6761\u89C4\u5219`, Date.now() - pipelineStart);
219022
219067
  const totalDuration = Date.now() - pipelineStart;
219023
219068
  const keyFindings = attribution.mistakeStep > 0 ? `\u6839\u56E0: ${attribution.mistakeAgent}@Step ${attribution.mistakeStep}` : "\u672A\u68C0\u6D4B\u5230\u660E\u786E\u6839\u56E0";
219024
- if (logger) logger.info("analysis", "FocusPipeline", `Done: ${(totalDuration / 1e3).toFixed(1)}s, ${totalLLMCalls} LLM calls, ${keyFindings}`);
219069
+ if (logger) logger.info("FocusPipeline", `Done: ${(totalDuration / 1e3).toFixed(1)}s, ${totalLLMCalls} LLM calls, ${keyFindings}`);
219025
219070
  progress.showCompletion({ totalDurationMs: totalDuration, totalLLMCalls, keyFindings });
219026
219071
  progress.dispose();
219027
219072
  const report = { skeleton, scan: scanResult, zoneAnalyses, attribution, totalLLMCalls };
@@ -219134,11 +219179,11 @@ async function executeStep1(steps, question, harness, logger) {
219134
219179
  const prev = result[i];
219135
219180
  const next = result[i + 1];
219136
219181
  if (prev.stepEnd >= next.stepStart) {
219137
- if (logger) logger.warn("analysis", "Step1", `Fixing overlap: ${prev.id} (${prev.stepStart}-${prev.stepEnd}) overlaps ${next.id} (${next.stepStart}-${next.stepEnd}), truncating ${prev.id}.stepEnd to ${next.stepStart - 1}`);
219182
+ if (logger) logger.warn("Step1", `Fixing overlap: ${prev.id} (${prev.stepStart}-${prev.stepEnd}) overlaps ${next.id} (${next.stepStart}-${next.stepEnd}), truncating ${prev.id}.stepEnd to ${next.stepStart - 1}`);
219138
219183
  prev.stepEnd = next.stepStart - 1;
219139
219184
  }
219140
219185
  if (prev.stepEnd + 1 < next.stepStart) {
219141
- if (logger) logger.warn("analysis", "Step1", `Filling gap: gap ${prev.stepEnd + 1}-${next.stepStart - 1} between ${prev.id} and ${next.id}, extending ${prev.id}.stepEnd to ${next.stepStart - 1}`);
219186
+ if (logger) logger.warn("Step1", `Filling gap: gap ${prev.stepEnd + 1}-${next.stepStart - 1} between ${prev.id} and ${next.id}, extending ${prev.id}.stepEnd to ${next.stepStart - 1}`);
219142
219187
  prev.stepEnd = next.stepStart - 1;
219143
219188
  }
219144
219189
  }
@@ -219191,16 +219236,16 @@ async function executeStep3(subtasks, steps, harness, logger) {
219191
219236
  const prompt = buildStep3SingleSubtaskPrompt(subtask, steps, subtasks);
219192
219237
  const flows = await callAndValidate(harness, prompt, validateStepDataFlows, 4096, `Step 3 / ${subtask.id}`);
219193
219238
  allFlows.push(...flows);
219194
- if (logger) logger.info("analysis", "Step3", `Subtask ${subtask.id}: ${flows.length} data flows extracted`);
219239
+ if (logger) logger.info("Step3", `Subtask ${subtask.id}: ${flows.length} data flows extracted`);
219195
219240
  } catch (err) {
219196
- if (logger) logger.warn("analysis", "Step3", `Subtask ${subtask.id}: ${err instanceof Error ? err.message : String(err)}`);
219241
+ if (logger) logger.warn("Step3", `Subtask ${subtask.id}: ${err instanceof Error ? err.message : String(err)}`);
219197
219242
  failedSubtasks++;
219198
219243
  }
219199
219244
  }
219200
219245
  if (failedSubtasks > 0) {
219201
- if (logger) logger.warn("analysis", "Step3", `${failedSubtasks}/${subtasks.length} subtasks failed to produce data flows`);
219246
+ if (logger) logger.warn("Step3", `${failedSubtasks}/${subtasks.length} subtasks failed to produce data flows`);
219202
219247
  }
219203
- if (logger) logger.info("analysis", "Step3", `Subtask summary: ${allFlows.length} total data flows from ${subtasks.length - failedSubtasks}/${subtasks.length} subtasks`);
219248
+ if (logger) logger.info("Step3", `Subtask summary: ${allFlows.length} total data flows from ${subtasks.length - failedSubtasks}/${subtasks.length} subtasks`);
219204
219249
  return { agents, dataFlows: allFlows };
219205
219250
  }
219206
219251
  async function executeStep4(subtasks, agentNodes, harness) {
@@ -219407,7 +219452,7 @@ function loadRuleStore(projectPath, logger) {
219407
219452
  updatedAt: parsed.updatedAt ?? Date.now()
219408
219453
  };
219409
219454
  } catch (err) {
219410
- if (logger) logger.warn("analysis", "RuleStore", `Failed to load rules.json, starting fresh: ${err.message}`);
219455
+ if (logger) logger.warn("RuleStore", `Failed to load rules.json, starting fresh: ${err.message}`);
219411
219456
  return createEmptyStore(projectPath);
219412
219457
  }
219413
219458
  }
@@ -219418,7 +219463,7 @@ function saveRuleStore(store, logger) {
219418
219463
  store.updatedAt = Date.now();
219419
219464
  writeFileSync13(path, JSON.stringify(store, null, 2), "utf8");
219420
219465
  } catch (err) {
219421
- if (logger) logger.error("analysis", "RuleStore", `Failed to save rules.json: ${err.message}`);
219466
+ if (logger) logger.error("RuleStore", `Failed to save rules.json: ${err.message}`);
219422
219467
  }
219423
219468
  }
219424
219469
  function createEmptyStore(projectPath) {
@@ -219476,7 +219521,7 @@ async function semanticMerge(newRules, existingStore, harness, logger) {
219476
219521
  rawOutput = await callMergeLLM(RULE_MERGE_SYSTEM, prompt + "\n\n\u26A0 Output PURE JSON array only.", harness);
219477
219522
  continue;
219478
219523
  }
219479
- if (logger) logger.warn("analysis", "RuleStore", "No JSON in merge LLM response, adding all as new");
219524
+ if (logger) logger.warn("RuleStore", "No JSON in merge LLM response, adding all as new");
219480
219525
  break;
219481
219526
  }
219482
219527
  const parsed = validateMergeDecisions(JSON.parse(json));
@@ -219494,10 +219539,10 @@ async function semanticMerge(newRules, existingStore, harness, logger) {
219494
219539
  );
219495
219540
  continue;
219496
219541
  }
219497
- if (logger) logger.warn("analysis", "RuleStore", `Merge validation failed: ${parsed.errors.join("; ")}`);
219542
+ if (logger) logger.warn("RuleStore", `Merge validation failed: ${parsed.errors.join("; ")}`);
219498
219543
  }
219499
219544
  } catch (err) {
219500
- if (logger) logger.warn("analysis", "RuleStore", `Semantic merge LLM call failed, adding all as new: ${err.message}`);
219545
+ if (logger) logger.warn("RuleStore", `Semantic merge LLM call failed, adding all as new: ${err.message}`);
219501
219546
  }
219502
219547
  const merged = { ...existingStore.rules };
219503
219548
  const decisionMap = new Map(decisions.map((d) => [d.newRuleId, d]));
@@ -219578,7 +219623,7 @@ async function runEval(sessionId, ctx) {
219578
219623
  }
219579
219624
  }
219580
219625
  }
219581
- evalLogger.clear("analysis");
219626
+ evalLogger.clear();
219582
219627
  ui.addInfo(`\u6B63\u5728\u5206\u6790 session ${resolvedId.slice(0, 8)}...`);
219583
219628
  const onLog = (msg) => {
219584
219629
  ui.addInfo(msg);
@@ -219604,9 +219649,9 @@ async function runEval(sessionId, ctx) {
219604
219649
  [${analysisLabel}] Messages: ${result.metadata.totalMessages} | Tool calls: ${result.stats.toolCalls} | Error rate: ${result.stats.errorRate}${attributionInfo} | Rules: ${rulesTriggered}`
219605
219650
  );
219606
219651
  } catch (err) {
219607
- evalLogger.error("analysis", "Pipeline", `crash: ${err instanceof Error ? err.message : String(err)}`);
219652
+ evalLogger.error("Pipeline", `crash: ${err instanceof Error ? err.message : String(err)}`);
219608
219653
  if (err instanceof Error && err.stack) {
219609
- evalLogger.error("analysis", "Pipeline", `stack:
219654
+ evalLogger.error("Pipeline", `stack:
219610
219655
  ${err.stack}`);
219611
219656
  }
219612
219657
  ui.addError(`eval: ${err instanceof Error ? err.message : String(err)}`);
@@ -220624,18 +220669,18 @@ function safeResolveWithin(projectPath, relPath) {
220624
220669
  }
220625
220670
  return resolved;
220626
220671
  }
220627
- function isTextPath(filePath) {
220628
- const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
220672
+ function isTextPath(filePath2) {
220673
+ const ext = filePath2.slice(filePath2.lastIndexOf(".")).toLowerCase();
220629
220674
  if (BINARY_EXTENSIONS.has(ext)) return false;
220630
220675
  if (TEXT_EXTENSIONS.has(ext)) return true;
220631
220676
  return false;
220632
220677
  }
220633
- function isImagePath(filePath) {
220634
- const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
220678
+ function isImagePath(filePath2) {
220679
+ const ext = filePath2.slice(filePath2.lastIndexOf(".")).toLowerCase();
220635
220680
  return IMAGE_EXTENSIONS.has(ext);
220636
220681
  }
220637
- function imageMimeType(filePath) {
220638
- const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
220682
+ function imageMimeType(filePath2) {
220683
+ const ext = filePath2.slice(filePath2.lastIndexOf(".")).toLowerCase();
220639
220684
  return IMAGE_MIME_MAP[ext] ?? "image/png";
220640
220685
  }
220641
220686
  function isBinaryContent(buf) {
@@ -220645,8 +220690,8 @@ function isBinaryContent(buf) {
220645
220690
  }
220646
220691
  return false;
220647
220692
  }
220648
- function inferLanguage(filePath) {
220649
- const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
220693
+ function inferLanguage(filePath2) {
220694
+ const ext = filePath2.slice(filePath2.lastIndexOf(".")).toLowerCase();
220650
220695
  return LANG_MAP[ext] ?? "";
220651
220696
  }
220652
220697
  function isLikelyFilePath(text) {
@@ -221308,7 +221353,7 @@ var DEFAULT_MAX_CHARS;
221308
221353
  var init_tool_result_formatter = __esm({
221309
221354
  "src/ui/shared/tool-result-formatter.ts"() {
221310
221355
  "use strict";
221311
- DEFAULT_MAX_CHARS = 600;
221356
+ DEFAULT_MAX_CHARS = 2e3;
221312
221357
  }
221313
221358
  });
221314
221359
 
@@ -221570,8 +221615,8 @@ __export(web_backend_exports, {
221570
221615
  WebUiBackend: () => WebUiBackend
221571
221616
  });
221572
221617
  import { createServer, request as httpRequest2 } from "node:http";
221573
- import { readFileSync as readFileSync21, existsSync as existsSync24 } from "node:fs";
221574
- import { join as join29, extname as extname2, resolve as resolve10 } from "node:path";
221618
+ import { readFileSync as readFileSync22, existsSync as existsSync25, mkdirSync as mkdirSync13, writeFileSync as writeFileSync15, rmSync as rmSync3, readdirSync as readdirSync9, statSync as statSync5 } from "node:fs";
221619
+ import { join as join30, extname as extname2, resolve as resolve10 } from "node:path";
221575
221620
  import { fileURLToPath as fileURLToPath3 } from "node:url";
221576
221621
  function extractImagesFromToolResult(result) {
221577
221622
  if (!result || typeof result !== "object") return void 0;
@@ -221630,6 +221675,15 @@ var init_web_backend = __esm({
221630
221675
  contextWindowThrottlePending = false;
221631
221676
  lastArtifactHtml = "";
221632
221677
  isAssistantTurn = false;
221678
+ cleanupUploadDir(sessionId) {
221679
+ const uploadDir = join30(this.config.projectPath, ".dscode", "uploads", sessionId);
221680
+ if (existsSync25(uploadDir)) {
221681
+ try {
221682
+ rmSync3(uploadDir, { recursive: true, force: true });
221683
+ } catch {
221684
+ }
221685
+ }
221686
+ }
221633
221687
  constructor(options) {
221634
221688
  this.port = options.port;
221635
221689
  this.harness = options.harness;
@@ -221803,22 +221857,6 @@ var init_web_backend = __esm({
221803
221857
  toolStart(name, args) {
221804
221858
  this.broadcast({ type: "tool_start", name, args });
221805
221859
  }
221806
- toolEnd(name, result, isError) {
221807
- const resultStr = typeof result === "string" ? result.slice(0, 5e3) : JSON.stringify(result).slice(0, 5e3);
221808
- const images = extractImagesFromToolResult(result);
221809
- if (this.currentAssistant) {
221810
- this.currentAssistant.tools = this.currentAssistant.tools.filter((t) => t.name !== name || t.result !== "");
221811
- this.currentAssistant.tools.push({
221812
- name,
221813
- args: "",
221814
- result: resultStr,
221815
- isError,
221816
- images
221817
- });
221818
- }
221819
- this.broadcast({ type: "tool_end", name, result: resultStr, isError, images });
221820
- this.broadcastContextWindow(false);
221821
- }
221822
221860
  finishAssistantMessage() {
221823
221861
  this.broadcastSessionTime();
221824
221862
  this.stopSessionTimeBroadcast();
@@ -222024,24 +222062,90 @@ var init_web_backend = __esm({
222024
222062
  for (const warn of resolved.warnings) {
222025
222063
  client.send({ type: "info", display: "toast", text: `@${warn.path ?? ""}: ${warn.type}${warn.detail ? ` \u2014 ${warn.detail}` : ""}` });
222026
222064
  }
222065
+ if (cmd.uploadedFiles && cmd.uploadedFiles.length > 0) {
222066
+ const sm = this.harness.sessionManager;
222067
+ const sessionId = sm.getCurrentSessionId?.() ?? "default";
222068
+ const ts = Date.now();
222069
+ const uploadDir = join30(this.config.projectPath, ".dscode", "uploads", sessionId);
222070
+ mkdirSync13(uploadDir, { recursive: true });
222071
+ const tempPaths = [];
222072
+ for (const uf of cmd.uploadedFiles) {
222073
+ const tempName = `${ts}-${uf.name}`;
222074
+ const tempPath = join30(uploadDir, tempName);
222075
+ writeFileSync15(tempPath, Buffer.from(uf.content, "base64"));
222076
+ tempPaths.push(tempPath);
222077
+ }
222078
+ if (tempPaths.length > 0) {
222079
+ const pathLines = tempPaths.map((p) => `- \`${p}\``).join("\n");
222080
+ text = text ? `${text}
222081
+
222082
+ \u{1F4C1} Attached files:
222083
+ ${pathLines}` : `\u{1F4C1} Attached files:
222084
+ ${pathLines}`;
222085
+ }
222086
+ }
222027
222087
  if (cmd.fileRefs && cmd.fileRefs.length > 0) {
222028
- const refsResolved = resolveFileRefs(this.config.projectPath, cmd.fileRefs, this.config.atFile ?? {});
222029
- if (!refsResolved.reject) {
222030
- if (refsResolved.text) {
222031
- text = text ? `${text}
222088
+ const imageRefs = cmd.fileRefs.filter((f) => isImagePath(f));
222089
+ const nonImageRefs = cmd.fileRefs.filter((f) => !isImagePath(f));
222090
+ if (nonImageRefs.length > 0) {
222091
+ const pathLines = nonImageRefs.map((f) => `- \`${f}\``).join("\n");
222092
+ text = text ? `${text}
222093
+
222094
+ \u{1F4C1} Attached files:
222095
+ ${pathLines}` : `\u{1F4C1} Attached files:
222096
+ ${pathLines}`;
222097
+ }
222098
+ if (imageRefs.length > 0) {
222099
+ const refsResolved = resolveFileRefs(this.config.projectPath, imageRefs, this.config.atFile ?? {});
222100
+ if (!refsResolved.reject) {
222101
+ if (refsResolved.text) {
222102
+ text = text ? `${text}
222032
222103
 
222033
222104
  ${refsResolved.text}` : refsResolved.text;
222105
+ }
222106
+ if (refsResolved.images.length > 0) {
222107
+ const refImages = refsResolved.images.map((img) => ({
222108
+ data: img.data,
222109
+ mimeType: img.mimeType
222110
+ }));
222111
+ images = [...images ?? [], ...refImages];
222112
+ }
222034
222113
  }
222035
- if (refsResolved.images.length > 0) {
222036
- const refImages = refsResolved.images.map((img) => ({
222037
- data: img.data,
222038
- mimeType: img.mimeType
222039
- }));
222040
- images = [...images ?? [], ...refImages];
222114
+ for (const warn of refsResolved.warnings) {
222115
+ client.send({ type: "info", display: "toast", text: `${warn.path ?? ""}: ${warn.type}${warn.detail ? ` \u2014 ${warn.detail}` : ""}` });
222041
222116
  }
222042
222117
  }
222043
- for (const warn of refsResolved.warnings) {
222044
- client.send({ type: "info", display: "toast", text: `${warn.path ?? ""}: ${warn.type}${warn.detail ? ` \u2014 ${warn.detail}` : ""}` });
222118
+ }
222119
+ if (cmd.fileRefs && cmd.fileRefs.length > 0) {
222120
+ const imageRefs = cmd.fileRefs.filter((f) => isImagePath(f));
222121
+ const nonImageRefs = cmd.fileRefs.filter((f) => !isImagePath(f));
222122
+ if (nonImageRefs.length > 0) {
222123
+ const pathLines = nonImageRefs.map((f) => `- \`${f}\``).join("\n");
222124
+ text = text ? `${text}
222125
+
222126
+ \u{1F4C1} Attached files:
222127
+ ${pathLines}` : `\u{1F4C1} Attached files:
222128
+ ${pathLines}`;
222129
+ }
222130
+ if (imageRefs.length > 0) {
222131
+ const refsResolved = resolveFileRefs(this.config.projectPath, imageRefs, this.config.atFile ?? {});
222132
+ if (!refsResolved.reject) {
222133
+ if (refsResolved.text) {
222134
+ text = text ? `${text}
222135
+
222136
+ ${refsResolved.text}` : refsResolved.text;
222137
+ }
222138
+ if (refsResolved.images.length > 0) {
222139
+ const refImages = refsResolved.images.map((img) => ({
222140
+ data: img.data,
222141
+ mimeType: img.mimeType
222142
+ }));
222143
+ images = [...images ?? [], ...refImages];
222144
+ }
222145
+ }
222146
+ for (const warn of refsResolved.warnings) {
222147
+ client.send({ type: "info", display: "toast", text: `${warn.path ?? ""}: ${warn.type}${warn.detail ? ` \u2014 ${warn.detail}` : ""}` });
222148
+ }
222045
222149
  }
222046
222150
  }
222047
222151
  client.send({ type: "user_message", text, images: images && images.length > 0 ? images : void 0 });
@@ -222054,6 +222158,7 @@ ${refsResolved.text}` : refsResolved.text;
222054
222158
  mimeType: img.mimeType
222055
222159
  }));
222056
222160
  this.pendingImages = [];
222161
+ this.harness.logger.info("WebBackend", `promptWithImages: textLen=${text.length}, images=${imageContents.length}, img[0].dataLen=${imageContents[0]?.data?.length ?? 0}, mime=${imageContents[0]?.mimeType ?? "?"}`);
222057
222162
  await this.harness.promptWithImages(text, imageContents);
222058
222163
  } else {
222059
222164
  this.pendingImages = [];
@@ -222082,7 +222187,7 @@ ${refsResolved.text}` : refsResolved.text;
222082
222187
  fuzzyPattern: fuzzy,
222083
222188
  permissionArgs: this.currentPermissionArgs
222084
222189
  };
222085
- this.harness.logger.info("session", "WebBackend", "saving pendingPermission to metadata, then denying");
222190
+ this.harness.logger.info("WebBackend", "saving pendingPermission to metadata, then denying");
222086
222191
  sm.saveSession(this.harness.agent, meta.pendingPermission);
222087
222192
  }
222088
222193
  this.permissionResolve({ decision: "deny" });
@@ -222201,12 +222306,51 @@ ${refsResolved.text}` : refsResolved.text;
222201
222306
  await this.handleArtifact(client, cmd);
222202
222307
  break;
222203
222308
  }
222309
+ case "cache": {
222310
+ this.handleCache(client, cmd);
222311
+ break;
222312
+ }
222204
222313
  case "mcp": {
222205
222314
  this.handleMcp(client, cmd);
222206
222315
  break;
222207
222316
  }
222208
222317
  }
222209
222318
  }
222319
+ handleCache(client, cmd) {
222320
+ const uploadsDir = join30(this.config.projectPath, ".dscode", "uploads");
222321
+ if (cmd.action === "clear") {
222322
+ if (existsSync25(uploadsDir)) {
222323
+ try {
222324
+ rmSync3(uploadsDir, { recursive: true, force: true });
222325
+ } catch {
222326
+ }
222327
+ }
222328
+ client.send({ type: "cache_size", totalBytes: 0, fileCount: 0, sessionCount: 0 });
222329
+ return;
222330
+ }
222331
+ let totalBytes = 0;
222332
+ let fileCount = 0;
222333
+ let sessionCount = 0;
222334
+ if (existsSync25(uploadsDir)) {
222335
+ const sessionDirs = readdirSync9(uploadsDir, { withFileTypes: true }).filter((d) => d.isDirectory());
222336
+ sessionCount = sessionDirs.length;
222337
+ for (const dir of sessionDirs) {
222338
+ const sessionPath = join30(uploadsDir, dir.name);
222339
+ const files = readdirSync9(sessionPath, { withFileTypes: true });
222340
+ for (const f of files) {
222341
+ if (f.isFile()) {
222342
+ try {
222343
+ const st = statSync5(join30(sessionPath, f.name));
222344
+ totalBytes += st.size;
222345
+ fileCount++;
222346
+ } catch {
222347
+ }
222348
+ }
222349
+ }
222350
+ }
222351
+ }
222352
+ client.send({ type: "cache_size", totalBytes, fileCount, sessionCount });
222353
+ }
222210
222354
  async handleSlashCommand(client, text) {
222211
222355
  try {
222212
222356
  const executed = executeSlashCommand(text, { harness: this.harness, ui: this });
@@ -222531,15 +222675,15 @@ ${matchList}` });
222531
222675
  const meta = sessionManager.getCurrentMetadata();
222532
222676
  if (meta?.pendingPermission) {
222533
222677
  pendingPermission = meta.pendingPermission;
222534
- this.harness.logger.info("session", "WebBackend", "using pendingPermission from metadata");
222678
+ this.harness.logger.info("WebBackend", "using pendingPermission from metadata");
222535
222679
  }
222536
222680
  }
222537
222681
  this.harness.abort();
222538
222682
  if (pendingPermission) {
222539
- this.harness.logger.info("session", "WebBackend", `saving with pendingPermission: ${JSON.stringify(pendingPermission)}`);
222683
+ this.harness.logger.info("WebBackend", `saving with pendingPermission: ${JSON.stringify(pendingPermission)}`);
222540
222684
  sessionManager.saveSession(agent, pendingPermission);
222541
222685
  } else {
222542
- this.harness.logger.info("session", "WebBackend", "saving without pendingPermission");
222686
+ this.harness.logger.info("WebBackend", "saving without pendingPermission");
222543
222687
  this.harness.saveSessionNow();
222544
222688
  }
222545
222689
  const result = await sessionManager.loadSession(match.id, agent);
@@ -222598,6 +222742,7 @@ ${matchList}` });
222598
222742
  if (wasCurrent) {
222599
222743
  client.send({ type: "clear_conversation" });
222600
222744
  }
222745
+ this.cleanupUploadDir(cmd.id);
222601
222746
  client.send({ type: "info", display: "toast", text: "Session deleted." });
222602
222747
  const sessions = sessionManager.listSessions();
222603
222748
  const currentId = sessionManager.getCurrentSessionId?.() ?? void 0;
@@ -222781,15 +222926,15 @@ ${matchList}` });
222781
222926
  req.pipe(proxyReq);
222782
222927
  }
222783
222928
  serveSpa(req, res) {
222784
- const projectDist = join29(resolve10(this.projectRoot), "dist", "web");
222785
- const moduleDist = join29(fileURLToPath3(new URL(".", import.meta.url)), "web");
222786
- const webDist = existsSync24(projectDist) ? projectDist : moduleDist;
222787
- let filePath = join29(webDist, req.url === "/" ? "index.html" : req.url);
222788
- const qIdx = filePath.indexOf("?");
222789
- if (qIdx >= 0) filePath = filePath.slice(0, qIdx);
222790
- const hIdx = filePath.indexOf("#");
222791
- if (hIdx >= 0) filePath = filePath.slice(0, hIdx);
222792
- const ext = extname2(filePath);
222929
+ const projectDist = join30(resolve10(this.projectRoot), "dist", "web");
222930
+ const moduleDist = join30(fileURLToPath3(new URL(".", import.meta.url)), "web");
222931
+ const webDist = existsSync25(projectDist) ? projectDist : moduleDist;
222932
+ let filePath2 = join30(webDist, req.url === "/" ? "index.html" : req.url);
222933
+ const qIdx = filePath2.indexOf("?");
222934
+ if (qIdx >= 0) filePath2 = filePath2.slice(0, qIdx);
222935
+ const hIdx = filePath2.indexOf("#");
222936
+ if (hIdx >= 0) filePath2 = filePath2.slice(0, hIdx);
222937
+ const ext = extname2(filePath2);
222793
222938
  const mimeTypes = {
222794
222939
  ".html": "text/html",
222795
222940
  ".js": "application/javascript",
@@ -222802,12 +222947,12 @@ ${matchList}` });
222802
222947
  ".woff": "font/woff"
222803
222948
  };
222804
222949
  try {
222805
- if (existsSync24(filePath) && ext) {
222806
- const content = readFileSync21(filePath);
222950
+ if (existsSync25(filePath2) && ext) {
222951
+ const content = readFileSync22(filePath2);
222807
222952
  res.writeHead(200, { "Content-Type": mimeTypes[ext] ?? "application/octet-stream" });
222808
222953
  res.end(content);
222809
222954
  } else {
222810
- const html = readFileSync21(join29(webDist, "index.html"));
222955
+ const html = readFileSync22(join30(webDist, "index.html"));
222811
222956
  res.writeHead(200, { "Content-Type": "text/html" });
222812
222957
  res.end(html);
222813
222958
  }
@@ -222820,9 +222965,9 @@ ${matchList}` });
222820
222965
  try {
222821
222966
  let styleConstraints = "";
222822
222967
  try {
222823
- const skillPath = join29(this.config.projectPath, ".dscode", "html_output_skill");
222824
- if (existsSync24(skillPath)) {
222825
- styleConstraints = readFileSync21(skillPath, "utf-8");
222968
+ const skillPath = join30(this.config.projectPath, ".dscode", "html_output_skill");
222969
+ if (existsSync25(skillPath)) {
222970
+ styleConstraints = readFileSync22(skillPath, "utf-8");
222826
222971
  }
222827
222972
  } catch {
222828
222973
  }
@@ -222907,6 +223052,10 @@ Modify the HTML to fulfill the user's request. Output the complete modified HTML
222907
223052
  }
222908
223053
  }
222909
223054
  fullHtml = this.stripArtifactFences(fullHtml);
223055
+ try {
223056
+ writeFileSync15(join30(this.config.projectPath, "_artifact_debug.html"), fullHtml, "utf-8");
223057
+ } catch {
223058
+ }
222910
223059
  this.lastArtifactHtml = fullHtml;
222911
223060
  client.send({ type: "artifact_end" });
222912
223061
  } catch (err) {
@@ -223040,8 +223189,8 @@ Modify the HTML to fulfill the user's request. Output the complete modified HTML
223040
223189
 
223041
223190
  // src/core/main.ts
223042
223191
  init_config();
223043
- import { existsSync as existsSync25, readFileSync as readFileSync22 } from "node:fs";
223044
- import { dirname as dirname7, join as join30, resolve as resolve11 } from "node:path";
223192
+ import { existsSync as existsSync26, readFileSync as readFileSync23 } from "node:fs";
223193
+ import { dirname as dirname7, join as join31, resolve as resolve11 } from "node:path";
223045
223194
  import { fileURLToPath as fileURLToPath4 } from "node:url";
223046
223195
  import { randomBytes } from "node:crypto";
223047
223196
 
@@ -224043,13 +224192,13 @@ var CheckpointManager = class {
224043
224192
  return this.baseCommit;
224044
224193
  }
224045
224194
  /** Save a checkpoint of the file before modification. */
224046
- save(filePath, writerType) {
224047
- const fileExists4 = existsSync7(filePath);
224048
- const content = fileExists4 ? readFileSync6(filePath) : null;
224049
- this.store.delete(filePath);
224195
+ save(filePath2, writerType) {
224196
+ const fileExists4 = existsSync7(filePath2);
224197
+ const content = fileExists4 ? readFileSync6(filePath2) : null;
224198
+ this.store.delete(filePath2);
224050
224199
  this.store.save(
224051
224200
  {
224052
- filePath,
224201
+ filePath: filePath2,
224053
224202
  baseCommit: this.baseCommit,
224054
224203
  writerType,
224055
224204
  timestamp: Date.now(),
@@ -224060,31 +224209,31 @@ var CheckpointManager = class {
224060
224209
  );
224061
224210
  }
224062
224211
  /** Restore file from its checkpoint and remove the checkpoint. */
224063
- rollback(filePath) {
224064
- const entry = this.store.load(filePath);
224212
+ rollback(filePath2) {
224213
+ const entry = this.store.load(filePath2);
224065
224214
  if (!entry) {
224066
- throw new Error(`No checkpoint found for ${filePath}`);
224215
+ throw new Error(`No checkpoint found for ${filePath2}`);
224067
224216
  }
224068
224217
  if (entry.content !== null) {
224069
- const parentDir = dirname(filePath);
224218
+ const parentDir = dirname(filePath2);
224070
224219
  if (!existsSync7(parentDir)) {
224071
224220
  mkdirSync6(parentDir, { recursive: true });
224072
224221
  }
224073
- writeFileSync6(filePath, entry.content);
224222
+ writeFileSync6(filePath2, entry.content);
224074
224223
  } else {
224075
- if (existsSync7(filePath)) {
224076
- unlinkSync2(filePath);
224224
+ if (existsSync7(filePath2)) {
224225
+ unlinkSync2(filePath2);
224077
224226
  }
224078
224227
  }
224079
- this.store.delete(filePath);
224228
+ this.store.delete(filePath2);
224080
224229
  }
224081
224230
  /** Commit (remove) the checkpoint after a successful write. */
224082
- commit(filePath) {
224083
- this.store.delete(filePath);
224231
+ commit(filePath2) {
224232
+ this.store.delete(filePath2);
224084
224233
  }
224085
224234
  /** Check if a file has an uncommitted checkpoint. */
224086
- isDirty(filePath) {
224087
- return this.store.load(filePath) !== null;
224235
+ isDirty(filePath2) {
224236
+ return this.store.load(filePath2) !== null;
224088
224237
  }
224089
224238
  /** List all file paths with uncommitted checkpoints. */
224090
224239
  listDirty() {
@@ -224099,20 +224248,20 @@ var CheckpointManager = class {
224099
224248
  // src/checkpoint/write-tracker.ts
224100
224249
  var FileWriteTracker = class {
224101
224250
  writers = /* @__PURE__ */ new Map();
224102
- recordWrite(filePath, writerType) {
224103
- this.writers.set(filePath, writerType);
224251
+ recordWrite(filePath2, writerType) {
224252
+ this.writers.set(filePath2, writerType);
224104
224253
  }
224105
- getWriter(filePath) {
224106
- return this.writers.get(filePath) ?? null;
224254
+ getWriter(filePath2) {
224255
+ return this.writers.get(filePath2) ?? null;
224107
224256
  }
224108
- getContinuity(filePath, currentWriter) {
224109
- const prev = this.writers.get(filePath);
224257
+ getContinuity(filePath2, currentWriter) {
224258
+ const prev = this.writers.get(filePath2);
224110
224259
  if (prev === void 0 || prev === currentWriter) return "clean";
224111
224260
  return "mixed";
224112
224261
  }
224113
224262
  /** Mark a file as having been written by bash (external/black-box writer). */
224114
- markExternalWrite(filePath) {
224115
- this.writers.set(filePath, "bash");
224263
+ markExternalWrite(filePath2) {
224264
+ this.writers.set(filePath2, "bash");
224116
224265
  return { anchorInvalidated: true };
224117
224266
  }
224118
224267
  };
@@ -224126,11 +224275,11 @@ var FileSystemCheckpointStore = class {
224126
224275
  this.baseDir = baseDir;
224127
224276
  mkdirSync7(this.baseDir, { recursive: true });
224128
224277
  }
224129
- safeName(filePath) {
224130
- return filePath.replace(/[\/\\:]/g, "_").replace(/^_+/, "");
224278
+ safeName(filePath2) {
224279
+ return filePath2.replace(/[\/\\:]/g, "_").replace(/^_+/, "");
224131
224280
  }
224132
- findCheckpointDir(filePath) {
224133
- const prefix = this.safeName(filePath) + "-";
224281
+ findCheckpointDir(filePath2) {
224282
+ const prefix = this.safeName(filePath2) + "-";
224134
224283
  if (!existsSync8(this.baseDir)) return null;
224135
224284
  let entries;
224136
224285
  try {
@@ -224158,8 +224307,8 @@ var FileSystemCheckpointStore = class {
224158
224307
  writeFileSync7(join7(dir, "original"), content);
224159
224308
  }
224160
224309
  }
224161
- load(filePath) {
224162
- const dir = this.findCheckpointDir(filePath);
224310
+ load(filePath2) {
224311
+ const dir = this.findCheckpointDir(filePath2);
224163
224312
  if (!dir) return null;
224164
224313
  const metaPath = join7(dir, "meta.json");
224165
224314
  const originalPath = join7(dir, "original");
@@ -224167,8 +224316,8 @@ var FileSystemCheckpointStore = class {
224167
224316
  const content = existsSync8(originalPath) ? readFileSync7(originalPath) : null;
224168
224317
  return { meta, content };
224169
224318
  }
224170
- delete(filePath) {
224171
- const dir = this.findCheckpointDir(filePath);
224319
+ delete(filePath2) {
224320
+ const dir = this.findCheckpointDir(filePath2);
224172
224321
  if (dir) {
224173
224322
  rmSync(dir, { recursive: true, force: true });
224174
224323
  }
@@ -224221,27 +224370,27 @@ var SnapshotStore = class {
224221
224370
  * Record a snapshot of a file. Called after read_file returns hashed content
224222
224371
  * (so the model receives the file_version and can use it for later edits).
224223
224372
  */
224224
- record(filePath, fileVersion, content) {
224225
- const key = this.makeKey(filePath, fileVersion);
224373
+ record(filePath2, fileVersion, content) {
224374
+ const key = this.makeKey(filePath2, fileVersion);
224226
224375
  const existing = this.lruKeys.indexOf(key);
224227
224376
  if (existing >= 0) {
224228
224377
  this.lruKeys.splice(existing, 1);
224229
224378
  this.lruKeys.push(key);
224230
- const entries2 = this.byFile.get(filePath);
224379
+ const entries2 = this.byFile.get(filePath2);
224231
224380
  const entry2 = entries2.find((e) => e.fileVersion === fileVersion);
224232
224381
  if (entry2) entry2.timestamp = Date.now();
224233
224382
  return;
224234
224383
  }
224235
224384
  const entry = {
224236
- filePath,
224385
+ filePath: filePath2,
224237
224386
  fileVersion,
224238
224387
  content,
224239
224388
  timestamp: Date.now()
224240
224389
  };
224241
- let entries = this.byFile.get(filePath);
224390
+ let entries = this.byFile.get(filePath2);
224242
224391
  if (!entries) {
224243
224392
  entries = [];
224244
- this.byFile.set(filePath, entries);
224393
+ this.byFile.set(filePath2, entries);
224245
224394
  }
224246
224395
  entries.unshift(entry);
224247
224396
  while (entries.length > this.maxSnapshotsPerFile) {
@@ -224264,12 +224413,12 @@ var SnapshotStore = class {
224264
224413
  * Look up a snapshot by file path and version.
224265
224414
  * Returns the full file content or null if not found.
224266
224415
  */
224267
- lookup(filePath, fileVersion) {
224268
- const entries = this.byFile.get(filePath);
224416
+ lookup(filePath2, fileVersion) {
224417
+ const entries = this.byFile.get(filePath2);
224269
224418
  if (!entries) return null;
224270
224419
  const entry = entries.find((e) => e.fileVersion === fileVersion);
224271
224420
  if (!entry) return null;
224272
- const key = this.makeKey(filePath, fileVersion);
224421
+ const key = this.makeKey(filePath2, fileVersion);
224273
224422
  const idx = this.lruKeys.indexOf(key);
224274
224423
  if (idx >= 0) {
224275
224424
  this.lruKeys.splice(idx, 1);
@@ -224281,13 +224430,13 @@ var SnapshotStore = class {
224281
224430
  /**
224282
224431
  * Remove all snapshots for a file (called after successful write_file / overwrite_file).
224283
224432
  */
224284
- invalidate(filePath) {
224285
- const entries = this.byFile.get(filePath);
224433
+ invalidate(filePath2) {
224434
+ const entries = this.byFile.get(filePath2);
224286
224435
  if (!entries) return;
224287
224436
  for (const e of entries) {
224288
224437
  this.removeFromLru(this.makeKey(e.filePath, e.fileVersion));
224289
224438
  }
224290
- this.byFile.delete(filePath);
224439
+ this.byFile.delete(filePath2);
224291
224440
  }
224292
224441
  /** Remove all snapshots. */
224293
224442
  clear() {
@@ -224299,8 +224448,8 @@ var SnapshotStore = class {
224299
224448
  return this.lruKeys.length;
224300
224449
  }
224301
224450
  // --- Private helpers ---
224302
- makeKey(filePath, fileVersion) {
224303
- return `${filePath}::${fileVersion}`;
224451
+ makeKey(filePath2, fileVersion) {
224452
+ return `${filePath2}::${fileVersion}`;
224304
224453
  }
224305
224454
  parseKey(key) {
224306
224455
  const idx = key.lastIndexOf("::");
@@ -225406,17 +225555,17 @@ function buildDiffPreview(snapshotLines, expectedLines, currentLines, patch) {
225406
225555
 
225407
225556
  // src/drivers/edit/undo-store.ts
225408
225557
  var undoSnapshots = /* @__PURE__ */ new Map();
225409
- function captureUndoSnapshot(filePath, content) {
225410
- undoSnapshots.set(filePath, content);
225558
+ function captureUndoSnapshot(filePath2, content) {
225559
+ undoSnapshots.set(filePath2, content);
225411
225560
  }
225412
- function getUndoSnapshot(filePath) {
225413
- return undoSnapshots.get(filePath);
225561
+ function getUndoSnapshot(filePath2) {
225562
+ return undoSnapshots.get(filePath2);
225414
225563
  }
225415
- function clearUndoSnapshot(filePath) {
225416
- undoSnapshots.delete(filePath);
225564
+ function clearUndoSnapshot(filePath2) {
225565
+ undoSnapshots.delete(filePath2);
225417
225566
  }
225418
- function hasUndoSnapshot(filePath) {
225419
- return undoSnapshots.has(filePath);
225567
+ function hasUndoSnapshot(filePath2) {
225568
+ return undoSnapshots.has(filePath2);
225420
225569
  }
225421
225570
 
225422
225571
  // src/drivers/edit/syntax-validate.ts
@@ -225430,11 +225579,11 @@ var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
225430
225579
  ".html",
225431
225580
  ".json"
225432
225581
  ]);
225433
- function isSyntaxCheckSupported(filePath) {
225434
- return SUPPORTED_EXTENSIONS.has(extname(filePath).toLowerCase());
225582
+ function isSyntaxCheckSupported(filePath2) {
225583
+ return SUPPORTED_EXTENSIONS.has(extname(filePath2).toLowerCase());
225435
225584
  }
225436
- function getSyntaxTarget(filePath) {
225437
- const ext = extname(filePath).toLowerCase();
225585
+ function getSyntaxTarget(filePath2) {
225586
+ const ext = extname(filePath2).toLowerCase();
225438
225587
  switch (ext) {
225439
225588
  case ".js":
225440
225589
  return { kind: "js" };
@@ -225465,8 +225614,8 @@ async function getTypeScript() {
225465
225614
  }
225466
225615
  return _tsModule;
225467
225616
  }
225468
- async function validateSyntax(filePath, content) {
225469
- const target = getSyntaxTarget(filePath);
225617
+ async function validateSyntax(filePath2, content) {
225618
+ const target = getSyntaxTarget(filePath2);
225470
225619
  if (!target) {
225471
225620
  return { valid: true };
225472
225621
  }
@@ -225810,7 +225959,7 @@ var editTool = {
225810
225959
  };
225811
225960
  }
225812
225961
  if (file_path && !path) {
225813
- _editLogger.warn("tool", "Edit", "file_path is deprecated, use path instead");
225962
+ _editLogger.warn("Edit", "file_path is deprecated, use path instead");
225814
225963
  }
225815
225964
  const resolved = resolve4(effectivePath);
225816
225965
  if (!await fileExists3(resolved)) {
@@ -226738,10 +226887,10 @@ function stripBomAndDecode(buf) {
226738
226887
  }
226739
226888
  return buf.toString("utf8");
226740
226889
  }
226741
- function parseSkillManifest(filePath, source) {
226890
+ function parseSkillManifest(filePath2, source) {
226742
226891
  let raw;
226743
226892
  try {
226744
- const buf = readFileSync9(filePath);
226893
+ const buf = readFileSync9(filePath2);
226745
226894
  raw = stripBomAndDecode(buf);
226746
226895
  } catch {
226747
226896
  return null;
@@ -226758,7 +226907,7 @@ function parseSkillManifest(filePath, source) {
226758
226907
  tools: parsed.tools,
226759
226908
  instructions: body || void 0,
226760
226909
  source,
226761
- path: filePath
226910
+ path: filePath2
226762
226911
  };
226763
226912
  }
226764
226913
  function parseFrontmatter(text) {
@@ -227053,10 +227202,10 @@ function stripBomAndDecode2(buf) {
227053
227202
  function normalizeName(name) {
227054
227203
  return name.toLowerCase().replace(/\s+/g, "");
227055
227204
  }
227056
- function parseCommandFile(filePath, source, derivedName) {
227205
+ function parseCommandFile(filePath2, source, derivedName) {
227057
227206
  let raw;
227058
227207
  try {
227059
- const buf = readFileSync10(filePath);
227208
+ const buf = readFileSync10(filePath2);
227060
227209
  raw = stripBomAndDecode2(buf);
227061
227210
  } catch {
227062
227211
  return null;
@@ -227070,7 +227219,7 @@ function parseCommandFile(filePath, source, derivedName) {
227070
227219
  if (!parsed.description) return null;
227071
227220
  if (parsed.name !== void 0 && normalizeName(parsed.name) !== normalizeName(derivedName)) {
227072
227221
  console.warn(
227073
- `[commands] Skipping ${filePath}: frontmatter name "${parsed.name}" does not match path-derived name "${derivedName}"`
227222
+ `[commands] Skipping ${filePath2}: frontmatter name "${parsed.name}" does not match path-derived name "${derivedName}"`
227074
227223
  );
227075
227224
  return null;
227076
227225
  }
@@ -227079,7 +227228,7 @@ function parseCommandFile(filePath, source, derivedName) {
227079
227228
  description: parsed.description,
227080
227229
  body,
227081
227230
  source,
227082
- path: filePath
227231
+ path: filePath2
227083
227232
  };
227084
227233
  }
227085
227234
  function parseFrontmatter2(text) {
@@ -227273,10 +227422,10 @@ var PermissionManager = class {
227273
227422
  const toolName = context.toolCall.name;
227274
227423
  const argsStr = JSON.stringify(context.args);
227275
227424
  if ((toolName === "read_file" || toolName === "write_file") && this.denyRegexes.length > 0) {
227276
- const filePath = context.args?.path;
227277
- if (filePath) {
227425
+ const filePath2 = context.args?.path;
227426
+ if (filePath2) {
227278
227427
  for (const { pattern, regex } of this.denyRegexes) {
227279
- if (regex.test(filePath)) {
227428
+ if (regex.test(filePath2)) {
227280
227429
  return { block: true, reason: `Denied by file pattern: ${pattern}` };
227281
227430
  }
227282
227431
  }
@@ -227688,6 +227837,7 @@ var MCPClient = class {
227688
227837
  if (this.closing) return;
227689
227838
  if (!this.closed) {
227690
227839
  this.closed = true;
227840
+ this.emit({ type: "disconnected", serverName: this.config.name, reason: `MCP server "${this.config.name}" exited with code ${code}` });
227691
227841
  setImmediate(() => {
227692
227842
  const stderr = stderrBuf.trim().slice(0, 500);
227693
227843
  const detail = stderr ? `: ${stderr}` : "";
@@ -227695,6 +227845,14 @@ var MCPClient = class {
227695
227845
  });
227696
227846
  }
227697
227847
  });
227848
+ this.process.on("error", (err) => {
227849
+ if (this.closing) return;
227850
+ if (!this.closed) {
227851
+ this.closed = true;
227852
+ this.emit({ type: "disconnected", serverName: this.config.name, reason: `MCP server "${this.config.name}" error: ${err.message}` });
227853
+ this.rejectAllPending(new Error(`MCP server "${this.config.name}" error: ${err.message}`));
227854
+ }
227855
+ });
227698
227856
  this.process.on("error", (err) => {
227699
227857
  if (this.closing) return;
227700
227858
  if (!this.closed) {
@@ -227767,11 +227925,18 @@ var MCPClient = class {
227767
227925
  res.on("end", () => {
227768
227926
  if (!this.closed) {
227769
227927
  this.closed = true;
227928
+ this.emit({ type: "disconnected", serverName: this.config.name, reason: `MCP server "${this.config.name}" SSE connection closed` });
227770
227929
  this.rejectAllPending(new Error(`MCP server "${this.config.name}" SSE connection closed`));
227771
227930
  }
227772
227931
  });
227773
227932
  res.on("error", (err) => {
227774
- reject(new Error(`MCP server "${this.config.name}" SSE error: ${err.message}`));
227933
+ if (!this.closed) {
227934
+ this.closed = true;
227935
+ this.emit({ type: "disconnected", serverName: this.config.name, reason: `MCP server "${this.config.name}" SSE error: ${err.message}` });
227936
+ this.rejectAllPending(new Error(`MCP server "${this.config.name}" SSE error: ${err.message}`));
227937
+ } else {
227938
+ reject(new Error(`MCP server "${this.config.name}" SSE error: ${err.message}`));
227939
+ }
227775
227940
  });
227776
227941
  }
227777
227942
  );
@@ -227999,7 +228164,7 @@ var MCPClient = class {
227999
228164
  return;
228000
228165
  }
228001
228166
  if (this.resolvedTransport === "streamable-http") {
228002
- this.sendHttpMessage(this.config.url, { jsonrpc: "2.0", id, method, params: finalParams }, true, true).then((response) => {
228167
+ this.sendHttpWithSessionRecovery(this.config.url, { jsonrpc: "2.0", id, method, params: finalParams }, id).then((response) => {
228003
228168
  const entry2 = this.pending.get(id);
228004
228169
  if (!entry2) return;
228005
228170
  if (response.statusCode >= 400) {
@@ -228185,6 +228350,30 @@ var MCPClient = class {
228185
228350
  }
228186
228351
  return headers;
228187
228352
  }
228353
+ async sendHttpWithSessionRecovery(url, payload, id) {
228354
+ const response = await this.sendHttpMessage(url, payload, true, true);
228355
+ const newSessionId = this.getHeader(response.headers, "mcp-session-id");
228356
+ if (newSessionId && newSessionId !== this.sessionId) {
228357
+ this.sessionId = newSessionId;
228358
+ }
228359
+ const isSessionExpired = (response.statusCode === 404 || response.statusCode === 410) && !this.getHeader(response.headers, "mcp-session-id");
228360
+ if (isSessionExpired && !this.closed) {
228361
+ try {
228362
+ await this.connectStreamableHttp();
228363
+ const retryResponse = await this.sendHttpMessage(url, payload, true, true);
228364
+ const retrySessionId = this.getHeader(retryResponse.headers, "mcp-session-id");
228365
+ if (retrySessionId) {
228366
+ this.sessionId = retrySessionId;
228367
+ }
228368
+ return retryResponse;
228369
+ } catch (reinitErr) {
228370
+ this.closed = true;
228371
+ this.emit({ type: "disconnected", serverName: this.config.name, reason: `MCP session recovery failed: ${reinitErr.message}` });
228372
+ throw reinitErr;
228373
+ }
228374
+ }
228375
+ return response;
228376
+ }
228188
228377
  async sendHttpMessage(url, payload, isRequest, includeContentType) {
228189
228378
  const parsed = new URL(url);
228190
228379
  const requester = parsed.protocol === "https:" ? httpsRequest : httpRequest;
@@ -228362,6 +228551,20 @@ function normalizeHtmlMimeType(mimeType) {
228362
228551
  if (!mimeType) return "";
228363
228552
  return mimeType.split(";").map((part) => part.trim()).sort().join(";");
228364
228553
  }
228554
+ function classifyError(err, transport, statusCode) {
228555
+ const msg = err.message ?? "";
228556
+ if (/ECONNREFUSED|ETIMEDOUT/i.test(msg)) return "transient";
228557
+ if (statusCode !== void 0) {
228558
+ if (statusCode === 502 || statusCode === 503 || statusCode === 504) return "transient";
228559
+ if (statusCode === 401 || statusCode === 403) return "permanent";
228560
+ if (statusCode === 404 || statusCode === 410) return "session_expired";
228561
+ }
228562
+ if (/ENOTFOUND/i.test(msg)) return "permanent";
228563
+ if (transport === "stdio" && /exited/i.test(msg)) return "transient";
228564
+ if (transport === "sse" && /connection closed/i.test(msg)) return "transient";
228565
+ if (/invalid|no command|no url/i.test(msg)) return "permanent";
228566
+ return "transient";
228567
+ }
228365
228568
  var MCPManager = class {
228366
228569
  constructor(configs) {
228367
228570
  this.configs = configs;
@@ -228487,37 +228690,66 @@ var MCPManager = class {
228487
228690
  if (this.driverRegistry) {
228488
228691
  this.driverRegistry.unregister(mcpDriverName(name));
228489
228692
  }
228490
- state.status = "connecting";
228491
- try {
228492
- const client = new MCPClient(cfg);
228493
- client.onEvent((event) => this.handleClientEvent(event));
228494
- await client.connect();
228495
- this.clients.set(name, client);
228496
- const tools = await client.listTools();
228497
- this.registerDriver(name, tools);
228498
- state.status = "connected";
228499
- state.error = void 0;
228500
- state.toolCount = tools.length;
228501
- state.negotiatedProtocolVersion = client.getNegotiatedProtocolVersion() ?? void 0;
228502
- state.resolvedTransport = client.getResolvedTransport();
228503
- state.compatibilityMode = client.getCompatibilityMode();
228504
- state.refreshState = "idle";
228505
- state.refreshError = void 0;
228506
- state.lastRefreshAt = Date.now();
228507
- return true;
228508
- } catch (err) {
228509
- const state2 = this.states.get(name);
228510
- if (state2.status === "connected" || state2.status === "error") {
228693
+ state.status = "reconnecting";
228694
+ const client = new MCPClient(cfg);
228695
+ client.onEvent((event) => this.handleClientEvent(event));
228696
+ await client.connect();
228697
+ this.clients.set(name, client);
228698
+ const tools = await client.listTools();
228699
+ this.registerDriver(name, tools);
228700
+ state.status = "connected";
228701
+ state.error = void 0;
228702
+ state.toolCount = tools.length;
228703
+ state.negotiatedProtocolVersion = client.getNegotiatedProtocolVersion() ?? void 0;
228704
+ state.resolvedTransport = client.getResolvedTransport();
228705
+ state.compatibilityMode = client.getCompatibilityMode();
228706
+ state.refreshState = "idle";
228707
+ state.refreshError = void 0;
228708
+ state.lastRefreshAt = Date.now();
228709
+ return true;
228710
+ }
228711
+ reconnectTimers = /* @__PURE__ */ new Map();
228712
+ scheduleReconnect(name, attempt) {
228713
+ const state = this.states.get(name);
228714
+ if (!state) return;
228715
+ if (state.status === "disconnected") return;
228716
+ if (attempt >= 10) {
228717
+ state.status = "error";
228718
+ state.error = "Reconnect attempts exhausted";
228719
+ state.refreshState = "error";
228720
+ state.refreshError = "Reconnect attempts exhausted";
228721
+ this.emit({ type: "tools_refresh_failed", serverName: name, error: "Reconnect attempts exhausted" });
228722
+ return;
228723
+ }
228724
+ state.status = "reconnecting";
228725
+ const delay = Math.min(3e4, 1e3 * Math.pow(2, attempt)) + Math.random() * 500;
228726
+ const timer = setTimeout(async () => {
228727
+ this.reconnectTimers.delete(name);
228728
+ try {
228511
228729
  await this.reconnectServer(name);
228512
- } else {
228513
- state2.status = "error";
228514
- state2.error = err.message ?? "Unknown error";
228515
- state2.refreshState = "error";
228516
- state2.refreshError = err.message ?? "Unknown error";
228730
+ state.refreshState = "idle";
228731
+ state.refreshError = void 0;
228732
+ this.emit({ type: "tools_refreshed", serverName: name, toolCount: state.toolCount });
228733
+ } catch (err) {
228734
+ const cfg = this.configs.find((c2) => c2.name === name);
228735
+ const errorClass = classifyError(err, cfg?.transport ?? "stdio");
228736
+ if (errorClass === "permanent" || errorClass === "session_expired") {
228737
+ if (errorClass === "session_expired") {
228738
+ this.scheduleReconnect(name, 0);
228739
+ return;
228740
+ }
228741
+ state.status = "error";
228742
+ state.error = err.message ?? "Unknown error";
228743
+ state.refreshState = "error";
228744
+ state.refreshError = err.message ?? "Unknown error";
228745
+ state.toolCount = 0;
228746
+ this.emit({ type: "tools_refresh_failed", serverName: name, error: err.message ?? "Unknown error" });
228747
+ return;
228748
+ }
228749
+ this.scheduleReconnect(name, attempt + 1);
228517
228750
  }
228518
- state2.toolCount = 0;
228519
- return false;
228520
- }
228751
+ }, delay);
228752
+ this.reconnectTimers.set(name, timer);
228521
228753
  }
228522
228754
  async registerDrivers(registry) {
228523
228755
  this.driverRegistry = registry;
@@ -228533,7 +228765,7 @@ var MCPManager = class {
228533
228765
  } catch (err) {
228534
228766
  const state = this.states.get(name);
228535
228767
  if (state.status === "connected" || state.status === "error") {
228536
- await this.reconnectServer(name);
228768
+ this.scheduleReconnect(name, 0);
228537
228769
  } else {
228538
228770
  state.status = "error";
228539
228771
  state.error = err.message ?? "Unknown error";
@@ -228559,6 +228791,10 @@ var MCPManager = class {
228559
228791
  })
228560
228792
  );
228561
228793
  this.clients.clear();
228794
+ for (const timer of this.reconnectTimers.values()) {
228795
+ clearTimeout(timer);
228796
+ }
228797
+ this.reconnectTimers.clear();
228562
228798
  }
228563
228799
  async connectServer(name) {
228564
228800
  const existing = this.clients.get(name);
@@ -228726,6 +228962,10 @@ var MCPManager = class {
228726
228962
  if (event.type === "resources_list_changed") {
228727
228963
  state.lastRefreshAt = Date.now();
228728
228964
  }
228965
+ if (event.type === "disconnected") {
228966
+ state.status = "reconnecting";
228967
+ this.scheduleReconnect(event.serverName, 0);
228968
+ }
228729
228969
  }
228730
228970
  for (const listener of this.eventListeners) {
228731
228971
  listener(event);
@@ -229492,9 +229732,9 @@ var ConversationView = class {
229492
229732
  mkdirSync8(cacheDir, { recursive: true });
229493
229733
  const ext = displayMimeType.split("/")[1] || "png";
229494
229734
  const filename = `${Date.now()}.${ext}`;
229495
- const filePath = join16(cacheDir, filename);
229496
- writeFileSync9(filePath, Buffer.from(displayData, "base64"));
229497
- this.pushText(c.dim(`[image: ${filePath}]`));
229735
+ const filePath2 = join16(cacheDir, filename);
229736
+ writeFileSync9(filePath2, Buffer.from(displayData, "base64"));
229737
+ this.pushText(c.dim(`[image: ${filePath2}]`));
229498
229738
  if (format !== "webp") {
229499
229739
  try {
229500
229740
  const img = new Image(displayData, displayMimeType, this.imageTheme, {
@@ -229913,6 +230153,13 @@ var FileTracker = class {
229913
230153
  getDisplayPaths() {
229914
230154
  return [...this.entries.values()];
229915
230155
  }
230156
+ /** Reverse lookup: return the absolute path for a given display path, or undefined. */
230157
+ getAbsPath(displayPath) {
230158
+ for (const [absPath, dp] of this.entries) {
230159
+ if (dp === displayPath) return absPath;
230160
+ }
230161
+ return void 0;
230162
+ }
229916
230163
  /** Clear all tracked files. */
229917
230164
  clear() {
229918
230165
  this.entries.clear();
@@ -230008,6 +230255,19 @@ function formatCost(total) {
230008
230255
  if (total >= 0.01) return `$${total.toFixed(4)}`;
230009
230256
  return `\xA2${(total * 100).toFixed(2)}`;
230010
230257
  }
230258
+ function findFileMarkerAt(text, cursorLine, cursorCol) {
230259
+ const lines = text.split("\n");
230260
+ if (cursorLine < 0 || cursorLine >= lines.length) return void 0;
230261
+ const line = lines[cursorLine];
230262
+ const re = /\[file:([^\]]+)\]/g;
230263
+ let m;
230264
+ while ((m = re.exec(line)) !== null) {
230265
+ if (cursorCol >= m.index && cursorCol <= m.index + m[0].length) {
230266
+ return m[1];
230267
+ }
230268
+ }
230269
+ return void 0;
230270
+ }
230011
230271
  var TuiApp = class {
230012
230272
  deps;
230013
230273
  terminal;
@@ -230050,6 +230310,10 @@ var TuiApp = class {
230050
230310
  attachmentScrollOffset = 0;
230051
230311
  // Pre-drained images: captured in input listener before Editor's onChange("") clears them
230052
230312
  drainedSubmitImages = null;
230313
+ // Pre-drained files: captured in input listener before Editor's onChange("") clears them
230314
+ drainedSubmitFiles = null;
230315
+ // Absolute path shown when cursor is on a [file:xxx] placeholder
230316
+ hoveredFilePath = null;
230053
230317
  lastPasteTime = 0;
230054
230318
  // ── Kitty protocol multi-chunk buffer ──
230055
230319
  // Kitty transmits large images in chunks. Accumulate base64 payloads here
@@ -230118,6 +230382,14 @@ var TuiApp = class {
230118
230382
  }
230119
230383
  }
230120
230384
  }
230385
+ const cursor = this.editor.getCursor();
230386
+ const editorText = this.editor.getText();
230387
+ const hoveredDisplayPath = findFileMarkerAt(editorText, cursor.line, cursor.col);
230388
+ if (hoveredDisplayPath) {
230389
+ this.hoveredFilePath = this.fileTracker.getAbsPath(hoveredDisplayPath) ?? null;
230390
+ } else {
230391
+ this.hoveredFilePath = null;
230392
+ }
230121
230393
  this.updateAttachmentBar();
230122
230394
  };
230123
230395
  this.tui.addInputListener((data) => {
@@ -230125,8 +230397,13 @@ var TuiApp = class {
230125
230397
  if (pasteResult) {
230126
230398
  return pasteResult;
230127
230399
  }
230128
- if ((matchesKey(data, Key.enter) || matchesKey(data, Key.return) || data === "\r" || data === "\n") && this.imagePasteHandler.imageCount > 0 && !this.processing) {
230129
- this.drainedSubmitImages = this.imagePasteHandler.drainImages();
230400
+ if ((matchesKey(data, Key.enter) || matchesKey(data, Key.return) || data === "\r" || data === "\n") && !this.processing) {
230401
+ if (this.imagePasteHandler.imageCount > 0) {
230402
+ this.drainedSubmitImages = this.imagePasteHandler.drainImages();
230403
+ }
230404
+ if (this.fileTracker.count > 0) {
230405
+ this.drainedSubmitFiles = this.fileTracker.drain();
230406
+ }
230130
230407
  }
230131
230408
  if (this.handleInput(data)) {
230132
230409
  return { consume: true };
@@ -230458,7 +230735,9 @@ var TuiApp = class {
230458
230735
  const chipsLine = parts.join(" ");
230459
230736
  const scrollHint = this.attachmentScrollOffset > 0 ? ` +${this.attachmentScrollOffset} more` : "";
230460
230737
  const hintLine = c.dim(`Ctrl+Shift+\u2190 \u2192 scroll${scrollHint} \xB7 Esc clear all`);
230461
- this.imageStatus.setText(`${chipsLine}
230738
+ const hoverLine = this.hoveredFilePath ? c.dim(`
230739
+ Path: ${this.hoveredFilePath}`) : "";
230740
+ this.imageStatus.setText(`${chipsLine}${hoverLine}
230462
230741
  ${hintLine}`);
230463
230742
  this.tui.requestRender(true);
230464
230743
  }
@@ -230921,7 +231200,8 @@ ${hintLine}`);
230921
231200
  }
230922
231201
  const hasText = text.length > 0;
230923
231202
  const hasImages = Boolean(images?.length);
230924
- const fileRefs = this.fileTracker.drain();
231203
+ const fileRefs = this.drainedSubmitFiles ?? this.fileTracker.drain();
231204
+ this.drainedSubmitFiles = null;
230925
231205
  const hasFiles = fileRefs.length > 0;
230926
231206
  if (!hasText && !hasImages && !hasFiles) {
230927
231207
  const now = Date.now();
@@ -230935,7 +231215,7 @@ ${hintLine}`);
230935
231215
  });
230936
231216
  return;
230937
231217
  }
230938
- if (!hasText && !hasImages) {
231218
+ if (!hasText && !hasImages && !hasFiles) {
230939
231219
  const now = Date.now();
230940
231220
  if (now - this.lastPasteTime < 100) return;
230941
231221
  this.lastPasteTime = now;
@@ -231022,30 +231302,41 @@ ${hintLine}`);
231022
231302
  images = [...images ?? [], ...atImages];
231023
231303
  }
231024
231304
  if (hasFiles) {
231025
- const dedupedFileRefs = fileRefs.filter((f) => !atPathAbsPaths.has(f));
231026
- if (dedupedFileRefs.length > 0) {
231027
- const refsResolved = resolveFileRefs(this.deps.config.projectPath, dedupedFileRefs, this.deps.config.atFile ?? {});
231028
- if (refsResolved.warnings.length > 0) {
231029
- for (const warn of refsResolved.warnings) {
231030
- this.conversation.addInfo(c.yellow(`${warn.path ?? ""}: ${warn.type}${warn.detail ? ` \u2014 ${warn.detail}` : ""}`));
231305
+ const imageRefs = fileRefs.filter((f) => isImagePath(f));
231306
+ const nonImageRefs = fileRefs.filter((f) => !isImagePath(f));
231307
+ const promptFilePaths = nonImageRefs.length > 0 ? [...nonImageRefs] : [];
231308
+ if (imageRefs.length > 0) {
231309
+ const dedupedImageRefs = imageRefs.filter((f) => !atPathAbsPaths.has(f));
231310
+ if (dedupedImageRefs.length > 0) {
231311
+ promptFilePaths.push(...dedupedImageRefs);
231312
+ const refsResolved = resolveFileRefs(this.deps.config.projectPath, dedupedImageRefs, this.deps.config.atFile ?? {});
231313
+ if (refsResolved.warnings.length > 0) {
231314
+ for (const warn of refsResolved.warnings) {
231315
+ this.conversation.addInfo(c.yellow(`${warn.path ?? ""}: ${warn.type}${warn.detail ? ` \u2014 ${warn.detail}` : ""}`));
231316
+ }
231317
+ }
231318
+ if (refsResolved.images.length > 0) {
231319
+ const refImages = refsResolved.images.map((img) => ({
231320
+ type: "image",
231321
+ data: img.data,
231322
+ mimeType: img.mimeType
231323
+ }));
231324
+ images = [...images ?? [], ...refImages];
231031
231325
  }
231032
231326
  }
231327
+ }
231328
+ if (promptFilePaths.length > 0) {
231329
+ const pathLines = promptFilePaths.map((f) => `- \`${f}\``).join("\n");
231033
231330
  text = text ? `${text}
231034
- ${refsResolved.text}` : refsResolved.text;
231035
- if (refsResolved.images.length > 0) {
231036
- const refImages = refsResolved.images.map((img) => ({
231037
- type: "image",
231038
- data: img.data,
231039
- mimeType: img.mimeType
231040
- }));
231041
- images = [...images ?? [], ...refImages];
231042
- }
231331
+
231332
+ \u{1F4C1} Attached files:
231333
+ ${pathLines}` : `\u{1F4C1} Attached files:
231334
+ ${pathLines}`;
231043
231335
  }
231044
231336
  }
231045
- if (images && images.length > 0 && !resolveModel(this.deps.config.provider, this.deps.config.modelId).input.includes("image"))
231046
- this.conversation.addInfo(
231047
- c.dim(`${resolveModel(this.deps.config.provider, this.deps.config.modelId).name} does not support image input natively \u2014 using vision model or OCR.`)
231048
- );
231337
+ this.conversation.addInfo(
231338
+ c.dim(`${resolveModel(this.deps.config.provider, this.deps.config.modelId).name} does not support image input natively \u2014 using vision model or OCR.`)
231339
+ );
231049
231340
  const imageIndicator = images ? c.dim(`[${images.length} image(s) attached]`) : "";
231050
231341
  const userMessage = hasText ? imageIndicator ? `${text}
231051
231342
  ${imageIndicator}` : text : imageIndicator;
@@ -231262,8 +231553,10 @@ async function ocrImages(images, signal) {
231262
231553
  }
231263
231554
 
231264
231555
  // src/drivers/vision/client.ts
231556
+ init_logger();
231265
231557
  init_models();
231266
231558
  init_models();
231559
+ var _clog = new Logger({ type: "harness", id: process.env.DSCODE_RUNTIME_ID ?? "vision-client" });
231267
231560
  function resolveVisionModel(visionConfig, fallbackApiKey, onWarning) {
231268
231561
  if (!visionConfig?.provider || !visionConfig?.model) return null;
231269
231562
  try {
@@ -231311,6 +231604,8 @@ async function describeImagesViaVisionModel(images, visionModel, apiKey, signal)
231311
231604
  if (err instanceof DOMException && err.name === "AbortError") {
231312
231605
  throw err;
231313
231606
  }
231607
+ const errMsg = err instanceof Error ? err.message : String(err);
231608
+ _clog.warn("vision-client", `describeImages FAILED: ${errMsg}. images=${images.length}, totalBase64=${images.reduce((s, i) => s + (i.data?.length ?? 0), 0)}, img[0].mime=${images[0]?.mimeType ?? "?"}`);
231314
231609
  throw err;
231315
231610
  }
231316
231611
  }
@@ -231344,12 +231639,14 @@ var ImagePipeline = class {
231344
231639
  onProgress?.({ phase: "compressing", cachedRefs: [] });
231345
231640
  const cachedRefs = await Promise.all(normalizedImages.map((img) => ImageCache.put(img)));
231346
231641
  onProgress?.({ phase: "compressing", cachedRefs });
231642
+ const compressedRaw = await Promise.all(cachedRefs.map((ref) => ImageCache.get(ref)));
231643
+ const compressedImages = compressedRaw.every((img) => img !== null) ? compressedRaw : normalizedImages;
231347
231644
  if (signal?.aborted) throw new DOMException("The operation was aborted", "AbortError");
231348
231645
  const vision = resolveVisionModel(this.visionConfig, this.fallbackApiKey, this.onWarning);
231349
231646
  if (vision) {
231350
231647
  try {
231351
231648
  onProgress?.({ phase: "describing", cachedRefs });
231352
- const description = await describeImagesViaVisionModel(normalizedImages, vision.model, vision.apiKey, signal);
231649
+ const description = await describeImagesViaVisionModel(compressedImages, vision.model, vision.apiKey, signal);
231353
231650
  if (!description || description.trim().length === 0) {
231354
231651
  throw new Error("Vision model returned empty description");
231355
231652
  }
@@ -231366,13 +231663,14 @@ ${description}
231366
231663
  if (err instanceof DOMException && err.name === "AbortError") {
231367
231664
  throw err;
231368
231665
  }
231369
- this.onWarning(`Vision model failed: ${err instanceof Error ? err.message : String(err)}. Falling back to OCR.`);
231666
+ const errMsg = err instanceof Error ? err.message : String(err);
231667
+ this.onWarning(`Vision model failed: ${errMsg}. Falling back to OCR.`);
231370
231668
  }
231371
231669
  }
231372
231670
  if (signal?.aborted) throw new DOMException("The operation was aborted", "AbortError");
231373
231671
  try {
231374
231672
  onProgress?.({ phase: "ocr", cachedRefs });
231375
- const result = await ocrImages(normalizedImages, signal);
231673
+ const result = await ocrImages(compressedImages, signal);
231376
231674
  onProgress?.({ phase: "done", cachedRefs });
231377
231675
  if (result.hasText) {
231378
231676
  const ocrText = text ? `${text}
@@ -231471,12 +231769,12 @@ var ConfigWatch = class {
231471
231769
 
231472
231770
  // src/context/anchor-invalidation.ts
231473
231771
  var pendingEvents = [];
231474
- function recordInvalidation(filePath, lineCount, fileVersion) {
231475
- const existing = pendingEvents.findIndex((e) => e.filePath === filePath);
231772
+ function recordInvalidation(filePath2, lineCount, fileVersion) {
231773
+ const existing = pendingEvents.findIndex((e) => e.filePath === filePath2);
231476
231774
  if (existing >= 0) {
231477
- pendingEvents[existing] = { filePath, lineCount, fileVersion };
231775
+ pendingEvents[existing] = { filePath: filePath2, lineCount, fileVersion };
231478
231776
  } else {
231479
- pendingEvents.push({ filePath, lineCount, fileVersion });
231777
+ pendingEvents.push({ filePath: filePath2, lineCount, fileVersion });
231480
231778
  }
231481
231779
  }
231482
231780
  function consumePendingNotices() {
@@ -231522,7 +231820,7 @@ var HarnessEventBus = class {
231522
231820
  try {
231523
231821
  handler(event);
231524
231822
  } catch (err) {
231525
- this.logger.error("tool", "EventBus", `handler error for "${event.type}": ${String(err)}`);
231823
+ this.logger.error("EventBus", `handler error for "${event.type}": ${String(err)}`);
231526
231824
  }
231527
231825
  }
231528
231826
  }
@@ -231559,6 +231857,8 @@ var Harness = class {
231559
231857
  shuttingDown = false;
231560
231858
  turnIndex = 0;
231561
231859
  visionAbortController = null;
231860
+ autoSaveTimer;
231861
+ lastSavedMessageCount = 0;
231562
231862
  constructor(config, logger, debug) {
231563
231863
  this.logger = logger;
231564
231864
  this.configStore = new ConfigWatch(config);
@@ -231647,7 +231947,7 @@ var Harness = class {
231647
231947
  await self.dumpDebugPrompt();
231648
231948
  return self.contextManager.transform(msgs, signal);
231649
231949
  } catch (err) {
231650
- this.logger.error("tool", "TransformContext", String(err));
231950
+ this.logger.error("TransformContext", String(err));
231651
231951
  return msgs;
231652
231952
  }
231653
231953
  },
@@ -231670,7 +231970,7 @@ var Harness = class {
231670
231970
  }
231671
231971
  }
231672
231972
  } catch (err) {
231673
- this.logger.error("tool", "AfterToolCall", String(err));
231973
+ this.logger.error("AfterToolCall", String(err));
231674
231974
  }
231675
231975
  if (_signal?.aborted) {
231676
231976
  return { terminate: true };
@@ -231713,6 +232013,7 @@ var Harness = class {
231713
232013
  const lastAssistantMsg = this.findLastAssistantMessage(msgs);
231714
232014
  if (!lastAssistantMsg || lastAssistantMsg.stopReason !== "error") {
231715
232015
  this.sessionManager.trySaveSession(this.agent);
232016
+ this.lastSavedMessageCount = this.agent.state.messages.length;
231716
232017
  return;
231717
232018
  }
231718
232019
  const errorMsg = lastAssistantMsg.errorMessage ?? "Unknown model error";
@@ -231728,6 +232029,7 @@ var Harness = class {
231728
232029
  this.events.emit({ type: "ui:error", text: `Model error: ${errorMsg}` });
231729
232030
  this.events.emit({ type: "turn:error", error: errorMsg, attempt: attempt + 1, maxRetries });
231730
232031
  this.sessionManager.trySaveSession(this.agent);
232032
+ this.lastSavedMessageCount = this.agent.state.messages.length;
231731
232033
  return;
231732
232034
  }
231733
232035
  if (attempt >= maxRetries) {
@@ -231743,6 +232045,7 @@ var Harness = class {
231743
232045
  this.events.emit({ type: "turn:error", error: errorMsg, attempt: attempt + 1, maxRetries });
231744
232046
  this.events.emit({ type: "ui:error", text: "\u2717 All retries exhausted" });
231745
232047
  this.sessionManager.trySaveSession(this.agent);
232048
+ this.lastSavedMessageCount = this.agent.state.messages.length;
231746
232049
  return;
231747
232050
  }
231748
232051
  this.events.emit({
@@ -231761,6 +232064,24 @@ var Harness = class {
231761
232064
  */
231762
232065
  saveSessionNow() {
231763
232066
  this.sessionManager.trySaveSession(this.agent);
232067
+ this.lastSavedMessageCount = this.agent.state.messages.length;
232068
+ }
232069
+ /**
232070
+ * Start periodic auto-save (every 15s) during agent execution.
232071
+ * Only saves when new messages have been added since last save.
232072
+ */
232073
+ startAutoSave() {
232074
+ this.autoSaveTimer = setInterval(() => {
232075
+ if (this.agent?.state?.messages) {
232076
+ const currentCount = this.agent.state.messages.length;
232077
+ if (currentCount !== this.lastSavedMessageCount) {
232078
+ this.logger.info("AutoSave", `Periodic auto-save (${currentCount} messages, was ${this.lastSavedMessageCount})`);
232079
+ this.sessionManager.trySaveSession(this.agent);
232080
+ this.lastSavedMessageCount = currentCount;
232081
+ }
232082
+ }
232083
+ }, 15e3);
232084
+ this.autoSaveTimer.unref();
231764
232085
  }
231765
232086
  findLastUserMessageIndex(messages) {
231766
232087
  for (let i = messages.length - 1; i >= 0; i--) {
@@ -231909,6 +232230,7 @@ var Harness = class {
231909
232230
  }));
231910
232231
  }
231911
232232
  this.sessionManager.trySaveSession(this.agent);
232233
+ this.lastSavedMessageCount = this.agent.state.messages.length;
231912
232234
  const restoredImgs = [];
231913
232235
  for (const ref of cachedRefs) {
231914
232236
  const cached2 = ImageCache.getSync(ref);
@@ -231969,6 +232291,7 @@ var Harness = class {
231969
232291
  this.toolRegistry.initialize(this.makeSkillTool());
231970
232292
  this.agent.state.tools = this.toolRegistry.buildToolsForRequest();
231971
232293
  }
232294
+ this.startAutoSave();
231972
232295
  if (!this.config.apiKey) {
231973
232296
  this.events.emit({ type: "ui:info", text: [
231974
232297
  "Welcome to DSCode! To get started, configure your API key:",
@@ -232030,6 +232353,7 @@ var Harness = class {
232030
232353
  return { success: false, error: `Path does not exist: ${resolvedPath}` };
232031
232354
  }
232032
232355
  this.sessionManager.trySaveSession(this.agent);
232356
+ this.lastSavedMessageCount = this.agent.state.messages.length;
232033
232357
  process.chdir(resolvedPath);
232034
232358
  this.configStore.setProjectPath(resolvedPath);
232035
232359
  this.sessionManager.updateProjectPath(this.config.dataDir, resolvedPath);
@@ -232063,7 +232387,7 @@ var Harness = class {
232063
232387
  this.ui.setMcpManager?.(this.mcpManager);
232064
232388
  this.ui.pushMcpState?.();
232065
232389
  } catch (err) {
232066
- this.logger.error("tool", "McpReload", String(err));
232390
+ this.logger.error("McpReload", String(err));
232067
232391
  }
232068
232392
  this.agent.state.tools = this.toolRegistry.buildToolsForRequest();
232069
232393
  const memories = this.memoryManager.getRelevantMemories();
@@ -232073,7 +232397,12 @@ var Harness = class {
232073
232397
  return { success: true };
232074
232398
  }
232075
232399
  async shutdown() {
232400
+ if (this.autoSaveTimer) {
232401
+ clearInterval(this.autoSaveTimer);
232402
+ this.autoSaveTimer = void 0;
232403
+ }
232076
232404
  this.sessionManager.trySaveSession(this.agent);
232405
+ this.lastSavedMessageCount = this.agent.state.messages.length;
232077
232406
  if (this.shuttingDown) return;
232078
232407
  this.shuttingDown = true;
232079
232408
  this.mcpEventUnsubscribe?.();
@@ -232086,14 +232415,14 @@ var Harness = class {
232086
232415
  try {
232087
232416
  await this.appHostManager.shutdown();
232088
232417
  } catch (err) {
232089
- this.logger.error("tool", "AppHostShutdown", String(err));
232418
+ this.logger.error("AppHostShutdown", String(err));
232090
232419
  }
232091
232420
  }
232092
232421
  if (this.mcpManager) {
232093
232422
  try {
232094
232423
  await this.mcpManager.shutdown();
232095
232424
  } catch (err) {
232096
- this.logger.error("tool", "McpShutdown", String(err));
232425
+ this.logger.error("McpShutdown", String(err));
232097
232426
  }
232098
232427
  }
232099
232428
  }
@@ -232185,13 +232514,13 @@ You have a \`skill\` tool available. When you decide to use a skill from the lis
232185
232514
  const hash = String(prompt.length);
232186
232515
  if (hash === this.debugPromptLastHash) return;
232187
232516
  this.debugPromptLastHash = hash;
232188
- const { mkdirSync: mkdirSync13, writeFileSync: writeFileSync14 } = await import("node:fs");
232189
- const { join: join31 } = await import("node:path");
232190
- const dumpDir = join31(this.config.projectPath, "dump");
232191
- mkdirSync13(dumpDir, { recursive: true });
232192
- const filePath = join31(dumpDir, "system-prompt.md");
232193
- writeFileSync14(filePath, prompt, "utf8");
232194
- process.stderr.write("\n[dscode] --debug: system prompt dumped to " + filePath + " (" + prompt.length + " chars)\n\n");
232517
+ const { mkdirSync: mkdirSync14, writeFileSync: writeFileSync16 } = await import("node:fs");
232518
+ const { join: join32 } = await import("node:path");
232519
+ const dumpDir = join32(this.config.projectPath, "dump");
232520
+ mkdirSync14(dumpDir, { recursive: true });
232521
+ const filePath2 = join32(dumpDir, "system-prompt.md");
232522
+ writeFileSync16(filePath2, prompt, "utf8");
232523
+ process.stderr.write("\n[dscode] --debug: system prompt dumped to " + filePath2 + " (" + prompt.length + " chars)\n\n");
232195
232524
  }
232196
232525
  makeSkillTool() {
232197
232526
  const skillManager = this.skillManager;
@@ -232327,6 +232656,14 @@ You have a \`skill\` tool available. When you decide to use a skill from the lis
232327
232656
  bindEvents() {
232328
232657
  this.agent.subscribe((event) => {
232329
232658
  switch (event.type) {
232659
+ case "message_end": {
232660
+ if (event.message?.role === "user") {
232661
+ this.logger.info("PreTurnSave", `Saving session pre-turn (${this.agent.state.messages.length} messages)`);
232662
+ this.sessionManager.trySaveSession(this.agent);
232663
+ this.lastSavedMessageCount = this.agent.state.messages.length;
232664
+ }
232665
+ break;
232666
+ }
232330
232667
  case "message_update": {
232331
232668
  const ev = event.assistantMessageEvent;
232332
232669
  if (!ev) break;
@@ -232368,12 +232705,14 @@ You have a \`skill\` tool available. When you decide to use a skill from the lis
232368
232705
  if (event.type === "agent_end") {
232369
232706
  this.events.emit({ type: "processing:stop" });
232370
232707
  this.sessionManager.trySaveSession(this.agent);
232708
+ this.lastSavedMessageCount = this.agent.state.messages.length;
232371
232709
  }
232372
232710
  if (event.type === "agent_start") {
232373
232711
  this.events.emit({ type: "turn:streaming:start" });
232374
232712
  }
232375
232713
  if (event.type === "turn_end") {
232376
232714
  this.sessionManager.trySaveSession(this.agent);
232715
+ this.lastSavedMessageCount = this.agent.state.messages.length;
232377
232716
  const turnEndMsg = event.message;
232378
232717
  const rawUsage = turnEndMsg?.usage;
232379
232718
  this.events.emit({ type: "turn:end", stopReason: turnEndMsg?.stopReason, usage: rawUsage ? { input: rawUsage.input, output: rawUsage.output, cacheRead: rawUsage.cacheRead, cacheWrite: rawUsage.cacheWrite, total: rawUsage.totalTokens, cost: { total: rawUsage.cost.total } } : void 0 });
@@ -232385,8 +232724,9 @@ You have a \`skill\` tool available. When you decide to use a skill from the lis
232385
232724
  }
232386
232725
  }
232387
232726
  } catch (err) {
232388
- this.logger.error("tool", "AgentEvent", String(err));
232727
+ this.logger.error("AgentEvent", String(err));
232389
232728
  this.sessionManager.trySaveSession(this.agent);
232729
+ this.lastSavedMessageCount = this.agent.state.messages.length;
232390
232730
  }
232391
232731
  });
232392
232732
  }
@@ -232481,6 +232821,132 @@ You have a \`skill\` tool available. When you decide to use a skill from the lis
232481
232821
 
232482
232822
  // src/core/main.ts
232483
232823
  init_logger();
232824
+
232825
+ // src/core/od-daemon.ts
232826
+ import { spawn as spawn2 } from "node:child_process";
232827
+ import { existsSync as existsSync24, readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "node:fs";
232828
+ import { homedir as homedir10 } from "node:os";
232829
+ import { join as join29 } from "node:path";
232830
+ function findProjectOd(odDir) {
232831
+ const expandedDir = expandTilde2(odDir);
232832
+ const odPath = join29(expandedDir, "node_modules", ".bin", "od");
232833
+ if (existsSync24(odPath)) return odPath;
232834
+ return null;
232835
+ }
232836
+ function resolveOdCommand(odDir, port) {
232837
+ const expandedDir = expandTilde2(odDir);
232838
+ const odPath = findProjectOd(odDir);
232839
+ if (odPath) {
232840
+ return { cmd: odPath, args: ["--port", String(port), "--no-open"], cwd: expandedDir };
232841
+ }
232842
+ return { cmd: "pnpm", args: ["tools-dev", "run", "web", "--", "--port", String(port), "--no-open"], cwd: expandedDir };
232843
+ }
232844
+ function startOdDaemon(odDir, odPort) {
232845
+ const { cmd, args, cwd } = resolveOdCommand(odDir, odPort);
232846
+ const child = spawn2(cmd, args, {
232847
+ cwd,
232848
+ stdio: "ignore",
232849
+ env: { ...process.env, OD_PORT: String(odPort) }
232850
+ });
232851
+ child.unref();
232852
+ return child;
232853
+ }
232854
+ async function waitForOdDaemon(port, timeoutMs = 3e4) {
232855
+ const start = Date.now();
232856
+ while (Date.now() - start < timeoutMs) {
232857
+ try {
232858
+ const res = await fetch(`http://127.0.0.1:${port}/api/projects`);
232859
+ if (res.status === 200) {
232860
+ console.log(`Open Design daemon ready on port ${port}`);
232861
+ return true;
232862
+ }
232863
+ } catch {
232864
+ }
232865
+ await new Promise((r) => setTimeout(r, 500));
232866
+ }
232867
+ console.warn(`Open Design daemon did not become healthy within ${timeoutMs / 1e3}s`);
232868
+ return false;
232869
+ }
232870
+ var cleanupHandlers = [];
232871
+ function runOdCleanup() {
232872
+ for (const handler of cleanupHandlers) {
232873
+ handler();
232874
+ }
232875
+ cleanupHandlers = [];
232876
+ }
232877
+ var cleanupHooksInstalled = false;
232878
+ function installCleanupHooks() {
232879
+ if (cleanupHooksInstalled) return;
232880
+ cleanupHooksInstalled = true;
232881
+ process.on("exit", runOdCleanup);
232882
+ process.on("SIGINT", runOdCleanup);
232883
+ process.on("SIGTERM", runOdCleanup);
232884
+ }
232885
+ function registerOdCleanup(odChild) {
232886
+ installCleanupHooks();
232887
+ const handler = () => {
232888
+ if (odChild.exitCode !== null || odChild.signalCode !== null) return;
232889
+ odChild.kill("SIGTERM");
232890
+ const forceKill = setTimeout(() => {
232891
+ if (odChild.exitCode === null && odChild.signalCode === null) {
232892
+ odChild.kill("SIGKILL");
232893
+ }
232894
+ }, 3e3);
232895
+ forceKill.unref();
232896
+ };
232897
+ cleanupHandlers.push(handler);
232898
+ }
232899
+ function expandTilde2(filePath2) {
232900
+ if (filePath2.startsWith("~")) {
232901
+ return join29(homedir10(), filePath2.slice(1));
232902
+ }
232903
+ return filePath2;
232904
+ }
232905
+ var MCP_PATH = join29(homedir10(), ".mcp.json");
232906
+ function ensureOdMcpEntry(odDir, odPort) {
232907
+ const expandedDir = expandTilde2(odDir);
232908
+ const daemonCliPath = join29(expandedDir, "apps", "daemon", "src", "cli.ts");
232909
+ const daemonUrl = `http://127.0.0.1:${odPort}`;
232910
+ let mcpConfig;
232911
+ try {
232912
+ if (existsSync24(MCP_PATH)) {
232913
+ const raw = readFileSync21(MCP_PATH, "utf8");
232914
+ mcpConfig = JSON.parse(raw);
232915
+ } else {
232916
+ mcpConfig = {};
232917
+ }
232918
+ } catch (e) {
232919
+ if (existsSync24(MCP_PATH)) {
232920
+ console.warn(
232921
+ `Cannot read ~/.mcp.json: ${e instanceof Error ? e.message : String(e)}`
232922
+ );
232923
+ return false;
232924
+ }
232925
+ mcpConfig = {};
232926
+ }
232927
+ const servers = mcpConfig.mcpServers ?? {};
232928
+ const existing = servers["open-design"];
232929
+ if (existing?.args?.[1] === daemonCliPath && existing?.args?.[4] === daemonUrl) {
232930
+ return true;
232931
+ }
232932
+ servers["open-design"] = {
232933
+ command: "npx",
232934
+ args: ["tsx", daemonCliPath, "mcp", "--daemon-url", daemonUrl]
232935
+ };
232936
+ mcpConfig.mcpServers = servers;
232937
+ try {
232938
+ writeFileSync14(MCP_PATH, JSON.stringify(mcpConfig, null, 2) + "\n");
232939
+ console.log(`~/.mcp.json updated with open-design MCP entry`);
232940
+ return true;
232941
+ } catch (e) {
232942
+ console.warn(
232943
+ `Cannot write ~/.mcp.json: ${e instanceof Error ? e.message : String(e)}`
232944
+ );
232945
+ return false;
232946
+ }
232947
+ }
232948
+
232949
+ // src/core/main.ts
232484
232950
  function generateRuntimeId() {
232485
232951
  return "rt_" + randomBytes(4).toString("hex");
232486
232952
  }
@@ -232497,12 +232963,12 @@ function emergencySaveSession() {
232497
232963
  }
232498
232964
  }
232499
232965
  process.on("unhandledRejection", (reason) => {
232500
- harnessLogger.error("lifecycle", "UnhandledRejection", String(reason));
232966
+ harnessLogger.error("UnhandledRejection", String(reason));
232501
232967
  emergencySaveSession();
232502
232968
  });
232503
232969
  process.on("uncaughtException", (err) => {
232504
232970
  const msg = err instanceof Error ? err.message : String(err);
232505
- harnessLogger.error("lifecycle", "UncaughtException", msg);
232971
+ harnessLogger.error("UncaughtException", msg);
232506
232972
  emergencySaveSession();
232507
232973
  setTimeout(() => {
232508
232974
  process.exit(1);
@@ -232512,28 +232978,28 @@ var sigintCount = 0;
232512
232978
  process.on("SIGINT", () => {
232513
232979
  sigintCount++;
232514
232980
  if (sigintCount === 1) {
232515
- harnessLogger.info("lifecycle", "SIGINT", "Shutting down... (press Ctrl+C again to force quit)");
232981
+ harnessLogger.info("SIGINT", "Shutting down... (press Ctrl+C again to force quit)");
232516
232982
  emergencySaveSession();
232517
232983
  } else {
232518
- harnessLogger.info("lifecycle", "SIGINT", "Force quitting...");
232984
+ harnessLogger.info("SIGINT", "Force quitting...");
232519
232985
  emergencySaveSession();
232520
232986
  process.exit(0);
232521
232987
  }
232522
232988
  });
232523
232989
  process.on("SIGTERM", () => {
232524
- harnessLogger.info("lifecycle", "SIGTERM", "Received SIGTERM, shutting down...");
232990
+ harnessLogger.info("SIGTERM", "Received SIGTERM, shutting down...");
232525
232991
  emergencySaveSession();
232526
232992
  process.exit(0);
232527
232993
  });
232528
232994
  function readCliVersion() {
232529
232995
  const currentDir = dirname7(fileURLToPath4(import.meta.url));
232530
232996
  const candidates = [
232531
- join30(currentDir, "..", "package.json"),
232532
- join30(currentDir, "..", "..", "package.json")
232997
+ join31(currentDir, "..", "package.json"),
232998
+ join31(currentDir, "..", "..", "package.json")
232533
232999
  ];
232534
233000
  for (const candidate of candidates) {
232535
- if (!existsSync25(candidate)) continue;
232536
- const pkg = JSON.parse(readFileSync22(candidate, "utf8"));
233001
+ if (!existsSync26(candidate)) continue;
233002
+ const pkg = JSON.parse(readFileSync23(candidate, "utf8"));
232537
233003
  if (typeof pkg.version === "string" && pkg.version.trim() !== "") {
232538
233004
  return pkg.version;
232539
233005
  }
@@ -232546,11 +233012,14 @@ function parseArgs() {
232546
233012
  let webPort = 3e3;
232547
233013
  let debug = false;
232548
233014
  let cwd;
233015
+ let withOd = false;
232549
233016
  for (let i = 0; i < args.length; i++) {
232550
233017
  if (args[i] === "--web") {
232551
233018
  web = true;
232552
233019
  } else if (args[i] === "--debug") {
232553
233020
  debug = true;
233021
+ } else if (args[i] === "--with-od") {
233022
+ withOd = true;
232554
233023
  } else if (args[i] === "--web-port" && i + 1 < args.length) {
232555
233024
  webPort = parseInt(args[++i], 10);
232556
233025
  if (isNaN(webPort) || webPort < 1 || webPort > 65535) {
@@ -232560,15 +233029,91 @@ function parseArgs() {
232560
233029
  cwd = resolve11(args[++i]);
232561
233030
  }
232562
233031
  }
232563
- return { web, webPort, debug, cwd };
233032
+ return { web, webPort, debug, cwd, withOd };
232564
233033
  }
232565
233034
  async function main() {
232566
233035
  if (process.argv.slice(2).some((arg) => arg === "--version" || arg === "-v")) {
232567
233036
  console.log(readCliVersion());
232568
233037
  return;
232569
233038
  }
232570
- const { web, webPort, debug, cwd } = parseArgs();
233039
+ const { web, webPort, debug, cwd, withOd } = parseArgs();
232571
233040
  const projectRoot = process.cwd();
233041
+ if (withOd) {
233042
+ const odDir = process.env.OPEN_DESIGN_DIR;
233043
+ const odPort = parseInt(process.env.OD_PORT ?? "7456", 10);
233044
+ if (!odDir) {
233045
+ console.warn("OPEN_DESIGN_DIR is not set. Create a .env file from .env.example");
233046
+ } else {
233047
+ ensureOdMcpEntry(odDir, odPort);
233048
+ const alreadyAlive = await waitForOdDaemon(odPort, 2e3);
233049
+ if (!alreadyAlive) {
233050
+ try {
233051
+ const odChild = startOdDaemon(odDir, odPort);
233052
+ registerOdCleanup(odChild);
233053
+ let odRestartCount = 0;
233054
+ let lastRestartTime = 0;
233055
+ const attachExitHandler = (child) => {
233056
+ child.on("exit", (code, signal) => {
233057
+ if (signal) {
233058
+ harnessLogger.info("ODDaemon", `Open Design daemon killed by signal ${signal}`);
233059
+ } else if (code !== 0 && code !== null) {
233060
+ harnessLogger.info("ODDaemon", `Open Design daemon exited with code ${code}`);
233061
+ }
233062
+ if (code !== 0 && code !== null && withOd) {
233063
+ const now = Date.now();
233064
+ if (now - lastRestartTime < 5e3) {
233065
+ odRestartCount++;
233066
+ } else {
233067
+ odRestartCount = 1;
233068
+ }
233069
+ lastRestartTime = now;
233070
+ if (odRestartCount > 3) {
233071
+ harnessLogger.warn("ODDaemon", "Open Design daemon restart loop detected (3 rapid restarts), giving up");
233072
+ return;
233073
+ }
233074
+ harnessLogger.info("ODDaemon", `Restarting Open Design daemon (attempt ${odRestartCount})...`);
233075
+ try {
233076
+ const newChild = startOdDaemon(odDir, odPort);
233077
+ registerOdCleanup(newChild);
233078
+ attachExitHandler(newChild);
233079
+ void waitForOdDaemon(odPort).then((healthy) => {
233080
+ if (healthy) {
233081
+ harnessLogger.info("ODDaemon", "Open Design daemon restarted successfully");
233082
+ }
233083
+ });
233084
+ } catch (e) {
233085
+ harnessLogger.warn("ODDaemon", `Failed to restart Open Design daemon: ${e instanceof Error ? e.message : String(e)}`);
233086
+ }
233087
+ }
233088
+ });
233089
+ child.on("error", (err) => {
233090
+ if (err.code === "ENOENT") {
233091
+ const expandedDir = expandTilde2(odDir);
233092
+ if (!existsSync26(expandedDir)) {
233093
+ console.warn(
233094
+ `Open Design directory not found: ${expandedDir}. Check OPEN_DESIGN_DIR in .env`
233095
+ );
233096
+ } else {
233097
+ console.warn(
233098
+ `Cannot start Open Design daemon: command not found. Install pnpm (https://pnpm.io/installation) or run \`cd ${expandedDir} && pnpm link --global\` for the global od command.`
233099
+ );
233100
+ }
233101
+ } else {
233102
+ harnessLogger.warn("ODDaemon", `Open Design daemon error: ${err.message}`);
233103
+ }
233104
+ });
233105
+ };
233106
+ attachExitHandler(odChild);
233107
+ await waitForOdDaemon(odPort);
233108
+ } catch (e) {
233109
+ harnessLogger.warn(
233110
+ "ODDaemon",
233111
+ `Failed to start Open Design daemon: ${e instanceof Error ? e.message : String(e)}`
233112
+ );
233113
+ }
233114
+ }
233115
+ }
233116
+ }
232572
233117
  const config = loadConfig(cwd);
232573
233118
  if (config.apiKey) {
232574
233119
  const envVar = PROVIDER_ENV_VARS[config.provider] ?? "DEEPSEEK_API_KEY";
@@ -232598,7 +233143,7 @@ async function main() {
232598
233143
  }
232599
233144
  }
232600
233145
  main().catch((err) => {
232601
- harnessLogger.error("lifecycle", "FatalStartup", err instanceof Error ? err.message : String(err));
233146
+ harnessLogger.error("FatalStartup", err instanceof Error ? err.message : String(err));
232602
233147
  emergencySaveSession();
232603
233148
  process.exit(1);
232604
233149
  });