@jun133/kitty 0.0.13 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -133
- package/dist/{App-V6SLDWQH.mjs → App-CBTIS4IK.mjs} +30 -18
- package/dist/{chunk-MMIH75OY.mjs → chunk-7FDXKNTM.mjs} +1612 -767
- package/dist/{chunk-DWGFLIQA.mjs → chunk-AQEMM5VH.mjs} +1 -1
- package/dist/{chunk-4BN45TQG.mjs → chunk-KUP5OMPB.mjs} +1 -1
- package/dist/{chunk-4HIVDFN5.mjs → chunk-NBKU7KA4.mjs} +1 -12
- package/dist/{chunk-3KMC6H5K.mjs → chunk-S4QTRPZ7.mjs} +238 -1221
- package/dist/{chunk-6WGSABUQ.mjs → chunk-WIKLME2V.mjs} +82 -14
- package/dist/cli.js +1477 -1438
- package/dist/cli.js.map +1 -1
- package/dist/{interactive-RSJ35TB5.mjs → interactive-BQMJFYHZ.mjs} +5 -5
- package/dist/{oneShot-7P5FDWFX.mjs → oneShot-4QBXBCNN.mjs} +3 -3
- package/dist/{session-XKWJHRVY.mjs → session-V7AYOK2Q.mjs} +2 -2
- package/dist/tui.mjs +106 -97
- package/package.json +1 -1
|
@@ -1,27 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
|
-
PROJECT_STATE_DIR_NAME,
|
|
3
|
-
PROJECT_STATE_IGNORE_FILE_NAME,
|
|
4
2
|
applyCurrentTurnFrame,
|
|
5
3
|
buildRunTurnResult,
|
|
4
|
+
collapseContentParts,
|
|
6
5
|
createEmptyAssistantResponseTransition,
|
|
7
6
|
createExecutionWaitYieldTransition,
|
|
8
7
|
createFinalizeTransition,
|
|
9
8
|
createInternalReminder,
|
|
10
9
|
createMessage,
|
|
11
|
-
createProviderClientPool,
|
|
12
10
|
createProviderRecoveryTransition,
|
|
13
11
|
createToolMessage,
|
|
14
|
-
ensureProjectStateDirectories,
|
|
15
12
|
expandStartToToolBoundary,
|
|
16
|
-
fetchAssistantResponse,
|
|
17
13
|
fingerprintFocus,
|
|
18
14
|
formatSessionMemorySectionList,
|
|
19
15
|
formatSessionMemorySectionTemplate,
|
|
20
16
|
formatTodoBlock,
|
|
21
|
-
getProjectStatePaths,
|
|
22
|
-
isAbortError,
|
|
23
17
|
isInternalMessage,
|
|
24
|
-
isRetryableApiError,
|
|
25
18
|
normalizeCheckpoint,
|
|
26
19
|
normalizeText,
|
|
27
20
|
normalizeTodoItems,
|
|
@@ -31,20 +24,20 @@ import {
|
|
|
31
24
|
noteCheckpointTransition,
|
|
32
25
|
noteCheckpointTurnInput,
|
|
33
26
|
noteSessionDiff,
|
|
27
|
+
readReasoningContent,
|
|
34
28
|
readUserInput,
|
|
35
|
-
recordObservabilityEvent,
|
|
36
29
|
recordSessionWorksetFile,
|
|
30
|
+
resolveModelProfile,
|
|
31
|
+
resolveProviderCapabilities,
|
|
37
32
|
shouldIncludeStoredAssistantReasoning,
|
|
38
|
-
sleepWithSignal,
|
|
39
33
|
sliceCurrentUserInputFrame,
|
|
40
34
|
takeLastUnique,
|
|
41
|
-
throwIfAborted,
|
|
42
35
|
updateSessionMemory
|
|
43
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-S4QTRPZ7.mjs";
|
|
44
37
|
|
|
45
38
|
// src/context/projectContext.ts
|
|
46
|
-
import
|
|
47
|
-
import
|
|
39
|
+
import fs6 from "fs/promises";
|
|
40
|
+
import path7 from "path";
|
|
48
41
|
|
|
49
42
|
// src/context/repoRoots.ts
|
|
50
43
|
import fs from "fs/promises";
|
|
@@ -248,13 +241,67 @@ function formatList(values) {
|
|
|
248
241
|
}
|
|
249
242
|
|
|
250
243
|
// src/skills/discovery.ts
|
|
251
|
-
import
|
|
252
|
-
import
|
|
244
|
+
import fs5 from "fs/promises";
|
|
245
|
+
import path6 from "path";
|
|
253
246
|
import fg2 from "fast-glob";
|
|
254
247
|
|
|
255
248
|
// src/utils/ignore.ts
|
|
249
|
+
import fs4 from "fs/promises";
|
|
250
|
+
import path4 from "path";
|
|
251
|
+
|
|
252
|
+
// src/project/statePaths.ts
|
|
256
253
|
import fs3 from "fs/promises";
|
|
257
254
|
import path3 from "path";
|
|
255
|
+
var PROJECT_STATE_DIR_NAME = ".kitty";
|
|
256
|
+
var PROJECT_STATE_ENV_FILE_NAME = ".env";
|
|
257
|
+
var PROJECT_STATE_ENV_EXAMPLE_FILE_NAME = ".env.example";
|
|
258
|
+
var PROJECT_STATE_IGNORE_FILE_NAME = ".kittyignore";
|
|
259
|
+
var PRESERVED_PROJECT_STATE_ENTRY_NAMES = [
|
|
260
|
+
PROJECT_STATE_ENV_FILE_NAME,
|
|
261
|
+
PROJECT_STATE_ENV_EXAMPLE_FILE_NAME
|
|
262
|
+
];
|
|
263
|
+
function getProjectStatePaths(rootDir) {
|
|
264
|
+
const normalizedRoot = path3.resolve(rootDir);
|
|
265
|
+
const kittyDir = path3.join(normalizedRoot, PROJECT_STATE_DIR_NAME);
|
|
266
|
+
const extensionsDir = path3.join(kittyDir, "extensions");
|
|
267
|
+
const memoryDir = path3.join(kittyDir, "memory");
|
|
268
|
+
const observabilityDir = path3.join(kittyDir, "observability");
|
|
269
|
+
return {
|
|
270
|
+
rootDir: normalizedRoot,
|
|
271
|
+
kittyDir,
|
|
272
|
+
cacheDir: path3.join(kittyDir, "cache"),
|
|
273
|
+
sessionsDir: path3.join(kittyDir, "sessions"),
|
|
274
|
+
changesDir: path3.join(kittyDir, "changes"),
|
|
275
|
+
eventsDir: path3.join(kittyDir, "events"),
|
|
276
|
+
extensionsDir,
|
|
277
|
+
memoryDir,
|
|
278
|
+
evidenceMemoryDir: path3.join(memoryDir, "evidence"),
|
|
279
|
+
projectMemoryDir: path3.join(memoryDir, "project"),
|
|
280
|
+
sessionMemoryDir: path3.join(memoryDir, "sessions"),
|
|
281
|
+
userMemoryDir: path3.join(memoryDir, "user"),
|
|
282
|
+
controlPlaneLedgerFile: path3.join(kittyDir, "control-plane.sqlite"),
|
|
283
|
+
observabilityDir,
|
|
284
|
+
observabilityEventsDir: path3.join(observabilityDir, "events"),
|
|
285
|
+
observabilityCrashesDir: path3.join(observabilityDir, "crashes")
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
async function ensureProjectStateDirectories(rootDir) {
|
|
289
|
+
const paths = getProjectStatePaths(rootDir);
|
|
290
|
+
await fs3.mkdir(paths.extensionsDir, { recursive: true });
|
|
291
|
+
await fs3.mkdir(paths.cacheDir, { recursive: true });
|
|
292
|
+
await fs3.mkdir(paths.sessionsDir, { recursive: true });
|
|
293
|
+
await fs3.mkdir(paths.changesDir, { recursive: true });
|
|
294
|
+
await fs3.mkdir(paths.eventsDir, { recursive: true });
|
|
295
|
+
await fs3.mkdir(paths.evidenceMemoryDir, { recursive: true });
|
|
296
|
+
await fs3.mkdir(paths.projectMemoryDir, { recursive: true });
|
|
297
|
+
await fs3.mkdir(paths.sessionMemoryDir, { recursive: true });
|
|
298
|
+
await fs3.mkdir(paths.userMemoryDir, { recursive: true });
|
|
299
|
+
await fs3.mkdir(paths.observabilityEventsDir, { recursive: true });
|
|
300
|
+
await fs3.mkdir(paths.observabilityCrashesDir, { recursive: true });
|
|
301
|
+
return paths;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// src/utils/ignore.ts
|
|
258
305
|
var BUILTIN_PATTERNS = [
|
|
259
306
|
".git/",
|
|
260
307
|
"node_modules/",
|
|
@@ -268,11 +315,11 @@ async function loadProjectIgnoreRules(rootDir, cwd) {
|
|
|
268
315
|
})).filter((rule) => Boolean(rule));
|
|
269
316
|
const candidateFiles = uniqueIgnoreFiles([
|
|
270
317
|
{
|
|
271
|
-
path:
|
|
318
|
+
path: path4.join(rootDir, PROJECT_STATE_DIR_NAME, PROJECT_STATE_IGNORE_FILE_NAME),
|
|
272
319
|
baseDir: rootDir
|
|
273
320
|
},
|
|
274
321
|
{
|
|
275
|
-
path:
|
|
322
|
+
path: path4.join(cwd, PROJECT_STATE_DIR_NAME, PROJECT_STATE_IGNORE_FILE_NAME),
|
|
276
323
|
baseDir: cwd
|
|
277
324
|
}
|
|
278
325
|
]);
|
|
@@ -332,7 +379,7 @@ function buildFastGlobIgnorePatterns(baseDir, rules) {
|
|
|
332
379
|
}
|
|
333
380
|
async function tryReadUtf8File(filePath) {
|
|
334
381
|
try {
|
|
335
|
-
return await
|
|
382
|
+
return await fs4.readFile(filePath, "utf8");
|
|
336
383
|
} catch (error) {
|
|
337
384
|
const errno = error;
|
|
338
385
|
if (errno.code === "ENOENT") {
|
|
@@ -383,11 +430,11 @@ function compileIgnoreRule(rawPattern, options) {
|
|
|
383
430
|
};
|
|
384
431
|
}
|
|
385
432
|
function toRelativePosix(baseDir, targetPath) {
|
|
386
|
-
const relativePath =
|
|
433
|
+
const relativePath = path4.relative(path4.resolve(baseDir), path4.resolve(targetPath));
|
|
387
434
|
if (!relativePath || relativePath === ".") {
|
|
388
435
|
return "";
|
|
389
436
|
}
|
|
390
|
-
if (relativePath.startsWith("..") ||
|
|
437
|
+
if (relativePath.startsWith("..") || path4.isAbsolute(relativePath)) {
|
|
391
438
|
return null;
|
|
392
439
|
}
|
|
393
440
|
return relativePath.replace(/\\/g, "/");
|
|
@@ -453,21 +500,21 @@ function uniqueIgnoreFiles(files) {
|
|
|
453
500
|
const seen = /* @__PURE__ */ new Set();
|
|
454
501
|
const unique = [];
|
|
455
502
|
for (const file of files) {
|
|
456
|
-
const normalizedPath =
|
|
503
|
+
const normalizedPath = path4.normalize(file.path);
|
|
457
504
|
if (seen.has(normalizedPath)) {
|
|
458
505
|
continue;
|
|
459
506
|
}
|
|
460
507
|
seen.add(normalizedPath);
|
|
461
508
|
unique.push({
|
|
462
509
|
path: normalizedPath,
|
|
463
|
-
baseDir:
|
|
510
|
+
baseDir: path4.resolve(file.baseDir)
|
|
464
511
|
});
|
|
465
512
|
}
|
|
466
513
|
return unique;
|
|
467
514
|
}
|
|
468
515
|
|
|
469
516
|
// src/skills/schema.ts
|
|
470
|
-
import
|
|
517
|
+
import path5 from "path";
|
|
471
518
|
var FRONTMATTER_PATTERN = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
472
519
|
var SkillSchemaError = class extends Error {
|
|
473
520
|
constructor(message, filePath) {
|
|
@@ -487,7 +534,7 @@ function parseSkillSource(text, input) {
|
|
|
487
534
|
return {
|
|
488
535
|
name,
|
|
489
536
|
description,
|
|
490
|
-
path:
|
|
537
|
+
path: path5.relative(input.rootDir, input.absolutePath) || "SKILL.md",
|
|
491
538
|
absolutePath: input.absolutePath,
|
|
492
539
|
body,
|
|
493
540
|
dependencies: parseDependencies(metadata.requires),
|
|
@@ -557,12 +604,12 @@ async function discoverSkills(rootDir, cwd, ignoreRules) {
|
|
|
557
604
|
const seenNames = /* @__PURE__ */ new Map();
|
|
558
605
|
const skills = [];
|
|
559
606
|
for (const skillFile of skillFiles) {
|
|
560
|
-
const normalizedPath =
|
|
607
|
+
const normalizedPath = path6.normalize(skillFile);
|
|
561
608
|
if (seenPaths.has(normalizedPath) || isPathIgnored(normalizedPath, ignoreRules)) {
|
|
562
609
|
continue;
|
|
563
610
|
}
|
|
564
611
|
seenPaths.add(normalizedPath);
|
|
565
|
-
const skill = parseSkillSource(await
|
|
612
|
+
const skill = parseSkillSource(await fs5.readFile(normalizedPath, "utf8"), {
|
|
566
613
|
absolutePath: normalizedPath,
|
|
567
614
|
rootDir
|
|
568
615
|
});
|
|
@@ -578,7 +625,7 @@ async function discoverSkills(rootDir, cwd, ignoreRules) {
|
|
|
578
625
|
return skills.sort((left, right) => left.name.localeCompare(right.name));
|
|
579
626
|
}
|
|
580
627
|
async function listSkillResources(skillPath, rootDir, ignoreRules) {
|
|
581
|
-
const skillDir =
|
|
628
|
+
const skillDir = path6.dirname(skillPath);
|
|
582
629
|
const files = await fg2([...SKILL_RESOURCE_GLOBS], {
|
|
583
630
|
cwd: skillDir,
|
|
584
631
|
absolute: true,
|
|
@@ -592,11 +639,11 @@ async function listSkillResources(skillPath, rootDir, ignoreRules) {
|
|
|
592
639
|
if (isPathIgnored(file, ignoreRules)) {
|
|
593
640
|
continue;
|
|
594
641
|
}
|
|
595
|
-
const stat = await
|
|
642
|
+
const stat = await fs5.stat(file);
|
|
596
643
|
resources.push({
|
|
597
|
-
path:
|
|
644
|
+
path: path6.relative(rootDir, file),
|
|
598
645
|
size: stat.size,
|
|
599
|
-
kind: readSkillResourceKind(
|
|
646
|
+
kind: readSkillResourceKind(path6.relative(skillDir, file))
|
|
600
647
|
});
|
|
601
648
|
}
|
|
602
649
|
return resources;
|
|
@@ -643,14 +690,14 @@ function buildSkillPackageHealth(skill) {
|
|
|
643
690
|
}
|
|
644
691
|
async function findSkillFiles(rootDir, cwd) {
|
|
645
692
|
const candidates = uniquePaths([
|
|
646
|
-
|
|
647
|
-
|
|
693
|
+
path6.join(rootDir, "SKILL.md"),
|
|
694
|
+
path6.join(cwd, "SKILL.md")
|
|
648
695
|
]);
|
|
649
696
|
const roots = uniquePaths([
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
697
|
+
path6.join(rootDir, ".skills"),
|
|
698
|
+
path6.join(rootDir, "skills"),
|
|
699
|
+
path6.join(cwd, ".skills"),
|
|
700
|
+
path6.join(cwd, "skills")
|
|
654
701
|
]);
|
|
655
702
|
const files = [];
|
|
656
703
|
for (const candidate of candidates) {
|
|
@@ -675,20 +722,20 @@ async function findSkillFiles(rootDir, cwd) {
|
|
|
675
722
|
}
|
|
676
723
|
async function isRegularFile(filePath) {
|
|
677
724
|
try {
|
|
678
|
-
return (await
|
|
725
|
+
return (await fs5.stat(filePath)).isFile();
|
|
679
726
|
} catch {
|
|
680
727
|
return false;
|
|
681
728
|
}
|
|
682
729
|
}
|
|
683
730
|
async function isDirectory2(filePath) {
|
|
684
731
|
try {
|
|
685
|
-
return (await
|
|
732
|
+
return (await fs5.stat(filePath)).isDirectory();
|
|
686
733
|
} catch {
|
|
687
734
|
return false;
|
|
688
735
|
}
|
|
689
736
|
}
|
|
690
737
|
function uniquePaths(paths) {
|
|
691
|
-
return [...new Set(paths.map((item) =>
|
|
738
|
+
return [...new Set(paths.map((item) => path6.normalize(item)))];
|
|
692
739
|
}
|
|
693
740
|
|
|
694
741
|
// src/context/projectContext.ts
|
|
@@ -717,8 +764,8 @@ async function getInstructionFiles(rootDir, cwd) {
|
|
|
717
764
|
const directories = getDirectoriesFromRootToCwd(rootDir, cwd);
|
|
718
765
|
const results = [];
|
|
719
766
|
for (const directory of directories) {
|
|
720
|
-
const overridePath =
|
|
721
|
-
const agentsPath =
|
|
767
|
+
const overridePath = path7.join(directory, "AGENTS.override.md");
|
|
768
|
+
const agentsPath = path7.join(directory, "AGENTS.md");
|
|
722
769
|
if (await isRegularFile2(overridePath)) {
|
|
723
770
|
results.push(await readInstructionFile(rootDir, overridePath, "AGENTS.override.md"));
|
|
724
771
|
continue;
|
|
@@ -730,22 +777,22 @@ async function getInstructionFiles(rootDir, cwd) {
|
|
|
730
777
|
return results;
|
|
731
778
|
}
|
|
732
779
|
function getDirectoriesFromRootToCwd(rootDir, cwd) {
|
|
733
|
-
const absoluteRoot =
|
|
734
|
-
const absoluteCwd =
|
|
735
|
-
const relativePath =
|
|
780
|
+
const absoluteRoot = path7.resolve(rootDir);
|
|
781
|
+
const absoluteCwd = path7.resolve(cwd);
|
|
782
|
+
const relativePath = path7.relative(absoluteRoot, absoluteCwd);
|
|
736
783
|
if (!relativePath || relativePath === ".") {
|
|
737
784
|
return [absoluteRoot];
|
|
738
785
|
}
|
|
739
|
-
const parts = relativePath.split(
|
|
786
|
+
const parts = relativePath.split(path7.sep).filter(Boolean);
|
|
740
787
|
const directories = [absoluteRoot];
|
|
741
788
|
for (let index = 0; index < parts.length; index += 1) {
|
|
742
|
-
directories.push(
|
|
789
|
+
directories.push(path7.join(absoluteRoot, ...parts.slice(0, index + 1)));
|
|
743
790
|
}
|
|
744
791
|
return directories;
|
|
745
792
|
}
|
|
746
793
|
async function isRegularFile2(filePath) {
|
|
747
794
|
try {
|
|
748
|
-
const stat = await
|
|
795
|
+
const stat = await fs6.stat(filePath);
|
|
749
796
|
return stat.isFile();
|
|
750
797
|
} catch {
|
|
751
798
|
return false;
|
|
@@ -754,9 +801,9 @@ async function isRegularFile2(filePath) {
|
|
|
754
801
|
async function readInstructionFile(rootDir, absolutePath, filename) {
|
|
755
802
|
return {
|
|
756
803
|
path: absolutePath,
|
|
757
|
-
relativePath:
|
|
804
|
+
relativePath: path7.relative(rootDir, absolutePath) || filename,
|
|
758
805
|
filename,
|
|
759
|
-
content: await
|
|
806
|
+
content: await fs6.readFile(absolutePath, "utf8")
|
|
760
807
|
};
|
|
761
808
|
}
|
|
762
809
|
function concatInstructionFiles(files, maxBytes) {
|
|
@@ -832,7 +879,7 @@ function getErrorMessage(error) {
|
|
|
832
879
|
return "Environment error: network connection failed; the current provider/base URL is unreachable. Check network, proxy settings, or `KITTY_BASE_URL`.";
|
|
833
880
|
}
|
|
834
881
|
if (status === 404 || lower.includes("returned 404")) {
|
|
835
|
-
return "User-fixable error: provider returned 404. Check
|
|
882
|
+
return "User-fixable error: provider endpoint returned 404. Check `KITTY_PROVIDER`, `KITTY_MODEL`, and `KITTY_BASE_URL` as one provider profile; the selected provider may use Responses instead of Chat Completions.";
|
|
836
883
|
}
|
|
837
884
|
if (typeof status === "number" && status >= 500) {
|
|
838
885
|
return `Provider error: service returned ${status}. Retry later or confirm the provider service is healthy.`;
|
|
@@ -884,39 +931,1149 @@ 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/responsesAdapter.ts
|
|
1539
|
+
var responsesAdapter = {
|
|
1540
|
+
wireApi: "responses",
|
|
1541
|
+
async fetchStreaming(client, request) {
|
|
1542
|
+
const startedAt = Date.now();
|
|
1543
|
+
let usage;
|
|
1544
|
+
throwIfAborted(request.abortSignal, "Streaming request aborted");
|
|
1545
|
+
try {
|
|
1546
|
+
const stream = await client.responses.create(
|
|
1547
|
+
{
|
|
1548
|
+
...buildResponsesRequestBody(request),
|
|
1549
|
+
stream: true
|
|
1550
|
+
},
|
|
1551
|
+
{
|
|
1552
|
+
signal: request.abortSignal
|
|
1553
|
+
}
|
|
1554
|
+
);
|
|
1555
|
+
if (request.abortSignal?.aborted) {
|
|
1556
|
+
abortStream2(stream);
|
|
1557
|
+
throw createAbortError("Streaming aborted");
|
|
1558
|
+
}
|
|
1559
|
+
let content = "";
|
|
1560
|
+
let reasoningContent = "";
|
|
1561
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
1562
|
+
for await (const event of stream) {
|
|
1563
|
+
if (request.abortSignal?.aborted) {
|
|
1564
|
+
abortStream2(stream);
|
|
1565
|
+
throw createAbortError("Streaming aborted");
|
|
1566
|
+
}
|
|
1567
|
+
usage = normalizeProviderUsage(event.response?.usage) ?? usage;
|
|
1568
|
+
if (event.type === "response.output_text.delta" && typeof event.delta === "string") {
|
|
1569
|
+
content += event.delta;
|
|
1570
|
+
request.callbacks?.onAssistantDelta?.(event.delta);
|
|
1571
|
+
continue;
|
|
1572
|
+
}
|
|
1573
|
+
if ((event.type === "response.reasoning_text.delta" || event.type === "response.reasoning_summary_text.delta") && typeof event.delta === "string") {
|
|
1574
|
+
reasoningContent += event.delta;
|
|
1575
|
+
request.callbacks?.onReasoningDelta?.(event.delta);
|
|
1576
|
+
continue;
|
|
1577
|
+
}
|
|
1578
|
+
if (event.type === "response.function_call_arguments.delta" && typeof event.delta === "string") {
|
|
1579
|
+
const index = typeof event.output_index === "number" ? event.output_index : 0;
|
|
1580
|
+
const existing = toolCalls.get(index) ?? {
|
|
1581
|
+
id: event.item_id ?? `tool-${index}`,
|
|
1582
|
+
name: "",
|
|
1583
|
+
arguments: ""
|
|
1584
|
+
};
|
|
1585
|
+
existing.arguments += event.delta;
|
|
1586
|
+
toolCalls.set(index, existing);
|
|
1587
|
+
continue;
|
|
1588
|
+
}
|
|
1589
|
+
if (event.type === "response.function_call_arguments.done") {
|
|
1590
|
+
const index = typeof event.output_index === "number" ? event.output_index : 0;
|
|
1591
|
+
const existing = toolCalls.get(index) ?? {
|
|
1592
|
+
id: event.item_id ?? `tool-${index}`,
|
|
1593
|
+
name: "",
|
|
1594
|
+
arguments: ""
|
|
1595
|
+
};
|
|
1596
|
+
if (typeof event.name === "string") {
|
|
1597
|
+
existing.name = event.name;
|
|
1598
|
+
}
|
|
1599
|
+
if (typeof event.arguments === "string" && event.arguments.length > 0) {
|
|
1600
|
+
existing.arguments = event.arguments;
|
|
1601
|
+
}
|
|
1602
|
+
toolCalls.set(index, existing);
|
|
1603
|
+
continue;
|
|
1604
|
+
}
|
|
1605
|
+
if (event.type === "response.output_item.done" && event.item?.type === "function_call") {
|
|
1606
|
+
const index = typeof event.output_index === "number" ? event.output_index : 0;
|
|
1607
|
+
toolCalls.set(index, {
|
|
1608
|
+
id: event.item.call_id ?? event.item.id ?? `tool-${index}`,
|
|
1609
|
+
name: event.item.name ?? "",
|
|
1610
|
+
arguments: event.item.arguments ?? ""
|
|
1611
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
return {
|
|
1615
|
+
content: content.length > 0 ? content : null,
|
|
1616
|
+
reasoningContent: reasoningContent.length > 0 ? reasoningContent : void 0,
|
|
1617
|
+
streamedAssistantContent: content.length > 0,
|
|
1618
|
+
streamedReasoningContent: reasoningContent.length > 0,
|
|
1619
|
+
toolCalls: [...toolCalls.entries()].sort((left, right) => left[0] - right[0]).map(([, toolCall]) => ({
|
|
1620
|
+
id: toolCall.id,
|
|
1621
|
+
type: "function",
|
|
1622
|
+
function: {
|
|
1623
|
+
name: toolCall.name,
|
|
1624
|
+
arguments: toolCall.arguments
|
|
1625
|
+
}
|
|
1626
|
+
}))
|
|
1627
|
+
};
|
|
1628
|
+
} finally {
|
|
1629
|
+
request.onRequestMetric?.({
|
|
1630
|
+
durationMs: Date.now() - startedAt,
|
|
1631
|
+
usage
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1634
|
+
},
|
|
1635
|
+
async fetchNonStreaming(client, request) {
|
|
1636
|
+
const startedAt = Date.now();
|
|
1637
|
+
let usage;
|
|
1638
|
+
throwIfAborted(request.abortSignal, "Request aborted");
|
|
1639
|
+
try {
|
|
1640
|
+
const response = await client.responses.create(
|
|
1641
|
+
{
|
|
1642
|
+
...buildResponsesRequestBody(request),
|
|
1643
|
+
stream: false
|
|
1644
|
+
},
|
|
1645
|
+
{
|
|
1646
|
+
signal: request.abortSignal
|
|
1647
|
+
}
|
|
1648
|
+
);
|
|
1649
|
+
usage = normalizeProviderUsage(response.usage);
|
|
1650
|
+
return {
|
|
1651
|
+
content: normalizeOutputText(response),
|
|
1652
|
+
reasoningContent: readResponseReasoning(response),
|
|
1653
|
+
streamedAssistantContent: false,
|
|
1654
|
+
streamedReasoningContent: false,
|
|
1655
|
+
toolCalls: readResponseToolCalls(response)
|
|
1656
|
+
};
|
|
1657
|
+
} finally {
|
|
1658
|
+
request.onRequestMetric?.({
|
|
1659
|
+
durationMs: Date.now() - startedAt,
|
|
1660
|
+
usage
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
};
|
|
1665
|
+
function buildResponsesRequestBody(request) {
|
|
1666
|
+
const capabilities = resolveProviderCapabilities({
|
|
1667
|
+
provider: request.provider,
|
|
1668
|
+
model: request.model
|
|
1669
|
+
});
|
|
1670
|
+
const body = {
|
|
1671
|
+
model: request.model,
|
|
1672
|
+
input: toResponsesInput(request.messages),
|
|
1673
|
+
tools: request.tools?.map((tool) => ({
|
|
1674
|
+
type: "function",
|
|
1675
|
+
name: tool.function.name,
|
|
1676
|
+
description: tool.function.description,
|
|
1677
|
+
parameters: tool.function.parameters ?? null,
|
|
1678
|
+
strict: false
|
|
1679
|
+
})),
|
|
1680
|
+
tool_choice: request.tools?.length ? "auto" : void 0
|
|
1681
|
+
};
|
|
1682
|
+
if (typeof request.maxOutputTokens === "number" && Number.isFinite(request.maxOutputTokens)) {
|
|
1683
|
+
body.max_output_tokens = Math.max(1, Math.trunc(request.maxOutputTokens));
|
|
1684
|
+
}
|
|
1685
|
+
const cachePolicy = resolveProviderCachePolicy({
|
|
1686
|
+
provider: request.provider,
|
|
1687
|
+
model: request.model,
|
|
1688
|
+
sessionId: request.sessionId,
|
|
1689
|
+
projectRoot: request.projectRoot
|
|
1690
|
+
});
|
|
1691
|
+
if (cachePolicy.promptCacheKey) {
|
|
1692
|
+
body.prompt_cache_key = cachePolicy.promptCacheKey;
|
|
1693
|
+
}
|
|
1694
|
+
const reasoningEffort = request.thinking === "disabled" ? void 0 : normalizeResponsesReasoningEffort(
|
|
1695
|
+
request.reasoningEffort ?? capabilities.defaultReasoningEffort
|
|
1696
|
+
);
|
|
1697
|
+
if (request.thinking !== "disabled" && (request.forceReasoning || capabilities.defaultReasoningEnabled || request.thinking === "enabled" || reasoningEffort)) {
|
|
1698
|
+
body.reasoning = {
|
|
1699
|
+
effort: reasoningEffort ?? "high",
|
|
1700
|
+
summary: "detailed"
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
return body;
|
|
1704
|
+
}
|
|
1705
|
+
function normalizeResponsesReasoningEffort(effort) {
|
|
1706
|
+
if (effort === "xhigh") {
|
|
1707
|
+
return "xhigh";
|
|
1708
|
+
}
|
|
1709
|
+
return effort === "max" ? void 0 : effort;
|
|
1710
|
+
}
|
|
1711
|
+
function toResponsesInput(messages) {
|
|
1712
|
+
const items = [];
|
|
1713
|
+
for (const message of messages) {
|
|
1714
|
+
if (message.role === "tool") {
|
|
1715
|
+
items.push({
|
|
1716
|
+
type: "function_call_output",
|
|
1717
|
+
call_id: message.toolCallId ?? "",
|
|
1718
|
+
output: message.content ?? ""
|
|
1719
|
+
});
|
|
1720
|
+
continue;
|
|
1721
|
+
}
|
|
1722
|
+
if (message.role === "assistant" && message.toolCalls?.length) {
|
|
1723
|
+
if (typeof message.content === "string" && message.content.trim().length > 0) {
|
|
1724
|
+
items.push({
|
|
1725
|
+
type: "message",
|
|
1726
|
+
role: "assistant",
|
|
1727
|
+
content: message.content
|
|
1728
|
+
});
|
|
1729
|
+
}
|
|
1730
|
+
for (const toolCall of message.toolCalls) {
|
|
1731
|
+
items.push({
|
|
1732
|
+
type: "function_call",
|
|
1733
|
+
call_id: toolCall.id,
|
|
1734
|
+
name: toolCall.function.name,
|
|
1735
|
+
arguments: toolCall.function.arguments
|
|
1736
|
+
});
|
|
1737
|
+
}
|
|
1738
|
+
continue;
|
|
1739
|
+
}
|
|
1740
|
+
items.push({
|
|
1741
|
+
type: "message",
|
|
1742
|
+
role: message.role,
|
|
1743
|
+
content: message.content ?? ""
|
|
1744
|
+
});
|
|
1745
|
+
}
|
|
1746
|
+
return items;
|
|
1747
|
+
}
|
|
1748
|
+
function normalizeOutputText(response) {
|
|
1749
|
+
const outputText = response.output_text;
|
|
1750
|
+
if (typeof outputText === "string" && outputText.trim().length > 0) {
|
|
1751
|
+
return outputText;
|
|
1752
|
+
}
|
|
1753
|
+
const output = response.output;
|
|
1754
|
+
if (!Array.isArray(output)) {
|
|
1755
|
+
return null;
|
|
1756
|
+
}
|
|
1757
|
+
const fragments = output.flatMap((item) => {
|
|
1758
|
+
if (!item || typeof item !== "object" || item.type !== "message") {
|
|
1759
|
+
return [];
|
|
1760
|
+
}
|
|
1761
|
+
const content = item.content;
|
|
1762
|
+
if (!Array.isArray(content)) {
|
|
1763
|
+
return [];
|
|
1764
|
+
}
|
|
1765
|
+
return content.flatMap((part) => {
|
|
1766
|
+
if (!part || typeof part !== "object" || part.type !== "output_text") {
|
|
1767
|
+
return [];
|
|
1768
|
+
}
|
|
1769
|
+
return typeof part.text === "string" ? [part.text] : [];
|
|
1770
|
+
});
|
|
1771
|
+
});
|
|
1772
|
+
return fragments.length > 0 ? fragments.join("") : null;
|
|
1773
|
+
}
|
|
1774
|
+
function readResponseToolCalls(response) {
|
|
1775
|
+
const output = response.output;
|
|
1776
|
+
if (!Array.isArray(output)) {
|
|
1777
|
+
return [];
|
|
1778
|
+
}
|
|
1779
|
+
return output.filter((item) => Boolean(item) && typeof item === "object" && item.type === "function_call").map((item) => ({
|
|
1780
|
+
id: item.call_id ?? item.id ?? crypto.randomUUID(),
|
|
1781
|
+
type: "function",
|
|
1782
|
+
function: {
|
|
1783
|
+
name: item.name ?? "",
|
|
1784
|
+
arguments: item.arguments ?? ""
|
|
1785
|
+
}
|
|
1786
|
+
}));
|
|
1787
|
+
}
|
|
1788
|
+
function readResponseReasoning(response) {
|
|
1789
|
+
const output = response.output;
|
|
1790
|
+
if (!Array.isArray(output)) {
|
|
1791
|
+
return void 0;
|
|
1792
|
+
}
|
|
1793
|
+
const fragments = output.flatMap((item) => {
|
|
1794
|
+
if (!item || typeof item !== "object" || item.type !== "reasoning") {
|
|
1795
|
+
return [];
|
|
1796
|
+
}
|
|
1797
|
+
const reasoningItem = item;
|
|
1798
|
+
const summary = Array.isArray(reasoningItem.summary) ? reasoningItem.summary.map((entry) => typeof entry?.text === "string" ? entry.text : "").filter(Boolean) : [];
|
|
1799
|
+
const content = Array.isArray(reasoningItem.content) ? reasoningItem.content.map((entry) => typeof entry?.text === "string" ? entry.text : "").filter(Boolean) : [];
|
|
1800
|
+
return [...content, ...summary];
|
|
1801
|
+
});
|
|
1802
|
+
return fragments.length > 0 ? fragments.join("") : void 0;
|
|
1803
|
+
}
|
|
1804
|
+
function abortStream2(stream) {
|
|
1805
|
+
try {
|
|
1806
|
+
stream?.controller?.abort();
|
|
1807
|
+
} catch {
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
// src/provider/client.ts
|
|
1812
|
+
import OpenAI from "openai";
|
|
1813
|
+
|
|
1814
|
+
// src/provider/connection.ts
|
|
1815
|
+
function buildProviderBaseUrlCandidates(baseUrl) {
|
|
1816
|
+
const normalized = trimTrailingSlash(baseUrl);
|
|
1817
|
+
if (!normalized) {
|
|
1818
|
+
return [normalized];
|
|
1819
|
+
}
|
|
1820
|
+
const candidates = [normalized];
|
|
1821
|
+
try {
|
|
1822
|
+
const parsed = new URL(normalized);
|
|
1823
|
+
if (parsed.pathname === "" || parsed.pathname === "/") {
|
|
1824
|
+
candidates.push(trimTrailingSlash(new URL("v1", ensureTrailingSlash2(parsed.toString())).toString()));
|
|
1825
|
+
}
|
|
1826
|
+
} catch {
|
|
1827
|
+
return candidates;
|
|
1828
|
+
}
|
|
1829
|
+
return [...new Set(candidates)];
|
|
1830
|
+
}
|
|
1831
|
+
function ensureTrailingSlash2(baseUrl) {
|
|
1832
|
+
return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
1833
|
+
}
|
|
1834
|
+
function trimTrailingSlash(baseUrl) {
|
|
1835
|
+
const trimmed = String(baseUrl ?? "").trim();
|
|
1836
|
+
if (!trimmed) {
|
|
1837
|
+
return trimmed;
|
|
1838
|
+
}
|
|
1839
|
+
return trimmed.endsWith("/") ? trimmed.slice(0, -1) : trimmed;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
// src/provider/client.ts
|
|
1843
|
+
function createProviderClientPool(config) {
|
|
1844
|
+
const capabilities = resolveProviderCapabilities({
|
|
1845
|
+
provider: config.provider,
|
|
1846
|
+
model: config.model
|
|
889
1847
|
});
|
|
890
|
-
const
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
transition
|
|
894
|
-
)
|
|
895
|
-
);
|
|
1848
|
+
const baseUrls = buildProviderBaseUrlCandidates(config.baseUrl);
|
|
1849
|
+
const clients = /* @__PURE__ */ new Map();
|
|
1850
|
+
let preferredBaseUrl;
|
|
896
1851
|
return {
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
1852
|
+
candidates() {
|
|
1853
|
+
const ordered = preferredBaseUrl ? [preferredBaseUrl, ...baseUrls.filter((baseUrl) => baseUrl !== preferredBaseUrl)] : baseUrls;
|
|
1854
|
+
return ordered.map((baseUrl) => ({
|
|
1855
|
+
baseUrl,
|
|
1856
|
+
client: getOrCreateClient(baseUrl)
|
|
1857
|
+
}));
|
|
1858
|
+
},
|
|
1859
|
+
markHealthy(baseUrl) {
|
|
1860
|
+
preferredBaseUrl = baseUrl;
|
|
1861
|
+
}
|
|
903
1862
|
};
|
|
1863
|
+
function getOrCreateClient(baseUrl) {
|
|
1864
|
+
const existing = clients.get(baseUrl);
|
|
1865
|
+
if (existing) {
|
|
1866
|
+
return existing;
|
|
1867
|
+
}
|
|
1868
|
+
const client = new OpenAI({
|
|
1869
|
+
apiKey: config.apiKey,
|
|
1870
|
+
baseURL: baseUrl,
|
|
1871
|
+
timeout: capabilities.requestTimeoutMs,
|
|
1872
|
+
maxRetries: 0
|
|
1873
|
+
});
|
|
1874
|
+
clients.set(baseUrl, client);
|
|
1875
|
+
return client;
|
|
1876
|
+
}
|
|
904
1877
|
}
|
|
905
|
-
function
|
|
906
|
-
|
|
907
|
-
|
|
1878
|
+
function isProviderClientPool(value) {
|
|
1879
|
+
return Boolean(
|
|
1880
|
+
value && typeof value === "object" && typeof value.candidates === "function" && typeof value.markHealthy === "function"
|
|
1881
|
+
);
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
// src/provider/request.ts
|
|
1885
|
+
async function fetchAssistantResponse(client, messages, request, tools, callbacks, abortSignal, onRequestMetric, observability) {
|
|
1886
|
+
const capabilities = resolveProviderCapabilities(request);
|
|
1887
|
+
const adapter = selectProviderWireAdapter(capabilities.wireApi);
|
|
1888
|
+
return tryFetch(
|
|
1889
|
+
adapter,
|
|
1890
|
+
client,
|
|
1891
|
+
messages,
|
|
1892
|
+
request,
|
|
1893
|
+
tools,
|
|
1894
|
+
callbacks,
|
|
1895
|
+
false,
|
|
1896
|
+
abortSignal,
|
|
1897
|
+
onRequestMetric,
|
|
1898
|
+
observability
|
|
1899
|
+
);
|
|
1900
|
+
}
|
|
1901
|
+
async function tryFetch(adapter, client, messages, request, tools, callbacks, forceReasoning, abortSignal, onRequestMetric, observability) {
|
|
1902
|
+
const startedAt = Date.now();
|
|
1903
|
+
let latestMetric;
|
|
1904
|
+
let resolvedBaseUrl;
|
|
1905
|
+
const forwardMetric = (metric) => {
|
|
1906
|
+
latestMetric = metric;
|
|
1907
|
+
onRequestMetric?.(metric);
|
|
1908
|
+
};
|
|
1909
|
+
if (observability) {
|
|
1910
|
+
await recordObservabilityEvent(observability.rootDir, {
|
|
1911
|
+
event: "model.request",
|
|
1912
|
+
status: "started",
|
|
1913
|
+
sessionId: observability.sessionId,
|
|
1914
|
+
identityKind: observability.identityKind,
|
|
1915
|
+
identityName: observability.identityName,
|
|
1916
|
+
model: request.model,
|
|
1917
|
+
details: {
|
|
1918
|
+
provider: request.provider,
|
|
1919
|
+
configuredModel: observability.configuredModel,
|
|
1920
|
+
requestModel: request.model,
|
|
1921
|
+
wireApi: adapter.wireApi,
|
|
1922
|
+
baseUrl: resolvedBaseUrl
|
|
1923
|
+
}
|
|
1924
|
+
});
|
|
1925
|
+
}
|
|
1926
|
+
try {
|
|
1927
|
+
const response = await withApiRetries(
|
|
1928
|
+
() => invokeWithProviderClients(client, async (providerClient, baseUrl) => {
|
|
1929
|
+
resolvedBaseUrl = baseUrl;
|
|
1930
|
+
return adapter.fetchStreaming(providerClient, {
|
|
1931
|
+
provider: request.provider,
|
|
1932
|
+
model: request.model,
|
|
1933
|
+
messages,
|
|
1934
|
+
tools,
|
|
1935
|
+
callbacks,
|
|
1936
|
+
forceReasoning,
|
|
1937
|
+
thinking: request.thinking,
|
|
1938
|
+
reasoningEffort: request.reasoningEffort,
|
|
1939
|
+
maxOutputTokens: request.maxOutputTokens,
|
|
1940
|
+
sessionId: request.sessionId,
|
|
1941
|
+
projectRoot: request.projectRoot,
|
|
1942
|
+
abortSignal,
|
|
1943
|
+
onRequestMetric: forwardMetric
|
|
1944
|
+
});
|
|
1945
|
+
}),
|
|
1946
|
+
abortSignal
|
|
1947
|
+
);
|
|
1948
|
+
if (observability) {
|
|
1949
|
+
await recordObservabilityEvent(observability.rootDir, {
|
|
1950
|
+
event: "model.request",
|
|
1951
|
+
status: "completed",
|
|
1952
|
+
sessionId: observability.sessionId,
|
|
1953
|
+
identityKind: observability.identityKind,
|
|
1954
|
+
identityName: observability.identityName,
|
|
1955
|
+
model: request.model,
|
|
1956
|
+
durationMs: Date.now() - startedAt,
|
|
1957
|
+
details: {
|
|
1958
|
+
provider: request.provider,
|
|
1959
|
+
configuredModel: observability.configuredModel,
|
|
1960
|
+
requestModel: request.model,
|
|
1961
|
+
wireApi: adapter.wireApi,
|
|
1962
|
+
baseUrl: resolvedBaseUrl,
|
|
1963
|
+
usage: latestMetric?.usage,
|
|
1964
|
+
usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
|
|
1965
|
+
}
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
return response;
|
|
1969
|
+
} catch (error) {
|
|
1970
|
+
if (isAbortError(error)) {
|
|
1971
|
+
throw error;
|
|
1972
|
+
}
|
|
1973
|
+
try {
|
|
1974
|
+
const response = await withApiRetries(
|
|
1975
|
+
() => invokeWithProviderClients(client, async (providerClient, baseUrl) => {
|
|
1976
|
+
resolvedBaseUrl = baseUrl;
|
|
1977
|
+
return adapter.fetchNonStreaming(providerClient, {
|
|
1978
|
+
provider: request.provider,
|
|
1979
|
+
model: request.model,
|
|
1980
|
+
messages,
|
|
1981
|
+
tools,
|
|
1982
|
+
callbacks,
|
|
1983
|
+
forceReasoning,
|
|
1984
|
+
thinking: request.thinking,
|
|
1985
|
+
reasoningEffort: request.reasoningEffort,
|
|
1986
|
+
maxOutputTokens: request.maxOutputTokens,
|
|
1987
|
+
sessionId: request.sessionId,
|
|
1988
|
+
projectRoot: request.projectRoot,
|
|
1989
|
+
abortSignal,
|
|
1990
|
+
onRequestMetric: forwardMetric
|
|
1991
|
+
});
|
|
1992
|
+
}),
|
|
1993
|
+
abortSignal
|
|
1994
|
+
);
|
|
1995
|
+
if (observability) {
|
|
1996
|
+
await recordObservabilityEvent(observability.rootDir, {
|
|
1997
|
+
event: "model.request",
|
|
1998
|
+
status: "completed",
|
|
1999
|
+
sessionId: observability.sessionId,
|
|
2000
|
+
identityKind: observability.identityKind,
|
|
2001
|
+
identityName: observability.identityName,
|
|
2002
|
+
model: request.model,
|
|
2003
|
+
durationMs: Date.now() - startedAt,
|
|
2004
|
+
details: {
|
|
2005
|
+
provider: request.provider,
|
|
2006
|
+
configuredModel: observability.configuredModel,
|
|
2007
|
+
requestModel: request.model,
|
|
2008
|
+
wireApi: adapter.wireApi,
|
|
2009
|
+
baseUrl: resolvedBaseUrl,
|
|
2010
|
+
usage: latestMetric?.usage,
|
|
2011
|
+
usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
|
|
2012
|
+
}
|
|
2013
|
+
});
|
|
2014
|
+
}
|
|
2015
|
+
return response;
|
|
2016
|
+
} catch (fallbackError) {
|
|
2017
|
+
if (!isAbortError(fallbackError) && observability) {
|
|
2018
|
+
await recordObservabilityEvent(observability.rootDir, {
|
|
2019
|
+
event: "model.request",
|
|
2020
|
+
status: "failed",
|
|
2021
|
+
sessionId: observability.sessionId,
|
|
2022
|
+
identityKind: observability.identityKind,
|
|
2023
|
+
identityName: observability.identityName,
|
|
2024
|
+
model: request.model,
|
|
2025
|
+
durationMs: Date.now() - startedAt,
|
|
2026
|
+
error: fallbackError,
|
|
2027
|
+
details: {
|
|
2028
|
+
provider: request.provider,
|
|
2029
|
+
configuredModel: observability.configuredModel,
|
|
2030
|
+
requestModel: request.model,
|
|
2031
|
+
wireApi: adapter.wireApi,
|
|
2032
|
+
baseUrl: resolvedBaseUrl,
|
|
2033
|
+
usage: latestMetric?.usage,
|
|
2034
|
+
usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
|
|
2035
|
+
}
|
|
2036
|
+
});
|
|
2037
|
+
}
|
|
2038
|
+
throw fallbackError;
|
|
2039
|
+
}
|
|
908
2040
|
}
|
|
909
2041
|
}
|
|
910
|
-
function
|
|
911
|
-
if (
|
|
912
|
-
|
|
2042
|
+
function selectProviderWireAdapter(wireApi) {
|
|
2043
|
+
if (wireApi === "responses") {
|
|
2044
|
+
return responsesAdapter;
|
|
913
2045
|
}
|
|
914
|
-
|
|
915
|
-
|
|
2046
|
+
return chatCompletionsAdapter;
|
|
2047
|
+
}
|
|
2048
|
+
async function invokeWithProviderClients(client, operation) {
|
|
2049
|
+
if (!isProviderClientPool(client)) {
|
|
2050
|
+
return operation(client, void 0);
|
|
2051
|
+
}
|
|
2052
|
+
let lastError;
|
|
2053
|
+
const candidates = client.candidates();
|
|
2054
|
+
for (let index = 0; index < candidates.length; index += 1) {
|
|
2055
|
+
const candidate = candidates[index];
|
|
2056
|
+
try {
|
|
2057
|
+
const result = await operation(candidate.client, candidate.baseUrl);
|
|
2058
|
+
client.markHealthy(candidate.baseUrl);
|
|
2059
|
+
return result;
|
|
2060
|
+
} catch (error) {
|
|
2061
|
+
lastError = error;
|
|
2062
|
+
if (isAbortError(error)) {
|
|
2063
|
+
throw error;
|
|
2064
|
+
}
|
|
2065
|
+
const hasMoreCandidates = index < candidates.length - 1;
|
|
2066
|
+
if (!hasMoreCandidates || !canRetryWithAlternateBaseUrl(error)) {
|
|
2067
|
+
throw error;
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
916
2070
|
}
|
|
2071
|
+
throw lastError;
|
|
917
2072
|
}
|
|
918
|
-
function
|
|
919
|
-
|
|
2073
|
+
function canRetryWithAlternateBaseUrl(error) {
|
|
2074
|
+
const status = error.status;
|
|
2075
|
+
const message = String(error.message ?? error).toLowerCase();
|
|
2076
|
+
return status === 404 || status === 405 || message.includes("404") || message.includes("not found");
|
|
920
2077
|
}
|
|
921
2078
|
|
|
922
2079
|
// src/provider/retryPolicy.ts
|
|
@@ -1650,7 +2807,8 @@ function buildCompressedContextRequest(systemPrompt, messages, config) {
|
|
|
1650
2807
|
const safeMaxChars = Math.max(8e3, config.maxContextChars);
|
|
1651
2808
|
const conversation = buildVisibleConversationWindow(messages);
|
|
1652
2809
|
const conversationMessages = conversation.messages;
|
|
1653
|
-
const
|
|
2810
|
+
const provider = config.provider ?? "openai-compatible";
|
|
2811
|
+
const fullMessages = composeChatMessages(systemPrompt, conversationMessages, config.model, provider);
|
|
1654
2812
|
const initialEstimatedChars = estimateChatMessagesChars(fullMessages);
|
|
1655
2813
|
const initialPromptMetrics = measureSystemPrompt(systemPrompt);
|
|
1656
2814
|
const initialSources = buildBudgetSources(systemPrompt, conversationMessages);
|
|
@@ -1679,7 +2837,7 @@ function buildCompressedContextRequest(systemPrompt, messages, config) {
|
|
|
1679
2837
|
const summary = compressedFrameHead.length > 0 ? summarizeConversation(compressedFrameHead, config.contextSummaryChars) : void 0;
|
|
1680
2838
|
const summaryPrompt = appendSummary(systemPrompt, summary);
|
|
1681
2839
|
let workingTail = compactTailMessages(tailMessages, "normal");
|
|
1682
|
-
let requestMessages = composeChatMessages(summaryPrompt, workingTail, config.model);
|
|
2840
|
+
let requestMessages = composeChatMessages(summaryPrompt, workingTail, config.model, provider);
|
|
1683
2841
|
let estimatedChars = estimateChatMessagesChars(requestMessages);
|
|
1684
2842
|
let promptMetrics = measureSystemPrompt(summaryPrompt);
|
|
1685
2843
|
let cacheLayout = buildCacheLayoutReport(summaryPrompt, workingTail);
|
|
@@ -1704,7 +2862,7 @@ function buildCompressedContextRequest(systemPrompt, messages, config) {
|
|
|
1704
2862
|
};
|
|
1705
2863
|
}
|
|
1706
2864
|
workingTail = compactTailMessages(tailMessages, "aggressive");
|
|
1707
|
-
requestMessages = composeChatMessages(summaryPrompt, workingTail, config.model);
|
|
2865
|
+
requestMessages = composeChatMessages(summaryPrompt, workingTail, config.model, provider);
|
|
1708
2866
|
estimatedChars = estimateChatMessagesChars(requestMessages);
|
|
1709
2867
|
promptMetrics = measureSystemPrompt(summaryPrompt);
|
|
1710
2868
|
cacheLayout = buildCacheLayoutReport(summaryPrompt, workingTail);
|
|
@@ -1740,7 +2898,8 @@ function buildCompressedContextRequest(systemPrompt, messages, config) {
|
|
|
1740
2898
|
const hardMessages = composeChatMessages(
|
|
1741
2899
|
hardPrompt,
|
|
1742
2900
|
compactedHardTail,
|
|
1743
|
-
config.model
|
|
2901
|
+
config.model,
|
|
2902
|
+
provider
|
|
1744
2903
|
);
|
|
1745
2904
|
const hardEstimatedChars = estimateChatMessagesChars(hardMessages);
|
|
1746
2905
|
const hardCacheLayout = buildCacheLayoutReport(hardPrompt, compactedHardTail);
|
|
@@ -1775,7 +2934,7 @@ function sliceTailMessages(messages, tailCount) {
|
|
|
1775
2934
|
const safeStartIndex = expandStartToToolBoundary(messages, startIndex);
|
|
1776
2935
|
return messages.slice(safeStartIndex);
|
|
1777
2936
|
}
|
|
1778
|
-
function composeChatMessages(systemPrompt, messages, model) {
|
|
2937
|
+
function composeChatMessages(systemPrompt, messages, model, provider) {
|
|
1779
2938
|
return [
|
|
1780
2939
|
{
|
|
1781
2940
|
role: "system",
|
|
@@ -1787,7 +2946,7 @@ function composeChatMessages(systemPrompt, messages, model) {
|
|
|
1787
2946
|
name: message.name,
|
|
1788
2947
|
toolCallId: message.tool_call_id,
|
|
1789
2948
|
toolCalls: message.tool_calls,
|
|
1790
|
-
reasoningContent: shouldIncludeStoredAssistantReasoning(messages, index, model) ? message.reasoningContent : void 0
|
|
2949
|
+
reasoningContent: shouldIncludeStoredAssistantReasoning(messages, index, model, provider) ? message.reasoningContent : void 0
|
|
1791
2950
|
}))
|
|
1792
2951
|
];
|
|
1793
2952
|
}
|
|
@@ -1934,8 +3093,8 @@ function buildCacheLayoutReport(systemPrompt, messages) {
|
|
|
1934
3093
|
}))
|
|
1935
3094
|
});
|
|
1936
3095
|
return {
|
|
1937
|
-
stablePrefixFingerprint:
|
|
1938
|
-
volatileTailFingerprint:
|
|
3096
|
+
stablePrefixFingerprint: stableHash2(stablePrefix),
|
|
3097
|
+
volatileTailFingerprint: stableHash2(volatileTail),
|
|
1939
3098
|
stablePrefixChars: stablePrefix.length,
|
|
1940
3099
|
volatileTailChars: volatileTail.length,
|
|
1941
3100
|
stableSources: typeof systemPrompt === "string" ? ["systemPrompt"] : [
|
|
@@ -1966,7 +3125,7 @@ function renderVolatileRuntimeFacts(systemPrompt) {
|
|
|
1966
3125
|
joinBlocks(systemPrompt.runtimeFactBlocks)
|
|
1967
3126
|
].join("\n").trim();
|
|
1968
3127
|
}
|
|
1969
|
-
function
|
|
3128
|
+
function stableHash2(value) {
|
|
1970
3129
|
let hash = 2166136261;
|
|
1971
3130
|
for (let index = 0; index < value.length; index += 1) {
|
|
1972
3131
|
hash ^= value.charCodeAt(index);
|
|
@@ -2202,6 +3361,7 @@ async function updateSessionMemoryAfterTurn(input) {
|
|
|
2202
3361
|
identityName: input.identity.name,
|
|
2203
3362
|
model: input.requestModel
|
|
2204
3363
|
});
|
|
3364
|
+
input.options.callbacks?.onStatus?.("\u603B\u7ED3\u4E2D");
|
|
2205
3365
|
try {
|
|
2206
3366
|
const memoryResponse = input.options.fetchSessionMemoryResponse ? await input.options.fetchSessionMemoryResponse(modelRequest) : await fetchAssistantResponse(
|
|
2207
3367
|
input.client,
|
|
@@ -2252,6 +3412,8 @@ async function updateSessionMemoryAfterTurn(input) {
|
|
|
2252
3412
|
error
|
|
2253
3413
|
});
|
|
2254
3414
|
return input.session;
|
|
3415
|
+
} finally {
|
|
3416
|
+
input.options.callbacks?.onStatus?.("");
|
|
2255
3417
|
}
|
|
2256
3418
|
}
|
|
2257
3419
|
async function updateSessionTitleAfterTurn(input) {
|
|
@@ -2299,6 +3461,7 @@ async function updateSessionTitleAfterTurn(input) {
|
|
|
2299
3461
|
identityName: input.identity.name,
|
|
2300
3462
|
model: input.requestModel
|
|
2301
3463
|
});
|
|
3464
|
+
input.options.callbacks?.onStatus?.("\u6807\u9898\u751F\u6210\u4E2D");
|
|
2302
3465
|
try {
|
|
2303
3466
|
const titleResponse = input.options.fetchSessionTitleResponse ? await input.options.fetchSessionTitleResponse(modelRequest) : await fetchAssistantResponse(
|
|
2304
3467
|
input.client,
|
|
@@ -2350,15 +3513,17 @@ async function updateSessionTitleAfterTurn(input) {
|
|
|
2350
3513
|
error
|
|
2351
3514
|
});
|
|
2352
3515
|
return input.session;
|
|
3516
|
+
} finally {
|
|
3517
|
+
input.options.callbacks?.onStatus?.("");
|
|
2353
3518
|
}
|
|
2354
3519
|
}
|
|
2355
3520
|
|
|
2356
3521
|
// src/control/ledger.ts
|
|
2357
3522
|
import Database from "better-sqlite3";
|
|
2358
|
-
import
|
|
3523
|
+
import fs8 from "fs";
|
|
2359
3524
|
|
|
2360
3525
|
// src/control/executions.ts
|
|
2361
|
-
import
|
|
3526
|
+
import path9 from "path";
|
|
2362
3527
|
|
|
2363
3528
|
// src/protocol/leadWait.ts
|
|
2364
3529
|
var LEAD_WAIT_PROTOCOL = "kitty.lead-wait-policy";
|
|
@@ -2587,7 +3752,7 @@ var ExecutionLedgerRepo = class {
|
|
|
2587
3752
|
prompt: input.prompt,
|
|
2588
3753
|
actorName: input.actorName,
|
|
2589
3754
|
actorRole: input.actorRole,
|
|
2590
|
-
cwd:
|
|
3755
|
+
cwd: path9.resolve(input.cwd),
|
|
2591
3756
|
requestedBy: input.requestedBy,
|
|
2592
3757
|
sessionId: input.sessionId,
|
|
2593
3758
|
pid: input.pid,
|
|
@@ -2618,10 +3783,10 @@ var ExecutionLedgerRepo = class {
|
|
|
2618
3783
|
}
|
|
2619
3784
|
list(input = {}) {
|
|
2620
3785
|
const rows = this.db.prepare("SELECT * FROM executions ORDER BY created_at ASC").all();
|
|
2621
|
-
const cwd = input.cwd ?
|
|
3786
|
+
const cwd = input.cwd ? path9.resolve(input.cwd) : void 0;
|
|
2622
3787
|
const statuses = new Set(input.statuses ?? []);
|
|
2623
3788
|
const kinds = new Set(input.kinds ?? []);
|
|
2624
|
-
return rows.map(fromExecutionRow).filter((record) => !input.kind || record.kind === input.kind).filter((record) => kinds.size === 0 || kinds.has(record.kind)).filter((record) => statuses.size === 0 || statuses.has(record.status)).filter((record) => !cwd || isSameOrDescendant(
|
|
3789
|
+
return rows.map(fromExecutionRow).filter((record) => !input.kind || record.kind === input.kind).filter((record) => kinds.size === 0 || kinds.has(record.kind)).filter((record) => statuses.size === 0 || statuses.has(record.status)).filter((record) => !cwd || isSameOrDescendant(path9.resolve(record.cwd), cwd) || isSameOrDescendant(cwd, path9.resolve(record.cwd)));
|
|
2625
3790
|
}
|
|
2626
3791
|
markRunning(id, input) {
|
|
2627
3792
|
const current = requireExecution(this.load(id), id);
|
|
@@ -2699,8 +3864,8 @@ function requireExecution(record, id) {
|
|
|
2699
3864
|
return record;
|
|
2700
3865
|
}
|
|
2701
3866
|
function isSameOrDescendant(targetPath, possibleAncestor) {
|
|
2702
|
-
const relative =
|
|
2703
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
3867
|
+
const relative = path9.relative(possibleAncestor, targetPath);
|
|
3868
|
+
return relative === "" || !relative.startsWith("..") && !path9.isAbsolute(relative);
|
|
2704
3869
|
}
|
|
2705
3870
|
|
|
2706
3871
|
// src/control/schema.ts
|
|
@@ -2814,7 +3979,7 @@ var TaskLifecycleLedgerRepo = class {
|
|
|
2814
3979
|
stage: existing?.stage === "completed" || !existing ? "normal_work" : existing.stage,
|
|
2815
3980
|
scope: existing?.scope,
|
|
2816
3981
|
boundary: existing?.boundary,
|
|
2817
|
-
reason:
|
|
3982
|
+
reason: normalizeText3(input.reason) ?? existing?.reason ?? "turn_started",
|
|
2818
3983
|
activeExecutionIds: existing?.activeExecutionIds ?? [],
|
|
2819
3984
|
activeTodoIds: existing?.activeTodoIds ?? [],
|
|
2820
3985
|
verificationFacts: existing?.verificationFacts ?? [],
|
|
@@ -2843,9 +4008,9 @@ var TaskLifecycleLedgerRepo = class {
|
|
|
2843
4008
|
return this.save({
|
|
2844
4009
|
...current,
|
|
2845
4010
|
stage: input.stage ?? current.stage,
|
|
2846
|
-
scope:
|
|
2847
|
-
boundary:
|
|
2848
|
-
reason:
|
|
4011
|
+
scope: normalizeText3(input.scope) ?? current.scope,
|
|
4012
|
+
boundary: normalizeText3(input.boundary) ?? current.boundary,
|
|
4013
|
+
reason: normalizeText3(input.reason) ?? current.reason,
|
|
2849
4014
|
activeExecutionIds: normalizeStringList2(input.activeExecutionIds ?? current.activeExecutionIds),
|
|
2850
4015
|
activeTodoIds: normalizeStringList2(input.activeTodoIds ?? current.activeTodoIds),
|
|
2851
4016
|
verificationFacts: normalizeStringList2(input.verificationFacts ?? current.verificationFacts),
|
|
@@ -2961,7 +4126,7 @@ function normalizeStringList2(value) {
|
|
|
2961
4126
|
const seen = /* @__PURE__ */ new Set();
|
|
2962
4127
|
const items = [];
|
|
2963
4128
|
for (const item of value) {
|
|
2964
|
-
const text =
|
|
4129
|
+
const text = normalizeText3(item);
|
|
2965
4130
|
if (!text || seen.has(text)) {
|
|
2966
4131
|
continue;
|
|
2967
4132
|
}
|
|
@@ -2970,7 +4135,7 @@ function normalizeStringList2(value) {
|
|
|
2970
4135
|
}
|
|
2971
4136
|
return items;
|
|
2972
4137
|
}
|
|
2973
|
-
function
|
|
4138
|
+
function normalizeText3(value) {
|
|
2974
4139
|
return typeof value === "string" && value.trim() ? value.replace(/\s+/g, " ").trim() : void 0;
|
|
2975
4140
|
}
|
|
2976
4141
|
|
|
@@ -3010,7 +4175,7 @@ var ControlPlaneLedger = class {
|
|
|
3010
4175
|
db;
|
|
3011
4176
|
constructor(rootDir) {
|
|
3012
4177
|
const statePaths = getProjectStatePaths(rootDir);
|
|
3013
|
-
|
|
4178
|
+
fs8.mkdirSync(statePaths.kittyDir, { recursive: true });
|
|
3014
4179
|
this.db = new Database(statePaths.controlPlaneLedgerFile);
|
|
3015
4180
|
this.db.pragma("journal_mode = WAL");
|
|
3016
4181
|
this.db.pragma("foreign_keys = ON");
|
|
@@ -3177,11 +4342,11 @@ function truncateWakeFact(value) {
|
|
|
3177
4342
|
}
|
|
3178
4343
|
|
|
3179
4344
|
// src/utils/fs.ts
|
|
3180
|
-
import
|
|
3181
|
-
import
|
|
4345
|
+
import fs9 from "fs/promises";
|
|
4346
|
+
import path10 from "path";
|
|
3182
4347
|
async function fileExists(targetPath) {
|
|
3183
4348
|
try {
|
|
3184
|
-
await
|
|
4349
|
+
await fs9.access(targetPath);
|
|
3185
4350
|
return true;
|
|
3186
4351
|
} catch {
|
|
3187
4352
|
return false;
|
|
@@ -3189,10 +4354,10 @@ async function fileExists(targetPath) {
|
|
|
3189
4354
|
}
|
|
3190
4355
|
function resolveUserPath(inputPath, cwd) {
|
|
3191
4356
|
const cleanPath = normalizeUserPathInput(inputPath);
|
|
3192
|
-
if (
|
|
3193
|
-
return
|
|
4357
|
+
if (path10.isAbsolute(cleanPath)) {
|
|
4358
|
+
return path10.normalize(cleanPath);
|
|
3194
4359
|
}
|
|
3195
|
-
return
|
|
4360
|
+
return path10.resolve(cwd, cleanPath);
|
|
3196
4361
|
}
|
|
3197
4362
|
function normalizeUserPathInput(inputPath) {
|
|
3198
4363
|
const trimmed = inputPath.trim();
|
|
@@ -3206,7 +4371,7 @@ function normalizeUserPathInput(inputPath) {
|
|
|
3206
4371
|
return trimmed;
|
|
3207
4372
|
}
|
|
3208
4373
|
async function ensureParentDirectory(filePath) {
|
|
3209
|
-
await
|
|
4374
|
+
await fs9.mkdir(path10.dirname(filePath), { recursive: true });
|
|
3210
4375
|
}
|
|
3211
4376
|
function truncateText(input, maxChars) {
|
|
3212
4377
|
if (input.length <= maxChars) {
|
|
@@ -3258,7 +4423,7 @@ function projectToolResultForModel(input) {
|
|
|
3258
4423
|
}
|
|
3259
4424
|
}
|
|
3260
4425
|
function projectExecutionAction(payload) {
|
|
3261
|
-
const execution =
|
|
4426
|
+
const execution = readObject2(payload.execution) ?? payload;
|
|
3262
4427
|
return joinLines([
|
|
3263
4428
|
readString(execution.id) ?? "execution",
|
|
3264
4429
|
readString(execution.kind),
|
|
@@ -3266,7 +4431,7 @@ function projectExecutionAction(payload) {
|
|
|
3266
4431
|
readString(execution.command),
|
|
3267
4432
|
readString(execution.summary),
|
|
3268
4433
|
readString(execution.outputPreview),
|
|
3269
|
-
|
|
4434
|
+
readObject2(execution.health) ? readString(readObject2(execution.health)?.message) : void 0,
|
|
3270
4435
|
readString(execution.error)
|
|
3271
4436
|
]);
|
|
3272
4437
|
}
|
|
@@ -3293,10 +4458,10 @@ function projectExecutionCheck(payload) {
|
|
|
3293
4458
|
]);
|
|
3294
4459
|
}
|
|
3295
4460
|
function projectRead(payload) {
|
|
3296
|
-
const
|
|
4461
|
+
const path29 = readString(payload.path) ?? readString(payload.requestedPath) ?? "file";
|
|
3297
4462
|
if (payload.readable === false) {
|
|
3298
4463
|
return joinLines([
|
|
3299
|
-
`${
|
|
4464
|
+
`${path29}: not readable`,
|
|
3300
4465
|
readString(payload.reason),
|
|
3301
4466
|
readString(payload.detectedCapability) ? `capability: ${readString(payload.detectedCapability)}` : void 0
|
|
3302
4467
|
]);
|
|
@@ -3304,35 +4469,35 @@ function projectRead(payload) {
|
|
|
3304
4469
|
const startLine = readNumber(payload.startLine);
|
|
3305
4470
|
const endLine = readNumber(payload.endLine);
|
|
3306
4471
|
const content = readString(payload.content) ?? "";
|
|
3307
|
-
const continuation =
|
|
3308
|
-
const continuationArgs =
|
|
4472
|
+
const continuation = readObject2(payload.continuation);
|
|
4473
|
+
const continuationArgs = readObject2(continuation?.continuationArgs);
|
|
3309
4474
|
return joinLines([
|
|
3310
|
-
`${
|
|
4475
|
+
`${path29}${startLine && endLine ? `:${startLine}-${endLine}` : ""}`,
|
|
3311
4476
|
truncateText(content, DEFAULT_MAX_CHARS),
|
|
3312
4477
|
continuationArgs ? `next: read ${JSON.stringify(continuationArgs)}` : void 0
|
|
3313
4478
|
]);
|
|
3314
4479
|
}
|
|
3315
4480
|
function projectEdit(payload) {
|
|
3316
|
-
const
|
|
4481
|
+
const path29 = readString(payload.path) ?? "file";
|
|
3317
4482
|
const applied = readNumber(payload.appliedEdits) ?? readNumber(payload.requestedEdits);
|
|
3318
4483
|
const diff = readString(payload.diff) ?? readString(payload.preview);
|
|
3319
4484
|
return joinLines([
|
|
3320
|
-
`edited ${
|
|
4485
|
+
`edited ${path29}${applied ? ` (${applied} replacement${applied === 1 ? "" : "s"})` : ""}`,
|
|
3321
4486
|
diff ? truncateText(diff, DIFF_MAX_CHARS) : void 0
|
|
3322
4487
|
]);
|
|
3323
4488
|
}
|
|
3324
4489
|
function projectWrite(payload) {
|
|
3325
|
-
const
|
|
4490
|
+
const path29 = readString(payload.path) ?? "file";
|
|
3326
4491
|
const bytes = readNumber(payload.bytes);
|
|
3327
4492
|
const existed = payload.existed === true;
|
|
3328
4493
|
const diff = readString(payload.diff) ?? readString(payload.preview);
|
|
3329
4494
|
return joinLines([
|
|
3330
|
-
`${existed ? "wrote" : "created"} ${
|
|
4495
|
+
`${existed ? "wrote" : "created"} ${path29}${bytes !== void 0 ? ` (${bytes} bytes)` : ""}`,
|
|
3331
4496
|
diff ? truncateText(diff, DIFF_MAX_CHARS) : void 0
|
|
3332
4497
|
]);
|
|
3333
4498
|
}
|
|
3334
4499
|
function projectBash(payload) {
|
|
3335
|
-
const governance =
|
|
4500
|
+
const governance = readObject2(payload.outputGovernance);
|
|
3336
4501
|
const projection = readString(governance?.projection);
|
|
3337
4502
|
if (projection) {
|
|
3338
4503
|
return projection;
|
|
@@ -3353,13 +4518,13 @@ function projectBash(payload) {
|
|
|
3353
4518
|
return joinLines(lines);
|
|
3354
4519
|
}
|
|
3355
4520
|
function projectSkillLoad(payload) {
|
|
3356
|
-
const skill =
|
|
4521
|
+
const skill = readObject2(payload.skill);
|
|
3357
4522
|
const name = readString(skill?.name) ?? "skill";
|
|
3358
4523
|
const description = readString(skill?.description);
|
|
3359
|
-
const
|
|
4524
|
+
const path29 = readString(skill?.path);
|
|
3360
4525
|
const body = readString(payload.body) ?? "";
|
|
3361
4526
|
return joinLines([
|
|
3362
|
-
`loaded skill: ${name}${
|
|
4527
|
+
`loaded skill: ${name}${path29 ? ` (${path29})` : ""}`,
|
|
3363
4528
|
description,
|
|
3364
4529
|
truncateText(body, SKILL_BODY_MAX_CHARS)
|
|
3365
4530
|
]);
|
|
@@ -3389,8 +4554,8 @@ function projectFailure(toolName, rawOutput, payload) {
|
|
|
3389
4554
|
if (!payload) {
|
|
3390
4555
|
return truncateText(rawOutput.trim(), DEFAULT_MAX_CHARS);
|
|
3391
4556
|
}
|
|
3392
|
-
const details =
|
|
3393
|
-
const readArgs =
|
|
4557
|
+
const details = readObject2(payload.details);
|
|
4558
|
+
const readArgs = readObject2(details?.readArgs);
|
|
3394
4559
|
const suggestions = readArray(details?.suggestions);
|
|
3395
4560
|
const lines = [
|
|
3396
4561
|
`${toolName} failed: ${readString(payload.error) ?? "unknown error"}`,
|
|
@@ -3412,7 +4577,7 @@ function parseObject(raw) {
|
|
|
3412
4577
|
return null;
|
|
3413
4578
|
}
|
|
3414
4579
|
}
|
|
3415
|
-
function
|
|
4580
|
+
function readObject2(value) {
|
|
3416
4581
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
3417
4582
|
}
|
|
3418
4583
|
function readArray(value) {
|
|
@@ -3447,14 +4612,14 @@ var ToolExecutionError = class extends Error {
|
|
|
3447
4612
|
};
|
|
3448
4613
|
|
|
3449
4614
|
// src/tools/outputCapture.ts
|
|
3450
|
-
import
|
|
3451
|
-
import
|
|
3452
|
-
import
|
|
4615
|
+
import crypto2 from "crypto";
|
|
4616
|
+
import fs10 from "fs/promises";
|
|
4617
|
+
import path11 from "path";
|
|
3453
4618
|
var DEFAULT_BASH_OUTPUT_PREVIEW_CHARS = 12e3;
|
|
3454
4619
|
async function createBashOutputCapture(input) {
|
|
3455
4620
|
const maxPreviewChars = typeof input.maxPreviewChars === "number" && Number.isFinite(input.maxPreviewChars) && input.maxPreviewChars > 0 ? Math.trunc(input.maxPreviewChars) : DEFAULT_BASH_OUTPUT_PREVIEW_CHARS;
|
|
3456
4621
|
const absoluteOutputPath = input.stateRootDir && input.sessionId ? await createAbsoluteOutputPath(input.stateRootDir, input.sessionId) : void 0;
|
|
3457
|
-
const outputPath = absoluteOutputPath && input.stateRootDir ?
|
|
4622
|
+
const outputPath = absoluteOutputPath && input.stateRootDir ? path11.relative(input.stateRootDir, absoluteOutputPath) || void 0 : void 0;
|
|
3458
4623
|
let preview = "";
|
|
3459
4624
|
let bufferedOutput = "";
|
|
3460
4625
|
let totalChars = 0;
|
|
@@ -3492,507 +4657,125 @@ async function createBashOutputCapture(input) {
|
|
|
3492
4657
|
truncated,
|
|
3493
4658
|
outputChars: totalChars,
|
|
3494
4659
|
outputBytes: totalBytes
|
|
3495
|
-
};
|
|
3496
|
-
}
|
|
3497
|
-
function queueWrite(chunk) {
|
|
3498
|
-
if (!absoluteOutputPath) {
|
|
3499
|
-
return;
|
|
3500
|
-
}
|
|
3501
|
-
pendingWrite = pendingWrite.then(() => fs8.appendFile(absoluteOutputPath, chunk, "utf8"));
|
|
3502
|
-
}
|
|
3503
|
-
return {
|
|
3504
|
-
append,
|
|
3505
|
-
finalize
|
|
3506
|
-
};
|
|
3507
|
-
}
|
|
3508
|
-
async function createAbsoluteOutputPath(stateRootDir, sessionId) {
|
|
3509
|
-
const paths = getProjectStatePaths(stateRootDir);
|
|
3510
|
-
const sessionDir = path9.join(paths.observabilityDir, "command-output", sessionId);
|
|
3511
|
-
await fs8.mkdir(sessionDir, { recursive: true });
|
|
3512
|
-
return path9.join(sessionDir, `${Date.now()}-bash-output-${crypto.randomUUID().slice(0, 8)}.txt`);
|
|
3513
|
-
}
|
|
3514
|
-
|
|
3515
|
-
// src/utils/commandRunner/shellRuntime.ts
|
|
3516
|
-
function getShellRuntimeInfo(platform = process.platform) {
|
|
3517
|
-
if (platform === "win32") {
|
|
3518
|
-
return {
|
|
3519
|
-
platform,
|
|
3520
|
-
shell: "powershell",
|
|
3521
|
-
executable: "powershell.exe",
|
|
3522
|
-
invocation: "powershell.exe -NoLogo -NoProfile -EncodedCommand <command>",
|
|
3523
|
-
guidance: "Windows runs commands through PowerShell; use PowerShell syntax, semicolon-separated commands, node -e, python -c, or an explicit bash invocation for Bash/POSIX syntax."
|
|
3524
|
-
};
|
|
3525
|
-
}
|
|
3526
|
-
return {
|
|
3527
|
-
platform,
|
|
3528
|
-
shell: "bash",
|
|
3529
|
-
executable: "/bin/bash",
|
|
3530
|
-
invocation: "/bin/bash -lc <command>",
|
|
3531
|
-
guidance: "Unix-like platforms run commands through bash -lc; use Bash syntax."
|
|
3532
|
-
};
|
|
3533
|
-
}
|
|
3534
|
-
|
|
3535
|
-
// src/utils/commandRunner/launch.ts
|
|
3536
|
-
async function launchCommand(command, cwd, timeoutMs, abortSignal) {
|
|
3537
|
-
const execa4 = await loadExeca();
|
|
3538
|
-
const shell = getShellRuntimeInfo();
|
|
3539
|
-
const subprocess = shell.shell === "powershell" ? execa4(shell.executable, ["-NoLogo", "-NoProfile", "-EncodedCommand", encodePowerShellCommand(command)], {
|
|
3540
|
-
cwd,
|
|
3541
|
-
timeout: timeoutMs,
|
|
3542
|
-
cancelSignal: abortSignal,
|
|
3543
|
-
all: true,
|
|
3544
|
-
buffer: false,
|
|
3545
|
-
reject: false,
|
|
3546
|
-
env: buildCommandEnvironment()
|
|
3547
|
-
}) : execa4(shell.executable, ["-lc", command], {
|
|
3548
|
-
cwd,
|
|
3549
|
-
timeout: timeoutMs,
|
|
3550
|
-
cancelSignal: abortSignal,
|
|
3551
|
-
all: true,
|
|
3552
|
-
buffer: false,
|
|
3553
|
-
reject: false,
|
|
3554
|
-
env: buildCommandEnvironment()
|
|
3555
|
-
});
|
|
3556
|
-
return { subprocess };
|
|
3557
|
-
}
|
|
3558
|
-
function encodePowerShellCommand(command) {
|
|
3559
|
-
const wrapped = [
|
|
3560
|
-
"$ProgressPreference = 'SilentlyContinue'",
|
|
3561
|
-
"[Console]::InputEncoding = [System.Text.Encoding]::UTF8",
|
|
3562
|
-
"[Console]::OutputEncoding = [System.Text.Encoding]::UTF8",
|
|
3563
|
-
"$OutputEncoding = [System.Text.Encoding]::UTF8",
|
|
3564
|
-
"try { chcp 65001 > $null } catch { }",
|
|
3565
|
-
`& { ${command} }`,
|
|
3566
|
-
"$code = if ($null -ne $LASTEXITCODE) { [int]$LASTEXITCODE } elseif ($?) { 0 } else { 1 }",
|
|
3567
|
-
"exit $code"
|
|
3568
|
-
].join("; ");
|
|
3569
|
-
return Buffer.from(wrapped, "utf16le").toString("base64");
|
|
3570
|
-
}
|
|
3571
|
-
function buildCommandEnvironment() {
|
|
3572
|
-
return {
|
|
3573
|
-
...process.env,
|
|
3574
|
-
LANG: process.env.LANG || "C.UTF-8",
|
|
3575
|
-
LC_ALL: process.env.LC_ALL || "C.UTF-8",
|
|
3576
|
-
PYTHONIOENCODING: process.env.PYTHONIOENCODING || "utf-8"
|
|
3577
|
-
};
|
|
3578
|
-
}
|
|
3579
|
-
|
|
3580
|
-
// src/utils/commandRunner/platformArgs.ts
|
|
3581
|
-
function splitByAndAnd(command) {
|
|
3582
|
-
const segments = [];
|
|
3583
|
-
let current = "";
|
|
3584
|
-
let inSingle = false;
|
|
3585
|
-
let inDouble = false;
|
|
3586
|
-
for (let index = 0; index < command.length; index += 1) {
|
|
3587
|
-
const char = command.charAt(index);
|
|
3588
|
-
if (char === "'" && !inDouble) {
|
|
3589
|
-
inSingle = !inSingle;
|
|
3590
|
-
current += char;
|
|
3591
|
-
continue;
|
|
3592
|
-
}
|
|
3593
|
-
if (char === '"' && !inSingle) {
|
|
3594
|
-
inDouble = !inDouble;
|
|
3595
|
-
current += char;
|
|
3596
|
-
continue;
|
|
3597
|
-
}
|
|
3598
|
-
if (!inSingle && !inDouble && char === "&" && command.charAt(index + 1) === "&") {
|
|
3599
|
-
if (current.trim()) {
|
|
3600
|
-
segments.push(current.trim());
|
|
3601
|
-
}
|
|
3602
|
-
current = "";
|
|
3603
|
-
index += 1;
|
|
3604
|
-
continue;
|
|
3605
|
-
}
|
|
3606
|
-
current += char;
|
|
3607
|
-
}
|
|
3608
|
-
if (current.trim()) {
|
|
3609
|
-
segments.push(current.trim());
|
|
3610
|
-
}
|
|
3611
|
-
return segments.length > 0 ? segments : [command];
|
|
3612
|
-
}
|
|
3613
|
-
function joinWithAndSemantics(segments) {
|
|
3614
|
-
if (segments.length <= 1) {
|
|
3615
|
-
return segments[0] ?? "";
|
|
3616
|
-
}
|
|
3617
|
-
let script = segments[0] ?? "";
|
|
3618
|
-
for (let index = 1; index < segments.length; index += 1) {
|
|
3619
|
-
const segment = segments[index];
|
|
3620
|
-
script += `; if ($?) { ${segment} }`;
|
|
3621
|
-
}
|
|
3622
|
-
return script;
|
|
3623
|
-
}
|
|
3624
|
-
function splitArgs(command) {
|
|
3625
|
-
const args = [];
|
|
3626
|
-
let current = "";
|
|
3627
|
-
let inSingle = false;
|
|
3628
|
-
let inDouble = false;
|
|
3629
|
-
for (let index = 0; index < command.length; index += 1) {
|
|
3630
|
-
const char = command.charAt(index);
|
|
3631
|
-
if (char === "'" && !inDouble) {
|
|
3632
|
-
inSingle = !inSingle;
|
|
3633
|
-
continue;
|
|
3634
|
-
}
|
|
3635
|
-
if (char === '"' && !inSingle) {
|
|
3636
|
-
inDouble = !inDouble;
|
|
3637
|
-
continue;
|
|
3638
|
-
}
|
|
3639
|
-
if (!inSingle && !inDouble && /\s/.test(char)) {
|
|
3640
|
-
if (current) {
|
|
3641
|
-
args.push(current);
|
|
3642
|
-
current = "";
|
|
3643
|
-
}
|
|
3644
|
-
continue;
|
|
3645
|
-
}
|
|
3646
|
-
current += char;
|
|
3647
|
-
}
|
|
3648
|
-
if (current) {
|
|
3649
|
-
args.push(current);
|
|
3650
|
-
}
|
|
3651
|
-
return args;
|
|
3652
|
-
}
|
|
3653
|
-
function expandPaths(paths) {
|
|
3654
|
-
return paths.flatMap((targetPath) => expandBraces(targetPath));
|
|
3655
|
-
}
|
|
3656
|
-
function normalizeWindowsPath(value) {
|
|
3657
|
-
if (value.includes("://")) {
|
|
3658
|
-
return value;
|
|
3659
|
-
}
|
|
3660
|
-
return value.replace(/\//g, "\\");
|
|
3661
|
-
}
|
|
3662
|
-
function quotePowerShell(value) {
|
|
3663
|
-
const escaped = value.replace(/'/g, "''");
|
|
3664
|
-
return `'${escaped}'`;
|
|
3665
|
-
}
|
|
3666
|
-
function expandBraces(input) {
|
|
3667
|
-
const start = findBraceStart(input);
|
|
3668
|
-
if (start === -1) {
|
|
3669
|
-
return [input];
|
|
3670
|
-
}
|
|
3671
|
-
const end = findMatchingBrace(input, start);
|
|
3672
|
-
if (end === -1) {
|
|
3673
|
-
return [input];
|
|
3674
|
-
}
|
|
3675
|
-
const prefix = input.slice(0, start);
|
|
3676
|
-
const suffix = input.slice(end + 1);
|
|
3677
|
-
const body = input.slice(start + 1, end);
|
|
3678
|
-
const parts = splitBraceParts(body);
|
|
3679
|
-
const expandedSuffix = expandBraces(suffix);
|
|
3680
|
-
const results = [];
|
|
3681
|
-
for (const part of parts) {
|
|
3682
|
-
for (const expandedPart of expandBraces(part)) {
|
|
3683
|
-
for (const tail of expandedSuffix) {
|
|
3684
|
-
results.push(`${prefix}${expandedPart}${tail}`);
|
|
3685
|
-
}
|
|
3686
|
-
}
|
|
3687
|
-
}
|
|
3688
|
-
return results;
|
|
3689
|
-
}
|
|
3690
|
-
function findBraceStart(input) {
|
|
3691
|
-
let inSingle = false;
|
|
3692
|
-
let inDouble = false;
|
|
3693
|
-
for (let index = 0; index < input.length; index += 1) {
|
|
3694
|
-
const char = input.charAt(index);
|
|
3695
|
-
if (char === "'" && !inDouble) {
|
|
3696
|
-
inSingle = !inSingle;
|
|
3697
|
-
continue;
|
|
3698
|
-
}
|
|
3699
|
-
if (char === '"' && !inSingle) {
|
|
3700
|
-
inDouble = !inDouble;
|
|
3701
|
-
continue;
|
|
3702
|
-
}
|
|
3703
|
-
if (!inSingle && !inDouble && char === "{") {
|
|
3704
|
-
return index;
|
|
3705
|
-
}
|
|
3706
|
-
}
|
|
3707
|
-
return -1;
|
|
3708
|
-
}
|
|
3709
|
-
function findMatchingBrace(input, start) {
|
|
3710
|
-
let depth = 0;
|
|
3711
|
-
let inSingle = false;
|
|
3712
|
-
let inDouble = false;
|
|
3713
|
-
for (let index = start; index < input.length; index += 1) {
|
|
3714
|
-
const char = input.charAt(index);
|
|
3715
|
-
if (char === "'" && !inDouble) {
|
|
3716
|
-
inSingle = !inSingle;
|
|
3717
|
-
continue;
|
|
3718
|
-
}
|
|
3719
|
-
if (char === '"' && !inSingle) {
|
|
3720
|
-
inDouble = !inDouble;
|
|
3721
|
-
continue;
|
|
3722
|
-
}
|
|
3723
|
-
if (inSingle || inDouble) {
|
|
3724
|
-
continue;
|
|
3725
|
-
}
|
|
3726
|
-
if (char === "{") {
|
|
3727
|
-
depth += 1;
|
|
3728
|
-
} else if (char === "}") {
|
|
3729
|
-
depth -= 1;
|
|
3730
|
-
if (depth === 0) {
|
|
3731
|
-
return index;
|
|
3732
|
-
}
|
|
3733
|
-
}
|
|
3734
|
-
}
|
|
3735
|
-
return -1;
|
|
3736
|
-
}
|
|
3737
|
-
function splitBraceParts(input) {
|
|
3738
|
-
const parts = [];
|
|
3739
|
-
let current = "";
|
|
3740
|
-
let depth = 0;
|
|
3741
|
-
for (let index = 0; index < input.length; index += 1) {
|
|
3742
|
-
const char = input.charAt(index);
|
|
3743
|
-
if (char === "{") {
|
|
3744
|
-
depth += 1;
|
|
3745
|
-
current += char;
|
|
3746
|
-
continue;
|
|
3747
|
-
}
|
|
3748
|
-
if (char === "}") {
|
|
3749
|
-
depth -= 1;
|
|
3750
|
-
current += char;
|
|
3751
|
-
continue;
|
|
3752
|
-
}
|
|
3753
|
-
if (char === "," && depth === 0) {
|
|
3754
|
-
parts.push(current);
|
|
3755
|
-
current = "";
|
|
3756
|
-
continue;
|
|
3757
|
-
}
|
|
3758
|
-
current += char;
|
|
3759
|
-
}
|
|
3760
|
-
if (current) {
|
|
3761
|
-
parts.push(current);
|
|
3762
|
-
}
|
|
3763
|
-
return parts.length > 0 ? parts : [input];
|
|
3764
|
-
}
|
|
3765
|
-
|
|
3766
|
-
// src/utils/commandRunner/platformTransforms.ts
|
|
3767
|
-
function startsWithExplicitShell(command) {
|
|
3768
|
-
return /^\s*(cmd(?:\.exe)?\s+\/c|powershell(?:\.exe)?\b|pwsh\b|bash\b)/i.test(command);
|
|
3769
|
-
}
|
|
3770
|
-
function normalizeWindowsSegment(segment) {
|
|
3771
|
-
const trimmed = segment.trim();
|
|
3772
|
-
if (!trimmed) {
|
|
3773
|
-
return segment;
|
|
3774
|
-
}
|
|
3775
|
-
const lowered = trimmed.toLowerCase();
|
|
3776
|
-
if (lowered.startsWith("get-childitem") || lowered.startsWith("new-item")) {
|
|
3777
|
-
return segment;
|
|
3778
|
-
}
|
|
3779
|
-
if (lowered.startsWith("ls")) {
|
|
3780
|
-
return normalizeLsSegment(trimmed);
|
|
3781
|
-
}
|
|
3782
|
-
if (lowered.startsWith("mkdir") || lowered.startsWith("md ")) {
|
|
3783
|
-
return normalizeMkdirSegment(trimmed);
|
|
3784
|
-
}
|
|
3785
|
-
if (lowered.startsWith("rm ")) {
|
|
3786
|
-
return normalizeRemoveSegment(trimmed);
|
|
3787
|
-
}
|
|
3788
|
-
if (lowered.startsWith("cp ")) {
|
|
3789
|
-
return normalizeCopySegment(trimmed);
|
|
3790
|
-
}
|
|
3791
|
-
if (lowered.startsWith("mv ")) {
|
|
3792
|
-
return normalizeMoveSegment(trimmed);
|
|
3793
|
-
}
|
|
3794
|
-
if (lowered.startsWith("touch ")) {
|
|
3795
|
-
return normalizeTouchSegment(trimmed);
|
|
4660
|
+
};
|
|
3796
4661
|
}
|
|
3797
|
-
|
|
3798
|
-
|
|
4662
|
+
function queueWrite(chunk) {
|
|
4663
|
+
if (!absoluteOutputPath) {
|
|
4664
|
+
return;
|
|
4665
|
+
}
|
|
4666
|
+
pendingWrite = pendingWrite.then(() => fs10.appendFile(absoluteOutputPath, chunk, "utf8"));
|
|
3799
4667
|
}
|
|
3800
|
-
return
|
|
4668
|
+
return {
|
|
4669
|
+
append,
|
|
4670
|
+
finalize
|
|
4671
|
+
};
|
|
3801
4672
|
}
|
|
3802
|
-
function
|
|
3803
|
-
const
|
|
3804
|
-
const
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
const targetPath = paths[0];
|
|
3808
|
-
let command = "Get-ChildItem";
|
|
3809
|
-
if (force) {
|
|
3810
|
-
command += " -Force";
|
|
3811
|
-
}
|
|
3812
|
-
if (targetPath) {
|
|
3813
|
-
command += ` -LiteralPath ${quotePowerShell(normalizeWindowsPath(targetPath))}`;
|
|
3814
|
-
}
|
|
3815
|
-
return command;
|
|
4673
|
+
async function createAbsoluteOutputPath(stateRootDir, sessionId) {
|
|
4674
|
+
const paths = getProjectStatePaths(stateRootDir);
|
|
4675
|
+
const sessionDir = path11.join(paths.observabilityDir, "command-output", sessionId);
|
|
4676
|
+
await fs10.mkdir(sessionDir, { recursive: true });
|
|
4677
|
+
return path11.join(sessionDir, `${Date.now()}-bash-output-${crypto2.randomUUID().slice(0, 8)}.txt`);
|
|
3816
4678
|
}
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
4679
|
+
|
|
4680
|
+
// src/utils/commandRunner/shellRuntime.ts
|
|
4681
|
+
function getShellRuntimeInfo(platform = process.platform) {
|
|
4682
|
+
if (platform === "win32") {
|
|
4683
|
+
return {
|
|
4684
|
+
platform,
|
|
4685
|
+
shell: "powershell",
|
|
4686
|
+
executable: "powershell.exe",
|
|
4687
|
+
invocation: "powershell.exe -NoLogo -NoProfile -EncodedCommand <command>",
|
|
4688
|
+
guidance: "Windows runs commands through PowerShell; use PowerShell syntax, semicolon-separated commands, node -e, python -c, or an explicit bash invocation for Bash/POSIX syntax."
|
|
4689
|
+
};
|
|
3821
4690
|
}
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
4691
|
+
return {
|
|
4692
|
+
platform,
|
|
4693
|
+
shell: "bash",
|
|
4694
|
+
executable: "/bin/bash",
|
|
4695
|
+
invocation: "/bin/bash -lc <command>",
|
|
4696
|
+
guidance: "Unix-like platforms run commands through bash -lc; use Bash syntax."
|
|
4697
|
+
};
|
|
4698
|
+
}
|
|
4699
|
+
|
|
4700
|
+
// src/utils/commandRunner/launch.ts
|
|
4701
|
+
async function launchCommand(command, cwd, timeoutMs, abortSignal) {
|
|
4702
|
+
const execa4 = await loadExeca();
|
|
4703
|
+
const shell = getShellRuntimeInfo();
|
|
4704
|
+
const subprocess = shell.shell === "powershell" ? execa4(shell.executable, ["-NoLogo", "-NoProfile", "-EncodedCommand", encodePowerShellCommand(command)], {
|
|
4705
|
+
cwd,
|
|
4706
|
+
timeout: timeoutMs,
|
|
4707
|
+
cancelSignal: abortSignal,
|
|
4708
|
+
all: true,
|
|
4709
|
+
buffer: false,
|
|
4710
|
+
reject: false,
|
|
4711
|
+
env: buildCommandEnvironment()
|
|
4712
|
+
}) : execa4(shell.executable, ["-lc", command], {
|
|
4713
|
+
cwd,
|
|
4714
|
+
timeout: timeoutMs,
|
|
4715
|
+
cancelSignal: abortSignal,
|
|
4716
|
+
all: true,
|
|
4717
|
+
buffer: false,
|
|
4718
|
+
reject: false,
|
|
4719
|
+
env: buildCommandEnvironment()
|
|
3831
4720
|
});
|
|
3832
|
-
|
|
3833
|
-
if (!needsNormalization) {
|
|
3834
|
-
return segment;
|
|
3835
|
-
}
|
|
3836
|
-
const expanded = expandPaths(paths);
|
|
3837
|
-
if (expanded.length === 0) {
|
|
3838
|
-
return segment;
|
|
3839
|
-
}
|
|
3840
|
-
const normalizedPaths = expanded.map((targetPath) => quotePowerShell(normalizeWindowsPath(targetPath)));
|
|
3841
|
-
return `New-Item -ItemType Directory -Force -Path ${normalizedPaths.join(", ")}`;
|
|
3842
|
-
}
|
|
3843
|
-
function normalizeRemoveSegment(segment) {
|
|
3844
|
-
const args = splitArgs(segment);
|
|
3845
|
-
if (args.length <= 1) {
|
|
3846
|
-
return segment;
|
|
3847
|
-
}
|
|
3848
|
-
const flags = args.slice(1).filter((arg) => arg.startsWith("-"));
|
|
3849
|
-
const paths = args.slice(1).filter((arg) => !arg.startsWith("-"));
|
|
3850
|
-
if (paths.length === 0) {
|
|
3851
|
-
return segment;
|
|
3852
|
-
}
|
|
3853
|
-
const recurse = flags.some((flag) => /r/i.test(flag));
|
|
3854
|
-
const force = flags.some((flag) => /f/i.test(flag));
|
|
3855
|
-
let command = "Remove-Item";
|
|
3856
|
-
if (recurse) {
|
|
3857
|
-
command += " -Recurse";
|
|
3858
|
-
}
|
|
3859
|
-
if (force) {
|
|
3860
|
-
command += " -Force";
|
|
3861
|
-
}
|
|
3862
|
-
command += ` -LiteralPath ${paths.map((targetPath) => quotePowerShell(normalizeWindowsPath(targetPath))).join(", ")}`;
|
|
3863
|
-
return command;
|
|
3864
|
-
}
|
|
3865
|
-
function normalizeCopySegment(segment) {
|
|
3866
|
-
const args = splitArgs(segment);
|
|
3867
|
-
if (args.length < 3) {
|
|
3868
|
-
return segment;
|
|
3869
|
-
}
|
|
3870
|
-
const flags = args.slice(1).filter((arg) => arg.startsWith("-"));
|
|
3871
|
-
const paths = args.slice(1).filter((arg) => !arg.startsWith("-"));
|
|
3872
|
-
if (paths.length < 2) {
|
|
3873
|
-
return segment;
|
|
3874
|
-
}
|
|
3875
|
-
const recurse = flags.some((flag) => /r/i.test(flag));
|
|
3876
|
-
const force = flags.some((flag) => /f/i.test(flag));
|
|
3877
|
-
const destination = paths[paths.length - 1];
|
|
3878
|
-
if (!destination) {
|
|
3879
|
-
return segment;
|
|
3880
|
-
}
|
|
3881
|
-
const sources = paths.slice(0, -1);
|
|
3882
|
-
let command = "Copy-Item";
|
|
3883
|
-
if (recurse) {
|
|
3884
|
-
command += " -Recurse";
|
|
3885
|
-
}
|
|
3886
|
-
if (force) {
|
|
3887
|
-
command += " -Force";
|
|
3888
|
-
}
|
|
3889
|
-
command += ` -Path ${sources.map((targetPath) => quotePowerShell(normalizeWindowsPath(targetPath))).join(", ")}`;
|
|
3890
|
-
command += ` -Destination ${quotePowerShell(normalizeWindowsPath(destination))}`;
|
|
3891
|
-
return command;
|
|
3892
|
-
}
|
|
3893
|
-
function normalizeMoveSegment(segment) {
|
|
3894
|
-
const args = splitArgs(segment);
|
|
3895
|
-
if (args.length < 3) {
|
|
3896
|
-
return segment;
|
|
3897
|
-
}
|
|
3898
|
-
const flags = args.slice(1).filter((arg) => arg.startsWith("-"));
|
|
3899
|
-
const paths = args.slice(1).filter((arg) => !arg.startsWith("-"));
|
|
3900
|
-
if (paths.length < 2) {
|
|
3901
|
-
return segment;
|
|
3902
|
-
}
|
|
3903
|
-
const force = flags.some((flag) => /f/i.test(flag));
|
|
3904
|
-
const destination = paths[paths.length - 1];
|
|
3905
|
-
if (!destination) {
|
|
3906
|
-
return segment;
|
|
3907
|
-
}
|
|
3908
|
-
const sources = paths.slice(0, -1);
|
|
3909
|
-
let command = "Move-Item";
|
|
3910
|
-
if (force) {
|
|
3911
|
-
command += " -Force";
|
|
3912
|
-
}
|
|
3913
|
-
command += ` -Path ${sources.map((targetPath) => quotePowerShell(normalizeWindowsPath(targetPath))).join(", ")}`;
|
|
3914
|
-
command += ` -Destination ${quotePowerShell(normalizeWindowsPath(destination))}`;
|
|
3915
|
-
return command;
|
|
3916
|
-
}
|
|
3917
|
-
function normalizeTouchSegment(segment) {
|
|
3918
|
-
const args = splitArgs(segment).slice(1);
|
|
3919
|
-
if (args.length === 0) {
|
|
3920
|
-
return segment;
|
|
3921
|
-
}
|
|
3922
|
-
const expanded = expandPaths(args);
|
|
3923
|
-
if (expanded.length === 0) {
|
|
3924
|
-
return segment;
|
|
3925
|
-
}
|
|
3926
|
-
const paths = expanded.map((targetPath) => quotePowerShell(normalizeWindowsPath(targetPath)));
|
|
3927
|
-
return `New-Item -ItemType File -Force -Path ${paths.join(", ")}`;
|
|
3928
|
-
}
|
|
3929
|
-
function normalizeCatSegment(segment) {
|
|
3930
|
-
const args = splitArgs(segment).slice(1);
|
|
3931
|
-
if (args.length === 0) {
|
|
3932
|
-
return segment;
|
|
3933
|
-
}
|
|
3934
|
-
const targetPath = args[0];
|
|
3935
|
-
if (!targetPath) {
|
|
3936
|
-
return segment;
|
|
3937
|
-
}
|
|
3938
|
-
return `Get-Content -LiteralPath ${quotePowerShell(normalizeWindowsPath(targetPath))}`;
|
|
3939
|
-
}
|
|
3940
|
-
|
|
3941
|
-
// src/utils/commandRunner/platform.ts
|
|
3942
|
-
function normalizeCommandForPlatform(command) {
|
|
3943
|
-
if (process.platform !== "win32") {
|
|
3944
|
-
return command;
|
|
3945
|
-
}
|
|
3946
|
-
const trimmed = command.trim();
|
|
3947
|
-
if (!trimmed) {
|
|
3948
|
-
return command;
|
|
3949
|
-
}
|
|
3950
|
-
const normalized = normalizeWindowsCommand(trimmed);
|
|
3951
|
-
return normalizeNpmCommandNames(normalized);
|
|
4721
|
+
return { subprocess };
|
|
3952
4722
|
}
|
|
3953
|
-
function
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
4723
|
+
function encodePowerShellCommand(command) {
|
|
4724
|
+
const wrapped = [
|
|
4725
|
+
"$ProgressPreference = 'SilentlyContinue'",
|
|
4726
|
+
"$ErrorActionPreference = 'Stop'",
|
|
4727
|
+
"[Console]::InputEncoding = [System.Text.Encoding]::UTF8",
|
|
4728
|
+
"[Console]::OutputEncoding = [System.Text.Encoding]::UTF8",
|
|
4729
|
+
"$OutputEncoding = [System.Text.Encoding]::UTF8",
|
|
4730
|
+
"try { chcp 65001 > $null } catch { }",
|
|
4731
|
+
"$code = 0",
|
|
4732
|
+
"try {",
|
|
4733
|
+
`& { ${command} }`,
|
|
4734
|
+
"$code = if ($null -ne $LASTEXITCODE) { [int]$LASTEXITCODE } elseif ($?) { 0 } else { 1 }",
|
|
4735
|
+
"} catch {",
|
|
4736
|
+
"[Console]::Error.WriteLine($_.Exception.Message)",
|
|
4737
|
+
"$code = 1",
|
|
4738
|
+
"}",
|
|
4739
|
+
"exit $code"
|
|
4740
|
+
].join("\n");
|
|
4741
|
+
return Buffer.from(wrapped, "utf16le").toString("base64");
|
|
3960
4742
|
}
|
|
3961
|
-
function
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
4743
|
+
function buildCommandEnvironment() {
|
|
4744
|
+
return {
|
|
4745
|
+
...process.env,
|
|
4746
|
+
LANG: process.env.LANG || "C.UTF-8",
|
|
4747
|
+
LC_ALL: process.env.LC_ALL || "C.UTF-8",
|
|
4748
|
+
PYTHONIOENCODING: process.env.PYTHONIOENCODING || "utf-8"
|
|
3967
4749
|
};
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
return
|
|
3978
|
-
}
|
|
4750
|
+
}
|
|
4751
|
+
|
|
4752
|
+
// src/utils/commandRunner/output.ts
|
|
4753
|
+
function normalizeCommandOutput(output) {
|
|
4754
|
+
if (!output.includes("#< CLIXML")) {
|
|
4755
|
+
return output;
|
|
4756
|
+
}
|
|
4757
|
+
const errors = [...output.matchAll(/<S\s+S="Error">([\s\S]*?)<\/S>/g)].map((match) => decodePowerShellText(match[1] ?? "")).map((line) => line.trimEnd()).filter(Boolean);
|
|
4758
|
+
if (errors.length === 0) {
|
|
4759
|
+
return output;
|
|
4760
|
+
}
|
|
4761
|
+
return errors.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
4762
|
+
}
|
|
4763
|
+
function decodePowerShellText(value) {
|
|
4764
|
+
return value.replace(/_x000D__x000A_/g, "\n").replace(/_x000D_/g, "\r").replace(/_x000A_/g, "\n").replace(/_x0009_/g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
3979
4765
|
}
|
|
3980
4766
|
|
|
3981
4767
|
// src/utils/commandRunner/run.ts
|
|
3982
4768
|
var STALL_KILL_TIMEOUT_MS = 5e3;
|
|
3983
4769
|
async function runCommandWithPolicy(options) {
|
|
3984
|
-
|
|
3985
|
-
return runCommandOnce({
|
|
3986
|
-
...options,
|
|
3987
|
-
command: normalizedCommand
|
|
3988
|
-
});
|
|
4770
|
+
return runCommandOnce(options);
|
|
3989
4771
|
}
|
|
3990
4772
|
async function runCommandOnce(options) {
|
|
3991
4773
|
const start = Date.now();
|
|
3992
4774
|
let stalled = false;
|
|
3993
4775
|
let stallTimer = null;
|
|
3994
4776
|
let forceKillTimer = null;
|
|
3995
|
-
const
|
|
4777
|
+
const launched = await launchCommand(options.command, options.cwd, options.timeoutMs, options.abortSignal);
|
|
4778
|
+
const { subprocess } = launched;
|
|
3996
4779
|
const outputCapture = await createBashOutputCapture(options.outputCapture ?? {});
|
|
3997
4780
|
const clearTimers = () => {
|
|
3998
4781
|
if (stallTimer) {
|
|
@@ -4045,9 +4828,11 @@ async function runCommandOnce(options) {
|
|
|
4045
4828
|
const result = await subprocess;
|
|
4046
4829
|
clearTimers();
|
|
4047
4830
|
const shellOutput = await outputCapture.finalize();
|
|
4831
|
+
const output = normalizeCommandOutput(shellOutput.outputPreview);
|
|
4048
4832
|
return {
|
|
4833
|
+
command: options.command,
|
|
4049
4834
|
exitCode: typeof result.exitCode === "number" ? result.exitCode : null,
|
|
4050
|
-
output
|
|
4835
|
+
output,
|
|
4051
4836
|
outputPath: shellOutput.outputPath,
|
|
4052
4837
|
truncated: shellOutput.truncated,
|
|
4053
4838
|
outputChars: shellOutput.outputChars,
|
|
@@ -4062,9 +4847,12 @@ async function runCommandOnce(options) {
|
|
|
4062
4847
|
const timedOut = isTimedOutError(error);
|
|
4063
4848
|
clearTimers();
|
|
4064
4849
|
const shellOutput = await outputCapture.finalize();
|
|
4850
|
+
const fallbackOutput = shellOutput.outputChars > 0 ? shellOutput.outputPreview : readProcessOutput(error);
|
|
4851
|
+
const output = normalizeCommandOutput(fallbackOutput);
|
|
4065
4852
|
return {
|
|
4853
|
+
command: options.command,
|
|
4066
4854
|
exitCode: readExitCode(error),
|
|
4067
|
-
output
|
|
4855
|
+
output,
|
|
4068
4856
|
outputPath: shellOutput.outputPath,
|
|
4069
4857
|
truncated: shellOutput.truncated,
|
|
4070
4858
|
outputChars: shellOutput.outputChars,
|
|
@@ -4100,8 +4888,8 @@ function isAbortedProcessResult(value, signal) {
|
|
|
4100
4888
|
}
|
|
4101
4889
|
|
|
4102
4890
|
// src/tools/core/shared.ts
|
|
4103
|
-
import
|
|
4104
|
-
import
|
|
4891
|
+
import fs11 from "fs/promises";
|
|
4892
|
+
import path12 from "path";
|
|
4105
4893
|
import { diffLines } from "diff";
|
|
4106
4894
|
|
|
4107
4895
|
// src/utils/text.ts
|
|
@@ -4315,15 +5103,15 @@ function comparePathForDiscovery(root, left, right) {
|
|
|
4315
5103
|
if (leftDepth !== rightDepth) {
|
|
4316
5104
|
return leftDepth - rightDepth;
|
|
4317
5105
|
}
|
|
4318
|
-
const leftName =
|
|
4319
|
-
const rightName =
|
|
5106
|
+
const leftName = path12.posix.basename(leftRelative).toLowerCase();
|
|
5107
|
+
const rightName = path12.posix.basename(rightRelative).toLowerCase();
|
|
4320
5108
|
if (leftName !== rightName) {
|
|
4321
5109
|
return leftName.localeCompare(rightName);
|
|
4322
5110
|
}
|
|
4323
5111
|
return leftRelative.localeCompare(rightRelative);
|
|
4324
5112
|
}
|
|
4325
5113
|
function toPosixRelative(root, targetPath) {
|
|
4326
|
-
return (
|
|
5114
|
+
return (path12.relative(root, targetPath) || path12.basename(targetPath)).replace(/\\/g, "/");
|
|
4327
5115
|
}
|
|
4328
5116
|
function pathDepth(relativePath) {
|
|
4329
5117
|
return relativePath.split("/").filter(Boolean).length;
|
|
@@ -4625,7 +5413,7 @@ var bashToolDefinition = {
|
|
|
4625
5413
|
const status = result.aborted ? "aborted" : result.stalled ? "stalled" : result.timedOut ? "timed_out" : result.exitCode === 0 ? "completed" : "failed";
|
|
4626
5414
|
const outputGovernance = governToolOutput({
|
|
4627
5415
|
toolName: "bash",
|
|
4628
|
-
command,
|
|
5416
|
+
command: result.command,
|
|
4629
5417
|
status,
|
|
4630
5418
|
exitCode: result.exitCode,
|
|
4631
5419
|
durationMs: result.durationMs,
|
|
@@ -4653,7 +5441,7 @@ var bashToolDefinition = {
|
|
|
4653
5441
|
return okResult(
|
|
4654
5442
|
JSON.stringify(
|
|
4655
5443
|
{
|
|
4656
|
-
command,
|
|
5444
|
+
command: result.command,
|
|
4657
5445
|
cwd: resolvedCwd,
|
|
4658
5446
|
exitCode: result.exitCode,
|
|
4659
5447
|
status,
|
|
@@ -4684,7 +5472,7 @@ var bashToolDefinition = {
|
|
|
4684
5472
|
};
|
|
4685
5473
|
|
|
4686
5474
|
// src/tools/edit.ts
|
|
4687
|
-
import
|
|
5475
|
+
import fs13 from "fs/promises";
|
|
4688
5476
|
|
|
4689
5477
|
// src/tools/core/changeTracking.ts
|
|
4690
5478
|
async function recordToolChange(context, input) {
|
|
@@ -4725,13 +5513,13 @@ function encodeUtf8(value) {
|
|
|
4725
5513
|
}
|
|
4726
5514
|
|
|
4727
5515
|
// src/tools/core/pathDisplay.ts
|
|
4728
|
-
import
|
|
5516
|
+
import path13 from "path";
|
|
4729
5517
|
function toToolRelativePath(cwd, targetPath) {
|
|
4730
|
-
const relative =
|
|
5518
|
+
const relative = path13.relative(cwd, targetPath);
|
|
4731
5519
|
if (!relative || relative === "") {
|
|
4732
5520
|
return ".";
|
|
4733
5521
|
}
|
|
4734
|
-
if (relative.startsWith("..") ||
|
|
5522
|
+
if (relative.startsWith("..") || path13.isAbsolute(relative)) {
|
|
4735
5523
|
return targetPath;
|
|
4736
5524
|
}
|
|
4737
5525
|
return relative.replace(/\\/g, "/");
|
|
@@ -4758,8 +5546,8 @@ function buildToolChangeFeedback(input) {
|
|
|
4758
5546
|
}
|
|
4759
5547
|
|
|
4760
5548
|
// src/tools/writeDiagnostics.ts
|
|
4761
|
-
import
|
|
4762
|
-
import
|
|
5549
|
+
import fs12 from "fs/promises";
|
|
5550
|
+
import path14 from "path";
|
|
4763
5551
|
var TYPE_SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
4764
5552
|
async function collectWriteDiagnostics(paths) {
|
|
4765
5553
|
const uniquePaths2 = takeUniquePaths(paths);
|
|
@@ -4806,7 +5594,7 @@ function createEmptyDiagnosticsReport(status) {
|
|
|
4806
5594
|
}
|
|
4807
5595
|
async function collectFileDiagnostics(targetPath) {
|
|
4808
5596
|
try {
|
|
4809
|
-
const extension =
|
|
5597
|
+
const extension = path14.extname(targetPath).toLowerCase();
|
|
4810
5598
|
if (extension === ".json") {
|
|
4811
5599
|
return await collectJsonDiagnostics(targetPath);
|
|
4812
5600
|
}
|
|
@@ -4822,7 +5610,7 @@ async function collectFileDiagnostics(targetPath) {
|
|
|
4822
5610
|
return null;
|
|
4823
5611
|
}
|
|
4824
5612
|
async function collectJsonDiagnostics(targetPath) {
|
|
4825
|
-
const content = await
|
|
5613
|
+
const content = await fs12.readFile(targetPath, "utf8");
|
|
4826
5614
|
const diagnostics = [];
|
|
4827
5615
|
try {
|
|
4828
5616
|
JSON.parse(content);
|
|
@@ -4843,7 +5631,7 @@ async function collectJsonDiagnostics(targetPath) {
|
|
|
4843
5631
|
async function collectTypeScriptDiagnostics(targetPath) {
|
|
4844
5632
|
const diagnostics = [];
|
|
4845
5633
|
const TypeScript = await import("typescript");
|
|
4846
|
-
const content = await
|
|
5634
|
+
const content = await fs12.readFile(targetPath, "utf8");
|
|
4847
5635
|
const transpiled = TypeScript.transpileModule(content, {
|
|
4848
5636
|
fileName: targetPath,
|
|
4849
5637
|
compilerOptions: {
|
|
@@ -4960,7 +5748,7 @@ var editToolDefinition = {
|
|
|
4960
5748
|
const resolved = resolveUserPath(targetPath, context.cwd);
|
|
4961
5749
|
const displayPath = toToolRelativePath(context.cwd, resolved);
|
|
4962
5750
|
return withFileEditLock(resolved, async () => {
|
|
4963
|
-
const beforeBuffer = await
|
|
5751
|
+
const beforeBuffer = await fs13.readFile(resolved);
|
|
4964
5752
|
const beforeEnvelope = decodeTextFileEnvelope(beforeBuffer);
|
|
4965
5753
|
if (!beforeEnvelope) {
|
|
4966
5754
|
throw new ToolExecutionError(`edit cannot edit binary or unsupported text encoding for ${displayPath}`, {
|
|
@@ -4978,7 +5766,7 @@ var editToolDefinition = {
|
|
|
4978
5766
|
});
|
|
4979
5767
|
}
|
|
4980
5768
|
const diff = buildDiffPreview(before, after);
|
|
4981
|
-
await
|
|
5769
|
+
await fs13.writeFile(resolved, encodeTextFileEnvelope(after, beforeEnvelope));
|
|
4982
5770
|
const changeRecord = await recordToolChange(context, {
|
|
4983
5771
|
toolName: "edit",
|
|
4984
5772
|
summary: `edit ${displayPath}`,
|
|
@@ -5132,9 +5920,9 @@ function lineForOffset(input, offset) {
|
|
|
5132
5920
|
}
|
|
5133
5921
|
return line;
|
|
5134
5922
|
}
|
|
5135
|
-
function buildReadArgs(
|
|
5923
|
+
function buildReadArgs(path29, line) {
|
|
5136
5924
|
return {
|
|
5137
|
-
path:
|
|
5925
|
+
path: path29,
|
|
5138
5926
|
offset: Math.max(1, (line ?? 1) - 20),
|
|
5139
5927
|
limit: 60
|
|
5140
5928
|
};
|
|
@@ -5188,8 +5976,8 @@ async function withFileEditLock(filePath, action) {
|
|
|
5188
5976
|
}
|
|
5189
5977
|
|
|
5190
5978
|
// src/tools/core/fileIntrospection.ts
|
|
5191
|
-
import
|
|
5192
|
-
import
|
|
5979
|
+
import fs14 from "fs/promises";
|
|
5980
|
+
import path15 from "path";
|
|
5193
5981
|
var KNOWN_BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
5194
5982
|
".epub",
|
|
5195
5983
|
".mobi",
|
|
@@ -5207,8 +5995,8 @@ var KNOWN_BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
5207
5995
|
".bin"
|
|
5208
5996
|
]);
|
|
5209
5997
|
async function inspectTextFile(filePath, _maxBytes) {
|
|
5210
|
-
const stat = await
|
|
5211
|
-
const extension =
|
|
5998
|
+
const stat = await fs14.stat(filePath);
|
|
5999
|
+
const extension = path15.extname(filePath).toLowerCase();
|
|
5212
6000
|
if (KNOWN_BINARY_EXTENSIONS.has(extension)) {
|
|
5213
6001
|
return {
|
|
5214
6002
|
readable: false,
|
|
@@ -5218,7 +6006,7 @@ async function inspectTextFile(filePath, _maxBytes) {
|
|
|
5218
6006
|
extension
|
|
5219
6007
|
};
|
|
5220
6008
|
}
|
|
5221
|
-
const buffer = await
|
|
6009
|
+
const buffer = await fs14.readFile(filePath);
|
|
5222
6010
|
const decoded = decodeTextFileEnvelope(buffer);
|
|
5223
6011
|
if (!decoded) {
|
|
5224
6012
|
return {
|
|
@@ -5242,11 +6030,11 @@ async function inspectTextFile(filePath, _maxBytes) {
|
|
|
5242
6030
|
}
|
|
5243
6031
|
|
|
5244
6032
|
// src/tools/core/pathSuggestions.ts
|
|
5245
|
-
import
|
|
6033
|
+
import path16 from "path";
|
|
5246
6034
|
import fg3 from "fast-glob";
|
|
5247
6035
|
async function findPathSuggestions(cwd, requestedPath, projectContext, limit = 8) {
|
|
5248
6036
|
const normalized = normalizeUserPathInput(requestedPath).replace(/\\/g, "/");
|
|
5249
|
-
const baseName =
|
|
6037
|
+
const baseName = path16.basename(normalized).trim();
|
|
5250
6038
|
const needle = baseName.length > 0 ? baseName : normalized.trim();
|
|
5251
6039
|
if (!needle) {
|
|
5252
6040
|
return [];
|
|
@@ -5263,8 +6051,8 @@ async function findPathSuggestions(cwd, requestedPath, projectContext, limit = 8
|
|
|
5263
6051
|
suppressErrors: true,
|
|
5264
6052
|
ignore: buildFastGlobIgnorePatterns(cwd, projectContext.ignoreRules)
|
|
5265
6053
|
});
|
|
5266
|
-
for (const entry of entries.sort((left, right) => comparePathForDiscovery(cwd,
|
|
5267
|
-
const absolutePath =
|
|
6054
|
+
for (const entry of entries.sort((left, right) => comparePathForDiscovery(cwd, path16.resolve(cwd, left), path16.resolve(cwd, right)))) {
|
|
6055
|
+
const absolutePath = path16.resolve(cwd, entry);
|
|
5268
6056
|
const isDirectory3 = entry.endsWith("/");
|
|
5269
6057
|
if (isPathIgnored(absolutePath, projectContext.ignoreRules, isDirectory3)) {
|
|
5270
6058
|
continue;
|
|
@@ -5429,7 +6217,7 @@ function fitWindowWithinBudget(lines, start, requestedEndExclusive, maxChars) {
|
|
|
5429
6217
|
}
|
|
5430
6218
|
|
|
5431
6219
|
// src/tools/write.ts
|
|
5432
|
-
import
|
|
6220
|
+
import fs15 from "fs/promises";
|
|
5433
6221
|
var writeToolDefinition = {
|
|
5434
6222
|
definition: {
|
|
5435
6223
|
type: "function",
|
|
@@ -5465,12 +6253,12 @@ var writeToolDefinition = {
|
|
|
5465
6253
|
const resolved = resolveUserPath(targetPath, context.cwd);
|
|
5466
6254
|
const displayPath = toToolRelativePath(context.cwd, resolved);
|
|
5467
6255
|
const existed = await fileExists(resolved);
|
|
5468
|
-
const before = existed ? await
|
|
6256
|
+
const before = existed ? await fs15.readFile(resolved, "utf8") : "";
|
|
5469
6257
|
const preview = buildDiffPreview(before, content);
|
|
5470
6258
|
if (createDirectories) {
|
|
5471
6259
|
await ensureParentDirectory(resolved);
|
|
5472
6260
|
}
|
|
5473
|
-
await
|
|
6261
|
+
await fs15.writeFile(resolved, content, "utf8");
|
|
5474
6262
|
const changeRecord = await recordToolChange(context, {
|
|
5475
6263
|
toolName: "write",
|
|
5476
6264
|
summary: `write ${displayPath}`,
|
|
@@ -5525,7 +6313,7 @@ var writeToolDefinition = {
|
|
|
5525
6313
|
};
|
|
5526
6314
|
|
|
5527
6315
|
// src/tools/sendFile.ts
|
|
5528
|
-
import
|
|
6316
|
+
import fs16 from "fs/promises";
|
|
5529
6317
|
var sendFileToolDefinition = {
|
|
5530
6318
|
definition: {
|
|
5531
6319
|
type: "function",
|
|
@@ -5568,7 +6356,7 @@ var sendFileToolDefinition = {
|
|
|
5568
6356
|
};
|
|
5569
6357
|
}
|
|
5570
6358
|
try {
|
|
5571
|
-
await
|
|
6359
|
+
await fs16.access(filePath);
|
|
5572
6360
|
} catch {
|
|
5573
6361
|
return {
|
|
5574
6362
|
ok: false,
|
|
@@ -6100,6 +6888,53 @@ async function executeToolBatch(params) {
|
|
|
6100
6888
|
};
|
|
6101
6889
|
}
|
|
6102
6890
|
|
|
6891
|
+
// src/session/events.ts
|
|
6892
|
+
import fs17 from "fs/promises";
|
|
6893
|
+
import path17 from "path";
|
|
6894
|
+
var SessionEventStore = class {
|
|
6895
|
+
constructor(eventsDir) {
|
|
6896
|
+
this.eventsDir = eventsDir;
|
|
6897
|
+
}
|
|
6898
|
+
async append(event) {
|
|
6899
|
+
const record = {
|
|
6900
|
+
id: createEventId(),
|
|
6901
|
+
createdAt: event.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
6902
|
+
type: event.type,
|
|
6903
|
+
sessionId: event.sessionId,
|
|
6904
|
+
cwd: event.cwd,
|
|
6905
|
+
host: event.host,
|
|
6906
|
+
message: event.message,
|
|
6907
|
+
details: event.details
|
|
6908
|
+
};
|
|
6909
|
+
await fs17.mkdir(this.eventsDir, { recursive: true });
|
|
6910
|
+
await fs17.appendFile(this.getSessionEventPath(event.sessionId), `${JSON.stringify(record)}
|
|
6911
|
+
`, "utf8");
|
|
6912
|
+
return record;
|
|
6913
|
+
}
|
|
6914
|
+
async list(sessionId, limit = 100) {
|
|
6915
|
+
const filePath = this.getSessionEventPath(sessionId);
|
|
6916
|
+
let raw = "";
|
|
6917
|
+
try {
|
|
6918
|
+
raw = await fs17.readFile(filePath, "utf8");
|
|
6919
|
+
} catch (error) {
|
|
6920
|
+
if (error.code === "ENOENT") {
|
|
6921
|
+
return [];
|
|
6922
|
+
}
|
|
6923
|
+
throw error;
|
|
6924
|
+
}
|
|
6925
|
+
return raw.split(/\r?\n/).filter(Boolean).map((line) => JSON.parse(line)).slice(-limit);
|
|
6926
|
+
}
|
|
6927
|
+
getSessionEventPath(sessionId) {
|
|
6928
|
+
return path17.join(this.eventsDir, `${sanitizeSessionId(sessionId)}.jsonl`);
|
|
6929
|
+
}
|
|
6930
|
+
};
|
|
6931
|
+
function createEventId() {
|
|
6932
|
+
return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:.TZ]/g, "").slice(0, 14)}-${Math.random().toString(16).slice(2, 10)}`;
|
|
6933
|
+
}
|
|
6934
|
+
function sanitizeSessionId(sessionId) {
|
|
6935
|
+
return sessionId.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
6936
|
+
}
|
|
6937
|
+
|
|
6103
6938
|
// src/agent/turn/toolFailure.ts
|
|
6104
6939
|
function readToolFailureError(output) {
|
|
6105
6940
|
try {
|
|
@@ -6135,9 +6970,16 @@ async function processToolCallBatch(input) {
|
|
|
6135
6970
|
const batchToolMessages = [];
|
|
6136
6971
|
const batchModelOutputs = [];
|
|
6137
6972
|
const batchChangedPaths = /* @__PURE__ */ new Set();
|
|
6973
|
+
const sessionEvents = new SessionEventStore(options.config.paths.eventsDir);
|
|
6138
6974
|
const leadWaitExecutionsBefore = identity.kind === "lead" ? listLeadWaitExecutions(projectContext.stateRootDir) : [];
|
|
6139
6975
|
for (const toolCall of response.toolCalls) {
|
|
6140
6976
|
throwIfAborted(options.abortSignal, "Turn aborted by user.");
|
|
6977
|
+
await sessionEvents.append({
|
|
6978
|
+
type: "tool.started",
|
|
6979
|
+
sessionId: session.id,
|
|
6980
|
+
cwd: options.cwd,
|
|
6981
|
+
details: buildToolStartedEventDetails(toolCall, identity)
|
|
6982
|
+
});
|
|
6141
6983
|
options.callbacks?.onToolCall?.(toolCall.function.name, toolCall.function.arguments);
|
|
6142
6984
|
await recordObservabilityEvent(projectContext.stateRootDir, {
|
|
6143
6985
|
event: "tool.execution",
|
|
@@ -6171,6 +7013,7 @@ async function processToolCallBatch(input) {
|
|
|
6171
7013
|
} else if (metadata?.sessionDiff) {
|
|
6172
7014
|
session = await options.sessionStore.save(noteSessionDiff(session, metadata.sessionDiff));
|
|
6173
7015
|
}
|
|
7016
|
+
const failureError = result.ok ? void 0 : readToolFailureError(result.output);
|
|
6174
7017
|
await recordObservabilityEvent(projectContext.stateRootDir, {
|
|
6175
7018
|
event: "tool.execution",
|
|
6176
7019
|
status: result.ok ? "completed" : "failed",
|
|
@@ -6179,11 +7022,23 @@ async function processToolCallBatch(input) {
|
|
|
6179
7022
|
identityName: identity.name,
|
|
6180
7023
|
toolName: toolCall.function.name,
|
|
6181
7024
|
durationMs,
|
|
6182
|
-
error:
|
|
7025
|
+
error: failureError,
|
|
6183
7026
|
details: {
|
|
6184
7027
|
changedPathCount: metadata?.changedPaths?.length ?? 0
|
|
6185
7028
|
}
|
|
6186
7029
|
});
|
|
7030
|
+
await sessionEvents.append({
|
|
7031
|
+
type: result.ok ? "tool.completed" : "tool.failed",
|
|
7032
|
+
sessionId: session.id,
|
|
7033
|
+
cwd: options.cwd,
|
|
7034
|
+
details: buildToolFinishedEventDetails({
|
|
7035
|
+
toolCall,
|
|
7036
|
+
identity,
|
|
7037
|
+
durationMs,
|
|
7038
|
+
changedPathCount: metadata?.changedPaths?.length ?? 0,
|
|
7039
|
+
error: failureError ? formatToolFailureError(failureError) : void 0
|
|
7040
|
+
})
|
|
7041
|
+
});
|
|
6187
7042
|
if (metadata?.outputGovernance) {
|
|
6188
7043
|
await recordObservabilityEvent(projectContext.stateRootDir, {
|
|
6189
7044
|
event: "tool.output",
|
|
@@ -6277,6 +7132,33 @@ async function processToolCallBatch(input) {
|
|
|
6277
7132
|
}
|
|
6278
7133
|
};
|
|
6279
7134
|
}
|
|
7135
|
+
function buildToolStartedEventDetails(toolCall, identity) {
|
|
7136
|
+
return {
|
|
7137
|
+
toolName: toolCall.function.name,
|
|
7138
|
+
toolCallId: toolCall.id,
|
|
7139
|
+
identityKind: identity.kind,
|
|
7140
|
+
identityName: identity.name,
|
|
7141
|
+
argumentsPreview: previewToolArguments(toolCall.function.arguments)
|
|
7142
|
+
};
|
|
7143
|
+
}
|
|
7144
|
+
function buildToolFinishedEventDetails(input) {
|
|
7145
|
+
return {
|
|
7146
|
+
toolName: input.toolCall.function.name,
|
|
7147
|
+
toolCallId: input.toolCall.id,
|
|
7148
|
+
identityKind: input.identity.kind,
|
|
7149
|
+
identityName: input.identity.name,
|
|
7150
|
+
durationMs: input.durationMs,
|
|
7151
|
+
changedPathCount: input.changedPathCount,
|
|
7152
|
+
error: input.error
|
|
7153
|
+
};
|
|
7154
|
+
}
|
|
7155
|
+
function previewToolArguments(rawArgs) {
|
|
7156
|
+
const normalized = rawArgs.replace(/\s+/g, " ").trim();
|
|
7157
|
+
return normalized.length > 240 ? `${normalized.slice(0, 237)}...` : normalized;
|
|
7158
|
+
}
|
|
7159
|
+
function formatToolFailureError(error) {
|
|
7160
|
+
return error.code ? `${error.code}: ${error.message}` : error.message;
|
|
7161
|
+
}
|
|
6280
7162
|
|
|
6281
7163
|
// src/agent/turn/toolless.ts
|
|
6282
7164
|
async function resolveToollessTurn(params) {
|
|
@@ -6362,9 +7244,9 @@ function normalizeToolArguments(raw) {
|
|
|
6362
7244
|
}
|
|
6363
7245
|
|
|
6364
7246
|
// src/agent/changes/store.ts
|
|
6365
|
-
import
|
|
6366
|
-
import
|
|
6367
|
-
import
|
|
7247
|
+
import crypto3 from "crypto";
|
|
7248
|
+
import fs18 from "fs/promises";
|
|
7249
|
+
import path18 from "path";
|
|
6368
7250
|
var ChangeStore = class {
|
|
6369
7251
|
constructor(changesDir) {
|
|
6370
7252
|
this.changesDir = changesDir;
|
|
@@ -6372,8 +7254,8 @@ var ChangeStore = class {
|
|
|
6372
7254
|
async record(input) {
|
|
6373
7255
|
const id = createChangeId();
|
|
6374
7256
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
6375
|
-
const blobDir =
|
|
6376
|
-
await
|
|
7257
|
+
const blobDir = path18.join(this.changesDir, id);
|
|
7258
|
+
await fs18.mkdir(blobDir, { recursive: true });
|
|
6377
7259
|
const operations = await Promise.all(
|
|
6378
7260
|
input.operations.map(async (operation, index) => {
|
|
6379
7261
|
const beforeSnapshotPath = await this.writeSnapshot(
|
|
@@ -6413,21 +7295,21 @@ var ChangeStore = class {
|
|
|
6413
7295
|
preview: input.preview,
|
|
6414
7296
|
operations
|
|
6415
7297
|
};
|
|
6416
|
-
await
|
|
6417
|
-
await
|
|
7298
|
+
await fs18.mkdir(this.changesDir, { recursive: true });
|
|
7299
|
+
await fs18.writeFile(this.getMetadataPath(id), `${JSON.stringify(record, null, 2)}
|
|
6418
7300
|
`, "utf8");
|
|
6419
7301
|
return record;
|
|
6420
7302
|
}
|
|
6421
7303
|
async list(limit = 20) {
|
|
6422
|
-
await
|
|
6423
|
-
const entries = await
|
|
7304
|
+
await fs18.mkdir(this.changesDir, { recursive: true });
|
|
7305
|
+
const entries = await fs18.readdir(this.changesDir, { withFileTypes: true });
|
|
6424
7306
|
const changes = await Promise.all(
|
|
6425
|
-
entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => this.load(
|
|
7307
|
+
entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => this.load(path18.basename(entry.name, ".json")))
|
|
6426
7308
|
);
|
|
6427
7309
|
return changes.sort((left, right) => right.createdAt.localeCompare(left.createdAt)).slice(0, limit);
|
|
6428
7310
|
}
|
|
6429
7311
|
async load(id) {
|
|
6430
|
-
const raw = await
|
|
7312
|
+
const raw = await fs18.readFile(this.getMetadataPath(id), "utf8");
|
|
6431
7313
|
return JSON.parse(raw);
|
|
6432
7314
|
}
|
|
6433
7315
|
async loadLatestUndoable() {
|
|
@@ -6451,17 +7333,17 @@ var ChangeStore = class {
|
|
|
6451
7333
|
restoredPaths.push(operation.path);
|
|
6452
7334
|
if (operation.beforeSnapshotPath) {
|
|
6453
7335
|
const buffer = await this.readSnapshot(operation.beforeSnapshotPath);
|
|
6454
|
-
await
|
|
6455
|
-
await
|
|
7336
|
+
await fs18.mkdir(path18.dirname(operation.path), { recursive: true });
|
|
7337
|
+
await fs18.writeFile(operation.path, buffer);
|
|
6456
7338
|
continue;
|
|
6457
7339
|
}
|
|
6458
|
-
await
|
|
7340
|
+
await fs18.rm(operation.path, { force: true });
|
|
6459
7341
|
}
|
|
6460
7342
|
const updated = {
|
|
6461
7343
|
...record,
|
|
6462
7344
|
undoneAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6463
7345
|
};
|
|
6464
|
-
await
|
|
7346
|
+
await fs18.writeFile(this.getMetadataPath(updated.id), `${JSON.stringify(updated, null, 2)}
|
|
6465
7347
|
`, "utf8");
|
|
6466
7348
|
return {
|
|
6467
7349
|
record: updated,
|
|
@@ -6469,24 +7351,24 @@ var ChangeStore = class {
|
|
|
6469
7351
|
};
|
|
6470
7352
|
}
|
|
6471
7353
|
getMetadataPath(id) {
|
|
6472
|
-
return
|
|
7354
|
+
return path18.join(this.changesDir, `${id}.json`);
|
|
6473
7355
|
}
|
|
6474
7356
|
async writeSnapshot(blobDir, label, buffer) {
|
|
6475
7357
|
if (!buffer) {
|
|
6476
7358
|
return void 0;
|
|
6477
7359
|
}
|
|
6478
7360
|
const fileName = `${label}.bin`;
|
|
6479
|
-
const absolutePath =
|
|
6480
|
-
await
|
|
6481
|
-
return
|
|
7361
|
+
const absolutePath = path18.join(blobDir, fileName);
|
|
7362
|
+
await fs18.writeFile(absolutePath, buffer);
|
|
7363
|
+
return path18.relative(this.changesDir, absolutePath);
|
|
6482
7364
|
}
|
|
6483
7365
|
async readSnapshot(relativePath) {
|
|
6484
|
-
return
|
|
7366
|
+
return fs18.readFile(path18.join(this.changesDir, relativePath));
|
|
6485
7367
|
}
|
|
6486
7368
|
};
|
|
6487
7369
|
function createChangeId() {
|
|
6488
7370
|
const date = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
|
|
6489
|
-
const random =
|
|
7371
|
+
const random = crypto3.randomUUID().slice(0, 8);
|
|
6490
7372
|
return `${date}-${random}`;
|
|
6491
7373
|
}
|
|
6492
7374
|
|
|
@@ -6867,9 +7749,10 @@ var backgroundRunTool = {
|
|
|
6867
7749
|
registerBackgroundProcess(job.id, subprocess);
|
|
6868
7750
|
store.markRunning(job.id, { pid: subprocess.pid ?? 0 });
|
|
6869
7751
|
const outputTracker = createBackgroundOutputTracker((output) => {
|
|
7752
|
+
const normalizedOutput = normalizeCommandOutput(output);
|
|
6870
7753
|
store.updateRunningOutput(job.id, {
|
|
6871
|
-
output,
|
|
6872
|
-
summary: summarizeBackgroundOutput(
|
|
7754
|
+
output: normalizedOutput,
|
|
7755
|
+
summary: summarizeBackgroundOutput(normalizedOutput),
|
|
6873
7756
|
lastOutputAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6874
7757
|
});
|
|
6875
7758
|
});
|
|
@@ -6879,7 +7762,7 @@ var backgroundRunTool = {
|
|
|
6879
7762
|
void subprocess.then(async (result) => {
|
|
6880
7763
|
outputTracker.flush();
|
|
6881
7764
|
const running2 = store.load(job.id);
|
|
6882
|
-
const resultOutput = typeof result.all === "string" ? result.all : "";
|
|
7765
|
+
const resultOutput = normalizeCommandOutput(typeof result.all === "string" ? result.all : "");
|
|
6883
7766
|
const output = resultOutput || running2?.output || "";
|
|
6884
7767
|
store.close(job.id, {
|
|
6885
7768
|
status: result.exitCode === 0 ? "completed" : "failed",
|
|
@@ -6891,7 +7774,7 @@ var backgroundRunTool = {
|
|
|
6891
7774
|
}, async (error) => {
|
|
6892
7775
|
outputTracker.flush();
|
|
6893
7776
|
const running2 = store.load(job.id);
|
|
6894
|
-
const errorOutput = typeof error.all === "string" ? error.all : "";
|
|
7777
|
+
const errorOutput = normalizeCommandOutput(typeof error.all === "string" ? error.all : "");
|
|
6895
7778
|
const output = errorOutput || running2?.output || String(error.message);
|
|
6896
7779
|
store.close(job.id, {
|
|
6897
7780
|
status: "failed",
|
|
@@ -7059,20 +7942,20 @@ function createBackgroundTools() {
|
|
|
7059
7942
|
}
|
|
7060
7943
|
|
|
7061
7944
|
// src/extensions/tools/network/tools/downloadUrl.ts
|
|
7062
|
-
import
|
|
7945
|
+
import fs20 from "fs/promises";
|
|
7063
7946
|
|
|
7064
7947
|
// src/extensions/shared.ts
|
|
7065
|
-
import
|
|
7066
|
-
import
|
|
7948
|
+
import fs19 from "fs/promises";
|
|
7949
|
+
import path19 from "path";
|
|
7067
7950
|
async function ensureExtensionDir(rootDir, extensionId) {
|
|
7068
7951
|
const paths = await ensureProjectStateDirectories(rootDir);
|
|
7069
|
-
const dir =
|
|
7070
|
-
await
|
|
7952
|
+
const dir = path19.join(paths.extensionsDir, extensionId);
|
|
7953
|
+
await fs19.mkdir(dir, { recursive: true });
|
|
7071
7954
|
return dir;
|
|
7072
7955
|
}
|
|
7073
7956
|
async function readJsonFile(filePath, fallback) {
|
|
7074
7957
|
try {
|
|
7075
|
-
return JSON.parse(await
|
|
7958
|
+
return JSON.parse(await fs19.readFile(filePath, "utf8"));
|
|
7076
7959
|
} catch (error) {
|
|
7077
7960
|
if (error.code === "ENOENT") {
|
|
7078
7961
|
return fallback;
|
|
@@ -7081,8 +7964,8 @@ async function readJsonFile(filePath, fallback) {
|
|
|
7081
7964
|
}
|
|
7082
7965
|
}
|
|
7083
7966
|
async function writeJsonFile(filePath, value) {
|
|
7084
|
-
await
|
|
7085
|
-
await
|
|
7967
|
+
await fs19.mkdir(path19.dirname(filePath), { recursive: true });
|
|
7968
|
+
await fs19.writeFile(filePath, `${JSON.stringify(value, null, 2)}
|
|
7086
7969
|
`, "utf8");
|
|
7087
7970
|
}
|
|
7088
7971
|
function jsonResult(value) {
|
|
@@ -7098,7 +7981,7 @@ function sanitizeStateSegment(value) {
|
|
|
7098
7981
|
}
|
|
7099
7982
|
|
|
7100
7983
|
// src/extensions/tools/network/session.ts
|
|
7101
|
-
import
|
|
7984
|
+
import path20 from "path";
|
|
7102
7985
|
async function listHttpSessions(rootDir) {
|
|
7103
7986
|
const state = await readJsonFile(await sessionFile(rootDir), { sessions: [] });
|
|
7104
7987
|
return Array.isArray(state.sessions) ? state.sessions.map(normalizeSession) : [];
|
|
@@ -7123,7 +8006,7 @@ async function getHttpSessionStateFile(rootDir) {
|
|
|
7123
8006
|
return sessionFile(rootDir);
|
|
7124
8007
|
}
|
|
7125
8008
|
async function sessionFile(rootDir) {
|
|
7126
|
-
return
|
|
8009
|
+
return path20.join(await ensureExtensionDir(rootDir, "network"), "http-sessions.json");
|
|
7127
8010
|
}
|
|
7128
8011
|
function normalizeSession(value) {
|
|
7129
8012
|
return {
|
|
@@ -7352,7 +8235,7 @@ var downloadUrlTool = {
|
|
|
7352
8235
|
}
|
|
7353
8236
|
const bytes = Buffer.from(await response.arrayBuffer());
|
|
7354
8237
|
await ensureParentDirectory(targetPath);
|
|
7355
|
-
await
|
|
8238
|
+
await fs20.writeFile(targetPath, bytes);
|
|
7356
8239
|
return changedJsonResult({
|
|
7357
8240
|
ok: response.ok,
|
|
7358
8241
|
url,
|
|
@@ -7417,17 +8300,17 @@ var httpProbeTool = {
|
|
|
7417
8300
|
};
|
|
7418
8301
|
|
|
7419
8302
|
// src/extensions/tools/network/traceStore.ts
|
|
7420
|
-
import
|
|
7421
|
-
import
|
|
8303
|
+
import fs21 from "fs/promises";
|
|
8304
|
+
import path21 from "path";
|
|
7422
8305
|
async function writeNetworkTrace(rootDir, traceId, record) {
|
|
7423
8306
|
const filePath = await networkTraceFilePath(rootDir, traceId);
|
|
7424
|
-
await
|
|
7425
|
-
await
|
|
8307
|
+
await fs21.mkdir(path21.dirname(filePath), { recursive: true });
|
|
8308
|
+
await fs21.writeFile(filePath, `${JSON.stringify(record, null, 2)}
|
|
7426
8309
|
`, "utf8");
|
|
7427
8310
|
return filePath;
|
|
7428
8311
|
}
|
|
7429
8312
|
async function networkTraceFilePath(rootDir, traceId) {
|
|
7430
|
-
return
|
|
8313
|
+
return path21.join(await ensureExtensionDir(rootDir, "network"), "traces", `${sanitizeStateSegment(traceId)}.json`);
|
|
7431
8314
|
}
|
|
7432
8315
|
|
|
7433
8316
|
// src/extensions/tools/network/tools/httpRequest.ts
|
|
@@ -7827,14 +8710,14 @@ function readStringMap2(value) {
|
|
|
7827
8710
|
}
|
|
7828
8711
|
|
|
7829
8712
|
// src/extensions/tools/network/openapi.ts
|
|
7830
|
-
import
|
|
8713
|
+
import fs22 from "fs/promises";
|
|
7831
8714
|
async function loadOpenApiDocument(source, context) {
|
|
7832
8715
|
const normalizedSource = source.trim();
|
|
7833
8716
|
if (!normalizedSource) {
|
|
7834
8717
|
throw new ToolExecutionError("OpenAPI source is required.", { code: "OPENAPI_SOURCE_INVALID" });
|
|
7835
8718
|
}
|
|
7836
8719
|
const resolvedSource = /^https?:\/\//i.test(normalizedSource) ? normalizedSource : resolveUserPath(normalizedSource, context.cwd);
|
|
7837
|
-
const raw = /^https?:\/\//i.test(normalizedSource) ? await (await fetchWithTimeout(normalizedSource, { method: "GET" }, 2e4, context.abortSignal)).text() : stripBom(await
|
|
8720
|
+
const raw = /^https?:\/\//i.test(normalizedSource) ? await (await fetchWithTimeout(normalizedSource, { method: "GET" }, 2e4, context.abortSignal)).text() : stripBom(await fs22.readFile(resolvedSource, "utf8"));
|
|
7838
8721
|
const parsed = parseOpenApiDocument(raw, normalizedSource);
|
|
7839
8722
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
7840
8723
|
throw new ToolExecutionError("OpenAPI document root must be an object.", { code: "OPENAPI_ROOT_INVALID" });
|
|
@@ -8182,8 +9065,8 @@ async function recordSkillUse(rootDir, input) {
|
|
|
8182
9065
|
}
|
|
8183
9066
|
|
|
8184
9067
|
// src/extensions/tools/skills/tools/skillReadResource.ts
|
|
8185
|
-
import
|
|
8186
|
-
import
|
|
9068
|
+
import fs23 from "fs/promises";
|
|
9069
|
+
import path22 from "path";
|
|
8187
9070
|
var MAX_RESOURCE_CHARS = 24e3;
|
|
8188
9071
|
var skillReadResourceTool = {
|
|
8189
9072
|
definition: {
|
|
@@ -8220,8 +9103,8 @@ var skillReadResourceTool = {
|
|
|
8220
9103
|
if (!resource) {
|
|
8221
9104
|
throw new Error(`Skill "${name}" does not declare resource: ${requestedPath}`);
|
|
8222
9105
|
}
|
|
8223
|
-
const absolutePath =
|
|
8224
|
-
const content = await
|
|
9106
|
+
const absolutePath = path22.resolve(context.projectContext.rootDir, resource.path);
|
|
9107
|
+
const content = await fs23.readFile(absolutePath, "utf8");
|
|
8225
9108
|
return jsonResult({
|
|
8226
9109
|
ok: true,
|
|
8227
9110
|
skill: {
|
|
@@ -8239,7 +9122,7 @@ function normalizeResourcePath(value) {
|
|
|
8239
9122
|
}
|
|
8240
9123
|
|
|
8241
9124
|
// src/extensions/tools/skills/tools/skillRunScript.ts
|
|
8242
|
-
import
|
|
9125
|
+
import path23 from "path";
|
|
8243
9126
|
var skillRunScriptTool = {
|
|
8244
9127
|
definition: {
|
|
8245
9128
|
type: "function",
|
|
@@ -8283,12 +9166,12 @@ var skillRunScriptTool = {
|
|
|
8283
9166
|
if (!resource) {
|
|
8284
9167
|
throw new Error(`Skill "${name}" does not declare script resource: ${requestedPath}`);
|
|
8285
9168
|
}
|
|
8286
|
-
const skillDir =
|
|
8287
|
-
const relativeToSkill = normalizeResourcePath2(
|
|
9169
|
+
const skillDir = path23.dirname(skill.path);
|
|
9170
|
+
const relativeToSkill = normalizeResourcePath2(path23.relative(skillDir, resource.path));
|
|
8288
9171
|
if (!relativeToSkill.startsWith("scripts/")) {
|
|
8289
9172
|
throw new Error(`Skill "${name}" resource is not executable because it is outside scripts/: ${requestedPath}`);
|
|
8290
9173
|
}
|
|
8291
|
-
const scriptPath =
|
|
9174
|
+
const scriptPath = path23.resolve(context.projectContext.rootDir, resource.path);
|
|
8292
9175
|
const argumentText = typeof args.args === "string" ? args.args.trim() : "";
|
|
8293
9176
|
const command = buildScriptCommand(scriptPath, argumentText);
|
|
8294
9177
|
const result = await runCommandWithPolicy({
|
|
@@ -8363,7 +9246,7 @@ function quotePath(value) {
|
|
|
8363
9246
|
return `"${value.replace(/"/g, '\\"')}"`;
|
|
8364
9247
|
}
|
|
8365
9248
|
function buildScriptCommand(scriptPath, argumentText) {
|
|
8366
|
-
const extension =
|
|
9249
|
+
const extension = path23.extname(scriptPath).toLowerCase();
|
|
8367
9250
|
const quoted = quotePath(scriptPath);
|
|
8368
9251
|
const suffix = argumentText ? ` ${argumentText}` : "";
|
|
8369
9252
|
if (extension === ".js" || extension === ".mjs" || extension === ".cjs") {
|
|
@@ -8417,12 +9300,12 @@ var subagentCheckTool = {
|
|
|
8417
9300
|
|
|
8418
9301
|
// src/execution/launch.ts
|
|
8419
9302
|
import { spawn } from "child_process";
|
|
8420
|
-
import
|
|
9303
|
+
import path24 from "path";
|
|
8421
9304
|
function spawnExecutionWorker(input) {
|
|
8422
9305
|
if (process.env.KITTY_TEST_WORKER_MODE === "stub") {
|
|
8423
9306
|
return process.pid;
|
|
8424
9307
|
}
|
|
8425
|
-
const cliEntry =
|
|
9308
|
+
const cliEntry = path24.resolve(process.argv[1] ?? "");
|
|
8426
9309
|
if (!cliEntry) {
|
|
8427
9310
|
throw new Error("Unable to locate Kitty CLI entrypoint for execution worker.");
|
|
8428
9311
|
}
|
|
@@ -8628,7 +9511,7 @@ function parseWorktreeBlock(block) {
|
|
|
8628
9511
|
}
|
|
8629
9512
|
|
|
8630
9513
|
// src/extensions/tools/worktree/state.ts
|
|
8631
|
-
import
|
|
9514
|
+
import path25 from "path";
|
|
8632
9515
|
async function readWorktreeState(rootDir) {
|
|
8633
9516
|
return normalizeWorktreeState(await readJsonFile(await stateFile(rootDir), {
|
|
8634
9517
|
schemaVersion: 1,
|
|
@@ -8651,7 +9534,7 @@ async function recordWorktreeEvent(rootDir, event) {
|
|
|
8651
9534
|
return writeWorktreeState(rootDir, state);
|
|
8652
9535
|
}
|
|
8653
9536
|
async function stateFile(rootDir) {
|
|
8654
|
-
return
|
|
9537
|
+
return path25.join(await ensureExtensionDir(rootDir, "worktree"), "state.json");
|
|
8655
9538
|
}
|
|
8656
9539
|
function normalizeWorktreeState(value) {
|
|
8657
9540
|
return {
|
|
@@ -9274,8 +10157,8 @@ function looksLikeToolProtocolText(content) {
|
|
|
9274
10157
|
}
|
|
9275
10158
|
|
|
9276
10159
|
// src/observability/crashRecorder.ts
|
|
9277
|
-
import
|
|
9278
|
-
import
|
|
10160
|
+
import fs24 from "fs";
|
|
10161
|
+
import path26 from "path";
|
|
9279
10162
|
var activeCrashContexts = /* @__PURE__ */ new Map();
|
|
9280
10163
|
var nextCrashContextId = 0;
|
|
9281
10164
|
function enterCrashContext(context) {
|
|
@@ -9287,7 +10170,7 @@ function enterCrashContext(context) {
|
|
|
9287
10170
|
}
|
|
9288
10171
|
|
|
9289
10172
|
// src/observability/hostEvents.ts
|
|
9290
|
-
import
|
|
10173
|
+
import path27 from "path";
|
|
9291
10174
|
async function recordHostTurnStarted(rootDir, input) {
|
|
9292
10175
|
await recordObservabilityEvent(rootDir, {
|
|
9293
10176
|
event: "host.turn",
|
|
@@ -9318,53 +10201,6 @@ async function recordHostTurnFinished(rootDir, input) {
|
|
|
9318
10201
|
});
|
|
9319
10202
|
}
|
|
9320
10203
|
|
|
9321
|
-
// src/session/events.ts
|
|
9322
|
-
import fs22 from "fs/promises";
|
|
9323
|
-
import path25 from "path";
|
|
9324
|
-
var SessionEventStore = class {
|
|
9325
|
-
constructor(eventsDir) {
|
|
9326
|
-
this.eventsDir = eventsDir;
|
|
9327
|
-
}
|
|
9328
|
-
async append(event) {
|
|
9329
|
-
const record = {
|
|
9330
|
-
id: createEventId(),
|
|
9331
|
-
createdAt: event.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
9332
|
-
type: event.type,
|
|
9333
|
-
sessionId: event.sessionId,
|
|
9334
|
-
cwd: event.cwd,
|
|
9335
|
-
host: event.host,
|
|
9336
|
-
message: event.message,
|
|
9337
|
-
details: event.details
|
|
9338
|
-
};
|
|
9339
|
-
await fs22.mkdir(this.eventsDir, { recursive: true });
|
|
9340
|
-
await fs22.appendFile(this.getSessionEventPath(event.sessionId), `${JSON.stringify(record)}
|
|
9341
|
-
`, "utf8");
|
|
9342
|
-
return record;
|
|
9343
|
-
}
|
|
9344
|
-
async list(sessionId, limit = 100) {
|
|
9345
|
-
const filePath = this.getSessionEventPath(sessionId);
|
|
9346
|
-
let raw = "";
|
|
9347
|
-
try {
|
|
9348
|
-
raw = await fs22.readFile(filePath, "utf8");
|
|
9349
|
-
} catch (error) {
|
|
9350
|
-
if (error.code === "ENOENT") {
|
|
9351
|
-
return [];
|
|
9352
|
-
}
|
|
9353
|
-
throw error;
|
|
9354
|
-
}
|
|
9355
|
-
return raw.split(/\r?\n/).filter(Boolean).map((line) => JSON.parse(line)).slice(-limit);
|
|
9356
|
-
}
|
|
9357
|
-
getSessionEventPath(sessionId) {
|
|
9358
|
-
return path25.join(this.eventsDir, `${sanitizeSessionId(sessionId)}.jsonl`);
|
|
9359
|
-
}
|
|
9360
|
-
};
|
|
9361
|
-
function createEventId() {
|
|
9362
|
-
return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:.TZ]/g, "").slice(0, 14)}-${Math.random().toString(16).slice(2, 10)}`;
|
|
9363
|
-
}
|
|
9364
|
-
function sanitizeSessionId(sessionId) {
|
|
9365
|
-
return sessionId.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
9366
|
-
}
|
|
9367
|
-
|
|
9368
10204
|
// src/host/toolRegistry.ts
|
|
9369
10205
|
async function createHostToolRegistry(config, options = {}) {
|
|
9370
10206
|
const extraTools = options.extraTools ?? [];
|
|
@@ -9685,7 +10521,7 @@ function tryParseJson(input) {
|
|
|
9685
10521
|
}
|
|
9686
10522
|
|
|
9687
10523
|
// src/runtime-ui/pathDisplay.ts
|
|
9688
|
-
import
|
|
10524
|
+
import path28 from "path";
|
|
9689
10525
|
function normalizeDisplayPath(value, cwd) {
|
|
9690
10526
|
if (!value) {
|
|
9691
10527
|
return value;
|
|
@@ -9693,10 +10529,10 @@ function normalizeDisplayPath(value, cwd) {
|
|
|
9693
10529
|
if (!cwd) {
|
|
9694
10530
|
return value;
|
|
9695
10531
|
}
|
|
9696
|
-
const normalizedCwd =
|
|
9697
|
-
const normalizedValue =
|
|
9698
|
-
if (normalizedValue === normalizedCwd || normalizedValue.startsWith(`${normalizedCwd}${
|
|
9699
|
-
return
|
|
10532
|
+
const normalizedCwd = path28.resolve(cwd);
|
|
10533
|
+
const normalizedValue = path28.resolve(value);
|
|
10534
|
+
if (normalizedValue === normalizedCwd || normalizedValue.startsWith(`${normalizedCwd}${path28.sep}`)) {
|
|
10535
|
+
return path28.relative(normalizedCwd, normalizedValue) || ".";
|
|
9700
10536
|
}
|
|
9701
10537
|
return value;
|
|
9702
10538
|
}
|
|
@@ -9704,7 +10540,7 @@ function rewriteAbsolutePaths(value, cwd) {
|
|
|
9704
10540
|
if (!cwd) {
|
|
9705
10541
|
return value;
|
|
9706
10542
|
}
|
|
9707
|
-
const normalizedCwd =
|
|
10543
|
+
const normalizedCwd = path28.resolve(cwd).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9708
10544
|
const pattern = new RegExp(`${normalizedCwd}(?:\\\\[^\\s"']*|/[^\\s"']*)*`, "g");
|
|
9709
10545
|
return value.replace(pattern, (match) => normalizeDisplayPath(match, cwd) ?? match);
|
|
9710
10546
|
}
|
|
@@ -9764,24 +10600,24 @@ function buildToolCallDisplay(name, rawArgs, maxChars, cwd) {
|
|
|
9764
10600
|
};
|
|
9765
10601
|
}
|
|
9766
10602
|
const args = parsed;
|
|
9767
|
-
const
|
|
10603
|
+
const path29 = normalizeDisplayPath(readStringField(args, "path"), cwd);
|
|
9768
10604
|
switch (name) {
|
|
9769
10605
|
case "read": {
|
|
9770
10606
|
const offset = typeof args.offset === "number" ? Math.trunc(args.offset) : void 0;
|
|
9771
10607
|
const limit = typeof args.limit === "number" ? Math.trunc(args.limit) : void 0;
|
|
9772
10608
|
const range = offset === void 0 ? "" : limit === void 0 ? `:${offset}` : `:${offset}-${Math.max(offset, offset + limit - 1)}`;
|
|
9773
10609
|
return {
|
|
9774
|
-
summary: `${name} ${
|
|
10610
|
+
summary: `${name} ${path29 ?? "(missing path)"}${range}`
|
|
9775
10611
|
};
|
|
9776
10612
|
}
|
|
9777
10613
|
case "write":
|
|
9778
10614
|
return {
|
|
9779
|
-
summary: `${name} ${
|
|
10615
|
+
summary: `${name} ${path29 ?? "(missing path)"}`
|
|
9780
10616
|
};
|
|
9781
10617
|
case "edit": {
|
|
9782
10618
|
const edits = Array.isArray(args.edits) ? args.edits : [];
|
|
9783
10619
|
return {
|
|
9784
|
-
summary: `${name} ${
|
|
10620
|
+
summary: `${name} ${path29 ?? "(missing path)"}` + (edits.length > 0 ? ` edits=${edits.length}` : "")
|
|
9785
10621
|
};
|
|
9786
10622
|
}
|
|
9787
10623
|
case "bash": {
|
|
@@ -9793,7 +10629,7 @@ function buildToolCallDisplay(name, rawArgs, maxChars, cwd) {
|
|
|
9793
10629
|
}
|
|
9794
10630
|
case "download_url":
|
|
9795
10631
|
return {
|
|
9796
|
-
summary: `${name} ${readStringField(args, "url") ?? "(missing url)"} -> ${
|
|
10632
|
+
summary: `${name} ${readStringField(args, "url") ?? "(missing url)"} -> ${path29 ?? "(missing path)"}`
|
|
9797
10633
|
};
|
|
9798
10634
|
case "http_probe": {
|
|
9799
10635
|
const method = readStringField(args, "method") ?? "HEAD";
|
|
@@ -9834,14 +10670,14 @@ function buildToolCallDisplay(name, rawArgs, maxChars, cwd) {
|
|
|
9834
10670
|
case "worktree_create": {
|
|
9835
10671
|
const branch = readStringField(args, "branch");
|
|
9836
10672
|
return {
|
|
9837
|
-
summary: `${name} ${
|
|
10673
|
+
summary: `${name} ${path29 ?? "(missing path)"}${branch ? ` branch=${branch}` : ""}`
|
|
9838
10674
|
};
|
|
9839
10675
|
}
|
|
9840
10676
|
case "worktree_get":
|
|
9841
10677
|
case "worktree_keep":
|
|
9842
10678
|
case "worktree_remove":
|
|
9843
10679
|
return {
|
|
9844
|
-
summary: `${name} ${
|
|
10680
|
+
summary: `${name} ${path29 ?? "(missing path)"}`
|
|
9845
10681
|
};
|
|
9846
10682
|
case "worktree_events": {
|
|
9847
10683
|
const limit = typeof args.limit === "number" ? Math.trunc(args.limit) : void 0;
|
|
@@ -9980,7 +10816,7 @@ function colorizeMarker(marker) {
|
|
|
9980
10816
|
}
|
|
9981
10817
|
|
|
9982
10818
|
// src/utils/stdio.ts
|
|
9983
|
-
import
|
|
10819
|
+
import fs25 from "fs";
|
|
9984
10820
|
var stdoutBroken = false;
|
|
9985
10821
|
var stderrBroken = false;
|
|
9986
10822
|
function writeStdout(text) {
|
|
@@ -10006,7 +10842,7 @@ function writeToFd(fd, text, stream) {
|
|
|
10006
10842
|
if (target.isTTY) {
|
|
10007
10843
|
return target.write(text);
|
|
10008
10844
|
}
|
|
10009
|
-
|
|
10845
|
+
fs25.writeSync(fd, text, void 0, "utf8");
|
|
10010
10846
|
return true;
|
|
10011
10847
|
} catch (error) {
|
|
10012
10848
|
if (isIgnorableStreamError(error)) {
|
|
@@ -10289,10 +11125,17 @@ function formatRuntimeUiMessage(prefix, summary, detail) {
|
|
|
10289
11125
|
export {
|
|
10290
11126
|
resolveProjectRoots,
|
|
10291
11127
|
buildProjectMap,
|
|
11128
|
+
PROJECT_STATE_DIR_NAME,
|
|
11129
|
+
PROJECT_STATE_ENV_FILE_NAME,
|
|
11130
|
+
PROJECT_STATE_ENV_EXAMPLE_FILE_NAME,
|
|
11131
|
+
PROJECT_STATE_IGNORE_FILE_NAME,
|
|
11132
|
+
PRESERVED_PROJECT_STATE_ENTRY_NAMES,
|
|
11133
|
+
getProjectStatePaths,
|
|
10292
11134
|
loadProjectContext,
|
|
10293
11135
|
getErrorMessage,
|
|
10294
11136
|
ControlPlaneLedger,
|
|
10295
11137
|
ExecutionStore,
|
|
11138
|
+
SessionEventStore,
|
|
10296
11139
|
isProcessAlive,
|
|
10297
11140
|
terminatePid,
|
|
10298
11141
|
BackgroundExecutionStore,
|
|
@@ -10301,12 +11144,14 @@ export {
|
|
|
10301
11144
|
summarizeExecution,
|
|
10302
11145
|
summarizeExecutionSet,
|
|
10303
11146
|
EXTENSION_ENV_KEYS,
|
|
10304
|
-
SessionEventStore,
|
|
10305
11147
|
runHostTurn,
|
|
10306
11148
|
writeStdout,
|
|
10307
11149
|
writeStdoutLine,
|
|
10308
11150
|
writeStderrLine,
|
|
10309
11151
|
createRuntimeUiEvent,
|
|
11152
|
+
tryParseJson,
|
|
11153
|
+
buildToolCallDisplay,
|
|
11154
|
+
buildToolResultDisplay,
|
|
10310
11155
|
colorRuntimeUiText,
|
|
10311
11156
|
createRuntimeUiTerminalRenderer,
|
|
10312
11157
|
formatRuntimeUiEventLine
|