@jun133/kitty 0.0.14 → 0.0.16

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.
@@ -1,27 +1,20 @@
1
1
  import {
2
- PROJECT_STATE_DIR_NAME,
3
- PROJECT_STATE_IGNORE_FILE_NAME,
4
2
  applyCurrentTurnFrame,
5
3
  buildRunTurnResult,
4
+ collapseContentParts,
6
5
  createEmptyAssistantResponseTransition,
7
6
  createExecutionWaitYieldTransition,
8
7
  createFinalizeTransition,
9
8
  createInternalReminder,
10
9
  createMessage,
11
- createProviderClientPool,
12
10
  createProviderRecoveryTransition,
13
11
  createToolMessage,
14
- ensureProjectStateDirectories,
15
12
  expandStartToToolBoundary,
16
- fetchAssistantResponse,
17
13
  fingerprintFocus,
18
14
  formatSessionMemorySectionList,
19
15
  formatSessionMemorySectionTemplate,
20
16
  formatTodoBlock,
21
- getProjectStatePaths,
22
- isAbortError,
23
17
  isInternalMessage,
24
- isRetryableApiError,
25
18
  normalizeCheckpoint,
26
19
  normalizeText,
27
20
  normalizeTodoItems,
@@ -31,20 +24,20 @@ import {
31
24
  noteCheckpointTransition,
32
25
  noteCheckpointTurnInput,
33
26
  noteSessionDiff,
27
+ readReasoningContent,
34
28
  readUserInput,
35
- recordObservabilityEvent,
36
29
  recordSessionWorksetFile,
30
+ resolveModelProfile,
31
+ resolveProviderCapabilities,
37
32
  shouldIncludeStoredAssistantReasoning,
38
- sleepWithSignal,
39
33
  sliceCurrentUserInputFrame,
40
34
  takeLastUnique,
41
- throwIfAborted,
42
35
  updateSessionMemory
43
- } from "./chunk-3KMC6H5K.mjs";
36
+ } from "./chunk-C3MFBHV3.mjs";
44
37
 
45
38
  // src/context/projectContext.ts
46
- import fs5 from "fs/promises";
47
- import path6 from "path";
39
+ import fs6 from "fs/promises";
40
+ import path7 from "path";
48
41
 
49
42
  // src/context/repoRoots.ts
50
43
  import fs from "fs/promises";
@@ -248,13 +241,67 @@ function formatList(values) {
248
241
  }
249
242
 
250
243
  // src/skills/discovery.ts
251
- import fs4 from "fs/promises";
252
- import path5 from "path";
244
+ import fs5 from "fs/promises";
245
+ import path6 from "path";
253
246
  import fg2 from "fast-glob";
254
247
 
255
248
  // src/utils/ignore.ts
249
+ import fs4 from "fs/promises";
250
+ import path4 from "path";
251
+
252
+ // src/project/statePaths.ts
256
253
  import fs3 from "fs/promises";
257
254
  import path3 from "path";
255
+ var PROJECT_STATE_DIR_NAME = ".kitty";
256
+ var PROJECT_STATE_ENV_FILE_NAME = ".env";
257
+ var PROJECT_STATE_ENV_EXAMPLE_FILE_NAME = ".env.example";
258
+ var PROJECT_STATE_IGNORE_FILE_NAME = ".kittyignore";
259
+ var PRESERVED_PROJECT_STATE_ENTRY_NAMES = [
260
+ PROJECT_STATE_ENV_FILE_NAME,
261
+ PROJECT_STATE_ENV_EXAMPLE_FILE_NAME
262
+ ];
263
+ function getProjectStatePaths(rootDir) {
264
+ const normalizedRoot = path3.resolve(rootDir);
265
+ const kittyDir = path3.join(normalizedRoot, PROJECT_STATE_DIR_NAME);
266
+ const extensionsDir = path3.join(kittyDir, "extensions");
267
+ const memoryDir = path3.join(kittyDir, "memory");
268
+ const observabilityDir = path3.join(kittyDir, "observability");
269
+ return {
270
+ rootDir: normalizedRoot,
271
+ kittyDir,
272
+ cacheDir: path3.join(kittyDir, "cache"),
273
+ sessionsDir: path3.join(kittyDir, "sessions"),
274
+ changesDir: path3.join(kittyDir, "changes"),
275
+ eventsDir: path3.join(kittyDir, "events"),
276
+ extensionsDir,
277
+ memoryDir,
278
+ evidenceMemoryDir: path3.join(memoryDir, "evidence"),
279
+ projectMemoryDir: path3.join(memoryDir, "project"),
280
+ sessionMemoryDir: path3.join(memoryDir, "sessions"),
281
+ userMemoryDir: path3.join(memoryDir, "user"),
282
+ controlPlaneLedgerFile: path3.join(kittyDir, "control-plane.sqlite"),
283
+ observabilityDir,
284
+ observabilityEventsDir: path3.join(observabilityDir, "events"),
285
+ observabilityCrashesDir: path3.join(observabilityDir, "crashes")
286
+ };
287
+ }
288
+ async function ensureProjectStateDirectories(rootDir) {
289
+ const paths = getProjectStatePaths(rootDir);
290
+ await fs3.mkdir(paths.extensionsDir, { recursive: true });
291
+ await fs3.mkdir(paths.cacheDir, { recursive: true });
292
+ await fs3.mkdir(paths.sessionsDir, { recursive: true });
293
+ await fs3.mkdir(paths.changesDir, { recursive: true });
294
+ await fs3.mkdir(paths.eventsDir, { recursive: true });
295
+ await fs3.mkdir(paths.evidenceMemoryDir, { recursive: true });
296
+ await fs3.mkdir(paths.projectMemoryDir, { recursive: true });
297
+ await fs3.mkdir(paths.sessionMemoryDir, { recursive: true });
298
+ await fs3.mkdir(paths.userMemoryDir, { recursive: true });
299
+ await fs3.mkdir(paths.observabilityEventsDir, { recursive: true });
300
+ await fs3.mkdir(paths.observabilityCrashesDir, { recursive: true });
301
+ return paths;
302
+ }
303
+
304
+ // src/utils/ignore.ts
258
305
  var BUILTIN_PATTERNS = [
259
306
  ".git/",
260
307
  "node_modules/",
@@ -268,11 +315,11 @@ async function loadProjectIgnoreRules(rootDir, cwd) {
268
315
  })).filter((rule) => Boolean(rule));
269
316
  const candidateFiles = uniqueIgnoreFiles([
270
317
  {
271
- path: path3.join(rootDir, PROJECT_STATE_DIR_NAME, PROJECT_STATE_IGNORE_FILE_NAME),
318
+ path: path4.join(rootDir, PROJECT_STATE_DIR_NAME, PROJECT_STATE_IGNORE_FILE_NAME),
272
319
  baseDir: rootDir
273
320
  },
274
321
  {
275
- path: path3.join(cwd, PROJECT_STATE_DIR_NAME, PROJECT_STATE_IGNORE_FILE_NAME),
322
+ path: path4.join(cwd, PROJECT_STATE_DIR_NAME, PROJECT_STATE_IGNORE_FILE_NAME),
276
323
  baseDir: cwd
277
324
  }
278
325
  ]);
@@ -332,7 +379,7 @@ function buildFastGlobIgnorePatterns(baseDir, rules) {
332
379
  }
333
380
  async function tryReadUtf8File(filePath) {
334
381
  try {
335
- return await fs3.readFile(filePath, "utf8");
382
+ return await fs4.readFile(filePath, "utf8");
336
383
  } catch (error) {
337
384
  const errno = error;
338
385
  if (errno.code === "ENOENT") {
@@ -383,11 +430,11 @@ function compileIgnoreRule(rawPattern, options) {
383
430
  };
384
431
  }
385
432
  function toRelativePosix(baseDir, targetPath) {
386
- const relativePath = path3.relative(path3.resolve(baseDir), path3.resolve(targetPath));
433
+ const relativePath = path4.relative(path4.resolve(baseDir), path4.resolve(targetPath));
387
434
  if (!relativePath || relativePath === ".") {
388
435
  return "";
389
436
  }
390
- if (relativePath.startsWith("..") || path3.isAbsolute(relativePath)) {
437
+ if (relativePath.startsWith("..") || path4.isAbsolute(relativePath)) {
391
438
  return null;
392
439
  }
393
440
  return relativePath.replace(/\\/g, "/");
@@ -453,21 +500,21 @@ function uniqueIgnoreFiles(files) {
453
500
  const seen = /* @__PURE__ */ new Set();
454
501
  const unique = [];
455
502
  for (const file of files) {
456
- const normalizedPath = path3.normalize(file.path);
503
+ const normalizedPath = path4.normalize(file.path);
457
504
  if (seen.has(normalizedPath)) {
458
505
  continue;
459
506
  }
460
507
  seen.add(normalizedPath);
461
508
  unique.push({
462
509
  path: normalizedPath,
463
- baseDir: path3.resolve(file.baseDir)
510
+ baseDir: path4.resolve(file.baseDir)
464
511
  });
465
512
  }
466
513
  return unique;
467
514
  }
468
515
 
469
516
  // src/skills/schema.ts
470
- import path4 from "path";
517
+ import path5 from "path";
471
518
  var FRONTMATTER_PATTERN = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
472
519
  var SkillSchemaError = class extends Error {
473
520
  constructor(message, filePath) {
@@ -487,7 +534,7 @@ function parseSkillSource(text, input) {
487
534
  return {
488
535
  name,
489
536
  description,
490
- path: path4.relative(input.rootDir, input.absolutePath) || "SKILL.md",
537
+ path: path5.relative(input.rootDir, input.absolutePath) || "SKILL.md",
491
538
  absolutePath: input.absolutePath,
492
539
  body,
493
540
  dependencies: parseDependencies(metadata.requires),
@@ -557,12 +604,12 @@ async function discoverSkills(rootDir, cwd, ignoreRules) {
557
604
  const seenNames = /* @__PURE__ */ new Map();
558
605
  const skills = [];
559
606
  for (const skillFile of skillFiles) {
560
- const normalizedPath = path5.normalize(skillFile);
607
+ const normalizedPath = path6.normalize(skillFile);
561
608
  if (seenPaths.has(normalizedPath) || isPathIgnored(normalizedPath, ignoreRules)) {
562
609
  continue;
563
610
  }
564
611
  seenPaths.add(normalizedPath);
565
- const skill = parseSkillSource(await fs4.readFile(normalizedPath, "utf8"), {
612
+ const skill = parseSkillSource(await fs5.readFile(normalizedPath, "utf8"), {
566
613
  absolutePath: normalizedPath,
567
614
  rootDir
568
615
  });
@@ -578,7 +625,7 @@ async function discoverSkills(rootDir, cwd, ignoreRules) {
578
625
  return skills.sort((left, right) => left.name.localeCompare(right.name));
579
626
  }
580
627
  async function listSkillResources(skillPath, rootDir, ignoreRules) {
581
- const skillDir = path5.dirname(skillPath);
628
+ const skillDir = path6.dirname(skillPath);
582
629
  const files = await fg2([...SKILL_RESOURCE_GLOBS], {
583
630
  cwd: skillDir,
584
631
  absolute: true,
@@ -592,11 +639,11 @@ async function listSkillResources(skillPath, rootDir, ignoreRules) {
592
639
  if (isPathIgnored(file, ignoreRules)) {
593
640
  continue;
594
641
  }
595
- const stat = await fs4.stat(file);
642
+ const stat = await fs5.stat(file);
596
643
  resources.push({
597
- path: path5.relative(rootDir, file),
644
+ path: path6.relative(rootDir, file),
598
645
  size: stat.size,
599
- kind: readSkillResourceKind(path5.relative(skillDir, file))
646
+ kind: readSkillResourceKind(path6.relative(skillDir, file))
600
647
  });
601
648
  }
602
649
  return resources;
@@ -643,14 +690,14 @@ function buildSkillPackageHealth(skill) {
643
690
  }
644
691
  async function findSkillFiles(rootDir, cwd) {
645
692
  const candidates = uniquePaths([
646
- path5.join(rootDir, "SKILL.md"),
647
- path5.join(cwd, "SKILL.md")
693
+ path6.join(rootDir, "SKILL.md"),
694
+ path6.join(cwd, "SKILL.md")
648
695
  ]);
649
696
  const roots = uniquePaths([
650
- path5.join(rootDir, ".skills"),
651
- path5.join(rootDir, "skills"),
652
- path5.join(cwd, ".skills"),
653
- path5.join(cwd, "skills")
697
+ path6.join(rootDir, ".skills"),
698
+ path6.join(rootDir, "skills"),
699
+ path6.join(cwd, ".skills"),
700
+ path6.join(cwd, "skills")
654
701
  ]);
655
702
  const files = [];
656
703
  for (const candidate of candidates) {
@@ -675,20 +722,20 @@ async function findSkillFiles(rootDir, cwd) {
675
722
  }
676
723
  async function isRegularFile(filePath) {
677
724
  try {
678
- return (await fs4.stat(filePath)).isFile();
725
+ return (await fs5.stat(filePath)).isFile();
679
726
  } catch {
680
727
  return false;
681
728
  }
682
729
  }
683
730
  async function isDirectory2(filePath) {
684
731
  try {
685
- return (await fs4.stat(filePath)).isDirectory();
732
+ return (await fs5.stat(filePath)).isDirectory();
686
733
  } catch {
687
734
  return false;
688
735
  }
689
736
  }
690
737
  function uniquePaths(paths) {
691
- return [...new Set(paths.map((item) => path5.normalize(item)))];
738
+ return [...new Set(paths.map((item) => path6.normalize(item)))];
692
739
  }
693
740
 
694
741
  // src/context/projectContext.ts
@@ -717,8 +764,8 @@ async function getInstructionFiles(rootDir, cwd) {
717
764
  const directories = getDirectoriesFromRootToCwd(rootDir, cwd);
718
765
  const results = [];
719
766
  for (const directory of directories) {
720
- const overridePath = path6.join(directory, "AGENTS.override.md");
721
- const agentsPath = path6.join(directory, "AGENTS.md");
767
+ const overridePath = path7.join(directory, "AGENTS.override.md");
768
+ const agentsPath = path7.join(directory, "AGENTS.md");
722
769
  if (await isRegularFile2(overridePath)) {
723
770
  results.push(await readInstructionFile(rootDir, overridePath, "AGENTS.override.md"));
724
771
  continue;
@@ -730,22 +777,22 @@ async function getInstructionFiles(rootDir, cwd) {
730
777
  return results;
731
778
  }
732
779
  function getDirectoriesFromRootToCwd(rootDir, cwd) {
733
- const absoluteRoot = path6.resolve(rootDir);
734
- const absoluteCwd = path6.resolve(cwd);
735
- const relativePath = path6.relative(absoluteRoot, absoluteCwd);
780
+ const absoluteRoot = path7.resolve(rootDir);
781
+ const absoluteCwd = path7.resolve(cwd);
782
+ const relativePath = path7.relative(absoluteRoot, absoluteCwd);
736
783
  if (!relativePath || relativePath === ".") {
737
784
  return [absoluteRoot];
738
785
  }
739
- const parts = relativePath.split(path6.sep).filter(Boolean);
786
+ const parts = relativePath.split(path7.sep).filter(Boolean);
740
787
  const directories = [absoluteRoot];
741
788
  for (let index = 0; index < parts.length; index += 1) {
742
- directories.push(path6.join(absoluteRoot, ...parts.slice(0, index + 1)));
789
+ directories.push(path7.join(absoluteRoot, ...parts.slice(0, index + 1)));
743
790
  }
744
791
  return directories;
745
792
  }
746
793
  async function isRegularFile2(filePath) {
747
794
  try {
748
- const stat = await fs5.stat(filePath);
795
+ const stat = await fs6.stat(filePath);
749
796
  return stat.isFile();
750
797
  } catch {
751
798
  return false;
@@ -754,9 +801,9 @@ async function isRegularFile2(filePath) {
754
801
  async function readInstructionFile(rootDir, absolutePath, filename) {
755
802
  return {
756
803
  path: absolutePath,
757
- relativePath: path6.relative(rootDir, absolutePath) || filename,
804
+ relativePath: path7.relative(rootDir, absolutePath) || filename,
758
805
  filename,
759
- content: await fs5.readFile(absolutePath, "utf8")
806
+ content: await fs6.readFile(absolutePath, "utf8")
760
807
  };
761
808
  }
762
809
  function concatInstructionFiles(files, maxBytes) {
@@ -832,7 +879,7 @@ function getErrorMessage(error) {
832
879
  return "Environment error: network connection failed; the current provider/base URL is unreachable. Check network, proxy settings, or `KITTY_BASE_URL`.";
833
880
  }
834
881
  if (status === 404 || lower.includes("returned 404")) {
835
- return "User-fixable error: provider returned 404. Check whether `KITTY_BASE_URL` is the correct OpenAI-compatible API base URL.";
882
+ return "User-fixable error: provider endpoint returned 404. Check `KITTY_PROVIDER`, `KITTY_MODEL`, and `KITTY_BASE_URL` as one provider profile; the selected provider may use Responses instead of Chat Completions.";
836
883
  }
837
884
  if (typeof status === "number" && status >= 500) {
838
885
  return `Provider error: service returned ${status}. Retry later or confirm the provider service is healthy.`;
@@ -884,39 +931,1153 @@ async function handleCompletedAssistantResponse(params) {
884
931
  transition: transition2
885
932
  };
886
933
  }
887
- const transition = createFinalizeTransition({
888
- changedPaths: params.changedPaths
934
+ const transition = createFinalizeTransition({
935
+ changedPaths: params.changedPaths
936
+ });
937
+ const session = await params.options.sessionStore.save(
938
+ noteCheckpointCompleted(
939
+ await params.options.sessionStore.appendMessages(params.session, [assistantMessage]),
940
+ transition
941
+ )
942
+ );
943
+ return {
944
+ kind: "return",
945
+ result: buildRunTurnResult({
946
+ session,
947
+ changedPaths: params.changedPaths,
948
+ transition
949
+ })
950
+ };
951
+ }
952
+ function emitAssistantReasoning(response, options) {
953
+ if (response.reasoningContent && options.config.showReasoning && !response.streamedReasoningContent) {
954
+ options.callbacks?.onReasoning?.(response.reasoningContent);
955
+ }
956
+ }
957
+ function emitAssistantFinalOutput(response, options) {
958
+ if (response.content && !response.streamedAssistantContent) {
959
+ options.callbacks?.onAssistantText?.(response.content);
960
+ }
961
+ if (response.content) {
962
+ options.callbacks?.onAssistantDone?.(response.content);
963
+ }
964
+ }
965
+ function hasVisibleAssistantResult(content) {
966
+ return typeof content === "string" && content.trim().length > 0;
967
+ }
968
+
969
+ // src/utils/abort.ts
970
+ function createAbortError(message = "Operation aborted") {
971
+ const error = new Error(message);
972
+ error.name = "AbortError";
973
+ error.code = "ABORT_ERR";
974
+ return error;
975
+ }
976
+ function isAbortError(error) {
977
+ if (!error) {
978
+ return false;
979
+ }
980
+ if (error instanceof Error) {
981
+ if (error.name === "AbortError") {
982
+ return true;
983
+ }
984
+ const code = String(error.code ?? "");
985
+ if (code === "ABORT_ERR" || code === "ERR_ABORTED" || code === "ABORTED") {
986
+ return true;
987
+ }
988
+ const message = error.message.toLowerCase();
989
+ if (message.includes("abort") || message.includes("aborted") || message.includes("cancelled") || message.includes("canceled")) {
990
+ return true;
991
+ }
992
+ }
993
+ if (typeof error === "object" && error && "cause" in error) {
994
+ return isAbortError(error.cause);
995
+ }
996
+ return false;
997
+ }
998
+ function throwIfAborted(signal, message) {
999
+ if (signal?.aborted) {
1000
+ throw createAbortError(message ?? "Operation aborted");
1001
+ }
1002
+ }
1003
+ function sleepWithSignal(ms, signal) {
1004
+ if (!signal) {
1005
+ return new Promise((resolve) => {
1006
+ setTimeout(resolve, ms);
1007
+ });
1008
+ }
1009
+ if (signal.aborted) {
1010
+ return Promise.reject(createAbortError("Sleep aborted"));
1011
+ }
1012
+ return new Promise((resolve, reject) => {
1013
+ const timer = setTimeout(() => {
1014
+ signal.removeEventListener("abort", onAbort);
1015
+ resolve();
1016
+ }, ms);
1017
+ const onAbort = () => {
1018
+ clearTimeout(timer);
1019
+ signal.removeEventListener("abort", onAbort);
1020
+ reject(createAbortError("Sleep aborted"));
1021
+ };
1022
+ signal.addEventListener("abort", onAbort);
1023
+ });
1024
+ }
1025
+
1026
+ // src/provider/apiRetry.ts
1027
+ var API_MAX_RETRIES = 3;
1028
+ var API_RETRY_BASE_DELAY_MS = 1200;
1029
+ async function withApiRetries(operation, abortSignal) {
1030
+ let lastError;
1031
+ for (let attempt = 1; attempt <= API_MAX_RETRIES; attempt += 1) {
1032
+ try {
1033
+ return await operation();
1034
+ } catch (error) {
1035
+ if (isAbortError(error)) {
1036
+ throw error;
1037
+ }
1038
+ lastError = error;
1039
+ if (!isRetryableApiError(error) || attempt === API_MAX_RETRIES) {
1040
+ break;
1041
+ }
1042
+ await sleepWithSignal(API_RETRY_BASE_DELAY_MS * attempt, abortSignal);
1043
+ }
1044
+ }
1045
+ throw lastError;
1046
+ }
1047
+ function isRetryableApiError(error) {
1048
+ const status = error.status;
1049
+ if (typeof status === "number") {
1050
+ return status === 408 || status === 409 || status === 429 || status >= 500;
1051
+ }
1052
+ const message = String(error.message ?? error).toLowerCase();
1053
+ return message.includes("timeout") || message.includes("network") || message.includes("connection error") || message.includes("connection reset") || message.includes("econnreset") || message.includes("econnrefused") || message.includes("connect timeout") || message.includes("temporarily") || message.includes("rate limit") || message.includes("overloaded");
1054
+ }
1055
+
1056
+ // src/provider/usageNormalizer.ts
1057
+ function normalizeProviderUsage(usage) {
1058
+ if (!usage || typeof usage !== "object") {
1059
+ return void 0;
1060
+ }
1061
+ const record = usage;
1062
+ const promptDetails = readObject(record.prompt_tokens_details);
1063
+ const completionDetails = readObject(record.completion_tokens_details);
1064
+ const outputDetails = readObject(record.output_tokens_details);
1065
+ const cacheCreation = readObject(record.cache_creation);
1066
+ const inputTokens = readUsageNumber(record.prompt_tokens ?? record.input_tokens);
1067
+ const outputTokens = readUsageNumber(record.completion_tokens ?? record.output_tokens);
1068
+ const totalTokens = readUsageNumber(record.total_tokens);
1069
+ const reasoningTokens = readUsageNumber(
1070
+ completionDetails?.reasoning_tokens ?? outputDetails?.reasoning_tokens
1071
+ );
1072
+ const openAiCachedTokens = readUsageNumber(promptDetails?.cached_tokens);
1073
+ const deepSeekHitTokens = readUsageNumber(record.prompt_cache_hit_tokens);
1074
+ const deepSeekMissTokens = readUsageNumber(record.prompt_cache_miss_tokens);
1075
+ const anthropicCacheReadTokens = readUsageNumber(record.cache_read_input_tokens);
1076
+ const anthropicCacheCreationTokens = readUsageNumber(record.cache_creation_input_tokens) ?? sumUsageNumbers([
1077
+ cacheCreation?.ephemeral_1h_input_tokens,
1078
+ cacheCreation?.ephemeral_5m_input_tokens
1079
+ ]);
1080
+ const geminiCachedTokens = readUsageNumber(record.cachedContentTokenCount ?? record.cached_content_token_count);
1081
+ const cacheReadTokens = firstNumber(
1082
+ anthropicCacheReadTokens,
1083
+ openAiCachedTokens,
1084
+ geminiCachedTokens
1085
+ );
1086
+ const cacheHitTokens = firstNumber(deepSeekHitTokens, cacheReadTokens);
1087
+ const cacheMissTokens = deepSeekMissTokens;
1088
+ const cacheCreationTokens = anthropicCacheCreationTokens;
1089
+ const snapshot = {
1090
+ inputTokens,
1091
+ outputTokens,
1092
+ totalTokens,
1093
+ reasoningTokens,
1094
+ cacheReadTokens,
1095
+ cacheCreationTokens,
1096
+ cacheHitTokens,
1097
+ cacheMissTokens
1098
+ };
1099
+ const cacheHitRate = computeCacheHitRate(snapshot);
1100
+ if (cacheHitRate !== void 0) {
1101
+ snapshot.cacheHitRate = cacheHitRate;
1102
+ }
1103
+ return Object.values(snapshot).some((value) => typeof value === "number") ? snapshot : void 0;
1104
+ }
1105
+ function hasProviderUsageSnapshot(usage) {
1106
+ return Boolean(usage && Object.values(usage).some((value) => typeof value === "number"));
1107
+ }
1108
+ function computeCacheHitRate(snapshot) {
1109
+ if (typeof snapshot.cacheHitTokens === "number" && typeof snapshot.cacheMissTokens === "number") {
1110
+ return ratio(snapshot.cacheHitTokens, snapshot.cacheHitTokens + snapshot.cacheMissTokens);
1111
+ }
1112
+ if (typeof snapshot.cacheReadTokens === "number") {
1113
+ const denominator = (snapshot.inputTokens ?? 0) + snapshot.cacheReadTokens + (snapshot.cacheCreationTokens ?? 0);
1114
+ return ratio(snapshot.cacheReadTokens, denominator);
1115
+ }
1116
+ return void 0;
1117
+ }
1118
+ function ratio(numerator, denominator) {
1119
+ if (denominator <= 0) {
1120
+ return void 0;
1121
+ }
1122
+ return Math.round(numerator / denominator * 1e4) / 1e4;
1123
+ }
1124
+ function readObject(value) {
1125
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
1126
+ }
1127
+ function firstNumber(...values) {
1128
+ return values.find((value) => typeof value === "number");
1129
+ }
1130
+ function sumUsageNumbers(values) {
1131
+ const numbers = values.map(readUsageNumber).filter((value) => typeof value === "number");
1132
+ if (numbers.length === 0) {
1133
+ return void 0;
1134
+ }
1135
+ return numbers.reduce((total, value) => total + value, 0);
1136
+ }
1137
+ function readUsageNumber(value) {
1138
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.round(value) : void 0;
1139
+ }
1140
+
1141
+ // src/observability/writer.ts
1142
+ import fs7 from "fs/promises";
1143
+ import path8 from "path";
1144
+
1145
+ // src/observability/schema.ts
1146
+ var OBSERVABILITY_VERSION = 1;
1147
+ function buildObservabilityEventRecord(input) {
1148
+ return {
1149
+ version: OBSERVABILITY_VERSION,
1150
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1151
+ event: normalizeText2(input.event, "unknown"),
1152
+ status: normalizeText2(input.status, "unknown"),
1153
+ host: normalizeOptionalText(input.host),
1154
+ sessionId: normalizeOptionalText(input.sessionId),
1155
+ executionId: normalizeOptionalText(input.executionId),
1156
+ identityKind: normalizeOptionalText(input.identityKind),
1157
+ identityName: normalizeOptionalText(input.identityName),
1158
+ durationMs: normalizeOptionalNumber(input.durationMs),
1159
+ toolName: normalizeOptionalText(input.toolName),
1160
+ model: normalizeOptionalText(input.model),
1161
+ error: normalizeObservabilityError(input.error),
1162
+ details: normalizeDetails(input.details)
1163
+ };
1164
+ }
1165
+ function normalizeObservabilityError(error) {
1166
+ if (error == null) {
1167
+ return void 0;
1168
+ }
1169
+ if (typeof error === "object" && error !== null && "message" in error) {
1170
+ const record = error;
1171
+ const message2 = normalizeText2(record.message, "");
1172
+ if (!message2) {
1173
+ return void 0;
1174
+ }
1175
+ return {
1176
+ message: message2,
1177
+ code: normalizeOptionalText(record.code),
1178
+ details: normalizeValue(record.details)
1179
+ };
1180
+ }
1181
+ const message = readErrorMessage(error);
1182
+ return message ? { message } : void 0;
1183
+ }
1184
+ function normalizeDetails(details) {
1185
+ if (!details || typeof details !== "object") {
1186
+ return void 0;
1187
+ }
1188
+ const normalized = normalizeValue(details);
1189
+ return normalized && typeof normalized === "object" && !Array.isArray(normalized) ? normalized : void 0;
1190
+ }
1191
+ function normalizeValue(value, depth = 0) {
1192
+ if (value == null) {
1193
+ return void 0;
1194
+ }
1195
+ if (depth >= 4) {
1196
+ return "[truncated]";
1197
+ }
1198
+ if (typeof value === "string") {
1199
+ return value.length <= 2e3 ? value : `${value.slice(0, 1997)}...`;
1200
+ }
1201
+ if (typeof value === "number") {
1202
+ return Number.isFinite(value) ? value : void 0;
1203
+ }
1204
+ if (typeof value === "boolean") {
1205
+ return value;
1206
+ }
1207
+ if (value instanceof Error) {
1208
+ return {
1209
+ name: normalizeText2(value.name, "Error"),
1210
+ message: normalizeText2(value.message, "Unknown error"),
1211
+ stack: normalizeValue(value.stack, depth + 1)
1212
+ };
1213
+ }
1214
+ if (Array.isArray(value)) {
1215
+ return value.slice(0, 20).map((item) => normalizeValue(item, depth + 1)).filter((item) => item !== void 0);
1216
+ }
1217
+ if (typeof value === "object") {
1218
+ const entries = Object.entries(value).slice(0, 30);
1219
+ const normalizedEntries = entries.map(([key, item]) => [key, normalizeValue(item, depth + 1)]).filter(([, item]) => item !== void 0);
1220
+ return Object.fromEntries(normalizedEntries);
1221
+ }
1222
+ return normalizeText2(String(value), "");
1223
+ }
1224
+ function normalizeText2(value, fallback) {
1225
+ const normalized = String(value ?? "").trim();
1226
+ return normalized || fallback;
1227
+ }
1228
+ function normalizeOptionalText(value) {
1229
+ const normalized = String(value ?? "").trim();
1230
+ return normalized || void 0;
1231
+ }
1232
+ function normalizeOptionalNumber(value) {
1233
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.round(value) : void 0;
1234
+ }
1235
+ function readErrorMessage(error) {
1236
+ if (error instanceof Error) {
1237
+ return normalizeText2(error.message, error.name || "Unknown error");
1238
+ }
1239
+ if (typeof error === "object" && error !== null && "message" in error) {
1240
+ return normalizeText2(error.message, "Unknown error");
1241
+ }
1242
+ return normalizeText2(String(error ?? "Unknown error"), "Unknown error");
1243
+ }
1244
+
1245
+ // src/observability/writer.ts
1246
+ async function appendObservabilityEvent(rootDir, input) {
1247
+ const paths = await ensureProjectStateDirectories(rootDir);
1248
+ const record = buildObservabilityEventRecord(input);
1249
+ const filePath = path8.join(paths.observabilityEventsDir, `${record.timestamp.slice(0, 10)}.jsonl`);
1250
+ await fs7.appendFile(filePath, `${JSON.stringify(record)}
1251
+ `, "utf8");
1252
+ return record;
1253
+ }
1254
+ async function recordObservabilityEvent(rootDir, input) {
1255
+ try {
1256
+ await appendObservabilityEvent(rootDir, input);
1257
+ } catch {
1258
+ }
1259
+ }
1260
+
1261
+ // src/provider/cachePolicy.ts
1262
+ function resolveProviderCachePolicy(input) {
1263
+ const profile = resolveModelProfile(input);
1264
+ if (profile.model.capabilities.cache === "prompt-cache-key") {
1265
+ return {
1266
+ provider: "openai",
1267
+ automaticPrefixCache: true,
1268
+ promptCacheKey: buildPromptCacheKey(input)
1269
+ };
1270
+ }
1271
+ if (profile.model.capabilities.cache === "provider-automatic") {
1272
+ return {
1273
+ provider: profile.provider.id === "deepseek" ? "deepseek" : "generic",
1274
+ automaticPrefixCache: true
1275
+ };
1276
+ }
1277
+ return {
1278
+ provider: "generic",
1279
+ automaticPrefixCache: false
1280
+ };
1281
+ }
1282
+ function buildPromptCacheKey(input) {
1283
+ const seed = input.sessionId || input.projectRoot;
1284
+ if (!seed) {
1285
+ return void 0;
1286
+ }
1287
+ return `kitty:${stableHash(seed)}`;
1288
+ }
1289
+ function stableHash(value) {
1290
+ let hash = 2166136261;
1291
+ for (let index = 0; index < value.length; index += 1) {
1292
+ hash ^= value.charCodeAt(index);
1293
+ hash = Math.imul(hash, 16777619);
1294
+ }
1295
+ return (hash >>> 0).toString(16).padStart(8, "0");
1296
+ }
1297
+
1298
+ // src/provider/chatRequestBody.ts
1299
+ function buildProviderRequestBody(input) {
1300
+ const capabilities = resolveProviderCapabilities(input);
1301
+ const thinking = capabilities.provider === "deepseek" ? resolveDeepSeekThinking(input.messages, input.thinking ?? "enabled") : input.thinking;
1302
+ const body = {
1303
+ model: input.model,
1304
+ messages: toChatCompletionMessages(input.messages),
1305
+ tools: input.tools,
1306
+ stream: input.stream
1307
+ };
1308
+ if (capabilities.provider !== "deepseek" && input.tools?.length) {
1309
+ body.tool_choice = "auto";
1310
+ }
1311
+ if (input.stream) {
1312
+ body.stream_options = {
1313
+ include_usage: true
1314
+ };
1315
+ }
1316
+ const cachePolicy = resolveProviderCachePolicy(input);
1317
+ if (cachePolicy.promptCacheKey) {
1318
+ body.prompt_cache_key = cachePolicy.promptCacheKey;
1319
+ }
1320
+ if (typeof input.maxOutputTokens === "number" && Number.isFinite(input.maxOutputTokens)) {
1321
+ body.max_tokens = Math.max(1, Math.trunc(input.maxOutputTokens));
1322
+ }
1323
+ if (capabilities.provider === "deepseek") {
1324
+ body.thinking = { type: thinking };
1325
+ if (thinking === "enabled") {
1326
+ body.reasoning_effort = normalizeDeepSeekReasoningEffort(input.reasoningEffort ?? capabilities.defaultReasoningEffort);
1327
+ }
1328
+ } else if (input.forceReasoning || capabilities.defaultReasoningEnabled) {
1329
+ body.thinking = { type: "enabled" };
1330
+ }
1331
+ return body;
1332
+ }
1333
+ function resolveDeepSeekThinking(messages, requested) {
1334
+ if (requested === "disabled") {
1335
+ return "disabled";
1336
+ }
1337
+ if (hasUnreplayableAssistantReasoning(messages)) {
1338
+ throw new Error("DeepSeek thinking tool-call replay requires stored reasoning_content. Start a new turn or disable KITTY_THINKING.");
1339
+ }
1340
+ return "enabled";
1341
+ }
1342
+ function hasUnreplayableAssistantReasoning(messages) {
1343
+ return messages.some(
1344
+ (message) => message.role === "assistant" && Array.isArray(message.toolCalls) && message.toolCalls.length > 0 && message.reasoningContent === void 0
1345
+ );
1346
+ }
1347
+ function normalizeDeepSeekReasoningEffort(effort) {
1348
+ if (effort === void 0 || effort === "minimal" || effort === "low" || effort === "medium" || effort === "high") {
1349
+ return "high";
1350
+ }
1351
+ if (effort === "xhigh" || effort === "max") {
1352
+ return "max";
1353
+ }
1354
+ return "high";
1355
+ }
1356
+
1357
+ // src/provider/chatCompletionsAdapter.ts
1358
+ var chatCompletionsAdapter = {
1359
+ wireApi: "chat.completions",
1360
+ async fetchStreaming(client, request) {
1361
+ const startedAt = Date.now();
1362
+ let usage;
1363
+ throwIfAborted(request.abortSignal, "Streaming request aborted");
1364
+ try {
1365
+ const stream = await client.chat.completions.create(
1366
+ {
1367
+ ...buildProviderRequestBody({
1368
+ provider: request.provider,
1369
+ model: request.model,
1370
+ messages: request.messages,
1371
+ tools: request.tools,
1372
+ stream: true,
1373
+ forceReasoning: request.forceReasoning,
1374
+ thinking: request.thinking,
1375
+ reasoningEffort: request.reasoningEffort,
1376
+ maxOutputTokens: request.maxOutputTokens,
1377
+ sessionId: request.sessionId,
1378
+ projectRoot: request.projectRoot
1379
+ }),
1380
+ signal: request.abortSignal
1381
+ }
1382
+ );
1383
+ if (request.abortSignal?.aborted) {
1384
+ abortStream(stream);
1385
+ throw createAbortError("Streaming aborted");
1386
+ }
1387
+ let content = "";
1388
+ let reasoningContent = "";
1389
+ const toolCallParts = /* @__PURE__ */ new Map();
1390
+ for await (const chunk of stream) {
1391
+ if (request.abortSignal?.aborted) {
1392
+ abortStream(stream);
1393
+ throw createAbortError("Streaming aborted");
1394
+ }
1395
+ usage = normalizeProviderUsage(chunk.usage) ?? usage;
1396
+ const delta = chunk.choices?.[0]?.delta;
1397
+ if (!delta) {
1398
+ continue;
1399
+ }
1400
+ if (typeof delta.content === "string" && delta.content.length > 0) {
1401
+ content += delta.content;
1402
+ request.callbacks?.onAssistantDelta?.(delta.content);
1403
+ }
1404
+ if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
1405
+ reasoningContent += delta.reasoning_content;
1406
+ request.callbacks?.onReasoningDelta?.(delta.reasoning_content);
1407
+ }
1408
+ if (Array.isArray(delta.tool_calls)) {
1409
+ for (const toolCall of delta.tool_calls) {
1410
+ const index = typeof toolCall.index === "number" ? toolCall.index : 0;
1411
+ const existing = toolCallParts.get(index) ?? {
1412
+ id: toolCall.id ?? `tool-${index}`,
1413
+ name: "",
1414
+ arguments: ""
1415
+ };
1416
+ if (toolCall.id) {
1417
+ existing.id = toolCall.id;
1418
+ }
1419
+ if (toolCall.function?.name) {
1420
+ existing.name += toolCall.function.name;
1421
+ }
1422
+ if (toolCall.function?.arguments) {
1423
+ existing.arguments += toolCall.function.arguments;
1424
+ }
1425
+ toolCallParts.set(index, existing);
1426
+ }
1427
+ }
1428
+ }
1429
+ return {
1430
+ content: content.length > 0 ? content : null,
1431
+ reasoningContent: reasoningContent.length > 0 ? reasoningContent : void 0,
1432
+ streamedAssistantContent: content.length > 0,
1433
+ streamedReasoningContent: reasoningContent.length > 0,
1434
+ toolCalls: [...toolCallParts.entries()].sort((left, right) => left[0] - right[0]).map(([, toolCall]) => ({
1435
+ id: toolCall.id,
1436
+ type: "function",
1437
+ function: {
1438
+ name: toolCall.name,
1439
+ arguments: toolCall.arguments
1440
+ }
1441
+ }))
1442
+ };
1443
+ } finally {
1444
+ request.onRequestMetric?.({
1445
+ durationMs: Date.now() - startedAt,
1446
+ usage
1447
+ });
1448
+ }
1449
+ },
1450
+ async fetchNonStreaming(client, request) {
1451
+ const startedAt = Date.now();
1452
+ let usage;
1453
+ throwIfAborted(request.abortSignal, "Request aborted");
1454
+ try {
1455
+ const completion = await client.chat.completions.create(
1456
+ {
1457
+ ...buildProviderRequestBody({
1458
+ provider: request.provider,
1459
+ model: request.model,
1460
+ messages: request.messages,
1461
+ tools: request.tools,
1462
+ stream: false,
1463
+ forceReasoning: request.forceReasoning,
1464
+ thinking: request.thinking,
1465
+ reasoningEffort: request.reasoningEffort,
1466
+ maxOutputTokens: request.maxOutputTokens,
1467
+ sessionId: request.sessionId,
1468
+ projectRoot: request.projectRoot
1469
+ }),
1470
+ signal: request.abortSignal
1471
+ }
1472
+ );
1473
+ usage = normalizeProviderUsage(completion.usage);
1474
+ const message = completion.choices[0]?.message;
1475
+ if (!message) {
1476
+ throw new Error("API returned no message.");
1477
+ }
1478
+ return {
1479
+ content: typeof message.content === "string" ? message.content : collapseContentParts(message.content),
1480
+ reasoningContent: readReasoningContent(message),
1481
+ streamedAssistantContent: false,
1482
+ streamedReasoningContent: false,
1483
+ toolCalls: (message.tool_calls ?? []).filter((call) => call.type === "function").map((call) => ({
1484
+ id: call.id,
1485
+ type: "function",
1486
+ function: {
1487
+ name: call.function.name,
1488
+ arguments: call.function.arguments
1489
+ }
1490
+ }))
1491
+ };
1492
+ } finally {
1493
+ request.onRequestMetric?.({
1494
+ durationMs: Date.now() - startedAt,
1495
+ usage
1496
+ });
1497
+ }
1498
+ }
1499
+ };
1500
+ function abortStream(stream) {
1501
+ try {
1502
+ stream?.controller?.abort();
1503
+ } catch {
1504
+ }
1505
+ }
1506
+ function toChatCompletionMessages(messages) {
1507
+ return messages.map((message) => {
1508
+ if (message.role === "tool") {
1509
+ return {
1510
+ role: "tool",
1511
+ content: message.content ?? "",
1512
+ tool_call_id: message.toolCallId ?? ""
1513
+ };
1514
+ }
1515
+ if (message.role === "assistant" && message.toolCalls?.length) {
1516
+ const assistantMessage = {
1517
+ role: "assistant",
1518
+ content: message.content ?? "",
1519
+ tool_calls: message.toolCalls
1520
+ };
1521
+ if (message.reasoningContent !== void 0) {
1522
+ assistantMessage.reasoning_content = message.reasoningContent;
1523
+ }
1524
+ return assistantMessage;
1525
+ }
1526
+ const baseMessage = {
1527
+ role: message.role,
1528
+ content: message.content ?? "",
1529
+ name: message.name
1530
+ };
1531
+ if (message.role === "assistant" && message.reasoningContent !== void 0) {
1532
+ baseMessage.reasoning_content = message.reasoningContent;
1533
+ }
1534
+ return baseMessage;
1535
+ });
1536
+ }
1537
+
1538
+ // src/provider/responsesRequest.ts
1539
+ function buildResponsesRequestBody(request) {
1540
+ const capabilities = resolveProviderCapabilities({
1541
+ provider: request.provider,
1542
+ model: request.model
1543
+ });
1544
+ const body = {
1545
+ model: request.model,
1546
+ input: toResponsesInput(request.messages),
1547
+ tools: request.tools?.map((tool) => ({
1548
+ type: "function",
1549
+ name: tool.function.name,
1550
+ description: tool.function.description,
1551
+ parameters: tool.function.parameters ?? null,
1552
+ strict: false
1553
+ })),
1554
+ tool_choice: request.tools?.length ? "auto" : void 0
1555
+ };
1556
+ if (typeof request.maxOutputTokens === "number" && Number.isFinite(request.maxOutputTokens)) {
1557
+ body.max_output_tokens = Math.max(1, Math.trunc(request.maxOutputTokens));
1558
+ }
1559
+ const cachePolicy = resolveProviderCachePolicy({
1560
+ provider: request.provider,
1561
+ model: request.model,
1562
+ sessionId: request.sessionId,
1563
+ projectRoot: request.projectRoot
1564
+ });
1565
+ if (cachePolicy.promptCacheKey) {
1566
+ body.prompt_cache_key = cachePolicy.promptCacheKey;
1567
+ }
1568
+ const reasoningEffort = request.thinking === "disabled" ? void 0 : normalizeResponsesReasoningEffort(
1569
+ request.reasoningEffort ?? capabilities.defaultReasoningEffort
1570
+ );
1571
+ if (request.thinking !== "disabled" && (request.forceReasoning || capabilities.defaultReasoningEnabled || request.thinking === "enabled" || reasoningEffort)) {
1572
+ body.reasoning = {
1573
+ effort: reasoningEffort ?? "high",
1574
+ summary: "detailed"
1575
+ };
1576
+ }
1577
+ return body;
1578
+ }
1579
+ function normalizeResponsesReasoningEffort(effort) {
1580
+ if (effort === "xhigh") {
1581
+ return "xhigh";
1582
+ }
1583
+ return effort === "max" ? void 0 : effort;
1584
+ }
1585
+ function toResponsesInput(messages) {
1586
+ const items = [];
1587
+ for (const message of messages) {
1588
+ if (message.role === "tool") {
1589
+ items.push({
1590
+ type: "function_call_output",
1591
+ call_id: message.toolCallId ?? "",
1592
+ output: message.content ?? ""
1593
+ });
1594
+ continue;
1595
+ }
1596
+ if (message.role === "assistant" && message.toolCalls?.length) {
1597
+ if (typeof message.content === "string" && message.content.trim().length > 0) {
1598
+ items.push({
1599
+ type: "message",
1600
+ role: "assistant",
1601
+ content: message.content
1602
+ });
1603
+ }
1604
+ for (const toolCall of message.toolCalls) {
1605
+ items.push({
1606
+ type: "function_call",
1607
+ call_id: toolCall.id,
1608
+ name: toolCall.function.name,
1609
+ arguments: toolCall.function.arguments
1610
+ });
1611
+ }
1612
+ continue;
1613
+ }
1614
+ items.push({
1615
+ type: "message",
1616
+ role: message.role,
1617
+ content: message.content ?? ""
1618
+ });
1619
+ }
1620
+ return items;
1621
+ }
1622
+
1623
+ // src/provider/responsesResponse.ts
1624
+ function normalizeResponsesOutputText(response) {
1625
+ const outputText = response.output_text;
1626
+ if (typeof outputText === "string" && outputText.trim().length > 0) {
1627
+ return outputText;
1628
+ }
1629
+ const output = response.output;
1630
+ if (!Array.isArray(output)) {
1631
+ return null;
1632
+ }
1633
+ const fragments = output.flatMap((item) => {
1634
+ if (!item || typeof item !== "object" || item.type !== "message") {
1635
+ return [];
1636
+ }
1637
+ const content = item.content;
1638
+ if (!Array.isArray(content)) {
1639
+ return [];
1640
+ }
1641
+ return content.flatMap((part) => {
1642
+ if (!part || typeof part !== "object" || part.type !== "output_text") {
1643
+ return [];
1644
+ }
1645
+ return typeof part.text === "string" ? [part.text] : [];
1646
+ });
1647
+ });
1648
+ return fragments.length > 0 ? fragments.join("") : null;
1649
+ }
1650
+ function readResponsesToolCalls(response) {
1651
+ const output = response.output;
1652
+ if (!Array.isArray(output)) {
1653
+ return [];
1654
+ }
1655
+ return output.filter((item) => Boolean(item) && typeof item === "object" && item.type === "function_call").map((item) => ({
1656
+ id: item.call_id ?? item.id ?? crypto.randomUUID(),
1657
+ type: "function",
1658
+ function: {
1659
+ name: item.name ?? "",
1660
+ arguments: item.arguments ?? ""
1661
+ }
1662
+ }));
1663
+ }
1664
+ function readResponsesReasoning(response) {
1665
+ const output = response.output;
1666
+ if (!Array.isArray(output)) {
1667
+ return void 0;
1668
+ }
1669
+ const fragments = output.flatMap((item) => {
1670
+ if (!item || typeof item !== "object" || item.type !== "reasoning") {
1671
+ return [];
1672
+ }
1673
+ const reasoningItem = item;
1674
+ const summary = Array.isArray(reasoningItem.summary) ? reasoningItem.summary.map((entry) => typeof entry?.text === "string" ? entry.text : "").filter(Boolean) : [];
1675
+ const content = Array.isArray(reasoningItem.content) ? reasoningItem.content.map((entry) => typeof entry?.text === "string" ? entry.text : "").filter(Boolean) : [];
1676
+ return [...content, ...summary];
1677
+ });
1678
+ return fragments.length > 0 ? fragments.join("") : void 0;
1679
+ }
1680
+
1681
+ // src/provider/responsesAdapter.ts
1682
+ var responsesAdapter = {
1683
+ wireApi: "responses",
1684
+ async fetchStreaming(client, request) {
1685
+ const startedAt = Date.now();
1686
+ let usage;
1687
+ throwIfAborted(request.abortSignal, "Streaming request aborted");
1688
+ try {
1689
+ const stream = await client.responses.create(
1690
+ {
1691
+ ...buildResponsesRequestBody(request),
1692
+ stream: true
1693
+ },
1694
+ {
1695
+ signal: request.abortSignal
1696
+ }
1697
+ );
1698
+ if (request.abortSignal?.aborted) {
1699
+ abortStream2(stream);
1700
+ throw createAbortError("Streaming aborted");
1701
+ }
1702
+ let content = "";
1703
+ let reasoningContent = "";
1704
+ const toolCalls = /* @__PURE__ */ new Map();
1705
+ for await (const event of stream) {
1706
+ if (request.abortSignal?.aborted) {
1707
+ abortStream2(stream);
1708
+ throw createAbortError("Streaming aborted");
1709
+ }
1710
+ usage = normalizeProviderUsage(event.response?.usage) ?? usage;
1711
+ if (event.type === "response.output_text.delta" && typeof event.delta === "string") {
1712
+ content += event.delta;
1713
+ request.callbacks?.onAssistantDelta?.(event.delta);
1714
+ continue;
1715
+ }
1716
+ if ((event.type === "response.reasoning_text.delta" || event.type === "response.reasoning_summary_text.delta") && typeof event.delta === "string") {
1717
+ reasoningContent += event.delta;
1718
+ request.callbacks?.onReasoningDelta?.(event.delta);
1719
+ continue;
1720
+ }
1721
+ if (event.type === "response.function_call_arguments.delta" && typeof event.delta === "string") {
1722
+ const index = typeof event.output_index === "number" ? event.output_index : 0;
1723
+ const existing = toolCalls.get(index) ?? {
1724
+ id: event.item_id ?? `tool-${index}`,
1725
+ name: "",
1726
+ arguments: ""
1727
+ };
1728
+ existing.arguments += event.delta;
1729
+ toolCalls.set(index, existing);
1730
+ continue;
1731
+ }
1732
+ if (event.type === "response.function_call_arguments.done") {
1733
+ const index = typeof event.output_index === "number" ? event.output_index : 0;
1734
+ const existing = toolCalls.get(index) ?? {
1735
+ id: event.item_id ?? `tool-${index}`,
1736
+ name: "",
1737
+ arguments: ""
1738
+ };
1739
+ if (typeof event.name === "string") {
1740
+ existing.name = event.name;
1741
+ }
1742
+ if (typeof event.arguments === "string" && event.arguments.length > 0) {
1743
+ existing.arguments = event.arguments;
1744
+ }
1745
+ toolCalls.set(index, existing);
1746
+ continue;
1747
+ }
1748
+ if (event.type === "response.output_item.done" && event.item?.type === "function_call") {
1749
+ const index = typeof event.output_index === "number" ? event.output_index : 0;
1750
+ toolCalls.set(index, {
1751
+ id: event.item.call_id ?? event.item.id ?? `tool-${index}`,
1752
+ name: event.item.name ?? "",
1753
+ arguments: event.item.arguments ?? ""
1754
+ });
1755
+ }
1756
+ }
1757
+ return {
1758
+ content: content.length > 0 ? content : null,
1759
+ reasoningContent: reasoningContent.length > 0 ? reasoningContent : void 0,
1760
+ streamedAssistantContent: content.length > 0,
1761
+ streamedReasoningContent: reasoningContent.length > 0,
1762
+ toolCalls: [...toolCalls.entries()].sort((left, right) => left[0] - right[0]).map(([, toolCall]) => ({
1763
+ id: toolCall.id,
1764
+ type: "function",
1765
+ function: {
1766
+ name: toolCall.name,
1767
+ arguments: toolCall.arguments
1768
+ }
1769
+ }))
1770
+ };
1771
+ } finally {
1772
+ request.onRequestMetric?.({
1773
+ durationMs: Date.now() - startedAt,
1774
+ usage
1775
+ });
1776
+ }
1777
+ },
1778
+ async fetchNonStreaming(client, request) {
1779
+ const startedAt = Date.now();
1780
+ let usage;
1781
+ throwIfAborted(request.abortSignal, "Request aborted");
1782
+ try {
1783
+ const response = await client.responses.create(
1784
+ {
1785
+ ...buildResponsesRequestBody(request),
1786
+ stream: false
1787
+ },
1788
+ {
1789
+ signal: request.abortSignal
1790
+ }
1791
+ );
1792
+ usage = normalizeProviderUsage(response.usage);
1793
+ return {
1794
+ content: normalizeResponsesOutputText(response),
1795
+ reasoningContent: readResponsesReasoning(response),
1796
+ streamedAssistantContent: false,
1797
+ streamedReasoningContent: false,
1798
+ toolCalls: readResponsesToolCalls(response)
1799
+ };
1800
+ } finally {
1801
+ request.onRequestMetric?.({
1802
+ durationMs: Date.now() - startedAt,
1803
+ usage
1804
+ });
1805
+ }
1806
+ }
1807
+ };
1808
+ function abortStream2(stream) {
1809
+ try {
1810
+ stream?.controller?.abort();
1811
+ } catch {
1812
+ }
1813
+ }
1814
+
1815
+ // src/provider/client.ts
1816
+ import OpenAI from "openai";
1817
+
1818
+ // src/provider/connection.ts
1819
+ function buildProviderBaseUrlCandidates(baseUrl) {
1820
+ const normalized = trimTrailingSlash(baseUrl);
1821
+ if (!normalized) {
1822
+ return [normalized];
1823
+ }
1824
+ const candidates = [normalized];
1825
+ try {
1826
+ const parsed = new URL(normalized);
1827
+ if (parsed.pathname === "" || parsed.pathname === "/") {
1828
+ candidates.push(trimTrailingSlash(new URL("v1", ensureTrailingSlash2(parsed.toString())).toString()));
1829
+ }
1830
+ } catch {
1831
+ return candidates;
1832
+ }
1833
+ return [...new Set(candidates)];
1834
+ }
1835
+ function ensureTrailingSlash2(baseUrl) {
1836
+ return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
1837
+ }
1838
+ function trimTrailingSlash(baseUrl) {
1839
+ const trimmed = String(baseUrl ?? "").trim();
1840
+ if (!trimmed) {
1841
+ return trimmed;
1842
+ }
1843
+ return trimmed.endsWith("/") ? trimmed.slice(0, -1) : trimmed;
1844
+ }
1845
+
1846
+ // src/provider/client.ts
1847
+ function createProviderClientPool(config) {
1848
+ const capabilities = resolveProviderCapabilities({
1849
+ provider: config.provider,
1850
+ model: config.model
889
1851
  });
890
- const session = await params.options.sessionStore.save(
891
- noteCheckpointCompleted(
892
- await params.options.sessionStore.appendMessages(params.session, [assistantMessage]),
893
- transition
894
- )
895
- );
1852
+ const baseUrls = buildProviderBaseUrlCandidates(config.baseUrl);
1853
+ const clients = /* @__PURE__ */ new Map();
1854
+ let preferredBaseUrl;
896
1855
  return {
897
- kind: "return",
898
- result: buildRunTurnResult({
899
- session,
900
- changedPaths: params.changedPaths,
901
- transition
902
- })
1856
+ candidates() {
1857
+ const ordered = preferredBaseUrl ? [preferredBaseUrl, ...baseUrls.filter((baseUrl) => baseUrl !== preferredBaseUrl)] : baseUrls;
1858
+ return ordered.map((baseUrl) => ({
1859
+ baseUrl,
1860
+ client: getOrCreateClient(baseUrl)
1861
+ }));
1862
+ },
1863
+ markHealthy(baseUrl) {
1864
+ preferredBaseUrl = baseUrl;
1865
+ }
903
1866
  };
1867
+ function getOrCreateClient(baseUrl) {
1868
+ const existing = clients.get(baseUrl);
1869
+ if (existing) {
1870
+ return existing;
1871
+ }
1872
+ const client = new OpenAI({
1873
+ apiKey: config.apiKey,
1874
+ baseURL: baseUrl,
1875
+ timeout: capabilities.requestTimeoutMs,
1876
+ maxRetries: 0
1877
+ });
1878
+ clients.set(baseUrl, client);
1879
+ return client;
1880
+ }
904
1881
  }
905
- function emitAssistantReasoning(response, options) {
906
- if (response.reasoningContent && options.config.showReasoning && !response.streamedReasoningContent) {
907
- options.callbacks?.onReasoning?.(response.reasoningContent);
1882
+ function isProviderClientPool(value) {
1883
+ return Boolean(
1884
+ value && typeof value === "object" && typeof value.candidates === "function" && typeof value.markHealthy === "function"
1885
+ );
1886
+ }
1887
+
1888
+ // src/provider/request.ts
1889
+ async function fetchAssistantResponse(client, messages, request, tools, callbacks, abortSignal, onRequestMetric, observability) {
1890
+ const capabilities = resolveProviderCapabilities(request);
1891
+ const adapter = selectProviderWireAdapter(capabilities.wireApi);
1892
+ return tryFetch(
1893
+ adapter,
1894
+ client,
1895
+ messages,
1896
+ request,
1897
+ tools,
1898
+ callbacks,
1899
+ false,
1900
+ abortSignal,
1901
+ onRequestMetric,
1902
+ observability
1903
+ );
1904
+ }
1905
+ async function tryFetch(adapter, client, messages, request, tools, callbacks, forceReasoning, abortSignal, onRequestMetric, observability) {
1906
+ const startedAt = Date.now();
1907
+ let latestMetric;
1908
+ let resolvedBaseUrl;
1909
+ const forwardMetric = (metric) => {
1910
+ latestMetric = metric;
1911
+ onRequestMetric?.(metric);
1912
+ };
1913
+ if (observability) {
1914
+ await recordObservabilityEvent(observability.rootDir, {
1915
+ event: "model.request",
1916
+ status: "started",
1917
+ sessionId: observability.sessionId,
1918
+ identityKind: observability.identityKind,
1919
+ identityName: observability.identityName,
1920
+ model: request.model,
1921
+ details: {
1922
+ provider: request.provider,
1923
+ configuredModel: observability.configuredModel,
1924
+ requestModel: request.model,
1925
+ wireApi: adapter.wireApi,
1926
+ baseUrl: resolvedBaseUrl
1927
+ }
1928
+ });
1929
+ }
1930
+ try {
1931
+ const response = await withApiRetries(
1932
+ () => invokeWithProviderClients(client, async (providerClient, baseUrl) => {
1933
+ resolvedBaseUrl = baseUrl;
1934
+ return adapter.fetchStreaming(providerClient, {
1935
+ provider: request.provider,
1936
+ model: request.model,
1937
+ messages,
1938
+ tools,
1939
+ callbacks,
1940
+ forceReasoning,
1941
+ thinking: request.thinking,
1942
+ reasoningEffort: request.reasoningEffort,
1943
+ maxOutputTokens: request.maxOutputTokens,
1944
+ sessionId: request.sessionId,
1945
+ projectRoot: request.projectRoot,
1946
+ abortSignal,
1947
+ onRequestMetric: forwardMetric
1948
+ });
1949
+ }),
1950
+ abortSignal
1951
+ );
1952
+ if (observability) {
1953
+ await recordObservabilityEvent(observability.rootDir, {
1954
+ event: "model.request",
1955
+ status: "completed",
1956
+ sessionId: observability.sessionId,
1957
+ identityKind: observability.identityKind,
1958
+ identityName: observability.identityName,
1959
+ model: request.model,
1960
+ durationMs: Date.now() - startedAt,
1961
+ details: {
1962
+ provider: request.provider,
1963
+ configuredModel: observability.configuredModel,
1964
+ requestModel: request.model,
1965
+ wireApi: adapter.wireApi,
1966
+ baseUrl: resolvedBaseUrl,
1967
+ usage: latestMetric?.usage,
1968
+ usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
1969
+ }
1970
+ });
1971
+ }
1972
+ return response;
1973
+ } catch (error) {
1974
+ if (isAbortError(error)) {
1975
+ throw error;
1976
+ }
1977
+ try {
1978
+ const response = await withApiRetries(
1979
+ () => invokeWithProviderClients(client, async (providerClient, baseUrl) => {
1980
+ resolvedBaseUrl = baseUrl;
1981
+ return adapter.fetchNonStreaming(providerClient, {
1982
+ provider: request.provider,
1983
+ model: request.model,
1984
+ messages,
1985
+ tools,
1986
+ callbacks,
1987
+ forceReasoning,
1988
+ thinking: request.thinking,
1989
+ reasoningEffort: request.reasoningEffort,
1990
+ maxOutputTokens: request.maxOutputTokens,
1991
+ sessionId: request.sessionId,
1992
+ projectRoot: request.projectRoot,
1993
+ abortSignal,
1994
+ onRequestMetric: forwardMetric
1995
+ });
1996
+ }),
1997
+ abortSignal
1998
+ );
1999
+ if (observability) {
2000
+ await recordObservabilityEvent(observability.rootDir, {
2001
+ event: "model.request",
2002
+ status: "completed",
2003
+ sessionId: observability.sessionId,
2004
+ identityKind: observability.identityKind,
2005
+ identityName: observability.identityName,
2006
+ model: request.model,
2007
+ durationMs: Date.now() - startedAt,
2008
+ details: {
2009
+ provider: request.provider,
2010
+ configuredModel: observability.configuredModel,
2011
+ requestModel: request.model,
2012
+ wireApi: adapter.wireApi,
2013
+ baseUrl: resolvedBaseUrl,
2014
+ usage: latestMetric?.usage,
2015
+ usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
2016
+ }
2017
+ });
2018
+ }
2019
+ return response;
2020
+ } catch (fallbackError) {
2021
+ if (!isAbortError(fallbackError) && observability) {
2022
+ await recordObservabilityEvent(observability.rootDir, {
2023
+ event: "model.request",
2024
+ status: "failed",
2025
+ sessionId: observability.sessionId,
2026
+ identityKind: observability.identityKind,
2027
+ identityName: observability.identityName,
2028
+ model: request.model,
2029
+ durationMs: Date.now() - startedAt,
2030
+ error: fallbackError,
2031
+ details: {
2032
+ provider: request.provider,
2033
+ configuredModel: observability.configuredModel,
2034
+ requestModel: request.model,
2035
+ wireApi: adapter.wireApi,
2036
+ baseUrl: resolvedBaseUrl,
2037
+ usage: latestMetric?.usage,
2038
+ usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
2039
+ }
2040
+ });
2041
+ }
2042
+ throw fallbackError;
2043
+ }
908
2044
  }
909
2045
  }
910
- function emitAssistantFinalOutput(response, options) {
911
- if (response.content && !response.streamedAssistantContent) {
912
- options.callbacks?.onAssistantText?.(response.content);
2046
+ function selectProviderWireAdapter(wireApi) {
2047
+ if (wireApi === "responses") {
2048
+ return responsesAdapter;
913
2049
  }
914
- if (response.content) {
915
- options.callbacks?.onAssistantDone?.(response.content);
2050
+ return chatCompletionsAdapter;
2051
+ }
2052
+ async function invokeWithProviderClients(client, operation) {
2053
+ if (!isProviderClientPool(client)) {
2054
+ return operation(client, void 0);
2055
+ }
2056
+ let lastError;
2057
+ const candidates = client.candidates();
2058
+ for (let index = 0; index < candidates.length; index += 1) {
2059
+ const candidate = candidates[index];
2060
+ try {
2061
+ const result = await operation(candidate.client, candidate.baseUrl);
2062
+ client.markHealthy(candidate.baseUrl);
2063
+ return result;
2064
+ } catch (error) {
2065
+ lastError = error;
2066
+ if (isAbortError(error)) {
2067
+ throw error;
2068
+ }
2069
+ const hasMoreCandidates = index < candidates.length - 1;
2070
+ if (!hasMoreCandidates || !canRetryWithAlternateBaseUrl(error)) {
2071
+ throw error;
2072
+ }
2073
+ }
916
2074
  }
2075
+ throw lastError;
917
2076
  }
918
- function hasVisibleAssistantResult(content) {
919
- return typeof content === "string" && content.trim().length > 0;
2077
+ function canRetryWithAlternateBaseUrl(error) {
2078
+ const status = error.status;
2079
+ const message = String(error.message ?? error).toLowerCase();
2080
+ return status === 404 || status === 405 || message.includes("404") || message.includes("not found");
920
2081
  }
921
2082
 
922
2083
  // src/provider/retryPolicy.ts
@@ -1097,25 +2258,25 @@ function buildSessionConversationBriefBlock(brief) {
1097
2258
  if (!brief?.modelSummary) {
1098
2259
  return void 0;
1099
2260
  }
1100
- return buildFieldBlock("Internal continuity state", [
2261
+ return buildFieldBlock("Conversation continuity evidence", [
1101
2262
  {
1102
2263
  label: "Purpose",
1103
2264
  value: "Use these facts as private continuity state. Answer the current request directly. Quote prior turns only when the user asks."
1104
2265
  },
1105
2266
  brief.modelSummary ? {
1106
- label: "Session memory",
2267
+ label: "Model-written session memory",
1107
2268
  value: brief.modelSummary
1108
- } : { label: "Session memory", value: void 0 },
2269
+ } : { label: "Model-written session memory", value: void 0 },
1109
2270
  brief.modelSummaryUpdatedAt ? {
1110
- label: "Memory updated at",
2271
+ label: "Updated",
1111
2272
  value: brief.modelSummaryUpdatedAt
1112
- } : { label: "Memory updated at", value: void 0 },
2273
+ } : { label: "Updated", value: void 0 },
1113
2274
  {
1114
- label: "Visible turns",
2275
+ label: "Near-field visible turns",
1115
2276
  value: `${brief.userTurnCount} user turn(s) with current input / ${brief.assistantTurnCount} assistant response(s)`
1116
2277
  },
1117
2278
  {
1118
- label: "Tool activity",
2279
+ label: "Recent tool activity",
1119
2280
  value: formatSignals(brief.toolActivity)
1120
2281
  }
1121
2282
  ]);
@@ -1243,7 +2404,7 @@ function buildHistoryBoundaryBlock(memory) {
1243
2404
  return buildFieldBlock("History boundary", [
1244
2405
  {
1245
2406
  label: "Policy",
1246
- value: "Raw session history stays out of the current request. Internal continuity state and current workset are automatic facts for judgment, not text to narrate."
2407
+ value: "Raw session history stays out of the current request. Conversation continuity evidence and current workset are automatic facts for judgment, not text to narrate."
1247
2408
  }
1248
2409
  ]);
1249
2410
  }
@@ -1486,6 +2647,10 @@ function buildTaskLifecyclePromptBlock(lifecycle) {
1486
2647
  return void 0;
1487
2648
  }
1488
2649
  const fields = [
2650
+ {
2651
+ label: "Purpose",
2652
+ value: "Current task-state evidence. Use it to orient the next action; do not treat it as a new user request."
2653
+ },
1489
2654
  { label: "Stage", value: lifecycle.stage },
1490
2655
  lifecycle.scope ? { label: "Scope", value: lifecycle.scope } : void 0,
1491
2656
  lifecycle.boundary ? { label: "Boundary", value: lifecycle.boundary } : void 0,
@@ -1496,14 +2661,14 @@ function buildTaskLifecyclePromptBlock(lifecycle) {
1496
2661
  lifecycle.completionFacts.length > 0 ? { label: "Completion facts", value: formatLimitedList(lifecycle.completionFacts, 4) } : void 0,
1497
2662
  { label: "Updated", value: lifecycle.updatedAt }
1498
2663
  ];
1499
- return buildFieldBlock("Task lifecycle", fields.filter((field) => Boolean(field)));
2664
+ return buildFieldBlock("Current task scene evidence", fields.filter((field) => Boolean(field)));
1500
2665
  }
1501
2666
  function buildProjectMapPromptBlock(projectMap) {
1502
2667
  if (!projectMap) {
1503
2668
  return void 0;
1504
2669
  }
1505
2670
  const fields = [
1506
- { label: "Purpose", value: "Machine facts for orientation. Use as evidence, not as a route command." },
2671
+ { label: "Purpose", value: "Project orientation evidence. Use as facts for the current turn; do not treat this block as a task route." },
1507
2672
  { label: "Root", value: projectMap.rootDir },
1508
2673
  { label: "Top-level dirs", value: formatLimitedList(projectMap.topLevelDirectories, 10) },
1509
2674
  { label: "Entries", value: formatLimitedList(projectMap.entryFiles, 8) },
@@ -1517,7 +2682,7 @@ function buildProjectMapPromptBlock(projectMap) {
1517
2682
  projectMap.git.recentChanges.length > 0 ? { label: "Recent changes", value: formatLimitedList(projectMap.git.recentChanges, 6) } : void 0,
1518
2683
  { label: "Updated", value: projectMap.updatedAt }
1519
2684
  ];
1520
- return buildFieldBlock("Project map", fields.filter((field) => Boolean(field)));
2685
+ return buildFieldBlock("Project orientation evidence", fields.filter((field) => Boolean(field)));
1521
2686
  }
1522
2687
 
1523
2688
  // src/agent/prompt/metrics.ts
@@ -1650,7 +2815,8 @@ function buildCompressedContextRequest(systemPrompt, messages, config) {
1650
2815
  const safeMaxChars = Math.max(8e3, config.maxContextChars);
1651
2816
  const conversation = buildVisibleConversationWindow(messages);
1652
2817
  const conversationMessages = conversation.messages;
1653
- const fullMessages = composeChatMessages(systemPrompt, conversationMessages, config.model);
2818
+ const provider = config.provider ?? "openai-compatible";
2819
+ const fullMessages = composeChatMessages(systemPrompt, conversationMessages, config.model, provider);
1654
2820
  const initialEstimatedChars = estimateChatMessagesChars(fullMessages);
1655
2821
  const initialPromptMetrics = measureSystemPrompt(systemPrompt);
1656
2822
  const initialSources = buildBudgetSources(systemPrompt, conversationMessages);
@@ -1679,7 +2845,7 @@ function buildCompressedContextRequest(systemPrompt, messages, config) {
1679
2845
  const summary = compressedFrameHead.length > 0 ? summarizeConversation(compressedFrameHead, config.contextSummaryChars) : void 0;
1680
2846
  const summaryPrompt = appendSummary(systemPrompt, summary);
1681
2847
  let workingTail = compactTailMessages(tailMessages, "normal");
1682
- let requestMessages = composeChatMessages(summaryPrompt, workingTail, config.model);
2848
+ let requestMessages = composeChatMessages(summaryPrompt, workingTail, config.model, provider);
1683
2849
  let estimatedChars = estimateChatMessagesChars(requestMessages);
1684
2850
  let promptMetrics = measureSystemPrompt(summaryPrompt);
1685
2851
  let cacheLayout = buildCacheLayoutReport(summaryPrompt, workingTail);
@@ -1704,7 +2870,7 @@ function buildCompressedContextRequest(systemPrompt, messages, config) {
1704
2870
  };
1705
2871
  }
1706
2872
  workingTail = compactTailMessages(tailMessages, "aggressive");
1707
- requestMessages = composeChatMessages(summaryPrompt, workingTail, config.model);
2873
+ requestMessages = composeChatMessages(summaryPrompt, workingTail, config.model, provider);
1708
2874
  estimatedChars = estimateChatMessagesChars(requestMessages);
1709
2875
  promptMetrics = measureSystemPrompt(summaryPrompt);
1710
2876
  cacheLayout = buildCacheLayoutReport(summaryPrompt, workingTail);
@@ -1740,7 +2906,8 @@ function buildCompressedContextRequest(systemPrompt, messages, config) {
1740
2906
  const hardMessages = composeChatMessages(
1741
2907
  hardPrompt,
1742
2908
  compactedHardTail,
1743
- config.model
2909
+ config.model,
2910
+ provider
1744
2911
  );
1745
2912
  const hardEstimatedChars = estimateChatMessagesChars(hardMessages);
1746
2913
  const hardCacheLayout = buildCacheLayoutReport(hardPrompt, compactedHardTail);
@@ -1775,7 +2942,7 @@ function sliceTailMessages(messages, tailCount) {
1775
2942
  const safeStartIndex = expandStartToToolBoundary(messages, startIndex);
1776
2943
  return messages.slice(safeStartIndex);
1777
2944
  }
1778
- function composeChatMessages(systemPrompt, messages, model) {
2945
+ function composeChatMessages(systemPrompt, messages, model, provider) {
1779
2946
  return [
1780
2947
  {
1781
2948
  role: "system",
@@ -1787,7 +2954,7 @@ function composeChatMessages(systemPrompt, messages, model) {
1787
2954
  name: message.name,
1788
2955
  toolCallId: message.tool_call_id,
1789
2956
  toolCalls: message.tool_calls,
1790
- reasoningContent: shouldIncludeStoredAssistantReasoning(messages, index, model) ? message.reasoningContent : void 0
2957
+ reasoningContent: shouldIncludeStoredAssistantReasoning(messages, index, model, provider) ? message.reasoningContent : void 0
1791
2958
  }))
1792
2959
  ];
1793
2960
  }
@@ -1934,8 +3101,8 @@ function buildCacheLayoutReport(systemPrompt, messages) {
1934
3101
  }))
1935
3102
  });
1936
3103
  return {
1937
- stablePrefixFingerprint: stableHash(stablePrefix),
1938
- volatileTailFingerprint: stableHash(volatileTail),
3104
+ stablePrefixFingerprint: stableHash2(stablePrefix),
3105
+ volatileTailFingerprint: stableHash2(volatileTail),
1939
3106
  stablePrefixChars: stablePrefix.length,
1940
3107
  volatileTailChars: volatileTail.length,
1941
3108
  stableSources: typeof systemPrompt === "string" ? ["systemPrompt"] : [
@@ -1966,7 +3133,7 @@ function renderVolatileRuntimeFacts(systemPrompt) {
1966
3133
  joinBlocks(systemPrompt.runtimeFactBlocks)
1967
3134
  ].join("\n").trim();
1968
3135
  }
1969
- function stableHash(value) {
3136
+ function stableHash2(value) {
1970
3137
  let hash = 2166136261;
1971
3138
  for (let index = 0; index < value.length; index += 1) {
1972
3139
  hash ^= value.charCodeAt(index);
@@ -2202,6 +3369,7 @@ async function updateSessionMemoryAfterTurn(input) {
2202
3369
  identityName: input.identity.name,
2203
3370
  model: input.requestModel
2204
3371
  });
3372
+ input.options.callbacks?.onStatus?.("\u603B\u7ED3\u4E2D");
2205
3373
  try {
2206
3374
  const memoryResponse = input.options.fetchSessionMemoryResponse ? await input.options.fetchSessionMemoryResponse(modelRequest) : await fetchAssistantResponse(
2207
3375
  input.client,
@@ -2252,6 +3420,8 @@ async function updateSessionMemoryAfterTurn(input) {
2252
3420
  error
2253
3421
  });
2254
3422
  return input.session;
3423
+ } finally {
3424
+ input.options.callbacks?.onStatus?.("");
2255
3425
  }
2256
3426
  }
2257
3427
  async function updateSessionTitleAfterTurn(input) {
@@ -2299,6 +3469,7 @@ async function updateSessionTitleAfterTurn(input) {
2299
3469
  identityName: input.identity.name,
2300
3470
  model: input.requestModel
2301
3471
  });
3472
+ input.options.callbacks?.onStatus?.("\u6807\u9898\u751F\u6210\u4E2D");
2302
3473
  try {
2303
3474
  const titleResponse = input.options.fetchSessionTitleResponse ? await input.options.fetchSessionTitleResponse(modelRequest) : await fetchAssistantResponse(
2304
3475
  input.client,
@@ -2350,15 +3521,17 @@ async function updateSessionTitleAfterTurn(input) {
2350
3521
  error
2351
3522
  });
2352
3523
  return input.session;
3524
+ } finally {
3525
+ input.options.callbacks?.onStatus?.("");
2353
3526
  }
2354
3527
  }
2355
3528
 
2356
3529
  // src/control/ledger.ts
2357
3530
  import Database from "better-sqlite3";
2358
- import fs6 from "fs";
3531
+ import fs8 from "fs";
2359
3532
 
2360
3533
  // src/control/executions.ts
2361
- import path7 from "path";
3534
+ import path9 from "path";
2362
3535
 
2363
3536
  // src/protocol/leadWait.ts
2364
3537
  var LEAD_WAIT_PROTOCOL = "kitty.lead-wait-policy";
@@ -2587,7 +3760,7 @@ var ExecutionLedgerRepo = class {
2587
3760
  prompt: input.prompt,
2588
3761
  actorName: input.actorName,
2589
3762
  actorRole: input.actorRole,
2590
- cwd: path7.resolve(input.cwd),
3763
+ cwd: path9.resolve(input.cwd),
2591
3764
  requestedBy: input.requestedBy,
2592
3765
  sessionId: input.sessionId,
2593
3766
  pid: input.pid,
@@ -2618,10 +3791,10 @@ var ExecutionLedgerRepo = class {
2618
3791
  }
2619
3792
  list(input = {}) {
2620
3793
  const rows = this.db.prepare("SELECT * FROM executions ORDER BY created_at ASC").all();
2621
- const cwd = input.cwd ? path7.resolve(input.cwd) : void 0;
3794
+ const cwd = input.cwd ? path9.resolve(input.cwd) : void 0;
2622
3795
  const statuses = new Set(input.statuses ?? []);
2623
3796
  const kinds = new Set(input.kinds ?? []);
2624
- return rows.map(fromExecutionRow).filter((record) => !input.kind || record.kind === input.kind).filter((record) => kinds.size === 0 || kinds.has(record.kind)).filter((record) => statuses.size === 0 || statuses.has(record.status)).filter((record) => !cwd || isSameOrDescendant(path7.resolve(record.cwd), cwd) || isSameOrDescendant(cwd, path7.resolve(record.cwd)));
3797
+ return rows.map(fromExecutionRow).filter((record) => !input.kind || record.kind === input.kind).filter((record) => kinds.size === 0 || kinds.has(record.kind)).filter((record) => statuses.size === 0 || statuses.has(record.status)).filter((record) => !cwd || isSameOrDescendant(path9.resolve(record.cwd), cwd) || isSameOrDescendant(cwd, path9.resolve(record.cwd)));
2625
3798
  }
2626
3799
  markRunning(id, input) {
2627
3800
  const current = requireExecution(this.load(id), id);
@@ -2699,8 +3872,8 @@ function requireExecution(record, id) {
2699
3872
  return record;
2700
3873
  }
2701
3874
  function isSameOrDescendant(targetPath, possibleAncestor) {
2702
- const relative = path7.relative(possibleAncestor, targetPath);
2703
- return relative === "" || !relative.startsWith("..") && !path7.isAbsolute(relative);
3875
+ const relative = path9.relative(possibleAncestor, targetPath);
3876
+ return relative === "" || !relative.startsWith("..") && !path9.isAbsolute(relative);
2704
3877
  }
2705
3878
 
2706
3879
  // src/control/schema.ts
@@ -2814,7 +3987,7 @@ var TaskLifecycleLedgerRepo = class {
2814
3987
  stage: existing?.stage === "completed" || !existing ? "normal_work" : existing.stage,
2815
3988
  scope: existing?.scope,
2816
3989
  boundary: existing?.boundary,
2817
- reason: normalizeText2(input.reason) ?? existing?.reason ?? "turn_started",
3990
+ reason: normalizeText3(input.reason) ?? existing?.reason ?? "turn_started",
2818
3991
  activeExecutionIds: existing?.activeExecutionIds ?? [],
2819
3992
  activeTodoIds: existing?.activeTodoIds ?? [],
2820
3993
  verificationFacts: existing?.verificationFacts ?? [],
@@ -2843,9 +4016,9 @@ var TaskLifecycleLedgerRepo = class {
2843
4016
  return this.save({
2844
4017
  ...current,
2845
4018
  stage: input.stage ?? current.stage,
2846
- scope: normalizeText2(input.scope) ?? current.scope,
2847
- boundary: normalizeText2(input.boundary) ?? current.boundary,
2848
- reason: normalizeText2(input.reason) ?? current.reason,
4019
+ scope: normalizeText3(input.scope) ?? current.scope,
4020
+ boundary: normalizeText3(input.boundary) ?? current.boundary,
4021
+ reason: normalizeText3(input.reason) ?? current.reason,
2849
4022
  activeExecutionIds: normalizeStringList2(input.activeExecutionIds ?? current.activeExecutionIds),
2850
4023
  activeTodoIds: normalizeStringList2(input.activeTodoIds ?? current.activeTodoIds),
2851
4024
  verificationFacts: normalizeStringList2(input.verificationFacts ?? current.verificationFacts),
@@ -2961,7 +4134,7 @@ function normalizeStringList2(value) {
2961
4134
  const seen = /* @__PURE__ */ new Set();
2962
4135
  const items = [];
2963
4136
  for (const item of value) {
2964
- const text = normalizeText2(item);
4137
+ const text = normalizeText3(item);
2965
4138
  if (!text || seen.has(text)) {
2966
4139
  continue;
2967
4140
  }
@@ -2970,7 +4143,7 @@ function normalizeStringList2(value) {
2970
4143
  }
2971
4144
  return items;
2972
4145
  }
2973
- function normalizeText2(value) {
4146
+ function normalizeText3(value) {
2974
4147
  return typeof value === "string" && value.trim() ? value.replace(/\s+/g, " ").trim() : void 0;
2975
4148
  }
2976
4149
 
@@ -3010,7 +4183,7 @@ var ControlPlaneLedger = class {
3010
4183
  db;
3011
4184
  constructor(rootDir) {
3012
4185
  const statePaths = getProjectStatePaths(rootDir);
3013
- fs6.mkdirSync(statePaths.kittyDir, { recursive: true });
4186
+ fs8.mkdirSync(statePaths.kittyDir, { recursive: true });
3014
4187
  this.db = new Database(statePaths.controlPlaneLedgerFile);
3015
4188
  this.db.pragma("journal_mode = WAL");
3016
4189
  this.db.pragma("foreign_keys = ON");
@@ -3177,11 +4350,11 @@ function truncateWakeFact(value) {
3177
4350
  }
3178
4351
 
3179
4352
  // src/utils/fs.ts
3180
- import fs7 from "fs/promises";
3181
- import path8 from "path";
4353
+ import fs9 from "fs/promises";
4354
+ import path10 from "path";
3182
4355
  async function fileExists(targetPath) {
3183
4356
  try {
3184
- await fs7.access(targetPath);
4357
+ await fs9.access(targetPath);
3185
4358
  return true;
3186
4359
  } catch {
3187
4360
  return false;
@@ -3189,10 +4362,10 @@ async function fileExists(targetPath) {
3189
4362
  }
3190
4363
  function resolveUserPath(inputPath, cwd) {
3191
4364
  const cleanPath = normalizeUserPathInput(inputPath);
3192
- if (path8.isAbsolute(cleanPath)) {
3193
- return path8.normalize(cleanPath);
4365
+ if (path10.isAbsolute(cleanPath)) {
4366
+ return path10.normalize(cleanPath);
3194
4367
  }
3195
- return path8.resolve(cwd, cleanPath);
4368
+ return path10.resolve(cwd, cleanPath);
3196
4369
  }
3197
4370
  function normalizeUserPathInput(inputPath) {
3198
4371
  const trimmed = inputPath.trim();
@@ -3206,7 +4379,7 @@ function normalizeUserPathInput(inputPath) {
3206
4379
  return trimmed;
3207
4380
  }
3208
4381
  async function ensureParentDirectory(filePath) {
3209
- await fs7.mkdir(path8.dirname(filePath), { recursive: true });
4382
+ await fs9.mkdir(path10.dirname(filePath), { recursive: true });
3210
4383
  }
3211
4384
  function truncateText(input, maxChars) {
3212
4385
  if (input.length <= maxChars) {
@@ -3258,7 +4431,7 @@ function projectToolResultForModel(input) {
3258
4431
  }
3259
4432
  }
3260
4433
  function projectExecutionAction(payload) {
3261
- const execution = readObject(payload.execution) ?? payload;
4434
+ const execution = readObject2(payload.execution) ?? payload;
3262
4435
  return joinLines([
3263
4436
  readString(execution.id) ?? "execution",
3264
4437
  readString(execution.kind),
@@ -3266,7 +4439,7 @@ function projectExecutionAction(payload) {
3266
4439
  readString(execution.command),
3267
4440
  readString(execution.summary),
3268
4441
  readString(execution.outputPreview),
3269
- readObject(execution.health) ? readString(readObject(execution.health)?.message) : void 0,
4442
+ readObject2(execution.health) ? readString(readObject2(execution.health)?.message) : void 0,
3270
4443
  readString(execution.error)
3271
4444
  ]);
3272
4445
  }
@@ -3293,10 +4466,10 @@ function projectExecutionCheck(payload) {
3293
4466
  ]);
3294
4467
  }
3295
4468
  function projectRead(payload) {
3296
- const path27 = readString(payload.path) ?? readString(payload.requestedPath) ?? "file";
4469
+ const path29 = readString(payload.path) ?? readString(payload.requestedPath) ?? "file";
3297
4470
  if (payload.readable === false) {
3298
4471
  return joinLines([
3299
- `${path27}: not readable`,
4472
+ `${path29}: not readable`,
3300
4473
  readString(payload.reason),
3301
4474
  readString(payload.detectedCapability) ? `capability: ${readString(payload.detectedCapability)}` : void 0
3302
4475
  ]);
@@ -3304,35 +4477,35 @@ function projectRead(payload) {
3304
4477
  const startLine = readNumber(payload.startLine);
3305
4478
  const endLine = readNumber(payload.endLine);
3306
4479
  const content = readString(payload.content) ?? "";
3307
- const continuation = readObject(payload.continuation);
3308
- const continuationArgs = readObject(continuation?.continuationArgs);
4480
+ const continuation = readObject2(payload.continuation);
4481
+ const continuationArgs = readObject2(continuation?.continuationArgs);
3309
4482
  return joinLines([
3310
- `${path27}${startLine && endLine ? `:${startLine}-${endLine}` : ""}`,
4483
+ `${path29}${startLine && endLine ? `:${startLine}-${endLine}` : ""}`,
3311
4484
  truncateText(content, DEFAULT_MAX_CHARS),
3312
4485
  continuationArgs ? `next: read ${JSON.stringify(continuationArgs)}` : void 0
3313
4486
  ]);
3314
4487
  }
3315
4488
  function projectEdit(payload) {
3316
- const path27 = readString(payload.path) ?? "file";
4489
+ const path29 = readString(payload.path) ?? "file";
3317
4490
  const applied = readNumber(payload.appliedEdits) ?? readNumber(payload.requestedEdits);
3318
4491
  const diff = readString(payload.diff) ?? readString(payload.preview);
3319
4492
  return joinLines([
3320
- `edited ${path27}${applied ? ` (${applied} replacement${applied === 1 ? "" : "s"})` : ""}`,
4493
+ `edited ${path29}${applied ? ` (${applied} replacement${applied === 1 ? "" : "s"})` : ""}`,
3321
4494
  diff ? truncateText(diff, DIFF_MAX_CHARS) : void 0
3322
4495
  ]);
3323
4496
  }
3324
4497
  function projectWrite(payload) {
3325
- const path27 = readString(payload.path) ?? "file";
4498
+ const path29 = readString(payload.path) ?? "file";
3326
4499
  const bytes = readNumber(payload.bytes);
3327
4500
  const existed = payload.existed === true;
3328
4501
  const diff = readString(payload.diff) ?? readString(payload.preview);
3329
4502
  return joinLines([
3330
- `${existed ? "wrote" : "created"} ${path27}${bytes !== void 0 ? ` (${bytes} bytes)` : ""}`,
4503
+ `${existed ? "wrote" : "created"} ${path29}${bytes !== void 0 ? ` (${bytes} bytes)` : ""}`,
3331
4504
  diff ? truncateText(diff, DIFF_MAX_CHARS) : void 0
3332
4505
  ]);
3333
4506
  }
3334
4507
  function projectBash(payload) {
3335
- const governance = readObject(payload.outputGovernance);
4508
+ const governance = readObject2(payload.outputGovernance);
3336
4509
  const projection = readString(governance?.projection);
3337
4510
  if (projection) {
3338
4511
  return projection;
@@ -3353,13 +4526,13 @@ function projectBash(payload) {
3353
4526
  return joinLines(lines);
3354
4527
  }
3355
4528
  function projectSkillLoad(payload) {
3356
- const skill = readObject(payload.skill);
4529
+ const skill = readObject2(payload.skill);
3357
4530
  const name = readString(skill?.name) ?? "skill";
3358
4531
  const description = readString(skill?.description);
3359
- const path27 = readString(skill?.path);
4532
+ const path29 = readString(skill?.path);
3360
4533
  const body = readString(payload.body) ?? "";
3361
4534
  return joinLines([
3362
- `loaded skill: ${name}${path27 ? ` (${path27})` : ""}`,
4535
+ `loaded skill: ${name}${path29 ? ` (${path29})` : ""}`,
3363
4536
  description,
3364
4537
  truncateText(body, SKILL_BODY_MAX_CHARS)
3365
4538
  ]);
@@ -3389,8 +4562,8 @@ function projectFailure(toolName, rawOutput, payload) {
3389
4562
  if (!payload) {
3390
4563
  return truncateText(rawOutput.trim(), DEFAULT_MAX_CHARS);
3391
4564
  }
3392
- const details = readObject(payload.details);
3393
- const readArgs = readObject(details?.readArgs);
4565
+ const details = readObject2(payload.details);
4566
+ const readArgs = readObject2(details?.readArgs);
3394
4567
  const suggestions = readArray(details?.suggestions);
3395
4568
  const lines = [
3396
4569
  `${toolName} failed: ${readString(payload.error) ?? "unknown error"}`,
@@ -3412,7 +4585,7 @@ function parseObject(raw) {
3412
4585
  return null;
3413
4586
  }
3414
4587
  }
3415
- function readObject(value) {
4588
+ function readObject2(value) {
3416
4589
  return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
3417
4590
  }
3418
4591
  function readArray(value) {
@@ -3447,14 +4620,14 @@ var ToolExecutionError = class extends Error {
3447
4620
  };
3448
4621
 
3449
4622
  // src/tools/outputCapture.ts
3450
- import crypto from "crypto";
3451
- import fs8 from "fs/promises";
3452
- import path9 from "path";
4623
+ import crypto2 from "crypto";
4624
+ import fs10 from "fs/promises";
4625
+ import path11 from "path";
3453
4626
  var DEFAULT_BASH_OUTPUT_PREVIEW_CHARS = 12e3;
3454
4627
  async function createBashOutputCapture(input) {
3455
4628
  const maxPreviewChars = typeof input.maxPreviewChars === "number" && Number.isFinite(input.maxPreviewChars) && input.maxPreviewChars > 0 ? Math.trunc(input.maxPreviewChars) : DEFAULT_BASH_OUTPUT_PREVIEW_CHARS;
3456
4629
  const absoluteOutputPath = input.stateRootDir && input.sessionId ? await createAbsoluteOutputPath(input.stateRootDir, input.sessionId) : void 0;
3457
- const outputPath = absoluteOutputPath && input.stateRootDir ? path9.relative(input.stateRootDir, absoluteOutputPath) || void 0 : void 0;
4630
+ const outputPath = absoluteOutputPath && input.stateRootDir ? path11.relative(input.stateRootDir, absoluteOutputPath) || void 0 : void 0;
3458
4631
  let preview = "";
3459
4632
  let bufferedOutput = "";
3460
4633
  let totalChars = 0;
@@ -3498,7 +4671,7 @@ async function createBashOutputCapture(input) {
3498
4671
  if (!absoluteOutputPath) {
3499
4672
  return;
3500
4673
  }
3501
- pendingWrite = pendingWrite.then(() => fs8.appendFile(absoluteOutputPath, chunk, "utf8"));
4674
+ pendingWrite = pendingWrite.then(() => fs10.appendFile(absoluteOutputPath, chunk, "utf8"));
3502
4675
  }
3503
4676
  return {
3504
4677
  append,
@@ -3507,9 +4680,9 @@ async function createBashOutputCapture(input) {
3507
4680
  }
3508
4681
  async function createAbsoluteOutputPath(stateRootDir, sessionId) {
3509
4682
  const paths = getProjectStatePaths(stateRootDir);
3510
- const sessionDir = path9.join(paths.observabilityDir, "command-output", sessionId);
3511
- await fs8.mkdir(sessionDir, { recursive: true });
3512
- return path9.join(sessionDir, `${Date.now()}-bash-output-${crypto.randomUUID().slice(0, 8)}.txt`);
4683
+ const sessionDir = path11.join(paths.observabilityDir, "command-output", sessionId);
4684
+ await fs10.mkdir(sessionDir, { recursive: true });
4685
+ return path11.join(sessionDir, `${Date.now()}-bash-output-${crypto2.randomUUID().slice(0, 8)}.txt`);
3513
4686
  }
3514
4687
 
3515
4688
  // src/utils/commandRunner/shellRuntime.ts
@@ -3723,8 +4896,8 @@ function isAbortedProcessResult(value, signal) {
3723
4896
  }
3724
4897
 
3725
4898
  // src/tools/core/shared.ts
3726
- import fs9 from "fs/promises";
3727
- import path10 from "path";
4899
+ import fs11 from "fs/promises";
4900
+ import path12 from "path";
3728
4901
  import { diffLines } from "diff";
3729
4902
 
3730
4903
  // src/utils/text.ts
@@ -3938,21 +5111,21 @@ function comparePathForDiscovery(root, left, right) {
3938
5111
  if (leftDepth !== rightDepth) {
3939
5112
  return leftDepth - rightDepth;
3940
5113
  }
3941
- const leftName = path10.posix.basename(leftRelative).toLowerCase();
3942
- const rightName = path10.posix.basename(rightRelative).toLowerCase();
5114
+ const leftName = path12.posix.basename(leftRelative).toLowerCase();
5115
+ const rightName = path12.posix.basename(rightRelative).toLowerCase();
3943
5116
  if (leftName !== rightName) {
3944
5117
  return leftName.localeCompare(rightName);
3945
5118
  }
3946
5119
  return leftRelative.localeCompare(rightRelative);
3947
5120
  }
3948
5121
  function toPosixRelative(root, targetPath) {
3949
- return (path10.relative(root, targetPath) || path10.basename(targetPath)).replace(/\\/g, "/");
5122
+ return (path12.relative(root, targetPath) || path12.basename(targetPath)).replace(/\\/g, "/");
3950
5123
  }
3951
5124
  function pathDepth(relativePath) {
3952
5125
  return relativePath.split("/").filter(Boolean).length;
3953
5126
  }
3954
5127
 
3955
- // src/tools/outputKernel/classifier.ts
5128
+ // src/tools/outputGovernance/classifier.ts
3956
5129
  function classifyToolOutput(source) {
3957
5130
  const output = source.output.trim();
3958
5131
  if (!output) {
@@ -3993,7 +5166,7 @@ function looksLikeBuild(command, text) {
3993
5166
  return /\b(build|compile|cargo check|cargo clippy|npm run build|pnpm build)\b/.test(command) || /\b(compilation failed|build failed|compiled successfully|error\[e\d+\])\b/.test(text);
3994
5167
  }
3995
5168
 
3996
- // src/tools/outputKernel/metrics.ts
5169
+ // src/tools/outputGovernance/metrics.ts
3997
5170
  function estimateTextTokens(value) {
3998
5171
  const trimmed = value.trim();
3999
5172
  if (!trimmed) {
@@ -4018,7 +5191,7 @@ function computeSavings(input) {
4018
5191
  };
4019
5192
  }
4020
5193
 
4021
- // src/tools/outputKernel/projectors/shared.ts
5194
+ // src/tools/outputGovernance/projectors/shared.ts
4022
5195
  function buildHeader(source, label) {
4023
5196
  return [
4024
5197
  `${source.toolName}: ${label}`,
@@ -4044,7 +5217,7 @@ function dedupeProjectedLines(lines) {
4044
5217
  return result;
4045
5218
  }
4046
5219
 
4047
- // src/tools/outputKernel/projectors/diagnostic.ts
5220
+ // src/tools/outputGovernance/projectors/diagnostic.ts
4048
5221
  var STRUCTURED_MAX_LINES = 28;
4049
5222
  function buildDiagnosticProjection(source, label) {
4050
5223
  const lines = splitOutputLines(source.output);
@@ -4063,7 +5236,7 @@ function isSummaryLine(line) {
4063
5236
  return /\b(\d+\s+(passed|failed|skipped|errors?|warnings?)|test result|found \d+ errors?|failed tests?|build failed|compiled successfully)\b/i.test(line);
4064
5237
  }
4065
5238
 
4066
- // src/tools/outputKernel/projectors/gitDiff.ts
5239
+ // src/tools/outputGovernance/projectors/gitDiff.ts
4067
5240
  var DIFF_MAX_FILES = 24;
4068
5241
  function buildGitDiffProjection(source) {
4069
5242
  const lines = splitOutputLines(source.output);
@@ -4078,7 +5251,7 @@ function buildGitDiffProjection(source) {
4078
5251
  ].filter((line) => Boolean(line)).join("\n");
4079
5252
  }
4080
5253
 
4081
- // src/tools/outputKernel/projectors/generic.ts
5254
+ // src/tools/outputGovernance/projectors/generic.ts
4082
5255
  var GENERIC_MAX_CHARS = 1500;
4083
5256
  function projectEmptyOutput(source) {
4084
5257
  return {
@@ -4120,7 +5293,7 @@ function buildGenericPreview(source) {
4120
5293
  ].filter(Boolean).join("\n");
4121
5294
  }
4122
5295
 
4123
- // src/tools/outputKernel/projectors/search.ts
5296
+ // src/tools/outputGovernance/projectors/search.ts
4124
5297
  var SEARCH_MAX_MATCHES = 24;
4125
5298
  function buildSearchProjection(source) {
4126
5299
  const nonEmptyLines = splitOutputLines(source.output).filter((line) => line.trim().length > 0);
@@ -4133,7 +5306,7 @@ function buildSearchProjection(source) {
4133
5306
  ].join("\n");
4134
5307
  }
4135
5308
 
4136
- // src/tools/outputKernel/projectors/recovery.ts
5309
+ // src/tools/outputGovernance/projectors/recovery.ts
4137
5310
  function appendRecoveryHint(projection, governance) {
4138
5311
  if (!governance.recoveryHint) {
4139
5312
  return projection;
@@ -4145,7 +5318,7 @@ function appendRecoveryHint(projection, governance) {
4145
5318
  ${governance.recoveryHint}`;
4146
5319
  }
4147
5320
 
4148
- // src/tools/outputKernel/projectors.ts
5321
+ // src/tools/outputGovernance/projectors.ts
4149
5322
  function projectOutputByKind(kind, source) {
4150
5323
  switch (kind) {
4151
5324
  case "empty":
@@ -4165,7 +5338,7 @@ function projectOutputByKind(kind, source) {
4165
5338
  }
4166
5339
  }
4167
5340
 
4168
- // src/tools/outputKernel/index.ts
5341
+ // src/tools/outputGovernance/index.ts
4169
5342
  function governToolOutput(source) {
4170
5343
  const kind = classifyToolOutput(source);
4171
5344
  const projected = projectOutputByKind(kind, source);
@@ -4307,7 +5480,7 @@ var bashToolDefinition = {
4307
5480
  };
4308
5481
 
4309
5482
  // src/tools/edit.ts
4310
- import fs11 from "fs/promises";
5483
+ import fs13 from "fs/promises";
4311
5484
 
4312
5485
  // src/tools/core/changeTracking.ts
4313
5486
  async function recordToolChange(context, input) {
@@ -4348,13 +5521,13 @@ function encodeUtf8(value) {
4348
5521
  }
4349
5522
 
4350
5523
  // src/tools/core/pathDisplay.ts
4351
- import path11 from "path";
5524
+ import path13 from "path";
4352
5525
  function toToolRelativePath(cwd, targetPath) {
4353
- const relative = path11.relative(cwd, targetPath);
5526
+ const relative = path13.relative(cwd, targetPath);
4354
5527
  if (!relative || relative === "") {
4355
5528
  return ".";
4356
5529
  }
4357
- if (relative.startsWith("..") || path11.isAbsolute(relative)) {
5530
+ if (relative.startsWith("..") || path13.isAbsolute(relative)) {
4358
5531
  return targetPath;
4359
5532
  }
4360
5533
  return relative.replace(/\\/g, "/");
@@ -4381,8 +5554,8 @@ function buildToolChangeFeedback(input) {
4381
5554
  }
4382
5555
 
4383
5556
  // src/tools/writeDiagnostics.ts
4384
- import fs10 from "fs/promises";
4385
- import path12 from "path";
5557
+ import fs12 from "fs/promises";
5558
+ import path14 from "path";
4386
5559
  var TYPE_SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
4387
5560
  async function collectWriteDiagnostics(paths) {
4388
5561
  const uniquePaths2 = takeUniquePaths(paths);
@@ -4429,7 +5602,7 @@ function createEmptyDiagnosticsReport(status) {
4429
5602
  }
4430
5603
  async function collectFileDiagnostics(targetPath) {
4431
5604
  try {
4432
- const extension = path12.extname(targetPath).toLowerCase();
5605
+ const extension = path14.extname(targetPath).toLowerCase();
4433
5606
  if (extension === ".json") {
4434
5607
  return await collectJsonDiagnostics(targetPath);
4435
5608
  }
@@ -4445,7 +5618,7 @@ async function collectFileDiagnostics(targetPath) {
4445
5618
  return null;
4446
5619
  }
4447
5620
  async function collectJsonDiagnostics(targetPath) {
4448
- const content = await fs10.readFile(targetPath, "utf8");
5621
+ const content = await fs12.readFile(targetPath, "utf8");
4449
5622
  const diagnostics = [];
4450
5623
  try {
4451
5624
  JSON.parse(content);
@@ -4466,7 +5639,7 @@ async function collectJsonDiagnostics(targetPath) {
4466
5639
  async function collectTypeScriptDiagnostics(targetPath) {
4467
5640
  const diagnostics = [];
4468
5641
  const TypeScript = await import("typescript");
4469
- const content = await fs10.readFile(targetPath, "utf8");
5642
+ const content = await fs12.readFile(targetPath, "utf8");
4470
5643
  const transpiled = TypeScript.transpileModule(content, {
4471
5644
  fileName: targetPath,
4472
5645
  compilerOptions: {
@@ -4583,7 +5756,7 @@ var editToolDefinition = {
4583
5756
  const resolved = resolveUserPath(targetPath, context.cwd);
4584
5757
  const displayPath = toToolRelativePath(context.cwd, resolved);
4585
5758
  return withFileEditLock(resolved, async () => {
4586
- const beforeBuffer = await fs11.readFile(resolved);
5759
+ const beforeBuffer = await fs13.readFile(resolved);
4587
5760
  const beforeEnvelope = decodeTextFileEnvelope(beforeBuffer);
4588
5761
  if (!beforeEnvelope) {
4589
5762
  throw new ToolExecutionError(`edit cannot edit binary or unsupported text encoding for ${displayPath}`, {
@@ -4601,7 +5774,7 @@ var editToolDefinition = {
4601
5774
  });
4602
5775
  }
4603
5776
  const diff = buildDiffPreview(before, after);
4604
- await fs11.writeFile(resolved, encodeTextFileEnvelope(after, beforeEnvelope));
5777
+ await fs13.writeFile(resolved, encodeTextFileEnvelope(after, beforeEnvelope));
4605
5778
  const changeRecord = await recordToolChange(context, {
4606
5779
  toolName: "edit",
4607
5780
  summary: `edit ${displayPath}`,
@@ -4755,9 +5928,9 @@ function lineForOffset(input, offset) {
4755
5928
  }
4756
5929
  return line;
4757
5930
  }
4758
- function buildReadArgs(path27, line) {
5931
+ function buildReadArgs(path29, line) {
4759
5932
  return {
4760
- path: path27,
5933
+ path: path29,
4761
5934
  offset: Math.max(1, (line ?? 1) - 20),
4762
5935
  limit: 60
4763
5936
  };
@@ -4811,8 +5984,8 @@ async function withFileEditLock(filePath, action) {
4811
5984
  }
4812
5985
 
4813
5986
  // src/tools/core/fileIntrospection.ts
4814
- import fs12 from "fs/promises";
4815
- import path13 from "path";
5987
+ import fs14 from "fs/promises";
5988
+ import path15 from "path";
4816
5989
  var KNOWN_BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
4817
5990
  ".epub",
4818
5991
  ".mobi",
@@ -4830,8 +6003,8 @@ var KNOWN_BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
4830
6003
  ".bin"
4831
6004
  ]);
4832
6005
  async function inspectTextFile(filePath, _maxBytes) {
4833
- const stat = await fs12.stat(filePath);
4834
- const extension = path13.extname(filePath).toLowerCase();
6006
+ const stat = await fs14.stat(filePath);
6007
+ const extension = path15.extname(filePath).toLowerCase();
4835
6008
  if (KNOWN_BINARY_EXTENSIONS.has(extension)) {
4836
6009
  return {
4837
6010
  readable: false,
@@ -4841,7 +6014,7 @@ async function inspectTextFile(filePath, _maxBytes) {
4841
6014
  extension
4842
6015
  };
4843
6016
  }
4844
- const buffer = await fs12.readFile(filePath);
6017
+ const buffer = await fs14.readFile(filePath);
4845
6018
  const decoded = decodeTextFileEnvelope(buffer);
4846
6019
  if (!decoded) {
4847
6020
  return {
@@ -4865,11 +6038,11 @@ async function inspectTextFile(filePath, _maxBytes) {
4865
6038
  }
4866
6039
 
4867
6040
  // src/tools/core/pathSuggestions.ts
4868
- import path14 from "path";
6041
+ import path16 from "path";
4869
6042
  import fg3 from "fast-glob";
4870
6043
  async function findPathSuggestions(cwd, requestedPath, projectContext, limit = 8) {
4871
6044
  const normalized = normalizeUserPathInput(requestedPath).replace(/\\/g, "/");
4872
- const baseName = path14.basename(normalized).trim();
6045
+ const baseName = path16.basename(normalized).trim();
4873
6046
  const needle = baseName.length > 0 ? baseName : normalized.trim();
4874
6047
  if (!needle) {
4875
6048
  return [];
@@ -4886,8 +6059,8 @@ async function findPathSuggestions(cwd, requestedPath, projectContext, limit = 8
4886
6059
  suppressErrors: true,
4887
6060
  ignore: buildFastGlobIgnorePatterns(cwd, projectContext.ignoreRules)
4888
6061
  });
4889
- for (const entry of entries.sort((left, right) => comparePathForDiscovery(cwd, path14.resolve(cwd, left), path14.resolve(cwd, right)))) {
4890
- const absolutePath = path14.resolve(cwd, entry);
6062
+ for (const entry of entries.sort((left, right) => comparePathForDiscovery(cwd, path16.resolve(cwd, left), path16.resolve(cwd, right)))) {
6063
+ const absolutePath = path16.resolve(cwd, entry);
4891
6064
  const isDirectory3 = entry.endsWith("/");
4892
6065
  if (isPathIgnored(absolutePath, projectContext.ignoreRules, isDirectory3)) {
4893
6066
  continue;
@@ -5052,7 +6225,7 @@ function fitWindowWithinBudget(lines, start, requestedEndExclusive, maxChars) {
5052
6225
  }
5053
6226
 
5054
6227
  // src/tools/write.ts
5055
- import fs13 from "fs/promises";
6228
+ import fs15 from "fs/promises";
5056
6229
  var writeToolDefinition = {
5057
6230
  definition: {
5058
6231
  type: "function",
@@ -5088,12 +6261,12 @@ var writeToolDefinition = {
5088
6261
  const resolved = resolveUserPath(targetPath, context.cwd);
5089
6262
  const displayPath = toToolRelativePath(context.cwd, resolved);
5090
6263
  const existed = await fileExists(resolved);
5091
- const before = existed ? await fs13.readFile(resolved, "utf8") : "";
6264
+ const before = existed ? await fs15.readFile(resolved, "utf8") : "";
5092
6265
  const preview = buildDiffPreview(before, content);
5093
6266
  if (createDirectories) {
5094
6267
  await ensureParentDirectory(resolved);
5095
6268
  }
5096
- await fs13.writeFile(resolved, content, "utf8");
6269
+ await fs15.writeFile(resolved, content, "utf8");
5097
6270
  const changeRecord = await recordToolChange(context, {
5098
6271
  toolName: "write",
5099
6272
  summary: `write ${displayPath}`,
@@ -5148,7 +6321,7 @@ var writeToolDefinition = {
5148
6321
  };
5149
6322
 
5150
6323
  // src/tools/sendFile.ts
5151
- import fs14 from "fs/promises";
6324
+ import fs16 from "fs/promises";
5152
6325
  var sendFileToolDefinition = {
5153
6326
  definition: {
5154
6327
  type: "function",
@@ -5191,7 +6364,7 @@ var sendFileToolDefinition = {
5191
6364
  };
5192
6365
  }
5193
6366
  try {
5194
- await fs14.access(filePath);
6367
+ await fs16.access(filePath);
5195
6368
  } catch {
5196
6369
  return {
5197
6370
  ok: false,
@@ -5724,8 +6897,8 @@ async function executeToolBatch(params) {
5724
6897
  }
5725
6898
 
5726
6899
  // src/session/events.ts
5727
- import fs15 from "fs/promises";
5728
- import path15 from "path";
6900
+ import fs17 from "fs/promises";
6901
+ import path17 from "path";
5729
6902
  var SessionEventStore = class {
5730
6903
  constructor(eventsDir) {
5731
6904
  this.eventsDir = eventsDir;
@@ -5741,8 +6914,8 @@ var SessionEventStore = class {
5741
6914
  message: event.message,
5742
6915
  details: event.details
5743
6916
  };
5744
- await fs15.mkdir(this.eventsDir, { recursive: true });
5745
- await fs15.appendFile(this.getSessionEventPath(event.sessionId), `${JSON.stringify(record)}
6917
+ await fs17.mkdir(this.eventsDir, { recursive: true });
6918
+ await fs17.appendFile(this.getSessionEventPath(event.sessionId), `${JSON.stringify(record)}
5746
6919
  `, "utf8");
5747
6920
  return record;
5748
6921
  }
@@ -5750,7 +6923,7 @@ var SessionEventStore = class {
5750
6923
  const filePath = this.getSessionEventPath(sessionId);
5751
6924
  let raw = "";
5752
6925
  try {
5753
- raw = await fs15.readFile(filePath, "utf8");
6926
+ raw = await fs17.readFile(filePath, "utf8");
5754
6927
  } catch (error) {
5755
6928
  if (error.code === "ENOENT") {
5756
6929
  return [];
@@ -5760,7 +6933,7 @@ var SessionEventStore = class {
5760
6933
  return raw.split(/\r?\n/).filter(Boolean).map((line) => JSON.parse(line)).slice(-limit);
5761
6934
  }
5762
6935
  getSessionEventPath(sessionId) {
5763
- return path15.join(this.eventsDir, `${sanitizeSessionId(sessionId)}.jsonl`);
6936
+ return path17.join(this.eventsDir, `${sanitizeSessionId(sessionId)}.jsonl`);
5764
6937
  }
5765
6938
  };
5766
6939
  function createEventId() {
@@ -6079,9 +7252,9 @@ function normalizeToolArguments(raw) {
6079
7252
  }
6080
7253
 
6081
7254
  // src/agent/changes/store.ts
6082
- import crypto2 from "crypto";
6083
- import fs16 from "fs/promises";
6084
- import path16 from "path";
7255
+ import crypto3 from "crypto";
7256
+ import fs18 from "fs/promises";
7257
+ import path18 from "path";
6085
7258
  var ChangeStore = class {
6086
7259
  constructor(changesDir) {
6087
7260
  this.changesDir = changesDir;
@@ -6089,8 +7262,8 @@ var ChangeStore = class {
6089
7262
  async record(input) {
6090
7263
  const id = createChangeId();
6091
7264
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
6092
- const blobDir = path16.join(this.changesDir, id);
6093
- await fs16.mkdir(blobDir, { recursive: true });
7265
+ const blobDir = path18.join(this.changesDir, id);
7266
+ await fs18.mkdir(blobDir, { recursive: true });
6094
7267
  const operations = await Promise.all(
6095
7268
  input.operations.map(async (operation, index) => {
6096
7269
  const beforeSnapshotPath = await this.writeSnapshot(
@@ -6130,21 +7303,21 @@ var ChangeStore = class {
6130
7303
  preview: input.preview,
6131
7304
  operations
6132
7305
  };
6133
- await fs16.mkdir(this.changesDir, { recursive: true });
6134
- await fs16.writeFile(this.getMetadataPath(id), `${JSON.stringify(record, null, 2)}
7306
+ await fs18.mkdir(this.changesDir, { recursive: true });
7307
+ await fs18.writeFile(this.getMetadataPath(id), `${JSON.stringify(record, null, 2)}
6135
7308
  `, "utf8");
6136
7309
  return record;
6137
7310
  }
6138
7311
  async list(limit = 20) {
6139
- await fs16.mkdir(this.changesDir, { recursive: true });
6140
- const entries = await fs16.readdir(this.changesDir, { withFileTypes: true });
7312
+ await fs18.mkdir(this.changesDir, { recursive: true });
7313
+ const entries = await fs18.readdir(this.changesDir, { withFileTypes: true });
6141
7314
  const changes = await Promise.all(
6142
- entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => this.load(path16.basename(entry.name, ".json")))
7315
+ entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => this.load(path18.basename(entry.name, ".json")))
6143
7316
  );
6144
7317
  return changes.sort((left, right) => right.createdAt.localeCompare(left.createdAt)).slice(0, limit);
6145
7318
  }
6146
7319
  async load(id) {
6147
- const raw = await fs16.readFile(this.getMetadataPath(id), "utf8");
7320
+ const raw = await fs18.readFile(this.getMetadataPath(id), "utf8");
6148
7321
  return JSON.parse(raw);
6149
7322
  }
6150
7323
  async loadLatestUndoable() {
@@ -6168,17 +7341,17 @@ var ChangeStore = class {
6168
7341
  restoredPaths.push(operation.path);
6169
7342
  if (operation.beforeSnapshotPath) {
6170
7343
  const buffer = await this.readSnapshot(operation.beforeSnapshotPath);
6171
- await fs16.mkdir(path16.dirname(operation.path), { recursive: true });
6172
- await fs16.writeFile(operation.path, buffer);
7344
+ await fs18.mkdir(path18.dirname(operation.path), { recursive: true });
7345
+ await fs18.writeFile(operation.path, buffer);
6173
7346
  continue;
6174
7347
  }
6175
- await fs16.rm(operation.path, { force: true });
7348
+ await fs18.rm(operation.path, { force: true });
6176
7349
  }
6177
7350
  const updated = {
6178
7351
  ...record,
6179
7352
  undoneAt: (/* @__PURE__ */ new Date()).toISOString()
6180
7353
  };
6181
- await fs16.writeFile(this.getMetadataPath(updated.id), `${JSON.stringify(updated, null, 2)}
7354
+ await fs18.writeFile(this.getMetadataPath(updated.id), `${JSON.stringify(updated, null, 2)}
6182
7355
  `, "utf8");
6183
7356
  return {
6184
7357
  record: updated,
@@ -6186,24 +7359,24 @@ var ChangeStore = class {
6186
7359
  };
6187
7360
  }
6188
7361
  getMetadataPath(id) {
6189
- return path16.join(this.changesDir, `${id}.json`);
7362
+ return path18.join(this.changesDir, `${id}.json`);
6190
7363
  }
6191
7364
  async writeSnapshot(blobDir, label, buffer) {
6192
7365
  if (!buffer) {
6193
7366
  return void 0;
6194
7367
  }
6195
7368
  const fileName = `${label}.bin`;
6196
- const absolutePath = path16.join(blobDir, fileName);
6197
- await fs16.writeFile(absolutePath, buffer);
6198
- return path16.relative(this.changesDir, absolutePath);
7369
+ const absolutePath = path18.join(blobDir, fileName);
7370
+ await fs18.writeFile(absolutePath, buffer);
7371
+ return path18.relative(this.changesDir, absolutePath);
6199
7372
  }
6200
7373
  async readSnapshot(relativePath) {
6201
- return fs16.readFile(path16.join(this.changesDir, relativePath));
7374
+ return fs18.readFile(path18.join(this.changesDir, relativePath));
6202
7375
  }
6203
7376
  };
6204
7377
  function createChangeId() {
6205
7378
  const date = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
6206
- const random = crypto2.randomUUID().slice(0, 8);
7379
+ const random = crypto3.randomUUID().slice(0, 8);
6207
7380
  return `${date}-${random}`;
6208
7381
  }
6209
7382
 
@@ -6777,20 +7950,20 @@ function createBackgroundTools() {
6777
7950
  }
6778
7951
 
6779
7952
  // src/extensions/tools/network/tools/downloadUrl.ts
6780
- import fs18 from "fs/promises";
7953
+ import fs20 from "fs/promises";
6781
7954
 
6782
7955
  // src/extensions/shared.ts
6783
- import fs17 from "fs/promises";
6784
- import path17 from "path";
7956
+ import fs19 from "fs/promises";
7957
+ import path19 from "path";
6785
7958
  async function ensureExtensionDir(rootDir, extensionId) {
6786
7959
  const paths = await ensureProjectStateDirectories(rootDir);
6787
- const dir = path17.join(paths.extensionsDir, extensionId);
6788
- await fs17.mkdir(dir, { recursive: true });
7960
+ const dir = path19.join(paths.extensionsDir, extensionId);
7961
+ await fs19.mkdir(dir, { recursive: true });
6789
7962
  return dir;
6790
7963
  }
6791
7964
  async function readJsonFile(filePath, fallback) {
6792
7965
  try {
6793
- return JSON.parse(await fs17.readFile(filePath, "utf8"));
7966
+ return JSON.parse(await fs19.readFile(filePath, "utf8"));
6794
7967
  } catch (error) {
6795
7968
  if (error.code === "ENOENT") {
6796
7969
  return fallback;
@@ -6799,8 +7972,8 @@ async function readJsonFile(filePath, fallback) {
6799
7972
  }
6800
7973
  }
6801
7974
  async function writeJsonFile(filePath, value) {
6802
- await fs17.mkdir(path17.dirname(filePath), { recursive: true });
6803
- await fs17.writeFile(filePath, `${JSON.stringify(value, null, 2)}
7975
+ await fs19.mkdir(path19.dirname(filePath), { recursive: true });
7976
+ await fs19.writeFile(filePath, `${JSON.stringify(value, null, 2)}
6804
7977
  `, "utf8");
6805
7978
  }
6806
7979
  function jsonResult(value) {
@@ -6816,7 +7989,7 @@ function sanitizeStateSegment(value) {
6816
7989
  }
6817
7990
 
6818
7991
  // src/extensions/tools/network/session.ts
6819
- import path18 from "path";
7992
+ import path20 from "path";
6820
7993
  async function listHttpSessions(rootDir) {
6821
7994
  const state = await readJsonFile(await sessionFile(rootDir), { sessions: [] });
6822
7995
  return Array.isArray(state.sessions) ? state.sessions.map(normalizeSession) : [];
@@ -6841,7 +8014,7 @@ async function getHttpSessionStateFile(rootDir) {
6841
8014
  return sessionFile(rootDir);
6842
8015
  }
6843
8016
  async function sessionFile(rootDir) {
6844
- return path18.join(await ensureExtensionDir(rootDir, "network"), "http-sessions.json");
8017
+ return path20.join(await ensureExtensionDir(rootDir, "network"), "http-sessions.json");
6845
8018
  }
6846
8019
  function normalizeSession(value) {
6847
8020
  return {
@@ -7070,7 +8243,7 @@ var downloadUrlTool = {
7070
8243
  }
7071
8244
  const bytes = Buffer.from(await response.arrayBuffer());
7072
8245
  await ensureParentDirectory(targetPath);
7073
- await fs18.writeFile(targetPath, bytes);
8246
+ await fs20.writeFile(targetPath, bytes);
7074
8247
  return changedJsonResult({
7075
8248
  ok: response.ok,
7076
8249
  url,
@@ -7135,17 +8308,17 @@ var httpProbeTool = {
7135
8308
  };
7136
8309
 
7137
8310
  // src/extensions/tools/network/traceStore.ts
7138
- import fs19 from "fs/promises";
7139
- import path19 from "path";
8311
+ import fs21 from "fs/promises";
8312
+ import path21 from "path";
7140
8313
  async function writeNetworkTrace(rootDir, traceId, record) {
7141
8314
  const filePath = await networkTraceFilePath(rootDir, traceId);
7142
- await fs19.mkdir(path19.dirname(filePath), { recursive: true });
7143
- await fs19.writeFile(filePath, `${JSON.stringify(record, null, 2)}
8315
+ await fs21.mkdir(path21.dirname(filePath), { recursive: true });
8316
+ await fs21.writeFile(filePath, `${JSON.stringify(record, null, 2)}
7144
8317
  `, "utf8");
7145
8318
  return filePath;
7146
8319
  }
7147
8320
  async function networkTraceFilePath(rootDir, traceId) {
7148
- return path19.join(await ensureExtensionDir(rootDir, "network"), "traces", `${sanitizeStateSegment(traceId)}.json`);
8321
+ return path21.join(await ensureExtensionDir(rootDir, "network"), "traces", `${sanitizeStateSegment(traceId)}.json`);
7149
8322
  }
7150
8323
 
7151
8324
  // src/extensions/tools/network/tools/httpRequest.ts
@@ -7545,14 +8718,14 @@ function readStringMap2(value) {
7545
8718
  }
7546
8719
 
7547
8720
  // src/extensions/tools/network/openapi.ts
7548
- import fs20 from "fs/promises";
8721
+ import fs22 from "fs/promises";
7549
8722
  async function loadOpenApiDocument(source, context) {
7550
8723
  const normalizedSource = source.trim();
7551
8724
  if (!normalizedSource) {
7552
8725
  throw new ToolExecutionError("OpenAPI source is required.", { code: "OPENAPI_SOURCE_INVALID" });
7553
8726
  }
7554
8727
  const resolvedSource = /^https?:\/\//i.test(normalizedSource) ? normalizedSource : resolveUserPath(normalizedSource, context.cwd);
7555
- const raw = /^https?:\/\//i.test(normalizedSource) ? await (await fetchWithTimeout(normalizedSource, { method: "GET" }, 2e4, context.abortSignal)).text() : stripBom(await fs20.readFile(resolvedSource, "utf8"));
8728
+ const raw = /^https?:\/\//i.test(normalizedSource) ? await (await fetchWithTimeout(normalizedSource, { method: "GET" }, 2e4, context.abortSignal)).text() : stripBom(await fs22.readFile(resolvedSource, "utf8"));
7556
8729
  const parsed = parseOpenApiDocument(raw, normalizedSource);
7557
8730
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
7558
8731
  throw new ToolExecutionError("OpenAPI document root must be an object.", { code: "OPENAPI_ROOT_INVALID" });
@@ -7900,8 +9073,8 @@ async function recordSkillUse(rootDir, input) {
7900
9073
  }
7901
9074
 
7902
9075
  // src/extensions/tools/skills/tools/skillReadResource.ts
7903
- import fs21 from "fs/promises";
7904
- import path20 from "path";
9076
+ import fs23 from "fs/promises";
9077
+ import path22 from "path";
7905
9078
  var MAX_RESOURCE_CHARS = 24e3;
7906
9079
  var skillReadResourceTool = {
7907
9080
  definition: {
@@ -7938,8 +9111,8 @@ var skillReadResourceTool = {
7938
9111
  if (!resource) {
7939
9112
  throw new Error(`Skill "${name}" does not declare resource: ${requestedPath}`);
7940
9113
  }
7941
- const absolutePath = path20.resolve(context.projectContext.rootDir, resource.path);
7942
- const content = await fs21.readFile(absolutePath, "utf8");
9114
+ const absolutePath = path22.resolve(context.projectContext.rootDir, resource.path);
9115
+ const content = await fs23.readFile(absolutePath, "utf8");
7943
9116
  return jsonResult({
7944
9117
  ok: true,
7945
9118
  skill: {
@@ -7957,7 +9130,7 @@ function normalizeResourcePath(value) {
7957
9130
  }
7958
9131
 
7959
9132
  // src/extensions/tools/skills/tools/skillRunScript.ts
7960
- import path21 from "path";
9133
+ import path23 from "path";
7961
9134
  var skillRunScriptTool = {
7962
9135
  definition: {
7963
9136
  type: "function",
@@ -8001,12 +9174,12 @@ var skillRunScriptTool = {
8001
9174
  if (!resource) {
8002
9175
  throw new Error(`Skill "${name}" does not declare script resource: ${requestedPath}`);
8003
9176
  }
8004
- const skillDir = path21.dirname(skill.path);
8005
- const relativeToSkill = normalizeResourcePath2(path21.relative(skillDir, resource.path));
9177
+ const skillDir = path23.dirname(skill.path);
9178
+ const relativeToSkill = normalizeResourcePath2(path23.relative(skillDir, resource.path));
8006
9179
  if (!relativeToSkill.startsWith("scripts/")) {
8007
9180
  throw new Error(`Skill "${name}" resource is not executable because it is outside scripts/: ${requestedPath}`);
8008
9181
  }
8009
- const scriptPath = path21.resolve(context.projectContext.rootDir, resource.path);
9182
+ const scriptPath = path23.resolve(context.projectContext.rootDir, resource.path);
8010
9183
  const argumentText = typeof args.args === "string" ? args.args.trim() : "";
8011
9184
  const command = buildScriptCommand(scriptPath, argumentText);
8012
9185
  const result = await runCommandWithPolicy({
@@ -8081,7 +9254,7 @@ function quotePath(value) {
8081
9254
  return `"${value.replace(/"/g, '\\"')}"`;
8082
9255
  }
8083
9256
  function buildScriptCommand(scriptPath, argumentText) {
8084
- const extension = path21.extname(scriptPath).toLowerCase();
9257
+ const extension = path23.extname(scriptPath).toLowerCase();
8085
9258
  const quoted = quotePath(scriptPath);
8086
9259
  const suffix = argumentText ? ` ${argumentText}` : "";
8087
9260
  if (extension === ".js" || extension === ".mjs" || extension === ".cjs") {
@@ -8135,12 +9308,12 @@ var subagentCheckTool = {
8135
9308
 
8136
9309
  // src/execution/launch.ts
8137
9310
  import { spawn } from "child_process";
8138
- import path22 from "path";
9311
+ import path24 from "path";
8139
9312
  function spawnExecutionWorker(input) {
8140
9313
  if (process.env.KITTY_TEST_WORKER_MODE === "stub") {
8141
9314
  return process.pid;
8142
9315
  }
8143
- const cliEntry = path22.resolve(process.argv[1] ?? "");
9316
+ const cliEntry = path24.resolve(process.argv[1] ?? "");
8144
9317
  if (!cliEntry) {
8145
9318
  throw new Error("Unable to locate Kitty CLI entrypoint for execution worker.");
8146
9319
  }
@@ -8346,7 +9519,7 @@ function parseWorktreeBlock(block) {
8346
9519
  }
8347
9520
 
8348
9521
  // src/extensions/tools/worktree/state.ts
8349
- import path23 from "path";
9522
+ import path25 from "path";
8350
9523
  async function readWorktreeState(rootDir) {
8351
9524
  return normalizeWorktreeState(await readJsonFile(await stateFile(rootDir), {
8352
9525
  schemaVersion: 1,
@@ -8369,7 +9542,7 @@ async function recordWorktreeEvent(rootDir, event) {
8369
9542
  return writeWorktreeState(rootDir, state);
8370
9543
  }
8371
9544
  async function stateFile(rootDir) {
8372
- return path23.join(await ensureExtensionDir(rootDir, "worktree"), "state.json");
9545
+ return path25.join(await ensureExtensionDir(rootDir, "worktree"), "state.json");
8373
9546
  }
8374
9547
  function normalizeWorktreeState(value) {
8375
9548
  return {
@@ -8991,9 +10164,56 @@ function looksLikeToolProtocolText(content) {
8991
10164
  return text.includes("<\uFF5C\uFF5CDSML\uFF5C\uFF5Ctool_calls>") || text.includes("<tool_call>") || text.includes('"tool_calls"');
8992
10165
  }
8993
10166
 
10167
+ // src/host/delegatedCloseout.ts
10168
+ async function completeExactDelegatedCloseout(input) {
10169
+ const answer = resolveExactDelegatedAnswer(input.executions);
10170
+ if (!answer) {
10171
+ return void 0;
10172
+ }
10173
+ const transition = createFinalizeTransition({
10174
+ changedPaths: []
10175
+ });
10176
+ const sessionWithAnswer = await input.sessionStore.appendMessages(input.session, [
10177
+ createMessage("assistant", answer)
10178
+ ]);
10179
+ const session = await input.sessionStore.save(noteCheckpointCompleted(sessionWithAnswer, transition));
10180
+ const ledger = new ControlPlaneLedger(input.stateRootDir);
10181
+ try {
10182
+ ledger.taskLifecycle.complete({
10183
+ sessionId: session.id,
10184
+ reason: "finalize.delegated_exact_output",
10185
+ completionFacts: [answer]
10186
+ });
10187
+ } finally {
10188
+ ledger.close();
10189
+ }
10190
+ input.callbacks?.onAssistantText?.(answer);
10191
+ input.callbacks?.onAssistantDone?.(answer);
10192
+ return buildRunTurnResult({
10193
+ session,
10194
+ changedPaths: [],
10195
+ transition
10196
+ });
10197
+ }
10198
+ function resolveExactDelegatedAnswer(executions) {
10199
+ if (executions.length !== 1) {
10200
+ return void 0;
10201
+ }
10202
+ const [execution] = executions;
10203
+ if (!execution || execution.status !== "completed") {
10204
+ return void 0;
10205
+ }
10206
+ const expected = execution.assignment?.expectedOutput?.trim();
10207
+ const output = execution.output?.trim();
10208
+ if (!expected || !output || expected !== output) {
10209
+ return void 0;
10210
+ }
10211
+ return output;
10212
+ }
10213
+
8994
10214
  // src/observability/crashRecorder.ts
8995
- import fs22 from "fs";
8996
- import path24 from "path";
10215
+ import fs24 from "fs";
10216
+ import path26 from "path";
8997
10217
  var activeCrashContexts = /* @__PURE__ */ new Map();
8998
10218
  var nextCrashContextId = 0;
8999
10219
  function enterCrashContext(context) {
@@ -9005,7 +10225,7 @@ function enterCrashContext(context) {
9005
10225
  }
9006
10226
 
9007
10227
  // src/observability/hostEvents.ts
9008
- import path25 from "path";
10228
+ import path27 from "path";
9009
10229
  async function recordHostTurnStarted(rootDir, input) {
9010
10230
  await recordObservabilityEvent(rootDir, {
9011
10231
  event: "host.turn",
@@ -9267,51 +10487,6 @@ async function runHostTurn(options, dependencies = {}) {
9267
10487
  await toolRegistry?.close?.().catch(() => void 0);
9268
10488
  }
9269
10489
  }
9270
- async function completeExactDelegatedCloseout(input) {
9271
- const answer = resolveExactDelegatedAnswer(input.executions);
9272
- if (!answer) {
9273
- return void 0;
9274
- }
9275
- const transition = createFinalizeTransition({
9276
- changedPaths: []
9277
- });
9278
- const sessionWithAnswer = await input.sessionStore.appendMessages(input.session, [
9279
- createMessage("assistant", answer)
9280
- ]);
9281
- const session = await input.sessionStore.save(noteCheckpointCompleted(sessionWithAnswer, transition));
9282
- const ledger = new ControlPlaneLedger(input.stateRootDir);
9283
- try {
9284
- ledger.taskLifecycle.complete({
9285
- sessionId: session.id,
9286
- reason: "finalize.delegated_exact_output",
9287
- completionFacts: [answer]
9288
- });
9289
- } finally {
9290
- ledger.close();
9291
- }
9292
- input.callbacks?.onAssistantText?.(answer);
9293
- input.callbacks?.onAssistantDone?.(answer);
9294
- return buildRunTurnResult({
9295
- session,
9296
- changedPaths: [],
9297
- transition
9298
- });
9299
- }
9300
- function resolveExactDelegatedAnswer(executions) {
9301
- if (executions.length !== 1) {
9302
- return void 0;
9303
- }
9304
- const [execution] = executions;
9305
- if (!execution || execution.status !== "completed") {
9306
- return void 0;
9307
- }
9308
- const expected = execution.assignment?.expectedOutput?.trim();
9309
- const output = execution.output?.trim();
9310
- if (!expected || !output || expected !== output) {
9311
- return void 0;
9312
- }
9313
- return output;
9314
- }
9315
10490
  function createToollessRegistry(registry) {
9316
10491
  return {
9317
10492
  ...registry,
@@ -9356,7 +10531,7 @@ function tryParseJson(input) {
9356
10531
  }
9357
10532
 
9358
10533
  // src/runtime-ui/pathDisplay.ts
9359
- import path26 from "path";
10534
+ import path28 from "path";
9360
10535
  function normalizeDisplayPath(value, cwd) {
9361
10536
  if (!value) {
9362
10537
  return value;
@@ -9364,10 +10539,10 @@ function normalizeDisplayPath(value, cwd) {
9364
10539
  if (!cwd) {
9365
10540
  return value;
9366
10541
  }
9367
- const normalizedCwd = path26.resolve(cwd);
9368
- const normalizedValue = path26.resolve(value);
9369
- if (normalizedValue === normalizedCwd || normalizedValue.startsWith(`${normalizedCwd}${path26.sep}`)) {
9370
- return path26.relative(normalizedCwd, normalizedValue) || ".";
10542
+ const normalizedCwd = path28.resolve(cwd);
10543
+ const normalizedValue = path28.resolve(value);
10544
+ if (normalizedValue === normalizedCwd || normalizedValue.startsWith(`${normalizedCwd}${path28.sep}`)) {
10545
+ return path28.relative(normalizedCwd, normalizedValue) || ".";
9371
10546
  }
9372
10547
  return value;
9373
10548
  }
@@ -9375,7 +10550,7 @@ function rewriteAbsolutePaths(value, cwd) {
9375
10550
  if (!cwd) {
9376
10551
  return value;
9377
10552
  }
9378
- const normalizedCwd = path26.resolve(cwd).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10553
+ const normalizedCwd = path28.resolve(cwd).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9379
10554
  const pattern = new RegExp(`${normalizedCwd}(?:\\\\[^\\s"']*|/[^\\s"']*)*`, "g");
9380
10555
  return value.replace(pattern, (match) => normalizeDisplayPath(match, cwd) ?? match);
9381
10556
  }
@@ -9435,24 +10610,24 @@ function buildToolCallDisplay(name, rawArgs, maxChars, cwd) {
9435
10610
  };
9436
10611
  }
9437
10612
  const args = parsed;
9438
- const path27 = normalizeDisplayPath(readStringField(args, "path"), cwd);
10613
+ const path29 = normalizeDisplayPath(readStringField(args, "path"), cwd);
9439
10614
  switch (name) {
9440
10615
  case "read": {
9441
10616
  const offset = typeof args.offset === "number" ? Math.trunc(args.offset) : void 0;
9442
10617
  const limit = typeof args.limit === "number" ? Math.trunc(args.limit) : void 0;
9443
10618
  const range = offset === void 0 ? "" : limit === void 0 ? `:${offset}` : `:${offset}-${Math.max(offset, offset + limit - 1)}`;
9444
10619
  return {
9445
- summary: `${name} ${path27 ?? "(missing path)"}${range}`
10620
+ summary: `${name} ${path29 ?? "(missing path)"}${range}`
9446
10621
  };
9447
10622
  }
9448
10623
  case "write":
9449
10624
  return {
9450
- summary: `${name} ${path27 ?? "(missing path)"}`
10625
+ summary: `${name} ${path29 ?? "(missing path)"}`
9451
10626
  };
9452
10627
  case "edit": {
9453
10628
  const edits = Array.isArray(args.edits) ? args.edits : [];
9454
10629
  return {
9455
- summary: `${name} ${path27 ?? "(missing path)"}` + (edits.length > 0 ? ` edits=${edits.length}` : "")
10630
+ summary: `${name} ${path29 ?? "(missing path)"}` + (edits.length > 0 ? ` edits=${edits.length}` : "")
9456
10631
  };
9457
10632
  }
9458
10633
  case "bash": {
@@ -9464,7 +10639,7 @@ function buildToolCallDisplay(name, rawArgs, maxChars, cwd) {
9464
10639
  }
9465
10640
  case "download_url":
9466
10641
  return {
9467
- summary: `${name} ${readStringField(args, "url") ?? "(missing url)"} -> ${path27 ?? "(missing path)"}`
10642
+ summary: `${name} ${readStringField(args, "url") ?? "(missing url)"} -> ${path29 ?? "(missing path)"}`
9468
10643
  };
9469
10644
  case "http_probe": {
9470
10645
  const method = readStringField(args, "method") ?? "HEAD";
@@ -9505,14 +10680,14 @@ function buildToolCallDisplay(name, rawArgs, maxChars, cwd) {
9505
10680
  case "worktree_create": {
9506
10681
  const branch = readStringField(args, "branch");
9507
10682
  return {
9508
- summary: `${name} ${path27 ?? "(missing path)"}${branch ? ` branch=${branch}` : ""}`
10683
+ summary: `${name} ${path29 ?? "(missing path)"}${branch ? ` branch=${branch}` : ""}`
9509
10684
  };
9510
10685
  }
9511
10686
  case "worktree_get":
9512
10687
  case "worktree_keep":
9513
10688
  case "worktree_remove":
9514
10689
  return {
9515
- summary: `${name} ${path27 ?? "(missing path)"}`
10690
+ summary: `${name} ${path29 ?? "(missing path)"}`
9516
10691
  };
9517
10692
  case "worktree_events": {
9518
10693
  const limit = typeof args.limit === "number" ? Math.trunc(args.limit) : void 0;
@@ -9651,7 +10826,7 @@ function colorizeMarker(marker) {
9651
10826
  }
9652
10827
 
9653
10828
  // src/utils/stdio.ts
9654
- import fs23 from "fs";
10829
+ import fs25 from "fs";
9655
10830
  var stdoutBroken = false;
9656
10831
  var stderrBroken = false;
9657
10832
  function writeStdout(text) {
@@ -9677,7 +10852,7 @@ function writeToFd(fd, text, stream) {
9677
10852
  if (target.isTTY) {
9678
10853
  return target.write(text);
9679
10854
  }
9680
- fs23.writeSync(fd, text, void 0, "utf8");
10855
+ fs25.writeSync(fd, text, void 0, "utf8");
9681
10856
  return true;
9682
10857
  } catch (error) {
9683
10858
  if (isIgnorableStreamError(error)) {
@@ -9960,6 +11135,12 @@ function formatRuntimeUiMessage(prefix, summary, detail) {
9960
11135
  export {
9961
11136
  resolveProjectRoots,
9962
11137
  buildProjectMap,
11138
+ PROJECT_STATE_DIR_NAME,
11139
+ PROJECT_STATE_ENV_FILE_NAME,
11140
+ PROJECT_STATE_ENV_EXAMPLE_FILE_NAME,
11141
+ PROJECT_STATE_IGNORE_FILE_NAME,
11142
+ PRESERVED_PROJECT_STATE_ENTRY_NAMES,
11143
+ getProjectStatePaths,
9963
11144
  loadProjectContext,
9964
11145
  getErrorMessage,
9965
11146
  ControlPlaneLedger,