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