@jun133/kitty 0.0.14 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -133
- package/dist/{chunk-KROQCOWD.mjs → chunk-7FDXKNTM.mjs} +1386 -215
- package/dist/{chunk-VR3L2EPP.mjs → chunk-AQEMM5VH.mjs} +1 -1
- package/dist/{chunk-4BN45TQG.mjs → chunk-KUP5OMPB.mjs} +1 -1
- package/dist/{chunk-3KMC6H5K.mjs → chunk-S4QTRPZ7.mjs} +238 -1221
- package/dist/{chunk-KKGULDIF.mjs → chunk-WIKLME2V.mjs} +50 -14
- package/dist/cli.js +2474 -2127
- package/dist/cli.js.map +1 -1
- package/dist/{interactive-KRRDJYBR.mjs → interactive-BQMJFYHZ.mjs} +5 -5
- package/dist/{oneShot-PHU3JOPP.mjs → oneShot-4QBXBCNN.mjs} +3 -3
- package/dist/{session-XKWJHRVY.mjs → session-V7AYOK2Q.mjs} +2 -2
- package/dist/tui.mjs +15 -5
- package/package.json +1 -1
|
@@ -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-
|
|
36
|
+
} from "./chunk-S4QTRPZ7.mjs";
|
|
44
37
|
|
|
45
38
|
// src/context/projectContext.ts
|
|
46
|
-
import
|
|
47
|
-
import
|
|
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
|
|
252
|
-
import
|
|
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:
|
|
318
|
+
path: path4.join(rootDir, PROJECT_STATE_DIR_NAME, PROJECT_STATE_IGNORE_FILE_NAME),
|
|
272
319
|
baseDir: rootDir
|
|
273
320
|
},
|
|
274
321
|
{
|
|
275
|
-
path:
|
|
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
|
|
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 =
|
|
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("..") ||
|
|
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 =
|
|
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:
|
|
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
|
|
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:
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
642
|
+
const stat = await fs5.stat(file);
|
|
596
643
|
resources.push({
|
|
597
|
-
path:
|
|
644
|
+
path: path6.relative(rootDir, file),
|
|
598
645
|
size: stat.size,
|
|
599
|
-
kind: readSkillResourceKind(
|
|
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
|
-
|
|
647
|
-
|
|
693
|
+
path6.join(rootDir, "SKILL.md"),
|
|
694
|
+
path6.join(cwd, "SKILL.md")
|
|
648
695
|
]);
|
|
649
696
|
const roots = uniquePaths([
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
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
|
|
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
|
|
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) =>
|
|
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 =
|
|
721
|
-
const agentsPath =
|
|
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 =
|
|
734
|
-
const absoluteCwd =
|
|
735
|
-
const relativePath =
|
|
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(
|
|
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(
|
|
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
|
|
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:
|
|
804
|
+
relativePath: path7.relative(rootDir, absolutePath) || filename,
|
|
758
805
|
filename,
|
|
759
|
-
content: await
|
|
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
|
|
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.`;
|
|
@@ -919,6 +966,1116 @@ function hasVisibleAssistantResult(content) {
|
|
|
919
966
|
return typeof content === "string" && content.trim().length > 0;
|
|
920
967
|
}
|
|
921
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/responsesAdapter.ts
|
|
1539
|
+
var responsesAdapter = {
|
|
1540
|
+
wireApi: "responses",
|
|
1541
|
+
async fetchStreaming(client, request) {
|
|
1542
|
+
const startedAt = Date.now();
|
|
1543
|
+
let usage;
|
|
1544
|
+
throwIfAborted(request.abortSignal, "Streaming request aborted");
|
|
1545
|
+
try {
|
|
1546
|
+
const stream = await client.responses.create(
|
|
1547
|
+
{
|
|
1548
|
+
...buildResponsesRequestBody(request),
|
|
1549
|
+
stream: true
|
|
1550
|
+
},
|
|
1551
|
+
{
|
|
1552
|
+
signal: request.abortSignal
|
|
1553
|
+
}
|
|
1554
|
+
);
|
|
1555
|
+
if (request.abortSignal?.aborted) {
|
|
1556
|
+
abortStream2(stream);
|
|
1557
|
+
throw createAbortError("Streaming aborted");
|
|
1558
|
+
}
|
|
1559
|
+
let content = "";
|
|
1560
|
+
let reasoningContent = "";
|
|
1561
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
1562
|
+
for await (const event of stream) {
|
|
1563
|
+
if (request.abortSignal?.aborted) {
|
|
1564
|
+
abortStream2(stream);
|
|
1565
|
+
throw createAbortError("Streaming aborted");
|
|
1566
|
+
}
|
|
1567
|
+
usage = normalizeProviderUsage(event.response?.usage) ?? usage;
|
|
1568
|
+
if (event.type === "response.output_text.delta" && typeof event.delta === "string") {
|
|
1569
|
+
content += event.delta;
|
|
1570
|
+
request.callbacks?.onAssistantDelta?.(event.delta);
|
|
1571
|
+
continue;
|
|
1572
|
+
}
|
|
1573
|
+
if ((event.type === "response.reasoning_text.delta" || event.type === "response.reasoning_summary_text.delta") && typeof event.delta === "string") {
|
|
1574
|
+
reasoningContent += event.delta;
|
|
1575
|
+
request.callbacks?.onReasoningDelta?.(event.delta);
|
|
1576
|
+
continue;
|
|
1577
|
+
}
|
|
1578
|
+
if (event.type === "response.function_call_arguments.delta" && typeof event.delta === "string") {
|
|
1579
|
+
const index = typeof event.output_index === "number" ? event.output_index : 0;
|
|
1580
|
+
const existing = toolCalls.get(index) ?? {
|
|
1581
|
+
id: event.item_id ?? `tool-${index}`,
|
|
1582
|
+
name: "",
|
|
1583
|
+
arguments: ""
|
|
1584
|
+
};
|
|
1585
|
+
existing.arguments += event.delta;
|
|
1586
|
+
toolCalls.set(index, existing);
|
|
1587
|
+
continue;
|
|
1588
|
+
}
|
|
1589
|
+
if (event.type === "response.function_call_arguments.done") {
|
|
1590
|
+
const index = typeof event.output_index === "number" ? event.output_index : 0;
|
|
1591
|
+
const existing = toolCalls.get(index) ?? {
|
|
1592
|
+
id: event.item_id ?? `tool-${index}`,
|
|
1593
|
+
name: "",
|
|
1594
|
+
arguments: ""
|
|
1595
|
+
};
|
|
1596
|
+
if (typeof event.name === "string") {
|
|
1597
|
+
existing.name = event.name;
|
|
1598
|
+
}
|
|
1599
|
+
if (typeof event.arguments === "string" && event.arguments.length > 0) {
|
|
1600
|
+
existing.arguments = event.arguments;
|
|
1601
|
+
}
|
|
1602
|
+
toolCalls.set(index, existing);
|
|
1603
|
+
continue;
|
|
1604
|
+
}
|
|
1605
|
+
if (event.type === "response.output_item.done" && event.item?.type === "function_call") {
|
|
1606
|
+
const index = typeof event.output_index === "number" ? event.output_index : 0;
|
|
1607
|
+
toolCalls.set(index, {
|
|
1608
|
+
id: event.item.call_id ?? event.item.id ?? `tool-${index}`,
|
|
1609
|
+
name: event.item.name ?? "",
|
|
1610
|
+
arguments: event.item.arguments ?? ""
|
|
1611
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
return {
|
|
1615
|
+
content: content.length > 0 ? content : null,
|
|
1616
|
+
reasoningContent: reasoningContent.length > 0 ? reasoningContent : void 0,
|
|
1617
|
+
streamedAssistantContent: content.length > 0,
|
|
1618
|
+
streamedReasoningContent: reasoningContent.length > 0,
|
|
1619
|
+
toolCalls: [...toolCalls.entries()].sort((left, right) => left[0] - right[0]).map(([, toolCall]) => ({
|
|
1620
|
+
id: toolCall.id,
|
|
1621
|
+
type: "function",
|
|
1622
|
+
function: {
|
|
1623
|
+
name: toolCall.name,
|
|
1624
|
+
arguments: toolCall.arguments
|
|
1625
|
+
}
|
|
1626
|
+
}))
|
|
1627
|
+
};
|
|
1628
|
+
} finally {
|
|
1629
|
+
request.onRequestMetric?.({
|
|
1630
|
+
durationMs: Date.now() - startedAt,
|
|
1631
|
+
usage
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1634
|
+
},
|
|
1635
|
+
async fetchNonStreaming(client, request) {
|
|
1636
|
+
const startedAt = Date.now();
|
|
1637
|
+
let usage;
|
|
1638
|
+
throwIfAborted(request.abortSignal, "Request aborted");
|
|
1639
|
+
try {
|
|
1640
|
+
const response = await client.responses.create(
|
|
1641
|
+
{
|
|
1642
|
+
...buildResponsesRequestBody(request),
|
|
1643
|
+
stream: false
|
|
1644
|
+
},
|
|
1645
|
+
{
|
|
1646
|
+
signal: request.abortSignal
|
|
1647
|
+
}
|
|
1648
|
+
);
|
|
1649
|
+
usage = normalizeProviderUsage(response.usage);
|
|
1650
|
+
return {
|
|
1651
|
+
content: normalizeOutputText(response),
|
|
1652
|
+
reasoningContent: readResponseReasoning(response),
|
|
1653
|
+
streamedAssistantContent: false,
|
|
1654
|
+
streamedReasoningContent: false,
|
|
1655
|
+
toolCalls: readResponseToolCalls(response)
|
|
1656
|
+
};
|
|
1657
|
+
} finally {
|
|
1658
|
+
request.onRequestMetric?.({
|
|
1659
|
+
durationMs: Date.now() - startedAt,
|
|
1660
|
+
usage
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
};
|
|
1665
|
+
function buildResponsesRequestBody(request) {
|
|
1666
|
+
const capabilities = resolveProviderCapabilities({
|
|
1667
|
+
provider: request.provider,
|
|
1668
|
+
model: request.model
|
|
1669
|
+
});
|
|
1670
|
+
const body = {
|
|
1671
|
+
model: request.model,
|
|
1672
|
+
input: toResponsesInput(request.messages),
|
|
1673
|
+
tools: request.tools?.map((tool) => ({
|
|
1674
|
+
type: "function",
|
|
1675
|
+
name: tool.function.name,
|
|
1676
|
+
description: tool.function.description,
|
|
1677
|
+
parameters: tool.function.parameters ?? null,
|
|
1678
|
+
strict: false
|
|
1679
|
+
})),
|
|
1680
|
+
tool_choice: request.tools?.length ? "auto" : void 0
|
|
1681
|
+
};
|
|
1682
|
+
if (typeof request.maxOutputTokens === "number" && Number.isFinite(request.maxOutputTokens)) {
|
|
1683
|
+
body.max_output_tokens = Math.max(1, Math.trunc(request.maxOutputTokens));
|
|
1684
|
+
}
|
|
1685
|
+
const cachePolicy = resolveProviderCachePolicy({
|
|
1686
|
+
provider: request.provider,
|
|
1687
|
+
model: request.model,
|
|
1688
|
+
sessionId: request.sessionId,
|
|
1689
|
+
projectRoot: request.projectRoot
|
|
1690
|
+
});
|
|
1691
|
+
if (cachePolicy.promptCacheKey) {
|
|
1692
|
+
body.prompt_cache_key = cachePolicy.promptCacheKey;
|
|
1693
|
+
}
|
|
1694
|
+
const reasoningEffort = request.thinking === "disabled" ? void 0 : normalizeResponsesReasoningEffort(
|
|
1695
|
+
request.reasoningEffort ?? capabilities.defaultReasoningEffort
|
|
1696
|
+
);
|
|
1697
|
+
if (request.thinking !== "disabled" && (request.forceReasoning || capabilities.defaultReasoningEnabled || request.thinking === "enabled" || reasoningEffort)) {
|
|
1698
|
+
body.reasoning = {
|
|
1699
|
+
effort: reasoningEffort ?? "high",
|
|
1700
|
+
summary: "detailed"
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
return body;
|
|
1704
|
+
}
|
|
1705
|
+
function normalizeResponsesReasoningEffort(effort) {
|
|
1706
|
+
if (effort === "xhigh") {
|
|
1707
|
+
return "xhigh";
|
|
1708
|
+
}
|
|
1709
|
+
return effort === "max" ? void 0 : effort;
|
|
1710
|
+
}
|
|
1711
|
+
function toResponsesInput(messages) {
|
|
1712
|
+
const items = [];
|
|
1713
|
+
for (const message of messages) {
|
|
1714
|
+
if (message.role === "tool") {
|
|
1715
|
+
items.push({
|
|
1716
|
+
type: "function_call_output",
|
|
1717
|
+
call_id: message.toolCallId ?? "",
|
|
1718
|
+
output: message.content ?? ""
|
|
1719
|
+
});
|
|
1720
|
+
continue;
|
|
1721
|
+
}
|
|
1722
|
+
if (message.role === "assistant" && message.toolCalls?.length) {
|
|
1723
|
+
if (typeof message.content === "string" && message.content.trim().length > 0) {
|
|
1724
|
+
items.push({
|
|
1725
|
+
type: "message",
|
|
1726
|
+
role: "assistant",
|
|
1727
|
+
content: message.content
|
|
1728
|
+
});
|
|
1729
|
+
}
|
|
1730
|
+
for (const toolCall of message.toolCalls) {
|
|
1731
|
+
items.push({
|
|
1732
|
+
type: "function_call",
|
|
1733
|
+
call_id: toolCall.id,
|
|
1734
|
+
name: toolCall.function.name,
|
|
1735
|
+
arguments: toolCall.function.arguments
|
|
1736
|
+
});
|
|
1737
|
+
}
|
|
1738
|
+
continue;
|
|
1739
|
+
}
|
|
1740
|
+
items.push({
|
|
1741
|
+
type: "message",
|
|
1742
|
+
role: message.role,
|
|
1743
|
+
content: message.content ?? ""
|
|
1744
|
+
});
|
|
1745
|
+
}
|
|
1746
|
+
return items;
|
|
1747
|
+
}
|
|
1748
|
+
function normalizeOutputText(response) {
|
|
1749
|
+
const outputText = response.output_text;
|
|
1750
|
+
if (typeof outputText === "string" && outputText.trim().length > 0) {
|
|
1751
|
+
return outputText;
|
|
1752
|
+
}
|
|
1753
|
+
const output = response.output;
|
|
1754
|
+
if (!Array.isArray(output)) {
|
|
1755
|
+
return null;
|
|
1756
|
+
}
|
|
1757
|
+
const fragments = output.flatMap((item) => {
|
|
1758
|
+
if (!item || typeof item !== "object" || item.type !== "message") {
|
|
1759
|
+
return [];
|
|
1760
|
+
}
|
|
1761
|
+
const content = item.content;
|
|
1762
|
+
if (!Array.isArray(content)) {
|
|
1763
|
+
return [];
|
|
1764
|
+
}
|
|
1765
|
+
return content.flatMap((part) => {
|
|
1766
|
+
if (!part || typeof part !== "object" || part.type !== "output_text") {
|
|
1767
|
+
return [];
|
|
1768
|
+
}
|
|
1769
|
+
return typeof part.text === "string" ? [part.text] : [];
|
|
1770
|
+
});
|
|
1771
|
+
});
|
|
1772
|
+
return fragments.length > 0 ? fragments.join("") : null;
|
|
1773
|
+
}
|
|
1774
|
+
function readResponseToolCalls(response) {
|
|
1775
|
+
const output = response.output;
|
|
1776
|
+
if (!Array.isArray(output)) {
|
|
1777
|
+
return [];
|
|
1778
|
+
}
|
|
1779
|
+
return output.filter((item) => Boolean(item) && typeof item === "object" && item.type === "function_call").map((item) => ({
|
|
1780
|
+
id: item.call_id ?? item.id ?? crypto.randomUUID(),
|
|
1781
|
+
type: "function",
|
|
1782
|
+
function: {
|
|
1783
|
+
name: item.name ?? "",
|
|
1784
|
+
arguments: item.arguments ?? ""
|
|
1785
|
+
}
|
|
1786
|
+
}));
|
|
1787
|
+
}
|
|
1788
|
+
function readResponseReasoning(response) {
|
|
1789
|
+
const output = response.output;
|
|
1790
|
+
if (!Array.isArray(output)) {
|
|
1791
|
+
return void 0;
|
|
1792
|
+
}
|
|
1793
|
+
const fragments = output.flatMap((item) => {
|
|
1794
|
+
if (!item || typeof item !== "object" || item.type !== "reasoning") {
|
|
1795
|
+
return [];
|
|
1796
|
+
}
|
|
1797
|
+
const reasoningItem = item;
|
|
1798
|
+
const summary = Array.isArray(reasoningItem.summary) ? reasoningItem.summary.map((entry) => typeof entry?.text === "string" ? entry.text : "").filter(Boolean) : [];
|
|
1799
|
+
const content = Array.isArray(reasoningItem.content) ? reasoningItem.content.map((entry) => typeof entry?.text === "string" ? entry.text : "").filter(Boolean) : [];
|
|
1800
|
+
return [...content, ...summary];
|
|
1801
|
+
});
|
|
1802
|
+
return fragments.length > 0 ? fragments.join("") : void 0;
|
|
1803
|
+
}
|
|
1804
|
+
function abortStream2(stream) {
|
|
1805
|
+
try {
|
|
1806
|
+
stream?.controller?.abort();
|
|
1807
|
+
} catch {
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
// src/provider/client.ts
|
|
1812
|
+
import OpenAI from "openai";
|
|
1813
|
+
|
|
1814
|
+
// src/provider/connection.ts
|
|
1815
|
+
function buildProviderBaseUrlCandidates(baseUrl) {
|
|
1816
|
+
const normalized = trimTrailingSlash(baseUrl);
|
|
1817
|
+
if (!normalized) {
|
|
1818
|
+
return [normalized];
|
|
1819
|
+
}
|
|
1820
|
+
const candidates = [normalized];
|
|
1821
|
+
try {
|
|
1822
|
+
const parsed = new URL(normalized);
|
|
1823
|
+
if (parsed.pathname === "" || parsed.pathname === "/") {
|
|
1824
|
+
candidates.push(trimTrailingSlash(new URL("v1", ensureTrailingSlash2(parsed.toString())).toString()));
|
|
1825
|
+
}
|
|
1826
|
+
} catch {
|
|
1827
|
+
return candidates;
|
|
1828
|
+
}
|
|
1829
|
+
return [...new Set(candidates)];
|
|
1830
|
+
}
|
|
1831
|
+
function ensureTrailingSlash2(baseUrl) {
|
|
1832
|
+
return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
1833
|
+
}
|
|
1834
|
+
function trimTrailingSlash(baseUrl) {
|
|
1835
|
+
const trimmed = String(baseUrl ?? "").trim();
|
|
1836
|
+
if (!trimmed) {
|
|
1837
|
+
return trimmed;
|
|
1838
|
+
}
|
|
1839
|
+
return trimmed.endsWith("/") ? trimmed.slice(0, -1) : trimmed;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
// src/provider/client.ts
|
|
1843
|
+
function createProviderClientPool(config) {
|
|
1844
|
+
const capabilities = resolveProviderCapabilities({
|
|
1845
|
+
provider: config.provider,
|
|
1846
|
+
model: config.model
|
|
1847
|
+
});
|
|
1848
|
+
const baseUrls = buildProviderBaseUrlCandidates(config.baseUrl);
|
|
1849
|
+
const clients = /* @__PURE__ */ new Map();
|
|
1850
|
+
let preferredBaseUrl;
|
|
1851
|
+
return {
|
|
1852
|
+
candidates() {
|
|
1853
|
+
const ordered = preferredBaseUrl ? [preferredBaseUrl, ...baseUrls.filter((baseUrl) => baseUrl !== preferredBaseUrl)] : baseUrls;
|
|
1854
|
+
return ordered.map((baseUrl) => ({
|
|
1855
|
+
baseUrl,
|
|
1856
|
+
client: getOrCreateClient(baseUrl)
|
|
1857
|
+
}));
|
|
1858
|
+
},
|
|
1859
|
+
markHealthy(baseUrl) {
|
|
1860
|
+
preferredBaseUrl = baseUrl;
|
|
1861
|
+
}
|
|
1862
|
+
};
|
|
1863
|
+
function getOrCreateClient(baseUrl) {
|
|
1864
|
+
const existing = clients.get(baseUrl);
|
|
1865
|
+
if (existing) {
|
|
1866
|
+
return existing;
|
|
1867
|
+
}
|
|
1868
|
+
const client = new OpenAI({
|
|
1869
|
+
apiKey: config.apiKey,
|
|
1870
|
+
baseURL: baseUrl,
|
|
1871
|
+
timeout: capabilities.requestTimeoutMs,
|
|
1872
|
+
maxRetries: 0
|
|
1873
|
+
});
|
|
1874
|
+
clients.set(baseUrl, client);
|
|
1875
|
+
return client;
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
function isProviderClientPool(value) {
|
|
1879
|
+
return Boolean(
|
|
1880
|
+
value && typeof value === "object" && typeof value.candidates === "function" && typeof value.markHealthy === "function"
|
|
1881
|
+
);
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
// src/provider/request.ts
|
|
1885
|
+
async function fetchAssistantResponse(client, messages, request, tools, callbacks, abortSignal, onRequestMetric, observability) {
|
|
1886
|
+
const capabilities = resolveProviderCapabilities(request);
|
|
1887
|
+
const adapter = selectProviderWireAdapter(capabilities.wireApi);
|
|
1888
|
+
return tryFetch(
|
|
1889
|
+
adapter,
|
|
1890
|
+
client,
|
|
1891
|
+
messages,
|
|
1892
|
+
request,
|
|
1893
|
+
tools,
|
|
1894
|
+
callbacks,
|
|
1895
|
+
false,
|
|
1896
|
+
abortSignal,
|
|
1897
|
+
onRequestMetric,
|
|
1898
|
+
observability
|
|
1899
|
+
);
|
|
1900
|
+
}
|
|
1901
|
+
async function tryFetch(adapter, client, messages, request, tools, callbacks, forceReasoning, abortSignal, onRequestMetric, observability) {
|
|
1902
|
+
const startedAt = Date.now();
|
|
1903
|
+
let latestMetric;
|
|
1904
|
+
let resolvedBaseUrl;
|
|
1905
|
+
const forwardMetric = (metric) => {
|
|
1906
|
+
latestMetric = metric;
|
|
1907
|
+
onRequestMetric?.(metric);
|
|
1908
|
+
};
|
|
1909
|
+
if (observability) {
|
|
1910
|
+
await recordObservabilityEvent(observability.rootDir, {
|
|
1911
|
+
event: "model.request",
|
|
1912
|
+
status: "started",
|
|
1913
|
+
sessionId: observability.sessionId,
|
|
1914
|
+
identityKind: observability.identityKind,
|
|
1915
|
+
identityName: observability.identityName,
|
|
1916
|
+
model: request.model,
|
|
1917
|
+
details: {
|
|
1918
|
+
provider: request.provider,
|
|
1919
|
+
configuredModel: observability.configuredModel,
|
|
1920
|
+
requestModel: request.model,
|
|
1921
|
+
wireApi: adapter.wireApi,
|
|
1922
|
+
baseUrl: resolvedBaseUrl
|
|
1923
|
+
}
|
|
1924
|
+
});
|
|
1925
|
+
}
|
|
1926
|
+
try {
|
|
1927
|
+
const response = await withApiRetries(
|
|
1928
|
+
() => invokeWithProviderClients(client, async (providerClient, baseUrl) => {
|
|
1929
|
+
resolvedBaseUrl = baseUrl;
|
|
1930
|
+
return adapter.fetchStreaming(providerClient, {
|
|
1931
|
+
provider: request.provider,
|
|
1932
|
+
model: request.model,
|
|
1933
|
+
messages,
|
|
1934
|
+
tools,
|
|
1935
|
+
callbacks,
|
|
1936
|
+
forceReasoning,
|
|
1937
|
+
thinking: request.thinking,
|
|
1938
|
+
reasoningEffort: request.reasoningEffort,
|
|
1939
|
+
maxOutputTokens: request.maxOutputTokens,
|
|
1940
|
+
sessionId: request.sessionId,
|
|
1941
|
+
projectRoot: request.projectRoot,
|
|
1942
|
+
abortSignal,
|
|
1943
|
+
onRequestMetric: forwardMetric
|
|
1944
|
+
});
|
|
1945
|
+
}),
|
|
1946
|
+
abortSignal
|
|
1947
|
+
);
|
|
1948
|
+
if (observability) {
|
|
1949
|
+
await recordObservabilityEvent(observability.rootDir, {
|
|
1950
|
+
event: "model.request",
|
|
1951
|
+
status: "completed",
|
|
1952
|
+
sessionId: observability.sessionId,
|
|
1953
|
+
identityKind: observability.identityKind,
|
|
1954
|
+
identityName: observability.identityName,
|
|
1955
|
+
model: request.model,
|
|
1956
|
+
durationMs: Date.now() - startedAt,
|
|
1957
|
+
details: {
|
|
1958
|
+
provider: request.provider,
|
|
1959
|
+
configuredModel: observability.configuredModel,
|
|
1960
|
+
requestModel: request.model,
|
|
1961
|
+
wireApi: adapter.wireApi,
|
|
1962
|
+
baseUrl: resolvedBaseUrl,
|
|
1963
|
+
usage: latestMetric?.usage,
|
|
1964
|
+
usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
|
|
1965
|
+
}
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
return response;
|
|
1969
|
+
} catch (error) {
|
|
1970
|
+
if (isAbortError(error)) {
|
|
1971
|
+
throw error;
|
|
1972
|
+
}
|
|
1973
|
+
try {
|
|
1974
|
+
const response = await withApiRetries(
|
|
1975
|
+
() => invokeWithProviderClients(client, async (providerClient, baseUrl) => {
|
|
1976
|
+
resolvedBaseUrl = baseUrl;
|
|
1977
|
+
return adapter.fetchNonStreaming(providerClient, {
|
|
1978
|
+
provider: request.provider,
|
|
1979
|
+
model: request.model,
|
|
1980
|
+
messages,
|
|
1981
|
+
tools,
|
|
1982
|
+
callbacks,
|
|
1983
|
+
forceReasoning,
|
|
1984
|
+
thinking: request.thinking,
|
|
1985
|
+
reasoningEffort: request.reasoningEffort,
|
|
1986
|
+
maxOutputTokens: request.maxOutputTokens,
|
|
1987
|
+
sessionId: request.sessionId,
|
|
1988
|
+
projectRoot: request.projectRoot,
|
|
1989
|
+
abortSignal,
|
|
1990
|
+
onRequestMetric: forwardMetric
|
|
1991
|
+
});
|
|
1992
|
+
}),
|
|
1993
|
+
abortSignal
|
|
1994
|
+
);
|
|
1995
|
+
if (observability) {
|
|
1996
|
+
await recordObservabilityEvent(observability.rootDir, {
|
|
1997
|
+
event: "model.request",
|
|
1998
|
+
status: "completed",
|
|
1999
|
+
sessionId: observability.sessionId,
|
|
2000
|
+
identityKind: observability.identityKind,
|
|
2001
|
+
identityName: observability.identityName,
|
|
2002
|
+
model: request.model,
|
|
2003
|
+
durationMs: Date.now() - startedAt,
|
|
2004
|
+
details: {
|
|
2005
|
+
provider: request.provider,
|
|
2006
|
+
configuredModel: observability.configuredModel,
|
|
2007
|
+
requestModel: request.model,
|
|
2008
|
+
wireApi: adapter.wireApi,
|
|
2009
|
+
baseUrl: resolvedBaseUrl,
|
|
2010
|
+
usage: latestMetric?.usage,
|
|
2011
|
+
usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
|
|
2012
|
+
}
|
|
2013
|
+
});
|
|
2014
|
+
}
|
|
2015
|
+
return response;
|
|
2016
|
+
} catch (fallbackError) {
|
|
2017
|
+
if (!isAbortError(fallbackError) && observability) {
|
|
2018
|
+
await recordObservabilityEvent(observability.rootDir, {
|
|
2019
|
+
event: "model.request",
|
|
2020
|
+
status: "failed",
|
|
2021
|
+
sessionId: observability.sessionId,
|
|
2022
|
+
identityKind: observability.identityKind,
|
|
2023
|
+
identityName: observability.identityName,
|
|
2024
|
+
model: request.model,
|
|
2025
|
+
durationMs: Date.now() - startedAt,
|
|
2026
|
+
error: fallbackError,
|
|
2027
|
+
details: {
|
|
2028
|
+
provider: request.provider,
|
|
2029
|
+
configuredModel: observability.configuredModel,
|
|
2030
|
+
requestModel: request.model,
|
|
2031
|
+
wireApi: adapter.wireApi,
|
|
2032
|
+
baseUrl: resolvedBaseUrl,
|
|
2033
|
+
usage: latestMetric?.usage,
|
|
2034
|
+
usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
|
|
2035
|
+
}
|
|
2036
|
+
});
|
|
2037
|
+
}
|
|
2038
|
+
throw fallbackError;
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
function selectProviderWireAdapter(wireApi) {
|
|
2043
|
+
if (wireApi === "responses") {
|
|
2044
|
+
return responsesAdapter;
|
|
2045
|
+
}
|
|
2046
|
+
return chatCompletionsAdapter;
|
|
2047
|
+
}
|
|
2048
|
+
async function invokeWithProviderClients(client, operation) {
|
|
2049
|
+
if (!isProviderClientPool(client)) {
|
|
2050
|
+
return operation(client, void 0);
|
|
2051
|
+
}
|
|
2052
|
+
let lastError;
|
|
2053
|
+
const candidates = client.candidates();
|
|
2054
|
+
for (let index = 0; index < candidates.length; index += 1) {
|
|
2055
|
+
const candidate = candidates[index];
|
|
2056
|
+
try {
|
|
2057
|
+
const result = await operation(candidate.client, candidate.baseUrl);
|
|
2058
|
+
client.markHealthy(candidate.baseUrl);
|
|
2059
|
+
return result;
|
|
2060
|
+
} catch (error) {
|
|
2061
|
+
lastError = error;
|
|
2062
|
+
if (isAbortError(error)) {
|
|
2063
|
+
throw error;
|
|
2064
|
+
}
|
|
2065
|
+
const hasMoreCandidates = index < candidates.length - 1;
|
|
2066
|
+
if (!hasMoreCandidates || !canRetryWithAlternateBaseUrl(error)) {
|
|
2067
|
+
throw error;
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
throw lastError;
|
|
2072
|
+
}
|
|
2073
|
+
function canRetryWithAlternateBaseUrl(error) {
|
|
2074
|
+
const status = error.status;
|
|
2075
|
+
const message = String(error.message ?? error).toLowerCase();
|
|
2076
|
+
return status === 404 || status === 405 || message.includes("404") || message.includes("not found");
|
|
2077
|
+
}
|
|
2078
|
+
|
|
922
2079
|
// src/provider/retryPolicy.ts
|
|
923
2080
|
function isRecoverableTurnError(error) {
|
|
924
2081
|
if (isRetryableApiError(error)) {
|
|
@@ -1650,7 +2807,8 @@ function buildCompressedContextRequest(systemPrompt, messages, config) {
|
|
|
1650
2807
|
const safeMaxChars = Math.max(8e3, config.maxContextChars);
|
|
1651
2808
|
const conversation = buildVisibleConversationWindow(messages);
|
|
1652
2809
|
const conversationMessages = conversation.messages;
|
|
1653
|
-
const
|
|
2810
|
+
const provider = config.provider ?? "openai-compatible";
|
|
2811
|
+
const fullMessages = composeChatMessages(systemPrompt, conversationMessages, config.model, provider);
|
|
1654
2812
|
const initialEstimatedChars = estimateChatMessagesChars(fullMessages);
|
|
1655
2813
|
const initialPromptMetrics = measureSystemPrompt(systemPrompt);
|
|
1656
2814
|
const initialSources = buildBudgetSources(systemPrompt, conversationMessages);
|
|
@@ -1679,7 +2837,7 @@ function buildCompressedContextRequest(systemPrompt, messages, config) {
|
|
|
1679
2837
|
const summary = compressedFrameHead.length > 0 ? summarizeConversation(compressedFrameHead, config.contextSummaryChars) : void 0;
|
|
1680
2838
|
const summaryPrompt = appendSummary(systemPrompt, summary);
|
|
1681
2839
|
let workingTail = compactTailMessages(tailMessages, "normal");
|
|
1682
|
-
let requestMessages = composeChatMessages(summaryPrompt, workingTail, config.model);
|
|
2840
|
+
let requestMessages = composeChatMessages(summaryPrompt, workingTail, config.model, provider);
|
|
1683
2841
|
let estimatedChars = estimateChatMessagesChars(requestMessages);
|
|
1684
2842
|
let promptMetrics = measureSystemPrompt(summaryPrompt);
|
|
1685
2843
|
let cacheLayout = buildCacheLayoutReport(summaryPrompt, workingTail);
|
|
@@ -1704,7 +2862,7 @@ function buildCompressedContextRequest(systemPrompt, messages, config) {
|
|
|
1704
2862
|
};
|
|
1705
2863
|
}
|
|
1706
2864
|
workingTail = compactTailMessages(tailMessages, "aggressive");
|
|
1707
|
-
requestMessages = composeChatMessages(summaryPrompt, workingTail, config.model);
|
|
2865
|
+
requestMessages = composeChatMessages(summaryPrompt, workingTail, config.model, provider);
|
|
1708
2866
|
estimatedChars = estimateChatMessagesChars(requestMessages);
|
|
1709
2867
|
promptMetrics = measureSystemPrompt(summaryPrompt);
|
|
1710
2868
|
cacheLayout = buildCacheLayoutReport(summaryPrompt, workingTail);
|
|
@@ -1740,7 +2898,8 @@ function buildCompressedContextRequest(systemPrompt, messages, config) {
|
|
|
1740
2898
|
const hardMessages = composeChatMessages(
|
|
1741
2899
|
hardPrompt,
|
|
1742
2900
|
compactedHardTail,
|
|
1743
|
-
config.model
|
|
2901
|
+
config.model,
|
|
2902
|
+
provider
|
|
1744
2903
|
);
|
|
1745
2904
|
const hardEstimatedChars = estimateChatMessagesChars(hardMessages);
|
|
1746
2905
|
const hardCacheLayout = buildCacheLayoutReport(hardPrompt, compactedHardTail);
|
|
@@ -1775,7 +2934,7 @@ function sliceTailMessages(messages, tailCount) {
|
|
|
1775
2934
|
const safeStartIndex = expandStartToToolBoundary(messages, startIndex);
|
|
1776
2935
|
return messages.slice(safeStartIndex);
|
|
1777
2936
|
}
|
|
1778
|
-
function composeChatMessages(systemPrompt, messages, model) {
|
|
2937
|
+
function composeChatMessages(systemPrompt, messages, model, provider) {
|
|
1779
2938
|
return [
|
|
1780
2939
|
{
|
|
1781
2940
|
role: "system",
|
|
@@ -1787,7 +2946,7 @@ function composeChatMessages(systemPrompt, messages, model) {
|
|
|
1787
2946
|
name: message.name,
|
|
1788
2947
|
toolCallId: message.tool_call_id,
|
|
1789
2948
|
toolCalls: message.tool_calls,
|
|
1790
|
-
reasoningContent: shouldIncludeStoredAssistantReasoning(messages, index, model) ? message.reasoningContent : void 0
|
|
2949
|
+
reasoningContent: shouldIncludeStoredAssistantReasoning(messages, index, model, provider) ? message.reasoningContent : void 0
|
|
1791
2950
|
}))
|
|
1792
2951
|
];
|
|
1793
2952
|
}
|
|
@@ -1934,8 +3093,8 @@ function buildCacheLayoutReport(systemPrompt, messages) {
|
|
|
1934
3093
|
}))
|
|
1935
3094
|
});
|
|
1936
3095
|
return {
|
|
1937
|
-
stablePrefixFingerprint:
|
|
1938
|
-
volatileTailFingerprint:
|
|
3096
|
+
stablePrefixFingerprint: stableHash2(stablePrefix),
|
|
3097
|
+
volatileTailFingerprint: stableHash2(volatileTail),
|
|
1939
3098
|
stablePrefixChars: stablePrefix.length,
|
|
1940
3099
|
volatileTailChars: volatileTail.length,
|
|
1941
3100
|
stableSources: typeof systemPrompt === "string" ? ["systemPrompt"] : [
|
|
@@ -1966,7 +3125,7 @@ function renderVolatileRuntimeFacts(systemPrompt) {
|
|
|
1966
3125
|
joinBlocks(systemPrompt.runtimeFactBlocks)
|
|
1967
3126
|
].join("\n").trim();
|
|
1968
3127
|
}
|
|
1969
|
-
function
|
|
3128
|
+
function stableHash2(value) {
|
|
1970
3129
|
let hash = 2166136261;
|
|
1971
3130
|
for (let index = 0; index < value.length; index += 1) {
|
|
1972
3131
|
hash ^= value.charCodeAt(index);
|
|
@@ -2202,6 +3361,7 @@ async function updateSessionMemoryAfterTurn(input) {
|
|
|
2202
3361
|
identityName: input.identity.name,
|
|
2203
3362
|
model: input.requestModel
|
|
2204
3363
|
});
|
|
3364
|
+
input.options.callbacks?.onStatus?.("\u603B\u7ED3\u4E2D");
|
|
2205
3365
|
try {
|
|
2206
3366
|
const memoryResponse = input.options.fetchSessionMemoryResponse ? await input.options.fetchSessionMemoryResponse(modelRequest) : await fetchAssistantResponse(
|
|
2207
3367
|
input.client,
|
|
@@ -2252,6 +3412,8 @@ async function updateSessionMemoryAfterTurn(input) {
|
|
|
2252
3412
|
error
|
|
2253
3413
|
});
|
|
2254
3414
|
return input.session;
|
|
3415
|
+
} finally {
|
|
3416
|
+
input.options.callbacks?.onStatus?.("");
|
|
2255
3417
|
}
|
|
2256
3418
|
}
|
|
2257
3419
|
async function updateSessionTitleAfterTurn(input) {
|
|
@@ -2299,6 +3461,7 @@ async function updateSessionTitleAfterTurn(input) {
|
|
|
2299
3461
|
identityName: input.identity.name,
|
|
2300
3462
|
model: input.requestModel
|
|
2301
3463
|
});
|
|
3464
|
+
input.options.callbacks?.onStatus?.("\u6807\u9898\u751F\u6210\u4E2D");
|
|
2302
3465
|
try {
|
|
2303
3466
|
const titleResponse = input.options.fetchSessionTitleResponse ? await input.options.fetchSessionTitleResponse(modelRequest) : await fetchAssistantResponse(
|
|
2304
3467
|
input.client,
|
|
@@ -2350,15 +3513,17 @@ async function updateSessionTitleAfterTurn(input) {
|
|
|
2350
3513
|
error
|
|
2351
3514
|
});
|
|
2352
3515
|
return input.session;
|
|
3516
|
+
} finally {
|
|
3517
|
+
input.options.callbacks?.onStatus?.("");
|
|
2353
3518
|
}
|
|
2354
3519
|
}
|
|
2355
3520
|
|
|
2356
3521
|
// src/control/ledger.ts
|
|
2357
3522
|
import Database from "better-sqlite3";
|
|
2358
|
-
import
|
|
3523
|
+
import fs8 from "fs";
|
|
2359
3524
|
|
|
2360
3525
|
// src/control/executions.ts
|
|
2361
|
-
import
|
|
3526
|
+
import path9 from "path";
|
|
2362
3527
|
|
|
2363
3528
|
// src/protocol/leadWait.ts
|
|
2364
3529
|
var LEAD_WAIT_PROTOCOL = "kitty.lead-wait-policy";
|
|
@@ -2587,7 +3752,7 @@ var ExecutionLedgerRepo = class {
|
|
|
2587
3752
|
prompt: input.prompt,
|
|
2588
3753
|
actorName: input.actorName,
|
|
2589
3754
|
actorRole: input.actorRole,
|
|
2590
|
-
cwd:
|
|
3755
|
+
cwd: path9.resolve(input.cwd),
|
|
2591
3756
|
requestedBy: input.requestedBy,
|
|
2592
3757
|
sessionId: input.sessionId,
|
|
2593
3758
|
pid: input.pid,
|
|
@@ -2618,10 +3783,10 @@ var ExecutionLedgerRepo = class {
|
|
|
2618
3783
|
}
|
|
2619
3784
|
list(input = {}) {
|
|
2620
3785
|
const rows = this.db.prepare("SELECT * FROM executions ORDER BY created_at ASC").all();
|
|
2621
|
-
const cwd = input.cwd ?
|
|
3786
|
+
const cwd = input.cwd ? path9.resolve(input.cwd) : void 0;
|
|
2622
3787
|
const statuses = new Set(input.statuses ?? []);
|
|
2623
3788
|
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(
|
|
3789
|
+
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
3790
|
}
|
|
2626
3791
|
markRunning(id, input) {
|
|
2627
3792
|
const current = requireExecution(this.load(id), id);
|
|
@@ -2699,8 +3864,8 @@ function requireExecution(record, id) {
|
|
|
2699
3864
|
return record;
|
|
2700
3865
|
}
|
|
2701
3866
|
function isSameOrDescendant(targetPath, possibleAncestor) {
|
|
2702
|
-
const relative =
|
|
2703
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
3867
|
+
const relative = path9.relative(possibleAncestor, targetPath);
|
|
3868
|
+
return relative === "" || !relative.startsWith("..") && !path9.isAbsolute(relative);
|
|
2704
3869
|
}
|
|
2705
3870
|
|
|
2706
3871
|
// src/control/schema.ts
|
|
@@ -2814,7 +3979,7 @@ var TaskLifecycleLedgerRepo = class {
|
|
|
2814
3979
|
stage: existing?.stage === "completed" || !existing ? "normal_work" : existing.stage,
|
|
2815
3980
|
scope: existing?.scope,
|
|
2816
3981
|
boundary: existing?.boundary,
|
|
2817
|
-
reason:
|
|
3982
|
+
reason: normalizeText3(input.reason) ?? existing?.reason ?? "turn_started",
|
|
2818
3983
|
activeExecutionIds: existing?.activeExecutionIds ?? [],
|
|
2819
3984
|
activeTodoIds: existing?.activeTodoIds ?? [],
|
|
2820
3985
|
verificationFacts: existing?.verificationFacts ?? [],
|
|
@@ -2843,9 +4008,9 @@ var TaskLifecycleLedgerRepo = class {
|
|
|
2843
4008
|
return this.save({
|
|
2844
4009
|
...current,
|
|
2845
4010
|
stage: input.stage ?? current.stage,
|
|
2846
|
-
scope:
|
|
2847
|
-
boundary:
|
|
2848
|
-
reason:
|
|
4011
|
+
scope: normalizeText3(input.scope) ?? current.scope,
|
|
4012
|
+
boundary: normalizeText3(input.boundary) ?? current.boundary,
|
|
4013
|
+
reason: normalizeText3(input.reason) ?? current.reason,
|
|
2849
4014
|
activeExecutionIds: normalizeStringList2(input.activeExecutionIds ?? current.activeExecutionIds),
|
|
2850
4015
|
activeTodoIds: normalizeStringList2(input.activeTodoIds ?? current.activeTodoIds),
|
|
2851
4016
|
verificationFacts: normalizeStringList2(input.verificationFacts ?? current.verificationFacts),
|
|
@@ -2961,7 +4126,7 @@ function normalizeStringList2(value) {
|
|
|
2961
4126
|
const seen = /* @__PURE__ */ new Set();
|
|
2962
4127
|
const items = [];
|
|
2963
4128
|
for (const item of value) {
|
|
2964
|
-
const text =
|
|
4129
|
+
const text = normalizeText3(item);
|
|
2965
4130
|
if (!text || seen.has(text)) {
|
|
2966
4131
|
continue;
|
|
2967
4132
|
}
|
|
@@ -2970,7 +4135,7 @@ function normalizeStringList2(value) {
|
|
|
2970
4135
|
}
|
|
2971
4136
|
return items;
|
|
2972
4137
|
}
|
|
2973
|
-
function
|
|
4138
|
+
function normalizeText3(value) {
|
|
2974
4139
|
return typeof value === "string" && value.trim() ? value.replace(/\s+/g, " ").trim() : void 0;
|
|
2975
4140
|
}
|
|
2976
4141
|
|
|
@@ -3010,7 +4175,7 @@ var ControlPlaneLedger = class {
|
|
|
3010
4175
|
db;
|
|
3011
4176
|
constructor(rootDir) {
|
|
3012
4177
|
const statePaths = getProjectStatePaths(rootDir);
|
|
3013
|
-
|
|
4178
|
+
fs8.mkdirSync(statePaths.kittyDir, { recursive: true });
|
|
3014
4179
|
this.db = new Database(statePaths.controlPlaneLedgerFile);
|
|
3015
4180
|
this.db.pragma("journal_mode = WAL");
|
|
3016
4181
|
this.db.pragma("foreign_keys = ON");
|
|
@@ -3177,11 +4342,11 @@ function truncateWakeFact(value) {
|
|
|
3177
4342
|
}
|
|
3178
4343
|
|
|
3179
4344
|
// src/utils/fs.ts
|
|
3180
|
-
import
|
|
3181
|
-
import
|
|
4345
|
+
import fs9 from "fs/promises";
|
|
4346
|
+
import path10 from "path";
|
|
3182
4347
|
async function fileExists(targetPath) {
|
|
3183
4348
|
try {
|
|
3184
|
-
await
|
|
4349
|
+
await fs9.access(targetPath);
|
|
3185
4350
|
return true;
|
|
3186
4351
|
} catch {
|
|
3187
4352
|
return false;
|
|
@@ -3189,10 +4354,10 @@ async function fileExists(targetPath) {
|
|
|
3189
4354
|
}
|
|
3190
4355
|
function resolveUserPath(inputPath, cwd) {
|
|
3191
4356
|
const cleanPath = normalizeUserPathInput(inputPath);
|
|
3192
|
-
if (
|
|
3193
|
-
return
|
|
4357
|
+
if (path10.isAbsolute(cleanPath)) {
|
|
4358
|
+
return path10.normalize(cleanPath);
|
|
3194
4359
|
}
|
|
3195
|
-
return
|
|
4360
|
+
return path10.resolve(cwd, cleanPath);
|
|
3196
4361
|
}
|
|
3197
4362
|
function normalizeUserPathInput(inputPath) {
|
|
3198
4363
|
const trimmed = inputPath.trim();
|
|
@@ -3206,7 +4371,7 @@ function normalizeUserPathInput(inputPath) {
|
|
|
3206
4371
|
return trimmed;
|
|
3207
4372
|
}
|
|
3208
4373
|
async function ensureParentDirectory(filePath) {
|
|
3209
|
-
await
|
|
4374
|
+
await fs9.mkdir(path10.dirname(filePath), { recursive: true });
|
|
3210
4375
|
}
|
|
3211
4376
|
function truncateText(input, maxChars) {
|
|
3212
4377
|
if (input.length <= maxChars) {
|
|
@@ -3258,7 +4423,7 @@ function projectToolResultForModel(input) {
|
|
|
3258
4423
|
}
|
|
3259
4424
|
}
|
|
3260
4425
|
function projectExecutionAction(payload) {
|
|
3261
|
-
const execution =
|
|
4426
|
+
const execution = readObject2(payload.execution) ?? payload;
|
|
3262
4427
|
return joinLines([
|
|
3263
4428
|
readString(execution.id) ?? "execution",
|
|
3264
4429
|
readString(execution.kind),
|
|
@@ -3266,7 +4431,7 @@ function projectExecutionAction(payload) {
|
|
|
3266
4431
|
readString(execution.command),
|
|
3267
4432
|
readString(execution.summary),
|
|
3268
4433
|
readString(execution.outputPreview),
|
|
3269
|
-
|
|
4434
|
+
readObject2(execution.health) ? readString(readObject2(execution.health)?.message) : void 0,
|
|
3270
4435
|
readString(execution.error)
|
|
3271
4436
|
]);
|
|
3272
4437
|
}
|
|
@@ -3293,10 +4458,10 @@ function projectExecutionCheck(payload) {
|
|
|
3293
4458
|
]);
|
|
3294
4459
|
}
|
|
3295
4460
|
function projectRead(payload) {
|
|
3296
|
-
const
|
|
4461
|
+
const path29 = readString(payload.path) ?? readString(payload.requestedPath) ?? "file";
|
|
3297
4462
|
if (payload.readable === false) {
|
|
3298
4463
|
return joinLines([
|
|
3299
|
-
`${
|
|
4464
|
+
`${path29}: not readable`,
|
|
3300
4465
|
readString(payload.reason),
|
|
3301
4466
|
readString(payload.detectedCapability) ? `capability: ${readString(payload.detectedCapability)}` : void 0
|
|
3302
4467
|
]);
|
|
@@ -3304,35 +4469,35 @@ function projectRead(payload) {
|
|
|
3304
4469
|
const startLine = readNumber(payload.startLine);
|
|
3305
4470
|
const endLine = readNumber(payload.endLine);
|
|
3306
4471
|
const content = readString(payload.content) ?? "";
|
|
3307
|
-
const continuation =
|
|
3308
|
-
const continuationArgs =
|
|
4472
|
+
const continuation = readObject2(payload.continuation);
|
|
4473
|
+
const continuationArgs = readObject2(continuation?.continuationArgs);
|
|
3309
4474
|
return joinLines([
|
|
3310
|
-
`${
|
|
4475
|
+
`${path29}${startLine && endLine ? `:${startLine}-${endLine}` : ""}`,
|
|
3311
4476
|
truncateText(content, DEFAULT_MAX_CHARS),
|
|
3312
4477
|
continuationArgs ? `next: read ${JSON.stringify(continuationArgs)}` : void 0
|
|
3313
4478
|
]);
|
|
3314
4479
|
}
|
|
3315
4480
|
function projectEdit(payload) {
|
|
3316
|
-
const
|
|
4481
|
+
const path29 = readString(payload.path) ?? "file";
|
|
3317
4482
|
const applied = readNumber(payload.appliedEdits) ?? readNumber(payload.requestedEdits);
|
|
3318
4483
|
const diff = readString(payload.diff) ?? readString(payload.preview);
|
|
3319
4484
|
return joinLines([
|
|
3320
|
-
`edited ${
|
|
4485
|
+
`edited ${path29}${applied ? ` (${applied} replacement${applied === 1 ? "" : "s"})` : ""}`,
|
|
3321
4486
|
diff ? truncateText(diff, DIFF_MAX_CHARS) : void 0
|
|
3322
4487
|
]);
|
|
3323
4488
|
}
|
|
3324
4489
|
function projectWrite(payload) {
|
|
3325
|
-
const
|
|
4490
|
+
const path29 = readString(payload.path) ?? "file";
|
|
3326
4491
|
const bytes = readNumber(payload.bytes);
|
|
3327
4492
|
const existed = payload.existed === true;
|
|
3328
4493
|
const diff = readString(payload.diff) ?? readString(payload.preview);
|
|
3329
4494
|
return joinLines([
|
|
3330
|
-
`${existed ? "wrote" : "created"} ${
|
|
4495
|
+
`${existed ? "wrote" : "created"} ${path29}${bytes !== void 0 ? ` (${bytes} bytes)` : ""}`,
|
|
3331
4496
|
diff ? truncateText(diff, DIFF_MAX_CHARS) : void 0
|
|
3332
4497
|
]);
|
|
3333
4498
|
}
|
|
3334
4499
|
function projectBash(payload) {
|
|
3335
|
-
const governance =
|
|
4500
|
+
const governance = readObject2(payload.outputGovernance);
|
|
3336
4501
|
const projection = readString(governance?.projection);
|
|
3337
4502
|
if (projection) {
|
|
3338
4503
|
return projection;
|
|
@@ -3353,13 +4518,13 @@ function projectBash(payload) {
|
|
|
3353
4518
|
return joinLines(lines);
|
|
3354
4519
|
}
|
|
3355
4520
|
function projectSkillLoad(payload) {
|
|
3356
|
-
const skill =
|
|
4521
|
+
const skill = readObject2(payload.skill);
|
|
3357
4522
|
const name = readString(skill?.name) ?? "skill";
|
|
3358
4523
|
const description = readString(skill?.description);
|
|
3359
|
-
const
|
|
4524
|
+
const path29 = readString(skill?.path);
|
|
3360
4525
|
const body = readString(payload.body) ?? "";
|
|
3361
4526
|
return joinLines([
|
|
3362
|
-
`loaded skill: ${name}${
|
|
4527
|
+
`loaded skill: ${name}${path29 ? ` (${path29})` : ""}`,
|
|
3363
4528
|
description,
|
|
3364
4529
|
truncateText(body, SKILL_BODY_MAX_CHARS)
|
|
3365
4530
|
]);
|
|
@@ -3389,8 +4554,8 @@ function projectFailure(toolName, rawOutput, payload) {
|
|
|
3389
4554
|
if (!payload) {
|
|
3390
4555
|
return truncateText(rawOutput.trim(), DEFAULT_MAX_CHARS);
|
|
3391
4556
|
}
|
|
3392
|
-
const details =
|
|
3393
|
-
const readArgs =
|
|
4557
|
+
const details = readObject2(payload.details);
|
|
4558
|
+
const readArgs = readObject2(details?.readArgs);
|
|
3394
4559
|
const suggestions = readArray(details?.suggestions);
|
|
3395
4560
|
const lines = [
|
|
3396
4561
|
`${toolName} failed: ${readString(payload.error) ?? "unknown error"}`,
|
|
@@ -3412,7 +4577,7 @@ function parseObject(raw) {
|
|
|
3412
4577
|
return null;
|
|
3413
4578
|
}
|
|
3414
4579
|
}
|
|
3415
|
-
function
|
|
4580
|
+
function readObject2(value) {
|
|
3416
4581
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
3417
4582
|
}
|
|
3418
4583
|
function readArray(value) {
|
|
@@ -3447,14 +4612,14 @@ var ToolExecutionError = class extends Error {
|
|
|
3447
4612
|
};
|
|
3448
4613
|
|
|
3449
4614
|
// src/tools/outputCapture.ts
|
|
3450
|
-
import
|
|
3451
|
-
import
|
|
3452
|
-
import
|
|
4615
|
+
import crypto2 from "crypto";
|
|
4616
|
+
import fs10 from "fs/promises";
|
|
4617
|
+
import path11 from "path";
|
|
3453
4618
|
var DEFAULT_BASH_OUTPUT_PREVIEW_CHARS = 12e3;
|
|
3454
4619
|
async function createBashOutputCapture(input) {
|
|
3455
4620
|
const maxPreviewChars = typeof input.maxPreviewChars === "number" && Number.isFinite(input.maxPreviewChars) && input.maxPreviewChars > 0 ? Math.trunc(input.maxPreviewChars) : DEFAULT_BASH_OUTPUT_PREVIEW_CHARS;
|
|
3456
4621
|
const absoluteOutputPath = input.stateRootDir && input.sessionId ? await createAbsoluteOutputPath(input.stateRootDir, input.sessionId) : void 0;
|
|
3457
|
-
const outputPath = absoluteOutputPath && input.stateRootDir ?
|
|
4622
|
+
const outputPath = absoluteOutputPath && input.stateRootDir ? path11.relative(input.stateRootDir, absoluteOutputPath) || void 0 : void 0;
|
|
3458
4623
|
let preview = "";
|
|
3459
4624
|
let bufferedOutput = "";
|
|
3460
4625
|
let totalChars = 0;
|
|
@@ -3498,7 +4663,7 @@ async function createBashOutputCapture(input) {
|
|
|
3498
4663
|
if (!absoluteOutputPath) {
|
|
3499
4664
|
return;
|
|
3500
4665
|
}
|
|
3501
|
-
pendingWrite = pendingWrite.then(() =>
|
|
4666
|
+
pendingWrite = pendingWrite.then(() => fs10.appendFile(absoluteOutputPath, chunk, "utf8"));
|
|
3502
4667
|
}
|
|
3503
4668
|
return {
|
|
3504
4669
|
append,
|
|
@@ -3507,9 +4672,9 @@ async function createBashOutputCapture(input) {
|
|
|
3507
4672
|
}
|
|
3508
4673
|
async function createAbsoluteOutputPath(stateRootDir, sessionId) {
|
|
3509
4674
|
const paths = getProjectStatePaths(stateRootDir);
|
|
3510
|
-
const sessionDir =
|
|
3511
|
-
await
|
|
3512
|
-
return
|
|
4675
|
+
const sessionDir = path11.join(paths.observabilityDir, "command-output", sessionId);
|
|
4676
|
+
await fs10.mkdir(sessionDir, { recursive: true });
|
|
4677
|
+
return path11.join(sessionDir, `${Date.now()}-bash-output-${crypto2.randomUUID().slice(0, 8)}.txt`);
|
|
3513
4678
|
}
|
|
3514
4679
|
|
|
3515
4680
|
// src/utils/commandRunner/shellRuntime.ts
|
|
@@ -3723,8 +4888,8 @@ function isAbortedProcessResult(value, signal) {
|
|
|
3723
4888
|
}
|
|
3724
4889
|
|
|
3725
4890
|
// src/tools/core/shared.ts
|
|
3726
|
-
import
|
|
3727
|
-
import
|
|
4891
|
+
import fs11 from "fs/promises";
|
|
4892
|
+
import path12 from "path";
|
|
3728
4893
|
import { diffLines } from "diff";
|
|
3729
4894
|
|
|
3730
4895
|
// src/utils/text.ts
|
|
@@ -3938,15 +5103,15 @@ function comparePathForDiscovery(root, left, right) {
|
|
|
3938
5103
|
if (leftDepth !== rightDepth) {
|
|
3939
5104
|
return leftDepth - rightDepth;
|
|
3940
5105
|
}
|
|
3941
|
-
const leftName =
|
|
3942
|
-
const rightName =
|
|
5106
|
+
const leftName = path12.posix.basename(leftRelative).toLowerCase();
|
|
5107
|
+
const rightName = path12.posix.basename(rightRelative).toLowerCase();
|
|
3943
5108
|
if (leftName !== rightName) {
|
|
3944
5109
|
return leftName.localeCompare(rightName);
|
|
3945
5110
|
}
|
|
3946
5111
|
return leftRelative.localeCompare(rightRelative);
|
|
3947
5112
|
}
|
|
3948
5113
|
function toPosixRelative(root, targetPath) {
|
|
3949
|
-
return (
|
|
5114
|
+
return (path12.relative(root, targetPath) || path12.basename(targetPath)).replace(/\\/g, "/");
|
|
3950
5115
|
}
|
|
3951
5116
|
function pathDepth(relativePath) {
|
|
3952
5117
|
return relativePath.split("/").filter(Boolean).length;
|
|
@@ -4307,7 +5472,7 @@ var bashToolDefinition = {
|
|
|
4307
5472
|
};
|
|
4308
5473
|
|
|
4309
5474
|
// src/tools/edit.ts
|
|
4310
|
-
import
|
|
5475
|
+
import fs13 from "fs/promises";
|
|
4311
5476
|
|
|
4312
5477
|
// src/tools/core/changeTracking.ts
|
|
4313
5478
|
async function recordToolChange(context, input) {
|
|
@@ -4348,13 +5513,13 @@ function encodeUtf8(value) {
|
|
|
4348
5513
|
}
|
|
4349
5514
|
|
|
4350
5515
|
// src/tools/core/pathDisplay.ts
|
|
4351
|
-
import
|
|
5516
|
+
import path13 from "path";
|
|
4352
5517
|
function toToolRelativePath(cwd, targetPath) {
|
|
4353
|
-
const relative =
|
|
5518
|
+
const relative = path13.relative(cwd, targetPath);
|
|
4354
5519
|
if (!relative || relative === "") {
|
|
4355
5520
|
return ".";
|
|
4356
5521
|
}
|
|
4357
|
-
if (relative.startsWith("..") ||
|
|
5522
|
+
if (relative.startsWith("..") || path13.isAbsolute(relative)) {
|
|
4358
5523
|
return targetPath;
|
|
4359
5524
|
}
|
|
4360
5525
|
return relative.replace(/\\/g, "/");
|
|
@@ -4381,8 +5546,8 @@ function buildToolChangeFeedback(input) {
|
|
|
4381
5546
|
}
|
|
4382
5547
|
|
|
4383
5548
|
// src/tools/writeDiagnostics.ts
|
|
4384
|
-
import
|
|
4385
|
-
import
|
|
5549
|
+
import fs12 from "fs/promises";
|
|
5550
|
+
import path14 from "path";
|
|
4386
5551
|
var TYPE_SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
4387
5552
|
async function collectWriteDiagnostics(paths) {
|
|
4388
5553
|
const uniquePaths2 = takeUniquePaths(paths);
|
|
@@ -4429,7 +5594,7 @@ function createEmptyDiagnosticsReport(status) {
|
|
|
4429
5594
|
}
|
|
4430
5595
|
async function collectFileDiagnostics(targetPath) {
|
|
4431
5596
|
try {
|
|
4432
|
-
const extension =
|
|
5597
|
+
const extension = path14.extname(targetPath).toLowerCase();
|
|
4433
5598
|
if (extension === ".json") {
|
|
4434
5599
|
return await collectJsonDiagnostics(targetPath);
|
|
4435
5600
|
}
|
|
@@ -4445,7 +5610,7 @@ async function collectFileDiagnostics(targetPath) {
|
|
|
4445
5610
|
return null;
|
|
4446
5611
|
}
|
|
4447
5612
|
async function collectJsonDiagnostics(targetPath) {
|
|
4448
|
-
const content = await
|
|
5613
|
+
const content = await fs12.readFile(targetPath, "utf8");
|
|
4449
5614
|
const diagnostics = [];
|
|
4450
5615
|
try {
|
|
4451
5616
|
JSON.parse(content);
|
|
@@ -4466,7 +5631,7 @@ async function collectJsonDiagnostics(targetPath) {
|
|
|
4466
5631
|
async function collectTypeScriptDiagnostics(targetPath) {
|
|
4467
5632
|
const diagnostics = [];
|
|
4468
5633
|
const TypeScript = await import("typescript");
|
|
4469
|
-
const content = await
|
|
5634
|
+
const content = await fs12.readFile(targetPath, "utf8");
|
|
4470
5635
|
const transpiled = TypeScript.transpileModule(content, {
|
|
4471
5636
|
fileName: targetPath,
|
|
4472
5637
|
compilerOptions: {
|
|
@@ -4583,7 +5748,7 @@ var editToolDefinition = {
|
|
|
4583
5748
|
const resolved = resolveUserPath(targetPath, context.cwd);
|
|
4584
5749
|
const displayPath = toToolRelativePath(context.cwd, resolved);
|
|
4585
5750
|
return withFileEditLock(resolved, async () => {
|
|
4586
|
-
const beforeBuffer = await
|
|
5751
|
+
const beforeBuffer = await fs13.readFile(resolved);
|
|
4587
5752
|
const beforeEnvelope = decodeTextFileEnvelope(beforeBuffer);
|
|
4588
5753
|
if (!beforeEnvelope) {
|
|
4589
5754
|
throw new ToolExecutionError(`edit cannot edit binary or unsupported text encoding for ${displayPath}`, {
|
|
@@ -4601,7 +5766,7 @@ var editToolDefinition = {
|
|
|
4601
5766
|
});
|
|
4602
5767
|
}
|
|
4603
5768
|
const diff = buildDiffPreview(before, after);
|
|
4604
|
-
await
|
|
5769
|
+
await fs13.writeFile(resolved, encodeTextFileEnvelope(after, beforeEnvelope));
|
|
4605
5770
|
const changeRecord = await recordToolChange(context, {
|
|
4606
5771
|
toolName: "edit",
|
|
4607
5772
|
summary: `edit ${displayPath}`,
|
|
@@ -4755,9 +5920,9 @@ function lineForOffset(input, offset) {
|
|
|
4755
5920
|
}
|
|
4756
5921
|
return line;
|
|
4757
5922
|
}
|
|
4758
|
-
function buildReadArgs(
|
|
5923
|
+
function buildReadArgs(path29, line) {
|
|
4759
5924
|
return {
|
|
4760
|
-
path:
|
|
5925
|
+
path: path29,
|
|
4761
5926
|
offset: Math.max(1, (line ?? 1) - 20),
|
|
4762
5927
|
limit: 60
|
|
4763
5928
|
};
|
|
@@ -4811,8 +5976,8 @@ async function withFileEditLock(filePath, action) {
|
|
|
4811
5976
|
}
|
|
4812
5977
|
|
|
4813
5978
|
// src/tools/core/fileIntrospection.ts
|
|
4814
|
-
import
|
|
4815
|
-
import
|
|
5979
|
+
import fs14 from "fs/promises";
|
|
5980
|
+
import path15 from "path";
|
|
4816
5981
|
var KNOWN_BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
4817
5982
|
".epub",
|
|
4818
5983
|
".mobi",
|
|
@@ -4830,8 +5995,8 @@ var KNOWN_BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
4830
5995
|
".bin"
|
|
4831
5996
|
]);
|
|
4832
5997
|
async function inspectTextFile(filePath, _maxBytes) {
|
|
4833
|
-
const stat = await
|
|
4834
|
-
const extension =
|
|
5998
|
+
const stat = await fs14.stat(filePath);
|
|
5999
|
+
const extension = path15.extname(filePath).toLowerCase();
|
|
4835
6000
|
if (KNOWN_BINARY_EXTENSIONS.has(extension)) {
|
|
4836
6001
|
return {
|
|
4837
6002
|
readable: false,
|
|
@@ -4841,7 +6006,7 @@ async function inspectTextFile(filePath, _maxBytes) {
|
|
|
4841
6006
|
extension
|
|
4842
6007
|
};
|
|
4843
6008
|
}
|
|
4844
|
-
const buffer = await
|
|
6009
|
+
const buffer = await fs14.readFile(filePath);
|
|
4845
6010
|
const decoded = decodeTextFileEnvelope(buffer);
|
|
4846
6011
|
if (!decoded) {
|
|
4847
6012
|
return {
|
|
@@ -4865,11 +6030,11 @@ async function inspectTextFile(filePath, _maxBytes) {
|
|
|
4865
6030
|
}
|
|
4866
6031
|
|
|
4867
6032
|
// src/tools/core/pathSuggestions.ts
|
|
4868
|
-
import
|
|
6033
|
+
import path16 from "path";
|
|
4869
6034
|
import fg3 from "fast-glob";
|
|
4870
6035
|
async function findPathSuggestions(cwd, requestedPath, projectContext, limit = 8) {
|
|
4871
6036
|
const normalized = normalizeUserPathInput(requestedPath).replace(/\\/g, "/");
|
|
4872
|
-
const baseName =
|
|
6037
|
+
const baseName = path16.basename(normalized).trim();
|
|
4873
6038
|
const needle = baseName.length > 0 ? baseName : normalized.trim();
|
|
4874
6039
|
if (!needle) {
|
|
4875
6040
|
return [];
|
|
@@ -4886,8 +6051,8 @@ async function findPathSuggestions(cwd, requestedPath, projectContext, limit = 8
|
|
|
4886
6051
|
suppressErrors: true,
|
|
4887
6052
|
ignore: buildFastGlobIgnorePatterns(cwd, projectContext.ignoreRules)
|
|
4888
6053
|
});
|
|
4889
|
-
for (const entry of entries.sort((left, right) => comparePathForDiscovery(cwd,
|
|
4890
|
-
const absolutePath =
|
|
6054
|
+
for (const entry of entries.sort((left, right) => comparePathForDiscovery(cwd, path16.resolve(cwd, left), path16.resolve(cwd, right)))) {
|
|
6055
|
+
const absolutePath = path16.resolve(cwd, entry);
|
|
4891
6056
|
const isDirectory3 = entry.endsWith("/");
|
|
4892
6057
|
if (isPathIgnored(absolutePath, projectContext.ignoreRules, isDirectory3)) {
|
|
4893
6058
|
continue;
|
|
@@ -5052,7 +6217,7 @@ function fitWindowWithinBudget(lines, start, requestedEndExclusive, maxChars) {
|
|
|
5052
6217
|
}
|
|
5053
6218
|
|
|
5054
6219
|
// src/tools/write.ts
|
|
5055
|
-
import
|
|
6220
|
+
import fs15 from "fs/promises";
|
|
5056
6221
|
var writeToolDefinition = {
|
|
5057
6222
|
definition: {
|
|
5058
6223
|
type: "function",
|
|
@@ -5088,12 +6253,12 @@ var writeToolDefinition = {
|
|
|
5088
6253
|
const resolved = resolveUserPath(targetPath, context.cwd);
|
|
5089
6254
|
const displayPath = toToolRelativePath(context.cwd, resolved);
|
|
5090
6255
|
const existed = await fileExists(resolved);
|
|
5091
|
-
const before = existed ? await
|
|
6256
|
+
const before = existed ? await fs15.readFile(resolved, "utf8") : "";
|
|
5092
6257
|
const preview = buildDiffPreview(before, content);
|
|
5093
6258
|
if (createDirectories) {
|
|
5094
6259
|
await ensureParentDirectory(resolved);
|
|
5095
6260
|
}
|
|
5096
|
-
await
|
|
6261
|
+
await fs15.writeFile(resolved, content, "utf8");
|
|
5097
6262
|
const changeRecord = await recordToolChange(context, {
|
|
5098
6263
|
toolName: "write",
|
|
5099
6264
|
summary: `write ${displayPath}`,
|
|
@@ -5148,7 +6313,7 @@ var writeToolDefinition = {
|
|
|
5148
6313
|
};
|
|
5149
6314
|
|
|
5150
6315
|
// src/tools/sendFile.ts
|
|
5151
|
-
import
|
|
6316
|
+
import fs16 from "fs/promises";
|
|
5152
6317
|
var sendFileToolDefinition = {
|
|
5153
6318
|
definition: {
|
|
5154
6319
|
type: "function",
|
|
@@ -5191,7 +6356,7 @@ var sendFileToolDefinition = {
|
|
|
5191
6356
|
};
|
|
5192
6357
|
}
|
|
5193
6358
|
try {
|
|
5194
|
-
await
|
|
6359
|
+
await fs16.access(filePath);
|
|
5195
6360
|
} catch {
|
|
5196
6361
|
return {
|
|
5197
6362
|
ok: false,
|
|
@@ -5724,8 +6889,8 @@ async function executeToolBatch(params) {
|
|
|
5724
6889
|
}
|
|
5725
6890
|
|
|
5726
6891
|
// src/session/events.ts
|
|
5727
|
-
import
|
|
5728
|
-
import
|
|
6892
|
+
import fs17 from "fs/promises";
|
|
6893
|
+
import path17 from "path";
|
|
5729
6894
|
var SessionEventStore = class {
|
|
5730
6895
|
constructor(eventsDir) {
|
|
5731
6896
|
this.eventsDir = eventsDir;
|
|
@@ -5741,8 +6906,8 @@ var SessionEventStore = class {
|
|
|
5741
6906
|
message: event.message,
|
|
5742
6907
|
details: event.details
|
|
5743
6908
|
};
|
|
5744
|
-
await
|
|
5745
|
-
await
|
|
6909
|
+
await fs17.mkdir(this.eventsDir, { recursive: true });
|
|
6910
|
+
await fs17.appendFile(this.getSessionEventPath(event.sessionId), `${JSON.stringify(record)}
|
|
5746
6911
|
`, "utf8");
|
|
5747
6912
|
return record;
|
|
5748
6913
|
}
|
|
@@ -5750,7 +6915,7 @@ var SessionEventStore = class {
|
|
|
5750
6915
|
const filePath = this.getSessionEventPath(sessionId);
|
|
5751
6916
|
let raw = "";
|
|
5752
6917
|
try {
|
|
5753
|
-
raw = await
|
|
6918
|
+
raw = await fs17.readFile(filePath, "utf8");
|
|
5754
6919
|
} catch (error) {
|
|
5755
6920
|
if (error.code === "ENOENT") {
|
|
5756
6921
|
return [];
|
|
@@ -5760,7 +6925,7 @@ var SessionEventStore = class {
|
|
|
5760
6925
|
return raw.split(/\r?\n/).filter(Boolean).map((line) => JSON.parse(line)).slice(-limit);
|
|
5761
6926
|
}
|
|
5762
6927
|
getSessionEventPath(sessionId) {
|
|
5763
|
-
return
|
|
6928
|
+
return path17.join(this.eventsDir, `${sanitizeSessionId(sessionId)}.jsonl`);
|
|
5764
6929
|
}
|
|
5765
6930
|
};
|
|
5766
6931
|
function createEventId() {
|
|
@@ -6079,9 +7244,9 @@ function normalizeToolArguments(raw) {
|
|
|
6079
7244
|
}
|
|
6080
7245
|
|
|
6081
7246
|
// src/agent/changes/store.ts
|
|
6082
|
-
import
|
|
6083
|
-
import
|
|
6084
|
-
import
|
|
7247
|
+
import crypto3 from "crypto";
|
|
7248
|
+
import fs18 from "fs/promises";
|
|
7249
|
+
import path18 from "path";
|
|
6085
7250
|
var ChangeStore = class {
|
|
6086
7251
|
constructor(changesDir) {
|
|
6087
7252
|
this.changesDir = changesDir;
|
|
@@ -6089,8 +7254,8 @@ var ChangeStore = class {
|
|
|
6089
7254
|
async record(input) {
|
|
6090
7255
|
const id = createChangeId();
|
|
6091
7256
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
6092
|
-
const blobDir =
|
|
6093
|
-
await
|
|
7257
|
+
const blobDir = path18.join(this.changesDir, id);
|
|
7258
|
+
await fs18.mkdir(blobDir, { recursive: true });
|
|
6094
7259
|
const operations = await Promise.all(
|
|
6095
7260
|
input.operations.map(async (operation, index) => {
|
|
6096
7261
|
const beforeSnapshotPath = await this.writeSnapshot(
|
|
@@ -6130,21 +7295,21 @@ var ChangeStore = class {
|
|
|
6130
7295
|
preview: input.preview,
|
|
6131
7296
|
operations
|
|
6132
7297
|
};
|
|
6133
|
-
await
|
|
6134
|
-
await
|
|
7298
|
+
await fs18.mkdir(this.changesDir, { recursive: true });
|
|
7299
|
+
await fs18.writeFile(this.getMetadataPath(id), `${JSON.stringify(record, null, 2)}
|
|
6135
7300
|
`, "utf8");
|
|
6136
7301
|
return record;
|
|
6137
7302
|
}
|
|
6138
7303
|
async list(limit = 20) {
|
|
6139
|
-
await
|
|
6140
|
-
const entries = await
|
|
7304
|
+
await fs18.mkdir(this.changesDir, { recursive: true });
|
|
7305
|
+
const entries = await fs18.readdir(this.changesDir, { withFileTypes: true });
|
|
6141
7306
|
const changes = await Promise.all(
|
|
6142
|
-
entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => this.load(
|
|
7307
|
+
entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => this.load(path18.basename(entry.name, ".json")))
|
|
6143
7308
|
);
|
|
6144
7309
|
return changes.sort((left, right) => right.createdAt.localeCompare(left.createdAt)).slice(0, limit);
|
|
6145
7310
|
}
|
|
6146
7311
|
async load(id) {
|
|
6147
|
-
const raw = await
|
|
7312
|
+
const raw = await fs18.readFile(this.getMetadataPath(id), "utf8");
|
|
6148
7313
|
return JSON.parse(raw);
|
|
6149
7314
|
}
|
|
6150
7315
|
async loadLatestUndoable() {
|
|
@@ -6168,17 +7333,17 @@ var ChangeStore = class {
|
|
|
6168
7333
|
restoredPaths.push(operation.path);
|
|
6169
7334
|
if (operation.beforeSnapshotPath) {
|
|
6170
7335
|
const buffer = await this.readSnapshot(operation.beforeSnapshotPath);
|
|
6171
|
-
await
|
|
6172
|
-
await
|
|
7336
|
+
await fs18.mkdir(path18.dirname(operation.path), { recursive: true });
|
|
7337
|
+
await fs18.writeFile(operation.path, buffer);
|
|
6173
7338
|
continue;
|
|
6174
7339
|
}
|
|
6175
|
-
await
|
|
7340
|
+
await fs18.rm(operation.path, { force: true });
|
|
6176
7341
|
}
|
|
6177
7342
|
const updated = {
|
|
6178
7343
|
...record,
|
|
6179
7344
|
undoneAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6180
7345
|
};
|
|
6181
|
-
await
|
|
7346
|
+
await fs18.writeFile(this.getMetadataPath(updated.id), `${JSON.stringify(updated, null, 2)}
|
|
6182
7347
|
`, "utf8");
|
|
6183
7348
|
return {
|
|
6184
7349
|
record: updated,
|
|
@@ -6186,24 +7351,24 @@ var ChangeStore = class {
|
|
|
6186
7351
|
};
|
|
6187
7352
|
}
|
|
6188
7353
|
getMetadataPath(id) {
|
|
6189
|
-
return
|
|
7354
|
+
return path18.join(this.changesDir, `${id}.json`);
|
|
6190
7355
|
}
|
|
6191
7356
|
async writeSnapshot(blobDir, label, buffer) {
|
|
6192
7357
|
if (!buffer) {
|
|
6193
7358
|
return void 0;
|
|
6194
7359
|
}
|
|
6195
7360
|
const fileName = `${label}.bin`;
|
|
6196
|
-
const absolutePath =
|
|
6197
|
-
await
|
|
6198
|
-
return
|
|
7361
|
+
const absolutePath = path18.join(blobDir, fileName);
|
|
7362
|
+
await fs18.writeFile(absolutePath, buffer);
|
|
7363
|
+
return path18.relative(this.changesDir, absolutePath);
|
|
6199
7364
|
}
|
|
6200
7365
|
async readSnapshot(relativePath) {
|
|
6201
|
-
return
|
|
7366
|
+
return fs18.readFile(path18.join(this.changesDir, relativePath));
|
|
6202
7367
|
}
|
|
6203
7368
|
};
|
|
6204
7369
|
function createChangeId() {
|
|
6205
7370
|
const date = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
|
|
6206
|
-
const random =
|
|
7371
|
+
const random = crypto3.randomUUID().slice(0, 8);
|
|
6207
7372
|
return `${date}-${random}`;
|
|
6208
7373
|
}
|
|
6209
7374
|
|
|
@@ -6777,20 +7942,20 @@ function createBackgroundTools() {
|
|
|
6777
7942
|
}
|
|
6778
7943
|
|
|
6779
7944
|
// src/extensions/tools/network/tools/downloadUrl.ts
|
|
6780
|
-
import
|
|
7945
|
+
import fs20 from "fs/promises";
|
|
6781
7946
|
|
|
6782
7947
|
// src/extensions/shared.ts
|
|
6783
|
-
import
|
|
6784
|
-
import
|
|
7948
|
+
import fs19 from "fs/promises";
|
|
7949
|
+
import path19 from "path";
|
|
6785
7950
|
async function ensureExtensionDir(rootDir, extensionId) {
|
|
6786
7951
|
const paths = await ensureProjectStateDirectories(rootDir);
|
|
6787
|
-
const dir =
|
|
6788
|
-
await
|
|
7952
|
+
const dir = path19.join(paths.extensionsDir, extensionId);
|
|
7953
|
+
await fs19.mkdir(dir, { recursive: true });
|
|
6789
7954
|
return dir;
|
|
6790
7955
|
}
|
|
6791
7956
|
async function readJsonFile(filePath, fallback) {
|
|
6792
7957
|
try {
|
|
6793
|
-
return JSON.parse(await
|
|
7958
|
+
return JSON.parse(await fs19.readFile(filePath, "utf8"));
|
|
6794
7959
|
} catch (error) {
|
|
6795
7960
|
if (error.code === "ENOENT") {
|
|
6796
7961
|
return fallback;
|
|
@@ -6799,8 +7964,8 @@ async function readJsonFile(filePath, fallback) {
|
|
|
6799
7964
|
}
|
|
6800
7965
|
}
|
|
6801
7966
|
async function writeJsonFile(filePath, value) {
|
|
6802
|
-
await
|
|
6803
|
-
await
|
|
7967
|
+
await fs19.mkdir(path19.dirname(filePath), { recursive: true });
|
|
7968
|
+
await fs19.writeFile(filePath, `${JSON.stringify(value, null, 2)}
|
|
6804
7969
|
`, "utf8");
|
|
6805
7970
|
}
|
|
6806
7971
|
function jsonResult(value) {
|
|
@@ -6816,7 +7981,7 @@ function sanitizeStateSegment(value) {
|
|
|
6816
7981
|
}
|
|
6817
7982
|
|
|
6818
7983
|
// src/extensions/tools/network/session.ts
|
|
6819
|
-
import
|
|
7984
|
+
import path20 from "path";
|
|
6820
7985
|
async function listHttpSessions(rootDir) {
|
|
6821
7986
|
const state = await readJsonFile(await sessionFile(rootDir), { sessions: [] });
|
|
6822
7987
|
return Array.isArray(state.sessions) ? state.sessions.map(normalizeSession) : [];
|
|
@@ -6841,7 +8006,7 @@ async function getHttpSessionStateFile(rootDir) {
|
|
|
6841
8006
|
return sessionFile(rootDir);
|
|
6842
8007
|
}
|
|
6843
8008
|
async function sessionFile(rootDir) {
|
|
6844
|
-
return
|
|
8009
|
+
return path20.join(await ensureExtensionDir(rootDir, "network"), "http-sessions.json");
|
|
6845
8010
|
}
|
|
6846
8011
|
function normalizeSession(value) {
|
|
6847
8012
|
return {
|
|
@@ -7070,7 +8235,7 @@ var downloadUrlTool = {
|
|
|
7070
8235
|
}
|
|
7071
8236
|
const bytes = Buffer.from(await response.arrayBuffer());
|
|
7072
8237
|
await ensureParentDirectory(targetPath);
|
|
7073
|
-
await
|
|
8238
|
+
await fs20.writeFile(targetPath, bytes);
|
|
7074
8239
|
return changedJsonResult({
|
|
7075
8240
|
ok: response.ok,
|
|
7076
8241
|
url,
|
|
@@ -7135,17 +8300,17 @@ var httpProbeTool = {
|
|
|
7135
8300
|
};
|
|
7136
8301
|
|
|
7137
8302
|
// src/extensions/tools/network/traceStore.ts
|
|
7138
|
-
import
|
|
7139
|
-
import
|
|
8303
|
+
import fs21 from "fs/promises";
|
|
8304
|
+
import path21 from "path";
|
|
7140
8305
|
async function writeNetworkTrace(rootDir, traceId, record) {
|
|
7141
8306
|
const filePath = await networkTraceFilePath(rootDir, traceId);
|
|
7142
|
-
await
|
|
7143
|
-
await
|
|
8307
|
+
await fs21.mkdir(path21.dirname(filePath), { recursive: true });
|
|
8308
|
+
await fs21.writeFile(filePath, `${JSON.stringify(record, null, 2)}
|
|
7144
8309
|
`, "utf8");
|
|
7145
8310
|
return filePath;
|
|
7146
8311
|
}
|
|
7147
8312
|
async function networkTraceFilePath(rootDir, traceId) {
|
|
7148
|
-
return
|
|
8313
|
+
return path21.join(await ensureExtensionDir(rootDir, "network"), "traces", `${sanitizeStateSegment(traceId)}.json`);
|
|
7149
8314
|
}
|
|
7150
8315
|
|
|
7151
8316
|
// src/extensions/tools/network/tools/httpRequest.ts
|
|
@@ -7545,14 +8710,14 @@ function readStringMap2(value) {
|
|
|
7545
8710
|
}
|
|
7546
8711
|
|
|
7547
8712
|
// src/extensions/tools/network/openapi.ts
|
|
7548
|
-
import
|
|
8713
|
+
import fs22 from "fs/promises";
|
|
7549
8714
|
async function loadOpenApiDocument(source, context) {
|
|
7550
8715
|
const normalizedSource = source.trim();
|
|
7551
8716
|
if (!normalizedSource) {
|
|
7552
8717
|
throw new ToolExecutionError("OpenAPI source is required.", { code: "OPENAPI_SOURCE_INVALID" });
|
|
7553
8718
|
}
|
|
7554
8719
|
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
|
|
8720
|
+
const raw = /^https?:\/\//i.test(normalizedSource) ? await (await fetchWithTimeout(normalizedSource, { method: "GET" }, 2e4, context.abortSignal)).text() : stripBom(await fs22.readFile(resolvedSource, "utf8"));
|
|
7556
8721
|
const parsed = parseOpenApiDocument(raw, normalizedSource);
|
|
7557
8722
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
7558
8723
|
throw new ToolExecutionError("OpenAPI document root must be an object.", { code: "OPENAPI_ROOT_INVALID" });
|
|
@@ -7900,8 +9065,8 @@ async function recordSkillUse(rootDir, input) {
|
|
|
7900
9065
|
}
|
|
7901
9066
|
|
|
7902
9067
|
// src/extensions/tools/skills/tools/skillReadResource.ts
|
|
7903
|
-
import
|
|
7904
|
-
import
|
|
9068
|
+
import fs23 from "fs/promises";
|
|
9069
|
+
import path22 from "path";
|
|
7905
9070
|
var MAX_RESOURCE_CHARS = 24e3;
|
|
7906
9071
|
var skillReadResourceTool = {
|
|
7907
9072
|
definition: {
|
|
@@ -7938,8 +9103,8 @@ var skillReadResourceTool = {
|
|
|
7938
9103
|
if (!resource) {
|
|
7939
9104
|
throw new Error(`Skill "${name}" does not declare resource: ${requestedPath}`);
|
|
7940
9105
|
}
|
|
7941
|
-
const absolutePath =
|
|
7942
|
-
const content = await
|
|
9106
|
+
const absolutePath = path22.resolve(context.projectContext.rootDir, resource.path);
|
|
9107
|
+
const content = await fs23.readFile(absolutePath, "utf8");
|
|
7943
9108
|
return jsonResult({
|
|
7944
9109
|
ok: true,
|
|
7945
9110
|
skill: {
|
|
@@ -7957,7 +9122,7 @@ function normalizeResourcePath(value) {
|
|
|
7957
9122
|
}
|
|
7958
9123
|
|
|
7959
9124
|
// src/extensions/tools/skills/tools/skillRunScript.ts
|
|
7960
|
-
import
|
|
9125
|
+
import path23 from "path";
|
|
7961
9126
|
var skillRunScriptTool = {
|
|
7962
9127
|
definition: {
|
|
7963
9128
|
type: "function",
|
|
@@ -8001,12 +9166,12 @@ var skillRunScriptTool = {
|
|
|
8001
9166
|
if (!resource) {
|
|
8002
9167
|
throw new Error(`Skill "${name}" does not declare script resource: ${requestedPath}`);
|
|
8003
9168
|
}
|
|
8004
|
-
const skillDir =
|
|
8005
|
-
const relativeToSkill = normalizeResourcePath2(
|
|
9169
|
+
const skillDir = path23.dirname(skill.path);
|
|
9170
|
+
const relativeToSkill = normalizeResourcePath2(path23.relative(skillDir, resource.path));
|
|
8006
9171
|
if (!relativeToSkill.startsWith("scripts/")) {
|
|
8007
9172
|
throw new Error(`Skill "${name}" resource is not executable because it is outside scripts/: ${requestedPath}`);
|
|
8008
9173
|
}
|
|
8009
|
-
const scriptPath =
|
|
9174
|
+
const scriptPath = path23.resolve(context.projectContext.rootDir, resource.path);
|
|
8010
9175
|
const argumentText = typeof args.args === "string" ? args.args.trim() : "";
|
|
8011
9176
|
const command = buildScriptCommand(scriptPath, argumentText);
|
|
8012
9177
|
const result = await runCommandWithPolicy({
|
|
@@ -8081,7 +9246,7 @@ function quotePath(value) {
|
|
|
8081
9246
|
return `"${value.replace(/"/g, '\\"')}"`;
|
|
8082
9247
|
}
|
|
8083
9248
|
function buildScriptCommand(scriptPath, argumentText) {
|
|
8084
|
-
const extension =
|
|
9249
|
+
const extension = path23.extname(scriptPath).toLowerCase();
|
|
8085
9250
|
const quoted = quotePath(scriptPath);
|
|
8086
9251
|
const suffix = argumentText ? ` ${argumentText}` : "";
|
|
8087
9252
|
if (extension === ".js" || extension === ".mjs" || extension === ".cjs") {
|
|
@@ -8135,12 +9300,12 @@ var subagentCheckTool = {
|
|
|
8135
9300
|
|
|
8136
9301
|
// src/execution/launch.ts
|
|
8137
9302
|
import { spawn } from "child_process";
|
|
8138
|
-
import
|
|
9303
|
+
import path24 from "path";
|
|
8139
9304
|
function spawnExecutionWorker(input) {
|
|
8140
9305
|
if (process.env.KITTY_TEST_WORKER_MODE === "stub") {
|
|
8141
9306
|
return process.pid;
|
|
8142
9307
|
}
|
|
8143
|
-
const cliEntry =
|
|
9308
|
+
const cliEntry = path24.resolve(process.argv[1] ?? "");
|
|
8144
9309
|
if (!cliEntry) {
|
|
8145
9310
|
throw new Error("Unable to locate Kitty CLI entrypoint for execution worker.");
|
|
8146
9311
|
}
|
|
@@ -8346,7 +9511,7 @@ function parseWorktreeBlock(block) {
|
|
|
8346
9511
|
}
|
|
8347
9512
|
|
|
8348
9513
|
// src/extensions/tools/worktree/state.ts
|
|
8349
|
-
import
|
|
9514
|
+
import path25 from "path";
|
|
8350
9515
|
async function readWorktreeState(rootDir) {
|
|
8351
9516
|
return normalizeWorktreeState(await readJsonFile(await stateFile(rootDir), {
|
|
8352
9517
|
schemaVersion: 1,
|
|
@@ -8369,7 +9534,7 @@ async function recordWorktreeEvent(rootDir, event) {
|
|
|
8369
9534
|
return writeWorktreeState(rootDir, state);
|
|
8370
9535
|
}
|
|
8371
9536
|
async function stateFile(rootDir) {
|
|
8372
|
-
return
|
|
9537
|
+
return path25.join(await ensureExtensionDir(rootDir, "worktree"), "state.json");
|
|
8373
9538
|
}
|
|
8374
9539
|
function normalizeWorktreeState(value) {
|
|
8375
9540
|
return {
|
|
@@ -8992,8 +10157,8 @@ function looksLikeToolProtocolText(content) {
|
|
|
8992
10157
|
}
|
|
8993
10158
|
|
|
8994
10159
|
// src/observability/crashRecorder.ts
|
|
8995
|
-
import
|
|
8996
|
-
import
|
|
10160
|
+
import fs24 from "fs";
|
|
10161
|
+
import path26 from "path";
|
|
8997
10162
|
var activeCrashContexts = /* @__PURE__ */ new Map();
|
|
8998
10163
|
var nextCrashContextId = 0;
|
|
8999
10164
|
function enterCrashContext(context) {
|
|
@@ -9005,7 +10170,7 @@ function enterCrashContext(context) {
|
|
|
9005
10170
|
}
|
|
9006
10171
|
|
|
9007
10172
|
// src/observability/hostEvents.ts
|
|
9008
|
-
import
|
|
10173
|
+
import path27 from "path";
|
|
9009
10174
|
async function recordHostTurnStarted(rootDir, input) {
|
|
9010
10175
|
await recordObservabilityEvent(rootDir, {
|
|
9011
10176
|
event: "host.turn",
|
|
@@ -9356,7 +10521,7 @@ function tryParseJson(input) {
|
|
|
9356
10521
|
}
|
|
9357
10522
|
|
|
9358
10523
|
// src/runtime-ui/pathDisplay.ts
|
|
9359
|
-
import
|
|
10524
|
+
import path28 from "path";
|
|
9360
10525
|
function normalizeDisplayPath(value, cwd) {
|
|
9361
10526
|
if (!value) {
|
|
9362
10527
|
return value;
|
|
@@ -9364,10 +10529,10 @@ function normalizeDisplayPath(value, cwd) {
|
|
|
9364
10529
|
if (!cwd) {
|
|
9365
10530
|
return value;
|
|
9366
10531
|
}
|
|
9367
|
-
const normalizedCwd =
|
|
9368
|
-
const normalizedValue =
|
|
9369
|
-
if (normalizedValue === normalizedCwd || normalizedValue.startsWith(`${normalizedCwd}${
|
|
9370
|
-
return
|
|
10532
|
+
const normalizedCwd = path28.resolve(cwd);
|
|
10533
|
+
const normalizedValue = path28.resolve(value);
|
|
10534
|
+
if (normalizedValue === normalizedCwd || normalizedValue.startsWith(`${normalizedCwd}${path28.sep}`)) {
|
|
10535
|
+
return path28.relative(normalizedCwd, normalizedValue) || ".";
|
|
9371
10536
|
}
|
|
9372
10537
|
return value;
|
|
9373
10538
|
}
|
|
@@ -9375,7 +10540,7 @@ function rewriteAbsolutePaths(value, cwd) {
|
|
|
9375
10540
|
if (!cwd) {
|
|
9376
10541
|
return value;
|
|
9377
10542
|
}
|
|
9378
|
-
const normalizedCwd =
|
|
10543
|
+
const normalizedCwd = path28.resolve(cwd).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9379
10544
|
const pattern = new RegExp(`${normalizedCwd}(?:\\\\[^\\s"']*|/[^\\s"']*)*`, "g");
|
|
9380
10545
|
return value.replace(pattern, (match) => normalizeDisplayPath(match, cwd) ?? match);
|
|
9381
10546
|
}
|
|
@@ -9435,24 +10600,24 @@ function buildToolCallDisplay(name, rawArgs, maxChars, cwd) {
|
|
|
9435
10600
|
};
|
|
9436
10601
|
}
|
|
9437
10602
|
const args = parsed;
|
|
9438
|
-
const
|
|
10603
|
+
const path29 = normalizeDisplayPath(readStringField(args, "path"), cwd);
|
|
9439
10604
|
switch (name) {
|
|
9440
10605
|
case "read": {
|
|
9441
10606
|
const offset = typeof args.offset === "number" ? Math.trunc(args.offset) : void 0;
|
|
9442
10607
|
const limit = typeof args.limit === "number" ? Math.trunc(args.limit) : void 0;
|
|
9443
10608
|
const range = offset === void 0 ? "" : limit === void 0 ? `:${offset}` : `:${offset}-${Math.max(offset, offset + limit - 1)}`;
|
|
9444
10609
|
return {
|
|
9445
|
-
summary: `${name} ${
|
|
10610
|
+
summary: `${name} ${path29 ?? "(missing path)"}${range}`
|
|
9446
10611
|
};
|
|
9447
10612
|
}
|
|
9448
10613
|
case "write":
|
|
9449
10614
|
return {
|
|
9450
|
-
summary: `${name} ${
|
|
10615
|
+
summary: `${name} ${path29 ?? "(missing path)"}`
|
|
9451
10616
|
};
|
|
9452
10617
|
case "edit": {
|
|
9453
10618
|
const edits = Array.isArray(args.edits) ? args.edits : [];
|
|
9454
10619
|
return {
|
|
9455
|
-
summary: `${name} ${
|
|
10620
|
+
summary: `${name} ${path29 ?? "(missing path)"}` + (edits.length > 0 ? ` edits=${edits.length}` : "")
|
|
9456
10621
|
};
|
|
9457
10622
|
}
|
|
9458
10623
|
case "bash": {
|
|
@@ -9464,7 +10629,7 @@ function buildToolCallDisplay(name, rawArgs, maxChars, cwd) {
|
|
|
9464
10629
|
}
|
|
9465
10630
|
case "download_url":
|
|
9466
10631
|
return {
|
|
9467
|
-
summary: `${name} ${readStringField(args, "url") ?? "(missing url)"} -> ${
|
|
10632
|
+
summary: `${name} ${readStringField(args, "url") ?? "(missing url)"} -> ${path29 ?? "(missing path)"}`
|
|
9468
10633
|
};
|
|
9469
10634
|
case "http_probe": {
|
|
9470
10635
|
const method = readStringField(args, "method") ?? "HEAD";
|
|
@@ -9505,14 +10670,14 @@ function buildToolCallDisplay(name, rawArgs, maxChars, cwd) {
|
|
|
9505
10670
|
case "worktree_create": {
|
|
9506
10671
|
const branch = readStringField(args, "branch");
|
|
9507
10672
|
return {
|
|
9508
|
-
summary: `${name} ${
|
|
10673
|
+
summary: `${name} ${path29 ?? "(missing path)"}${branch ? ` branch=${branch}` : ""}`
|
|
9509
10674
|
};
|
|
9510
10675
|
}
|
|
9511
10676
|
case "worktree_get":
|
|
9512
10677
|
case "worktree_keep":
|
|
9513
10678
|
case "worktree_remove":
|
|
9514
10679
|
return {
|
|
9515
|
-
summary: `${name} ${
|
|
10680
|
+
summary: `${name} ${path29 ?? "(missing path)"}`
|
|
9516
10681
|
};
|
|
9517
10682
|
case "worktree_events": {
|
|
9518
10683
|
const limit = typeof args.limit === "number" ? Math.trunc(args.limit) : void 0;
|
|
@@ -9651,7 +10816,7 @@ function colorizeMarker(marker) {
|
|
|
9651
10816
|
}
|
|
9652
10817
|
|
|
9653
10818
|
// src/utils/stdio.ts
|
|
9654
|
-
import
|
|
10819
|
+
import fs25 from "fs";
|
|
9655
10820
|
var stdoutBroken = false;
|
|
9656
10821
|
var stderrBroken = false;
|
|
9657
10822
|
function writeStdout(text) {
|
|
@@ -9677,7 +10842,7 @@ function writeToFd(fd, text, stream) {
|
|
|
9677
10842
|
if (target.isTTY) {
|
|
9678
10843
|
return target.write(text);
|
|
9679
10844
|
}
|
|
9680
|
-
|
|
10845
|
+
fs25.writeSync(fd, text, void 0, "utf8");
|
|
9681
10846
|
return true;
|
|
9682
10847
|
} catch (error) {
|
|
9683
10848
|
if (isIgnorableStreamError(error)) {
|
|
@@ -9960,6 +11125,12 @@ function formatRuntimeUiMessage(prefix, summary, detail) {
|
|
|
9960
11125
|
export {
|
|
9961
11126
|
resolveProjectRoots,
|
|
9962
11127
|
buildProjectMap,
|
|
11128
|
+
PROJECT_STATE_DIR_NAME,
|
|
11129
|
+
PROJECT_STATE_ENV_FILE_NAME,
|
|
11130
|
+
PROJECT_STATE_ENV_EXAMPLE_FILE_NAME,
|
|
11131
|
+
PROJECT_STATE_IGNORE_FILE_NAME,
|
|
11132
|
+
PRESERVED_PROJECT_STATE_ENTRY_NAMES,
|
|
11133
|
+
getProjectStatePaths,
|
|
9963
11134
|
loadProjectContext,
|
|
9964
11135
|
getErrorMessage,
|
|
9965
11136
|
ControlPlaneLedger,
|