@prestyj/cli 4.12.1 → 4.14.0

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.
Files changed (148) hide show
  1. package/dist/app-sidecar.js +258 -45
  2. package/dist/app-sidecar.js.map +1 -1
  3. package/dist/cli/auth.d.ts.map +1 -1
  4. package/dist/cli/auth.js +5 -2
  5. package/dist/cli/auth.js.map +1 -1
  6. package/dist/cli/shared.d.ts.map +1 -1
  7. package/dist/cli/shared.js +2 -0
  8. package/dist/cli/shared.js.map +1 -1
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/cli.js +6 -1
  11. package/dist/cli.js.map +1 -1
  12. package/dist/config.d.ts.map +1 -1
  13. package/dist/config.js +1 -0
  14. package/dist/config.js.map +1 -1
  15. package/dist/core/auth-providers.d.ts.map +1 -1
  16. package/dist/core/auth-providers.js +7 -0
  17. package/dist/core/auth-providers.js.map +1 -1
  18. package/dist/core/encode-cwd.d.ts +25 -0
  19. package/dist/core/encode-cwd.d.ts.map +1 -0
  20. package/dist/core/encode-cwd.js +40 -0
  21. package/dist/core/encode-cwd.js.map +1 -0
  22. package/dist/core/encode-cwd.test.d.ts +2 -0
  23. package/dist/core/encode-cwd.test.d.ts.map +1 -0
  24. package/dist/core/encode-cwd.test.js +61 -0
  25. package/dist/core/encode-cwd.test.js.map +1 -0
  26. package/dist/core/mcp/client.d.ts +11 -0
  27. package/dist/core/mcp/client.d.ts.map +1 -1
  28. package/dist/core/mcp/client.js +87 -35
  29. package/dist/core/mcp/client.js.map +1 -1
  30. package/dist/core/mcp/defaults.d.ts +1 -1
  31. package/dist/core/mcp/defaults.d.ts.map +1 -1
  32. package/dist/core/mcp/defaults.js +9 -40
  33. package/dist/core/mcp/defaults.js.map +1 -1
  34. package/dist/core/mcp/loopback.d.ts +27 -0
  35. package/dist/core/mcp/loopback.d.ts.map +1 -0
  36. package/dist/core/mcp/loopback.js +66 -0
  37. package/dist/core/mcp/loopback.js.map +1 -0
  38. package/dist/core/mcp/loopback.test.d.ts +2 -0
  39. package/dist/core/mcp/loopback.test.d.ts.map +1 -0
  40. package/dist/core/mcp/loopback.test.js +87 -0
  41. package/dist/core/mcp/loopback.test.js.map +1 -0
  42. package/dist/core/mcp/parse-add-command.d.ts.map +1 -1
  43. package/dist/core/mcp/parse-add-command.js +1 -0
  44. package/dist/core/mcp/parse-add-command.js.map +1 -1
  45. package/dist/core/mcp/parse-add-command.test.js +8 -2
  46. package/dist/core/mcp/parse-add-command.test.js.map +1 -1
  47. package/dist/core/mcp/resolve-stdio.d.ts +27 -0
  48. package/dist/core/mcp/resolve-stdio.d.ts.map +1 -0
  49. package/dist/core/mcp/resolve-stdio.js +149 -0
  50. package/dist/core/mcp/resolve-stdio.js.map +1 -0
  51. package/dist/core/mcp/resolve-stdio.test.d.ts +2 -0
  52. package/dist/core/mcp/resolve-stdio.test.d.ts.map +1 -0
  53. package/dist/core/mcp/resolve-stdio.test.js +63 -0
  54. package/dist/core/mcp/resolve-stdio.test.js.map +1 -0
  55. package/dist/core/mcp/store.d.ts +4 -4
  56. package/dist/core/mcp/store.d.ts.map +1 -1
  57. package/dist/core/mcp/store.js +7 -1
  58. package/dist/core/mcp/store.js.map +1 -1
  59. package/dist/core/mcp/store.test.js +11 -2
  60. package/dist/core/mcp/store.test.js.map +1 -1
  61. package/dist/core/mcp/types.d.ts +5 -1
  62. package/dist/core/mcp/types.d.ts.map +1 -1
  63. package/dist/core/project-discovery.d.ts.map +1 -1
  64. package/dist/core/project-discovery.js +2 -9
  65. package/dist/core/project-discovery.js.map +1 -1
  66. package/dist/core/radio.d.ts +7 -2
  67. package/dist/core/radio.d.ts.map +1 -1
  68. package/dist/core/radio.js.map +1 -1
  69. package/dist/core/resolve-start.test.js +1 -0
  70. package/dist/core/resolve-start.test.js.map +1 -1
  71. package/dist/core/session-manager.d.ts.map +1 -1
  72. package/dist/core/session-manager.js +1 -3
  73. package/dist/core/session-manager.js.map +1 -1
  74. package/dist/core/settings-manager.d.ts +1 -0
  75. package/dist/core/settings-manager.d.ts.map +1 -1
  76. package/dist/core/settings-manager.js +1 -0
  77. package/dist/core/settings-manager.js.map +1 -1
  78. package/dist/session.d.ts.map +1 -1
  79. package/dist/session.js +1 -3
  80. package/dist/session.js.map +1 -1
  81. package/dist/system-prompt.d.ts.map +1 -1
  82. package/dist/system-prompt.js +9 -4
  83. package/dist/system-prompt.js.map +1 -1
  84. package/dist/system-prompt.test.js +6 -3
  85. package/dist/system-prompt.test.js.map +1 -1
  86. package/dist/tools/bash.d.ts.map +1 -1
  87. package/dist/tools/bash.js +10 -2
  88. package/dist/tools/bash.js.map +1 -1
  89. package/dist/tools/code-skeleton.d.ts +29 -0
  90. package/dist/tools/code-skeleton.d.ts.map +1 -0
  91. package/dist/tools/code-skeleton.js +115 -0
  92. package/dist/tools/code-skeleton.js.map +1 -0
  93. package/dist/tools/code-skeleton.test.d.ts +2 -0
  94. package/dist/tools/code-skeleton.test.d.ts.map +1 -0
  95. package/dist/tools/code-skeleton.test.js +115 -0
  96. package/dist/tools/code-skeleton.test.js.map +1 -0
  97. package/dist/tools/compress-integration.test.d.ts +2 -0
  98. package/dist/tools/compress-integration.test.d.ts.map +1 -0
  99. package/dist/tools/compress-integration.test.js +59 -0
  100. package/dist/tools/compress-integration.test.js.map +1 -0
  101. package/dist/tools/compress.d.ts +57 -0
  102. package/dist/tools/compress.d.ts.map +1 -0
  103. package/dist/tools/compress.js +181 -0
  104. package/dist/tools/compress.js.map +1 -0
  105. package/dist/tools/compress.test.d.ts +2 -0
  106. package/dist/tools/compress.test.d.ts.map +1 -0
  107. package/dist/tools/compress.test.js +60 -0
  108. package/dist/tools/compress.test.js.map +1 -0
  109. package/dist/tools/edit-diff.d.ts.map +1 -1
  110. package/dist/tools/edit-diff.js +25 -8
  111. package/dist/tools/edit-diff.js.map +1 -1
  112. package/dist/tools/ls.d.ts.map +1 -1
  113. package/dist/tools/ls.js +7 -4
  114. package/dist/tools/ls.js.map +1 -1
  115. package/dist/tools/task-output.d.ts.map +1 -1
  116. package/dist/tools/task-output.js +13 -3
  117. package/dist/tools/task-output.js.map +1 -1
  118. package/dist/tools/tasks.d.ts +6 -0
  119. package/dist/tools/tasks.d.ts.map +1 -1
  120. package/dist/tools/tasks.js +41 -4
  121. package/dist/tools/tasks.js.map +1 -1
  122. package/dist/tools/tasks.test.d.ts +2 -0
  123. package/dist/tools/tasks.test.d.ts.map +1 -0
  124. package/dist/tools/tasks.test.js +62 -0
  125. package/dist/tools/tasks.test.js.map +1 -0
  126. package/dist/ui/App.d.ts.map +1 -1
  127. package/dist/ui/App.js.map +1 -1
  128. package/dist/ui/components/ChatScreen.d.ts +5 -5
  129. package/dist/ui/components/ChatScreen.d.ts.map +1 -1
  130. package/dist/ui/components/InputArea.d.ts +5 -5
  131. package/dist/ui/components/InputArea.d.ts.map +1 -1
  132. package/dist/ui/components/ModelSelector.d.ts.map +1 -1
  133. package/dist/ui/components/ModelSelector.js +1 -0
  134. package/dist/ui/components/ModelSelector.js.map +1 -1
  135. package/dist/ui/components/TaskPickerMenu.d.ts +2 -2
  136. package/dist/ui/components/TaskPickerMenu.d.ts.map +1 -1
  137. package/dist/ui/components/TaskPickerMenu.js.map +1 -1
  138. package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
  139. package/dist/ui/hooks/useAgentLoop.js +13 -4
  140. package/dist/ui/hooks/useAgentLoop.js.map +1 -1
  141. package/dist/ui/login.d.ts.map +1 -1
  142. package/dist/ui/login.js +1 -0
  143. package/dist/ui/login.js.map +1 -1
  144. package/package.json +6 -5
  145. package/dist/core/tasks-store.d.ts +0 -24
  146. package/dist/core/tasks-store.d.ts.map +0 -1
  147. package/dist/core/tasks-store.js +0 -81
  148. package/dist/core/tasks-store.js.map +0 -1
@@ -0,0 +1,115 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import ts from "typescript";
3
+ import { extractSkeleton } from "./code-skeleton.js";
4
+ /**
5
+ * Strong fidelity check: re-parse the skeleton and confirm `name` resolves to a
6
+ * real declaration (not a comment or substring). For callables we also assert
7
+ * the signature text carries a parameter list, so "name kept, signature lost"
8
+ * fails here instead of slipping through a regex.
9
+ */
10
+ function skeletonDeclares(skeleton, name) {
11
+ const sf = ts.createSourceFile("skel.ts", skeleton, ts.ScriptTarget.Latest, true);
12
+ let found = false;
13
+ const walk = (node) => {
14
+ if (found)
15
+ return;
16
+ if ((ts.isFunctionDeclaration(node) ||
17
+ ts.isClassDeclaration(node) ||
18
+ ts.isInterfaceDeclaration(node) ||
19
+ ts.isTypeAliasDeclaration(node) ||
20
+ ts.isEnumDeclaration(node)) &&
21
+ node.name?.getText(sf) === name) {
22
+ found = true;
23
+ }
24
+ else if (ts.isVariableDeclaration(node) && node.name.getText(sf) === name) {
25
+ found = true;
26
+ }
27
+ else if (ts.isExportDeclaration(node)) {
28
+ if (node.exportClause && ts.isNamedExports(node.exportClause)) {
29
+ if (node.exportClause.elements.some((e) => e.name.text === name))
30
+ found = true;
31
+ }
32
+ }
33
+ ts.forEachChild(node, walk);
34
+ };
35
+ walk(sf);
36
+ return found;
37
+ }
38
+ describe("extractSkeleton", () => {
39
+ it("keeps re-export barrels (export {} from / export *) — does NOT render them empty", () => {
40
+ const src = `
41
+ export { foo, bar } from "./other.js";
42
+ export * from "./barrel.js";
43
+ `;
44
+ const r = extractSkeleton(src);
45
+ expect(r.empty).toBe(false);
46
+ expect(r.skeleton).toContain('export { foo, bar } from "./other.js"');
47
+ expect(r.skeleton).toContain('export * from "./barrel.js"');
48
+ expect(skeletonDeclares(r.skeleton, "foo")).toBe(true);
49
+ expect(skeletonDeclares(r.skeleton, "bar")).toBe(true);
50
+ });
51
+ it("preserves the callable signature of an exported arrow const", () => {
52
+ const src = `export const handler = async (req: Request, opts: Opts): Promise<Response> => {
53
+ return new Response("hi");
54
+ };`;
55
+ const r = extractSkeleton(src);
56
+ expect(r.skeleton).toContain("req: Request");
57
+ expect(r.skeleton).toContain("opts: Opts");
58
+ expect(r.skeleton).toContain("Promise<Response>");
59
+ expect(r.skeleton).not.toContain('new Response("hi")'); // body gone
60
+ expect(r.skeleton).toContain("/* … */"); // stub marked
61
+ });
62
+ it("keeps function declarations' params/return and stubs the body", () => {
63
+ const src = `export function add(a: number, b: number): number {
64
+ const sum = a + b;
65
+ return sum;
66
+ }`;
67
+ const r = extractSkeleton(src);
68
+ expect(r.skeleton).toContain("add(a: number, b: number): number");
69
+ expect(r.skeleton).not.toContain("const sum");
70
+ expect(r.skeleton).toContain("/* … */");
71
+ });
72
+ it("keeps public class members but drops private ones", () => {
73
+ const src = `export class Svc extends Base {
74
+ private secret = 42;
75
+ async run(x: string): Promise<void> { await this.secret; }
76
+ }`;
77
+ const r = extractSkeleton(src);
78
+ expect(r.skeleton).toContain("class Svc extends Base");
79
+ expect(r.skeleton).toContain("run(x: string): Promise<void>");
80
+ expect(r.skeleton).not.toContain("secret");
81
+ });
82
+ it("keeps interfaces and type aliases whole (pure signal)", () => {
83
+ const src = `export interface User { id: string; name: string; }
84
+ export type ID = string | number;`;
85
+ const r = extractSkeleton(src);
86
+ expect(r.skeleton).toContain("id: string");
87
+ expect(r.skeleton).toContain("type ID = string | number");
88
+ });
89
+ it("omits internal (non-exported) consts and functions' bodies", () => {
90
+ const src = `const internal = computeThing();
91
+ export function api(): void { internal(); }`;
92
+ const r = extractSkeleton(src);
93
+ expect(r.skeleton).not.toContain("computeThing");
94
+ expect(r.skeleton).toContain("api(): void");
95
+ });
96
+ it("never fabricates: every line traces to source or is a marked stub", () => {
97
+ const src = `import { z } from "zod";
98
+ export function f(a: string): string { return a.trim(); }`;
99
+ const r = extractSkeleton(src);
100
+ const srcText = src;
101
+ for (const line of r.skeleton.split("\n")) {
102
+ const t = line.trim();
103
+ if (!t)
104
+ continue;
105
+ const isStub = t.includes("/* … */");
106
+ const tracesBack = srcText.includes(t.replace(/\s*\{ \/\* … \*\/ \}.*$/, "").trim());
107
+ expect(isStub || tracesBack).toBe(true);
108
+ }
109
+ });
110
+ it("reports empty for a file with no extractable API", () => {
111
+ const r = extractSkeleton(`const x = 1; console.log(x);`);
112
+ expect(r.empty).toBe(true);
113
+ });
114
+ });
115
+ //# sourceMappingURL=code-skeleton.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-skeleton.test.js","sourceRoot":"","sources":["../../src/tools/code-skeleton.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=compress-integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compress-integration.test.d.ts","sourceRoot":"","sources":["../../src/tools/compress-integration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,59 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { compressToolOutput } from "./compress.js";
3
+ import { truncateTail, MAX_LINES } from "./truncate.js";
4
+ /**
5
+ * Integration-level guarantees for the live tool path (bash / task_output).
6
+ * The contract: compression only fires on output that was ALREADY going to be
7
+ * truncated, and on that branch it must keep more signal than a blind tail
8
+ * slice — never less — while staying reversible (caller keeps the overflow file).
9
+ */
10
+ describe("compressToolOutput (live tool-output seam)", () => {
11
+ it("keeps a FATAL line that blind tail-truncation would drop", () => {
12
+ const lines = [];
13
+ for (let i = 0; i < 5000; i++) {
14
+ if (i === 1200)
15
+ lines.push("ERROR FATAL: segfault in worker pool");
16
+ else
17
+ lines.push(`INFO step ${i} ok`);
18
+ }
19
+ const raw = lines.join("\n");
20
+ // Today's behaviour: tail slice keeps only the last MAX_LINES — the FATAL at
21
+ // line 1200 is in the discarded head.
22
+ const tail = truncateTail(raw);
23
+ expect(tail.content).not.toContain("FATAL: segfault");
24
+ // Compression keeps it.
25
+ const c = compressToolOutput(raw);
26
+ expect(c.content).toContain("FATAL: segfault in worker pool");
27
+ expect(c.notice).toMatch(/fewer tokens/);
28
+ });
29
+ it("preserves the very end of the output (tail semantics)", () => {
30
+ const lines = Array.from({ length: 6000 }, (_, i) => `line ${i}`);
31
+ const c = compressToolOutput(lines.join("\n"));
32
+ expect(c.content).toContain("line 5999");
33
+ expect(c.content).toContain("line 5998");
34
+ });
35
+ it("collapses repeated spam into a count", () => {
36
+ const lines = ["boot"];
37
+ for (let i = 0; i < 3000; i++)
38
+ lines.push("WARN socket hang up");
39
+ lines.push("ERROR aborted");
40
+ const c = compressToolOutput(lines.join("\n"));
41
+ expect(c.content).toMatch(/×\d+/);
42
+ expect(c.content).toContain("ERROR aborted");
43
+ });
44
+ it("produces fewer tokens than the original on large output", () => {
45
+ const lines = Array.from({ length: 8000 }, (_, i) => `compiled module ${i} ok in ${i}ms`);
46
+ const raw = lines.join("\n");
47
+ const c = compressToolOutput(raw);
48
+ expect(c.content.length).toBeLessThan(raw.length / 2);
49
+ });
50
+ it("only the over-limit branch is affected — small output is never compressed here", () => {
51
+ // The tools call compressToolOutput ONLY when truncateTail reports truncated.
52
+ // A small output stays below MAX_LINES, so the branch never runs. Assert the
53
+ // gate, not the compressor: small output is not truncated.
54
+ const small = Array.from({ length: 10 }, (_, i) => `line ${i}`).join("\n");
55
+ expect(truncateTail(small).truncated).toBe(false);
56
+ expect(MAX_LINES).toBeGreaterThan(10);
57
+ });
58
+ });
59
+ //# sourceMappingURL=compress-integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compress-integration.test.js","sourceRoot":"","sources":["../../src/tools/compress-integration.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Content-aware tool-output compression.
3
+ *
4
+ * Today's tool path shrinks oversized output with blunt head/tail line cuts
5
+ * (`truncate.ts`). That throws away whichever half the model didn't get — a
6
+ * stack trace buried in the middle of a 10k-line log, the one failing assertion
7
+ * between thousands of passing ones, the shape of a giant JSON array.
8
+ *
9
+ * `compressOutput` keeps the *signal* instead of a positional slice:
10
+ * - logs: collapse repeated runs, keep every error/warn line + local context,
11
+ * keep head and tail, drop only the boring middle.
12
+ * - JSON: keep array shape + a sample, replace the long tail with a count.
13
+ * - text: middle-out (head + tail) so the command/context AND the result survive.
14
+ *
15
+ * It is lossy by design but information-preserving: the goal is fewer tokens
16
+ * with the same understanding. Pair with an overflow file for full reversibility.
17
+ */
18
+ export interface CompressResult {
19
+ content: string;
20
+ /** Which strategy fired — for telemetry / benchmarking. */
21
+ strategy: "json" | "log" | "text" | "none";
22
+ originalTokens: number;
23
+ compressedTokens: number;
24
+ }
25
+ export interface CompressOptions {
26
+ /** Don't compress below this token count — small output isn't worth touching. */
27
+ minTokens?: number;
28
+ /** Lines of context to keep on each side of an important log line. */
29
+ contextLines?: number;
30
+ /** Head/tail lines always retained. */
31
+ headLines?: number;
32
+ tailLines?: number;
33
+ /** Max elements kept per JSON array before summarising the rest. */
34
+ jsonArraySample?: number;
35
+ }
36
+ /**
37
+ * Compress a tool output string, auto-detecting its content type.
38
+ */
39
+ export declare function compressOutput(raw: string, opts?: CompressOptions): CompressResult;
40
+ export interface ToolOutputCompression {
41
+ content: string;
42
+ /** Human/agent-facing summary of what was kept, for the truncation notice. */
43
+ notice: string;
44
+ strategy: CompressResult["strategy"];
45
+ }
46
+ /**
47
+ * Compress output that a tool was ALREADY going to truncate.
48
+ *
49
+ * This is the only sanctioned live integration point. It is invoked solely on
50
+ * the over-limit branch of a tool (bash / task_output) — where today's
51
+ * alternative is a blind tail slice that discards the head and any mid-stream
52
+ * error. It keeps generous head/tail (tool output is tail-oriented) plus every
53
+ * error line and collapses repeats. The caller still writes the full original
54
+ * to an overflow file, so this stays fully reversible.
55
+ */
56
+ export declare function compressToolOutput(raw: string): ToolOutputCompression;
57
+ //# sourceMappingURL=compress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../../src/tools/compress.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IAC3C,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AA+BD;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,eAAoB,GAAG,cAAc,CAkBtF;AA2HD,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;CACtC;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB,CAQrE"}
@@ -0,0 +1,181 @@
1
+ import { estimateTokens } from "../core/compaction/token-estimator.js";
2
+ const DEFAULTS = {
3
+ minTokens: 400,
4
+ contextLines: 2,
5
+ headLines: 8,
6
+ tailLines: 8,
7
+ jsonArraySample: 5,
8
+ };
9
+ /** Lines that must never be dropped from a log. */
10
+ const IMPORTANT = /\b(error|fatal|panic|exception|traceback|fail(?:ed|ure)?|warn(?:ing)?|assert|fault|denied|refused|timeout|unhandled|rejected|✗|✖|❌)\b|^\s*at\s+|Error:|Caused by:/i;
11
+ function tokens(text) {
12
+ return estimateTokens(text);
13
+ }
14
+ function result(content, strategy, original) {
15
+ return {
16
+ content,
17
+ strategy,
18
+ originalTokens: tokens(original),
19
+ compressedTokens: tokens(content),
20
+ };
21
+ }
22
+ /**
23
+ * Compress a tool output string, auto-detecting its content type.
24
+ */
25
+ export function compressOutput(raw, opts = {}) {
26
+ const o = { ...DEFAULTS, ...opts };
27
+ if (tokens(raw) < o.minTokens)
28
+ return result(raw, "none", raw);
29
+ const trimmed = raw.trim();
30
+ if (trimmed.length > 1 && (trimmed[0] === "{" || trimmed[0] === "[")) {
31
+ const json = tryCompressJson(trimmed, o);
32
+ if (json !== null)
33
+ return result(json, "json", raw);
34
+ }
35
+ const lines = raw.split("\n");
36
+ const importantCount = lines.reduce((n, l) => (IMPORTANT.test(l) ? n + 1 : n), 0);
37
+ // Treat as a log when it's many lines OR carries error signal worth isolating.
38
+ if (lines.length > o.headLines + o.tailLines + 10 && (importantCount > 0 || lines.length > 200)) {
39
+ return result(compressLog(lines, o), "log", raw);
40
+ }
41
+ return result(compressText(lines, o), "text", raw);
42
+ }
43
+ /* ----------------------------------------------------------------------- */
44
+ /* Log: keep error lines + context, collapse repeats, drop the boring middle */
45
+ /* ----------------------------------------------------------------------- */
46
+ function compressLog(lines, o) {
47
+ // 1. Collapse consecutive identical / near-identical runs into one entry.
48
+ // `exact` tracks whether every line in the run was byte-identical, so the
49
+ // emitted marker can be honest: "(×N)" only when truly identical, otherwise
50
+ // "(×N similar)" so the model never assumes the shown text (e.g. a single
51
+ // timestamp) was repeated verbatim across the whole run.
52
+ const collapsed = [];
53
+ for (let i = 0; i < lines.length; i++) {
54
+ const prev = collapsed[collapsed.length - 1];
55
+ if (prev && normalize(prev.text) === normalize(lines[i])) {
56
+ prev.count++;
57
+ if (lines[i] !== prev.text)
58
+ prev.exact = false;
59
+ }
60
+ else {
61
+ collapsed.push({ text: lines[i], count: 1, idx: i, exact: true });
62
+ }
63
+ }
64
+ // 2. Mark which collapsed entries to keep: head, tail, important + context.
65
+ const keep = new Set();
66
+ for (let i = 0; i < Math.min(o.headLines, collapsed.length); i++)
67
+ keep.add(i);
68
+ for (let i = Math.max(0, collapsed.length - o.tailLines); i < collapsed.length; i++)
69
+ keep.add(i);
70
+ for (let i = 0; i < collapsed.length; i++) {
71
+ if (IMPORTANT.test(collapsed[i].text)) {
72
+ for (let j = i - o.contextLines; j <= i + o.contextLines; j++) {
73
+ if (j >= 0 && j < collapsed.length)
74
+ keep.add(j);
75
+ }
76
+ }
77
+ }
78
+ // 3. Emit, replacing dropped gaps with a single omission marker.
79
+ const out = [];
80
+ let droppedLines = 0;
81
+ let gapStart = -1;
82
+ const flushGap = () => {
83
+ if (droppedLines > 0) {
84
+ out.push(`… ${droppedLines} line${droppedLines === 1 ? "" : "s"} omitted …`);
85
+ droppedLines = 0;
86
+ gapStart = -1;
87
+ }
88
+ };
89
+ for (let i = 0; i < collapsed.length; i++) {
90
+ if (keep.has(i)) {
91
+ flushGap();
92
+ const e = collapsed[i];
93
+ if (e.count > 1)
94
+ out.push(`${e.text} (×${e.count}${e.exact ? "" : " similar"})`);
95
+ else
96
+ out.push(e.text);
97
+ }
98
+ else {
99
+ if (gapStart === -1)
100
+ gapStart = i;
101
+ droppedLines += collapsed[i].count;
102
+ }
103
+ }
104
+ flushGap();
105
+ return out.join("\n");
106
+ }
107
+ /**
108
+ * Strip only *volatile noise* (timestamps, hex addresses) so lines that differ
109
+ * solely by a leading timestamp collapse as repeats. Deliberately does NOT nuke
110
+ * arbitrary integers: when the number IS the content (`line 5998` vs `line 5999`,
111
+ * a port, an id), the lines must stay distinct so the tail isn't flattened into
112
+ * a single "(×N)" and genuinely different output isn't reported as repetition.
113
+ */
114
+ function normalize(line) {
115
+ return line
116
+ .replace(/\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?/g, "<ts>")
117
+ .replace(/\b\d{2}:\d{2}:\d{2}(?:\.\d+)?\b/g, "<ts>")
118
+ .replace(/0x[0-9a-f]+/gi, "<addr>")
119
+ .trim();
120
+ }
121
+ /* ----------------------------------------------------------------------- */
122
+ /* JSON: keep shape + a sample of long arrays, summarise the rest */
123
+ /* ----------------------------------------------------------------------- */
124
+ function tryCompressJson(text, o) {
125
+ let parsed;
126
+ try {
127
+ parsed = JSON.parse(text);
128
+ }
129
+ catch {
130
+ return null;
131
+ }
132
+ const shrunk = shrinkJson(parsed, o);
133
+ return JSON.stringify(shrunk, null, 2);
134
+ }
135
+ function shrinkJson(value, o) {
136
+ if (Array.isArray(value)) {
137
+ if (value.length <= o.jsonArraySample)
138
+ return value.map((v) => shrinkJson(v, o));
139
+ const sample = value.slice(0, o.jsonArraySample).map((v) => shrinkJson(v, o));
140
+ sample.push(`… ${value.length - o.jsonArraySample} more of ${value.length} items omitted …`);
141
+ return sample;
142
+ }
143
+ if (value && typeof value === "object") {
144
+ const out = {};
145
+ for (const [k, v] of Object.entries(value))
146
+ out[k] = shrinkJson(v, o);
147
+ return out;
148
+ }
149
+ return value;
150
+ }
151
+ /* ----------------------------------------------------------------------- */
152
+ /* Text: middle-out so both the command/context and the result survive */
153
+ /* ----------------------------------------------------------------------- */
154
+ function compressText(lines, o) {
155
+ const budget = o.headLines + o.tailLines;
156
+ if (lines.length <= budget)
157
+ return lines.join("\n");
158
+ const head = lines.slice(0, o.headLines);
159
+ const tail = lines.slice(lines.length - o.tailLines);
160
+ const dropped = lines.length - budget;
161
+ return [...head, `… ${dropped} lines omitted …`, ...tail].join("\n");
162
+ }
163
+ /**
164
+ * Compress output that a tool was ALREADY going to truncate.
165
+ *
166
+ * This is the only sanctioned live integration point. It is invoked solely on
167
+ * the over-limit branch of a tool (bash / task_output) — where today's
168
+ * alternative is a blind tail slice that discards the head and any mid-stream
169
+ * error. It keeps generous head/tail (tool output is tail-oriented) plus every
170
+ * error line and collapses repeats. The caller still writes the full original
171
+ * to an overflow file, so this stays fully reversible.
172
+ */
173
+ export function compressToolOutput(raw) {
174
+ const r = compressOutput(raw, { headLines: 12, tailLines: 30, minTokens: 0 });
175
+ const saved = r.originalTokens - r.compressedTokens;
176
+ const notice = r.strategy === "none"
177
+ ? ""
178
+ : `Compressed (${r.strategy}): kept errors + head/tail, collapsed repeats — ~${saved} fewer tokens.`;
179
+ return { content: r.content, notice, strategy: r.strategy };
180
+ }
181
+ //# sourceMappingURL=compress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compress.js","sourceRoot":"","sources":["../../src/tools/compress.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=compress.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compress.test.d.ts","sourceRoot":"","sources":["../../src/tools/compress.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,60 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { compressOutput } from "./compress.js";
3
+ describe("compressOutput", () => {
4
+ it("leaves small output untouched", () => {
5
+ const small = "line one\nline two\nline three";
6
+ const r = compressOutput(small);
7
+ expect(r.strategy).toBe("none");
8
+ expect(r.content).toBe(small);
9
+ });
10
+ it("keeps a FATAL line buried in the middle of a huge log", () => {
11
+ const lines = [];
12
+ for (let i = 0; i < 5000; i++) {
13
+ if (i === 2600)
14
+ lines.push("ERROR FATAL: heap allocation failed at chunk 8821");
15
+ else
16
+ lines.push(`INFO compiled module ${i} ok`);
17
+ }
18
+ const r = compressOutput(lines.join("\n"));
19
+ expect(r.strategy).toBe("log");
20
+ expect(r.content).toContain("FATAL: heap allocation failed at chunk 8821");
21
+ expect(r.compressedTokens).toBeLessThan(r.originalTokens * 0.2);
22
+ });
23
+ it("collapses repeated runs into a count", () => {
24
+ const lines = ["start"];
25
+ for (let i = 0; i < 200; i++)
26
+ lines.push("WARN retry: Connection refused: postgres:5432");
27
+ lines.push("ERROR giving up after 200 attempts");
28
+ const r = compressOutput(lines.join("\n"));
29
+ expect(r.content).toContain("Connection refused: postgres:5432");
30
+ expect(r.content).toContain("giving up after 200 attempts");
31
+ expect(r.content).toMatch(/×\d+/);
32
+ });
33
+ it("summarises a long JSON array but keeps shape and early matches", () => {
34
+ const matches = [];
35
+ for (let i = 0; i < 412; i++) {
36
+ matches.push({ path: i === 3 ? "src/core/auth.ts" : `src/gen/file-${i}.ts`, line: i });
37
+ }
38
+ const r = compressOutput(JSON.stringify({ total: 412, matches }, null, 2));
39
+ expect(r.strategy).toBe("json");
40
+ expect(r.content).toContain('"total": 412');
41
+ expect(r.content).toContain("src/core/auth.ts");
42
+ expect(r.content).toMatch(/more of 412 items omitted/);
43
+ expect(r.compressedTokens).toBeLessThan(r.originalTokens * 0.5);
44
+ });
45
+ it("preserves the failing assertion among thousands of passes", () => {
46
+ const lines = [];
47
+ for (let i = 0; i < 2000; i++) {
48
+ if (i === 1450) {
49
+ lines.push("✗ user-auth › rejects expired token");
50
+ lines.push(" AssertionError: Expected 401 but received 200");
51
+ }
52
+ else
53
+ lines.push(`✓ case ${i}`);
54
+ }
55
+ const r = compressOutput(lines.join("\n"));
56
+ expect(r.content).toContain("rejects expired token");
57
+ expect(r.content).toContain("Expected 401 but received 200");
58
+ });
59
+ });
60
+ //# sourceMappingURL=compress.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compress.test.js","sourceRoot":"","sources":["../../src/tools/compress.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"edit-diff.d.ts","sourceRoot":"","sources":["../../src/tools/edit-diff.ts"],"names":[],"mappings":"AAsEA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,IAAI,CAgDf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuDzF;AAcD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYjE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYlE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM3D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAgC5E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAkCzE;AASD;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAIhB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,YAAY,SAAI,EAChB,UAAU,SAAI,GACb,cAAc,GAAG,IAAI,CAuCvB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,GAAG,SAAI,GACN;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,CA6BrC;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAa7F"}
1
+ {"version":3,"file":"edit-diff.d.ts","sourceRoot":"","sources":["../../src/tools/edit-diff.ts"],"names":[],"mappings":"AAsEA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,IAAI,CAgDf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuDzF;AAcD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYjE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYlE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM3D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CA4C5E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAqCzE;AASD;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAIhB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,YAAY,SAAI,EAChB,UAAU,SAAI,GACb,cAAc,GAAG,IAAI,CAuCvB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,GAAG,SAAI,GACN;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,CA6BrC;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAa7F"}
@@ -286,18 +286,31 @@ export function fuzzyFindText(content, oldText) {
286
286
  }
287
287
  // Fuzzy match line-by-line so stripped trailing whitespace in earlier lines
288
288
  // cannot shift offsets and make us replace the wrong byte range.
289
+ //
290
+ // Performance: use a lazy normalization cache — each content line is
291
+ // normalized on first access and cached, so lines before the match are
292
+ // normalized exactly once (not once per window), and lines after the
293
+ // match are never touched. This gives O(n+m) worst-case (no match)
294
+ // while avoiding wasted work when the match is found early.
289
295
  const oldLines = oldText.split("\n");
290
296
  const contentLines = content.split("\n");
291
- const normalizedOldLines = oldLines.map(normalizeForFuzzyMatch);
297
+ const normalizedOldJoined = oldLines.map(normalizeForFuzzyMatch).join("\n");
298
+ const normalizedCache = new Array(contentLines.length);
292
299
  for (let startLine = 0; startLine + oldLines.length <= contentLines.length; startLine++) {
293
- const candidateLines = contentLines.slice(startLine, startLine + oldLines.length);
294
- const normalizedCandidate = candidateLines.map(normalizeForFuzzyMatch);
295
- if (normalizedCandidate.join("\n") !== normalizedOldLines.join("\n"))
300
+ // Build the normalized candidate window using the lazy cache
301
+ let normalizedCandidate = "";
302
+ for (let j = startLine; j < startLine + oldLines.length; j++) {
303
+ normalizedCandidate += normalizedCache[j] ??= normalizeForFuzzyMatch(contentLines[j]);
304
+ if (j < startLine + oldLines.length - 1)
305
+ normalizedCandidate += "\n";
306
+ }
307
+ if (normalizedCandidate !== normalizedOldJoined)
296
308
  continue;
297
309
  let actualIndex = 0;
298
310
  for (let i = 0; i < startLine; i++) {
299
311
  actualIndex += contentLines[i].length + 1; // +1 for \n
300
312
  }
313
+ const candidateLines = contentLines.slice(startLine, startLine + oldLines.length);
301
314
  return {
302
315
  found: true,
303
316
  index: actualIndex,
@@ -335,11 +348,15 @@ export function countOccurrences(content, oldText) {
335
348
  }
336
349
  const oldLines = oldText.split("\n");
337
350
  const contentLines = content.split("\n");
351
+ // Lazy normalization cache (same optimization as fuzzyFindText).
352
+ const normalizedCache = new Array(contentLines.length);
338
353
  for (let startLine = 0; startLine + oldLines.length <= contentLines.length; startLine++) {
339
- const normalizedCandidate = contentLines
340
- .slice(startLine, startLine + oldLines.length)
341
- .map(normalizeForFuzzyMatch)
342
- .join("\n");
354
+ let normalizedCandidate = "";
355
+ for (let j = startLine; j < startLine + oldLines.length; j++) {
356
+ normalizedCandidate += normalizedCache[j] ??= normalizeForFuzzyMatch(contentLines[j]);
357
+ if (j < startLine + oldLines.length - 1)
358
+ normalizedCandidate += "\n";
359
+ }
343
360
  if (normalizedCandidate === normalizedOld)
344
361
  count++;
345
362
  }