@aexol/spectral 0.7.1 → 0.7.5

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 (219) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/dist/agent/agents.js +1 -1
  3. package/dist/agent/index.js +199 -184
  4. package/dist/commands/serve.js +0 -3
  5. package/dist/designer/data/systems/renault/DESIGN.md +1 -1
  6. package/dist/designer/philosophies.js +668 -0
  7. package/dist/mcp/sampling-handler.js +1 -1
  8. package/dist/memory/commands/status.js +1 -1
  9. package/dist/memory/compaction.js +2 -2
  10. package/dist/memory/config.js +1 -1
  11. package/dist/memory/debug-log.js +1 -1
  12. package/dist/memory/hooks/compaction-hook.js +29 -0
  13. package/dist/memory/index.js +2 -0
  14. package/dist/memory/observer.js +2 -2
  15. package/dist/memory/project-observations-store.js +14 -0
  16. package/dist/memory/tokens.js +1 -1
  17. package/dist/memory/tools/read-project-observations.js +82 -0
  18. package/dist/memory/tools/recall-observation.js +2 -2
  19. package/dist/pi/agent-core/agent-loop.js +501 -0
  20. package/dist/pi/agent-core/agent.js +401 -0
  21. package/dist/pi/agent-core/harness/agent-harness.js +899 -0
  22. package/dist/pi/agent-core/harness/compaction/branch-summarization.js +173 -0
  23. package/dist/pi/agent-core/harness/compaction/compaction.js +532 -0
  24. package/dist/pi/agent-core/harness/compaction/utils.js +130 -0
  25. package/dist/pi/agent-core/harness/env/nodejs.js +485 -0
  26. package/dist/pi/agent-core/harness/messages.js +101 -0
  27. package/dist/pi/agent-core/harness/prompt-templates.js +229 -0
  28. package/dist/pi/agent-core/harness/session/jsonl-repo.js +100 -0
  29. package/dist/pi/agent-core/harness/session/jsonl-storage.js +230 -0
  30. package/dist/pi/agent-core/harness/session/memory-repo.js +41 -0
  31. package/dist/pi/agent-core/harness/session/memory-storage.js +113 -0
  32. package/dist/pi/agent-core/harness/session/repo-utils.js +38 -0
  33. package/dist/pi/agent-core/harness/session/session.js +196 -0
  34. package/dist/pi/agent-core/harness/session/uuid.js +49 -0
  35. package/dist/pi/agent-core/harness/skills.js +310 -0
  36. package/dist/pi/agent-core/harness/system-prompt.js +29 -0
  37. package/dist/pi/agent-core/harness/types.js +93 -0
  38. package/dist/pi/agent-core/harness/utils/shell-output.js +125 -0
  39. package/dist/pi/agent-core/harness/utils/truncate.js +289 -0
  40. package/dist/pi/agent-core/index.js +24 -0
  41. package/dist/pi/agent-core/node.js +2 -0
  42. package/dist/pi/agent-core/proxy.js +277 -0
  43. package/dist/pi/agent-core/types.js +1 -0
  44. package/dist/pi/ai/api-registry.js +43 -0
  45. package/dist/pi/ai/cli.js +120 -0
  46. package/dist/pi/ai/env-api-keys.js +169 -0
  47. package/dist/pi/ai/image-models.generated.js +441 -0
  48. package/dist/pi/ai/image-models.js +22 -0
  49. package/dist/pi/ai/images-api-registry.js +21 -0
  50. package/dist/pi/ai/images.js +13 -0
  51. package/dist/pi/ai/index.js +18 -0
  52. package/dist/pi/ai/models.generated.js +16220 -0
  53. package/dist/pi/ai/models.js +70 -0
  54. package/dist/pi/ai/oauth.js +1 -0
  55. package/dist/pi/ai/providers/anthropic.js +945 -0
  56. package/dist/pi/ai/providers/faux.js +367 -0
  57. package/dist/pi/ai/providers/github-copilot-headers.js +28 -0
  58. package/dist/pi/ai/providers/openai-completions.js +945 -0
  59. package/dist/pi/ai/providers/openai-prompt-cache.js +9 -0
  60. package/dist/pi/ai/providers/register-builtins.js +97 -0
  61. package/dist/pi/ai/providers/simple-options.js +40 -0
  62. package/dist/pi/ai/providers/transform-messages.js +183 -0
  63. package/dist/pi/ai/session-resources.js +21 -0
  64. package/dist/pi/ai/stream.js +26 -0
  65. package/dist/pi/ai/types.js +1 -0
  66. package/dist/pi/ai/utils/diagnostics.js +24 -0
  67. package/dist/pi/ai/utils/event-stream.js +80 -0
  68. package/dist/pi/ai/utils/hash.js +13 -0
  69. package/dist/pi/ai/utils/headers.js +7 -0
  70. package/dist/pi/ai/utils/json-parse.js +112 -0
  71. package/dist/pi/ai/utils/node-http-proxy.js +96 -0
  72. package/dist/pi/ai/utils/oauth/anthropic.js +334 -0
  73. package/dist/pi/ai/utils/oauth/device-code.js +54 -0
  74. package/dist/pi/ai/utils/oauth/github-copilot.js +270 -0
  75. package/dist/pi/ai/utils/oauth/index.js +121 -0
  76. package/dist/pi/ai/utils/oauth/oauth-page.js +104 -0
  77. package/dist/pi/ai/utils/oauth/openai-codex.js +384 -0
  78. package/dist/pi/ai/utils/oauth/pkce.js +30 -0
  79. package/dist/pi/ai/utils/oauth/types.js +1 -0
  80. package/dist/pi/ai/utils/overflow.js +150 -0
  81. package/dist/pi/ai/utils/sanitize-unicode.js +25 -0
  82. package/dist/pi/ai/utils/typebox-helpers.js +20 -0
  83. package/dist/pi/ai/utils/validation.js +280 -0
  84. package/dist/pi/coding-agent/bun/cli.js +7 -0
  85. package/dist/pi/coding-agent/bun/restore-sandbox-env.js +31 -0
  86. package/dist/pi/coding-agent/cli/args.js +340 -0
  87. package/dist/pi/coding-agent/cli/file-processor.js +82 -0
  88. package/dist/pi/coding-agent/cli/initial-message.js +21 -0
  89. package/dist/pi/coding-agent/cli.js +17 -0
  90. package/dist/pi/coding-agent/config.js +414 -0
  91. package/dist/pi/coding-agent/core/agent-session-runtime.js +299 -0
  92. package/dist/pi/coding-agent/core/agent-session-services.js +117 -0
  93. package/dist/pi/coding-agent/core/agent-session.js +2498 -0
  94. package/dist/pi/coding-agent/core/auth-guidance.js +20 -0
  95. package/dist/pi/coding-agent/core/auth-storage.js +441 -0
  96. package/dist/pi/coding-agent/core/bash-executor.js +110 -0
  97. package/dist/pi/coding-agent/core/compaction/branch-summarization.js +242 -0
  98. package/dist/pi/coding-agent/core/compaction/compaction.js +624 -0
  99. package/dist/pi/coding-agent/core/compaction/index.js +6 -0
  100. package/dist/pi/coding-agent/core/compaction/utils.js +152 -0
  101. package/dist/pi/coding-agent/core/defaults.js +1 -0
  102. package/dist/pi/coding-agent/core/diagnostics.js +1 -0
  103. package/dist/pi/coding-agent/core/event-bus.js +24 -0
  104. package/dist/pi/coding-agent/core/exec.js +74 -0
  105. package/dist/pi/coding-agent/core/export-html/ansi-to-html.js +248 -0
  106. package/dist/pi/coding-agent/core/export-html/index.js +225 -0
  107. package/dist/pi/coding-agent/core/export-html/tool-renderer.js +107 -0
  108. package/dist/pi/coding-agent/core/extensions/index.js +8 -0
  109. package/dist/pi/coding-agent/core/extensions/loader.js +485 -0
  110. package/dist/pi/coding-agent/core/extensions/runner.js +824 -0
  111. package/dist/pi/coding-agent/core/extensions/types.js +44 -0
  112. package/dist/pi/coding-agent/core/extensions/wrapper.js +21 -0
  113. package/dist/pi/coding-agent/core/footer-data-provider.js +309 -0
  114. package/dist/pi/coding-agent/core/http-dispatcher.js +47 -0
  115. package/dist/pi/coding-agent/core/index.js +11 -0
  116. package/dist/pi/coding-agent/core/keybindings.js +294 -0
  117. package/dist/pi/coding-agent/core/messages.js +122 -0
  118. package/dist/pi/coding-agent/core/model-registry.js +728 -0
  119. package/dist/pi/coding-agent/core/model-resolver.js +494 -0
  120. package/dist/pi/coding-agent/core/output-guard.js +58 -0
  121. package/dist/pi/coding-agent/core/package-manager.js +2020 -0
  122. package/dist/pi/coding-agent/core/prompt-templates.js +237 -0
  123. package/dist/pi/coding-agent/core/provider-display-names.js +32 -0
  124. package/dist/pi/coding-agent/core/resolve-config-value.js +125 -0
  125. package/dist/pi/coding-agent/core/resource-loader.js +733 -0
  126. package/dist/pi/coding-agent/core/sdk.js +282 -0
  127. package/dist/pi/coding-agent/core/session-cwd.js +37 -0
  128. package/dist/pi/coding-agent/core/session-manager.js +1146 -0
  129. package/dist/pi/coding-agent/core/settings-manager.js +794 -0
  130. package/dist/pi/coding-agent/core/skills.js +386 -0
  131. package/dist/pi/coding-agent/core/slash-commands.js +24 -0
  132. package/dist/pi/coding-agent/core/source-info.js +18 -0
  133. package/dist/pi/coding-agent/core/system-prompt.js +122 -0
  134. package/dist/pi/coding-agent/core/telemetry.js +8 -0
  135. package/dist/pi/coding-agent/core/timings.js +30 -0
  136. package/dist/pi/coding-agent/core/tools/bash.js +341 -0
  137. package/dist/pi/coding-agent/core/tools/edit-diff.js +344 -0
  138. package/dist/pi/coding-agent/core/tools/edit.js +324 -0
  139. package/dist/pi/coding-agent/core/tools/file-mutation-queue.js +36 -0
  140. package/dist/pi/coding-agent/core/tools/find.js +297 -0
  141. package/dist/pi/coding-agent/core/tools/grep.js +303 -0
  142. package/dist/pi/coding-agent/core/tools/index.js +111 -0
  143. package/dist/pi/coding-agent/core/tools/ls.js +168 -0
  144. package/dist/pi/coding-agent/core/tools/output-accumulator.js +183 -0
  145. package/dist/pi/coding-agent/core/tools/path-utils.js +61 -0
  146. package/dist/pi/coding-agent/core/tools/read.js +288 -0
  147. package/dist/pi/coding-agent/core/tools/render-utils.js +48 -0
  148. package/dist/pi/coding-agent/core/tools/tool-definition-wrapper.js +33 -0
  149. package/dist/pi/coding-agent/core/tools/truncate.js +214 -0
  150. package/dist/pi/coding-agent/core/tools/write.js +212 -0
  151. package/dist/pi/coding-agent/index.js +41 -0
  152. package/dist/pi/coding-agent/main.js +5 -0
  153. package/dist/pi/coding-agent/migrations.js +280 -0
  154. package/dist/pi/coding-agent/modes/index.js +7 -0
  155. package/dist/pi/coding-agent/modes/interactive/components/diff.js +132 -0
  156. package/dist/pi/coding-agent/modes/interactive/components/keybinding-hints.js +35 -0
  157. package/dist/pi/coding-agent/modes/interactive/components/visual-truncate.js +32 -0
  158. package/dist/pi/coding-agent/modes/interactive/interactive-mode.js +3 -0
  159. package/dist/pi/coding-agent/modes/interactive/theme/theme.js +1023 -0
  160. package/dist/pi/coding-agent/modes/print-mode.js +130 -0
  161. package/dist/pi/coding-agent/modes/rpc/jsonl.js +48 -0
  162. package/dist/pi/coding-agent/modes/rpc/rpc-client.js +409 -0
  163. package/dist/pi/coding-agent/modes/rpc/rpc-mode.js +600 -0
  164. package/dist/pi/coding-agent/modes/rpc/rpc-types.js +7 -0
  165. package/dist/pi/coding-agent/utils/ansi.js +51 -0
  166. package/dist/pi/coding-agent/utils/changelog.js +86 -0
  167. package/dist/pi/coding-agent/utils/child-process.js +87 -0
  168. package/dist/pi/coding-agent/utils/clipboard-image.js +244 -0
  169. package/dist/pi/coding-agent/utils/clipboard-native.js +13 -0
  170. package/dist/pi/coding-agent/utils/clipboard.js +116 -0
  171. package/dist/pi/coding-agent/utils/exif-orientation.js +157 -0
  172. package/dist/pi/coding-agent/utils/frontmatter.js +25 -0
  173. package/dist/pi/coding-agent/utils/fs-watch.js +24 -0
  174. package/dist/pi/coding-agent/utils/git.js +162 -0
  175. package/dist/pi/coding-agent/utils/html.js +39 -0
  176. package/dist/pi/coding-agent/utils/image-convert.js +38 -0
  177. package/dist/pi/coding-agent/utils/image-resize.js +136 -0
  178. package/dist/pi/coding-agent/utils/mime.js +68 -0
  179. package/dist/pi/coding-agent/utils/paths.js +91 -0
  180. package/dist/pi/coding-agent/utils/photon.js +120 -0
  181. package/dist/pi/coding-agent/utils/pi-user-agent.js +4 -0
  182. package/dist/pi/coding-agent/utils/shell.js +194 -0
  183. package/dist/pi/coding-agent/utils/sleep.js +16 -0
  184. package/dist/pi/coding-agent/utils/syntax-highlight.js +117 -0
  185. package/dist/pi/coding-agent/utils/tools-manager.js +327 -0
  186. package/dist/pi/coding-agent/utils/version-check.js +81 -0
  187. package/dist/pi/coding-agent/utils/windows-self-update.js +76 -0
  188. package/dist/pi/tui/autocomplete.js +631 -0
  189. package/dist/pi/tui/components/box.js +103 -0
  190. package/dist/pi/tui/components/cancellable-loader.js +34 -0
  191. package/dist/pi/tui/components/editor.js +1915 -0
  192. package/dist/pi/tui/components/image.js +88 -0
  193. package/dist/pi/tui/components/input.js +425 -0
  194. package/dist/pi/tui/components/loader.js +68 -0
  195. package/dist/pi/tui/components/markdown.js +633 -0
  196. package/dist/pi/tui/components/select-list.js +158 -0
  197. package/dist/pi/tui/components/settings-list.js +184 -0
  198. package/dist/pi/tui/components/spacer.js +22 -0
  199. package/dist/pi/tui/components/text.js +88 -0
  200. package/dist/pi/tui/components/truncated-text.js +50 -0
  201. package/dist/pi/tui/editor-component.js +1 -0
  202. package/dist/pi/tui/fuzzy.js +109 -0
  203. package/dist/pi/tui/index.js +31 -0
  204. package/dist/pi/tui/keybindings.js +173 -0
  205. package/dist/pi/tui/keys.js +1172 -0
  206. package/dist/pi/tui/kill-ring.js +43 -0
  207. package/dist/pi/tui/stdin-buffer.js +360 -0
  208. package/dist/pi/tui/terminal-image.js +335 -0
  209. package/dist/pi/tui/terminal.js +324 -0
  210. package/dist/pi/tui/tui.js +1076 -0
  211. package/dist/pi/tui/undo-stack.js +24 -0
  212. package/dist/pi/tui/utils.js +1016 -0
  213. package/dist/relay/dispatcher.js +30 -0
  214. package/dist/server/handlers/queue.js +52 -0
  215. package/dist/server/pi-bridge.js +9 -1
  216. package/dist/server/session-stream.js +76 -111
  217. package/dist/server/storage.js +154 -2
  218. package/dist/server/title-generator.js +14 -153
  219. package/package.json +24 -6
@@ -0,0 +1,485 @@
1
+ import { spawn } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
3
+ import { constants, createReadStream } from "node:fs";
4
+ import { access, appendFile, lstat, mkdir, mkdtemp, readdir, readFile, realpath, rm, writeFile, } from "node:fs/promises";
5
+ import { tmpdir } from "node:os";
6
+ import { isAbsolute, join, resolve } from "node:path";
7
+ import { createInterface } from "node:readline";
8
+ import { ExecutionError, err, FileError, ok, toError, } from "../types.js";
9
+ function resolvePath(cwd, path) {
10
+ return isAbsolute(path) ? path : resolve(cwd, path);
11
+ }
12
+ function fileKindFromStats(stats) {
13
+ if (stats.isFile())
14
+ return "file";
15
+ if (stats.isDirectory())
16
+ return "directory";
17
+ if (stats.isSymbolicLink())
18
+ return "symlink";
19
+ return undefined;
20
+ }
21
+ function fileInfoFromStats(path, stats) {
22
+ const kind = fileKindFromStats(stats);
23
+ if (!kind)
24
+ return err(new FileError("invalid", "Unsupported file type", path));
25
+ return ok({
26
+ name: path.replace(/\/+$/, "").split("/").pop() ?? path,
27
+ path,
28
+ kind,
29
+ size: stats.size,
30
+ mtimeMs: stats.mtimeMs,
31
+ });
32
+ }
33
+ function isNodeError(error) {
34
+ return error instanceof Error && "code" in error;
35
+ }
36
+ function toFileError(error, path) {
37
+ if (error instanceof FileError)
38
+ return error;
39
+ const cause = toError(error);
40
+ if (isNodeError(error)) {
41
+ const message = error.message;
42
+ switch (error.code) {
43
+ case "ABORT_ERR":
44
+ return new FileError("aborted", message, path, cause);
45
+ case "ENOENT":
46
+ return new FileError("not_found", message, path, cause);
47
+ case "EACCES":
48
+ case "EPERM":
49
+ return new FileError("permission_denied", message, path, cause);
50
+ case "ENOTDIR":
51
+ return new FileError("not_directory", message, path, cause);
52
+ case "EISDIR":
53
+ return new FileError("is_directory", message, path, cause);
54
+ case "EINVAL":
55
+ return new FileError("invalid", message, path, cause);
56
+ }
57
+ }
58
+ return new FileError("unknown", cause.message, path, cause);
59
+ }
60
+ function abortResult(signal, path) {
61
+ return signal?.aborted ? err(new FileError("aborted", "aborted", path)) : undefined;
62
+ }
63
+ async function pathExists(path) {
64
+ try {
65
+ await access(path, constants.F_OK);
66
+ return true;
67
+ }
68
+ catch {
69
+ return false;
70
+ }
71
+ }
72
+ async function runCommand(command, args, timeoutMs) {
73
+ return await new Promise((resolve) => {
74
+ let stdout = "";
75
+ let child;
76
+ try {
77
+ child = spawn(command, args, {
78
+ stdio: ["ignore", "pipe", "ignore"],
79
+ windowsHide: true,
80
+ });
81
+ }
82
+ catch {
83
+ resolve({ stdout: "", status: null });
84
+ return;
85
+ }
86
+ const timeout = setTimeout(() => {
87
+ if (child.pid)
88
+ killProcessTree(child.pid);
89
+ }, timeoutMs);
90
+ child.stdout?.setEncoding("utf8");
91
+ child.stdout?.on("data", (chunk) => {
92
+ stdout += chunk;
93
+ });
94
+ child.on("error", () => {
95
+ clearTimeout(timeout);
96
+ resolve({ stdout: "", status: null });
97
+ });
98
+ child.on("close", (status) => {
99
+ clearTimeout(timeout);
100
+ resolve({ stdout, status });
101
+ });
102
+ });
103
+ }
104
+ async function findBashOnPath() {
105
+ const result = process.platform === "win32"
106
+ ? await runCommand("where", ["bash.exe"], 5000)
107
+ : await runCommand("which", ["bash"], 5000);
108
+ if (result.status !== 0 || !result.stdout)
109
+ return null;
110
+ const firstMatch = result.stdout.trim().split(/\r?\n/)[0];
111
+ return firstMatch && (await pathExists(firstMatch)) ? firstMatch : null;
112
+ }
113
+ async function getShellConfig(customShellPath) {
114
+ if (customShellPath) {
115
+ if (await pathExists(customShellPath)) {
116
+ return ok({ shell: customShellPath, args: ["-c"] });
117
+ }
118
+ return err(new ExecutionError("shell_unavailable", `Custom shell path not found: ${customShellPath}`));
119
+ }
120
+ if (process.platform === "win32") {
121
+ const candidates = [];
122
+ const programFiles = process.env.ProgramFiles;
123
+ if (programFiles)
124
+ candidates.push(`${programFiles}\\Git\\bin\\bash.exe`);
125
+ const programFilesX86 = process.env["ProgramFiles(x86)"];
126
+ if (programFilesX86)
127
+ candidates.push(`${programFilesX86}\\Git\\bin\\bash.exe`);
128
+ for (const candidate of candidates) {
129
+ if (await pathExists(candidate)) {
130
+ return ok({ shell: candidate, args: ["-c"] });
131
+ }
132
+ }
133
+ const bashOnPath = await findBashOnPath();
134
+ if (bashOnPath) {
135
+ return ok({ shell: bashOnPath, args: ["-c"] });
136
+ }
137
+ return err(new ExecutionError("shell_unavailable", "No bash shell found"));
138
+ }
139
+ if (await pathExists("/bin/bash")) {
140
+ return ok({ shell: "/bin/bash", args: ["-c"] });
141
+ }
142
+ const bashOnPath = await findBashOnPath();
143
+ if (bashOnPath) {
144
+ return ok({ shell: bashOnPath, args: ["-c"] });
145
+ }
146
+ return ok({ shell: "sh", args: ["-c"] });
147
+ }
148
+ function getShellEnv(baseEnv, extraEnv) {
149
+ return {
150
+ ...process.env,
151
+ ...baseEnv,
152
+ ...extraEnv,
153
+ };
154
+ }
155
+ function killProcessTree(pid) {
156
+ if (process.platform === "win32") {
157
+ try {
158
+ spawn("taskkill", ["/F", "/T", "/PID", String(pid)], {
159
+ stdio: "ignore",
160
+ detached: true,
161
+ windowsHide: true,
162
+ });
163
+ }
164
+ catch {
165
+ // Ignore errors.
166
+ }
167
+ return;
168
+ }
169
+ try {
170
+ process.kill(-pid, "SIGKILL");
171
+ }
172
+ catch {
173
+ try {
174
+ process.kill(pid, "SIGKILL");
175
+ }
176
+ catch {
177
+ // Process already dead.
178
+ }
179
+ }
180
+ }
181
+ export class NodeExecutionEnv {
182
+ cwd;
183
+ shellPath;
184
+ shellEnv;
185
+ constructor(options) {
186
+ this.cwd = options.cwd;
187
+ this.shellPath = options.shellPath;
188
+ this.shellEnv = options.shellEnv;
189
+ }
190
+ async absolutePath(path) {
191
+ return ok(resolvePath(this.cwd, path));
192
+ }
193
+ async joinPath(parts) {
194
+ return ok(join(...parts));
195
+ }
196
+ async exec(command, options) {
197
+ if (options?.abortSignal?.aborted)
198
+ return err(new ExecutionError("aborted", "aborted"));
199
+ const cwd = options?.cwd ? resolvePath(this.cwd, options.cwd) : this.cwd;
200
+ const shellConfig = await getShellConfig(this.shellPath);
201
+ if (!shellConfig.ok)
202
+ return shellConfig;
203
+ return await new Promise((resolvePromise) => {
204
+ let stdout = "";
205
+ let stderr = "";
206
+ let settled = false;
207
+ let timedOut = false;
208
+ let callbackError;
209
+ let child;
210
+ let timeoutId;
211
+ const onAbort = () => {
212
+ if (child?.pid) {
213
+ killProcessTree(child.pid);
214
+ }
215
+ };
216
+ const settle = (result) => {
217
+ if (timeoutId)
218
+ clearTimeout(timeoutId);
219
+ if (options?.abortSignal)
220
+ options.abortSignal.removeEventListener("abort", onAbort);
221
+ if (settled)
222
+ return;
223
+ settled = true;
224
+ resolvePromise(result);
225
+ };
226
+ try {
227
+ child = spawn(shellConfig.value.shell, [...shellConfig.value.args, command], {
228
+ cwd,
229
+ detached: process.platform !== "win32",
230
+ env: getShellEnv(this.shellEnv, options?.env),
231
+ stdio: ["ignore", "pipe", "pipe"],
232
+ windowsHide: true,
233
+ });
234
+ }
235
+ catch (error) {
236
+ const cause = toError(error);
237
+ settle(err(new ExecutionError("spawn_error", cause.message, cause)));
238
+ return;
239
+ }
240
+ timeoutId =
241
+ typeof options?.timeout === "number"
242
+ ? setTimeout(() => {
243
+ timedOut = true;
244
+ if (child?.pid) {
245
+ killProcessTree(child.pid);
246
+ }
247
+ }, options.timeout * 1000)
248
+ : undefined;
249
+ if (options?.abortSignal) {
250
+ if (options.abortSignal.aborted) {
251
+ onAbort();
252
+ }
253
+ else {
254
+ options.abortSignal.addEventListener("abort", onAbort, { once: true });
255
+ }
256
+ }
257
+ child.stdout?.setEncoding("utf8");
258
+ child.stderr?.setEncoding("utf8");
259
+ child.stdout?.on("data", (chunk) => {
260
+ stdout += chunk;
261
+ try {
262
+ options?.onStdout?.(chunk);
263
+ }
264
+ catch (error) {
265
+ const cause = toError(error);
266
+ callbackError = new ExecutionError("callback_error", cause.message, cause);
267
+ onAbort();
268
+ }
269
+ });
270
+ child.stderr?.on("data", (chunk) => {
271
+ stderr += chunk;
272
+ try {
273
+ options?.onStderr?.(chunk);
274
+ }
275
+ catch (error) {
276
+ const cause = toError(error);
277
+ callbackError = new ExecutionError("callback_error", cause.message, cause);
278
+ onAbort();
279
+ }
280
+ });
281
+ child.on("error", (error) => {
282
+ settle(err(new ExecutionError("spawn_error", error.message, error)));
283
+ });
284
+ child.on("close", (code) => {
285
+ if (callbackError) {
286
+ settle(err(callbackError));
287
+ return;
288
+ }
289
+ if (timedOut) {
290
+ settle(err(new ExecutionError("timeout", `timeout:${options?.timeout}`)));
291
+ return;
292
+ }
293
+ if (options?.abortSignal?.aborted) {
294
+ settle(err(new ExecutionError("aborted", "aborted")));
295
+ return;
296
+ }
297
+ settle(ok({ stdout, stderr, exitCode: code ?? 0 }));
298
+ });
299
+ });
300
+ }
301
+ async readTextFile(path, abortSignal) {
302
+ const resolved = resolvePath(this.cwd, path);
303
+ const aborted = abortResult(abortSignal, resolved);
304
+ if (aborted)
305
+ return aborted;
306
+ try {
307
+ return ok(await readFile(resolved, { encoding: "utf8", signal: abortSignal }));
308
+ }
309
+ catch (error) {
310
+ return err(toFileError(error, resolved));
311
+ }
312
+ }
313
+ async readTextLines(path, options) {
314
+ const resolved = resolvePath(this.cwd, path);
315
+ const aborted = abortResult(options?.abortSignal, resolved);
316
+ if (aborted)
317
+ return aborted;
318
+ if (options?.maxLines !== undefined && options.maxLines <= 0)
319
+ return ok([]);
320
+ let stream;
321
+ let lineReader;
322
+ try {
323
+ stream = createReadStream(resolved, { encoding: "utf8", signal: options?.abortSignal });
324
+ lineReader = createInterface({ input: stream, crlfDelay: Infinity });
325
+ const lines = [];
326
+ for await (const line of lineReader) {
327
+ const loopAbort = abortResult(options?.abortSignal, resolved);
328
+ if (loopAbort)
329
+ return loopAbort;
330
+ lines.push(line);
331
+ if (options?.maxLines !== undefined && lines.length >= options.maxLines)
332
+ break;
333
+ }
334
+ const afterReadAbort = abortResult(options?.abortSignal, resolved);
335
+ if (afterReadAbort)
336
+ return afterReadAbort;
337
+ return ok(lines);
338
+ }
339
+ catch (error) {
340
+ return err(toFileError(error, resolved));
341
+ }
342
+ finally {
343
+ lineReader?.close();
344
+ stream?.destroy();
345
+ }
346
+ }
347
+ async readBinaryFile(path, abortSignal) {
348
+ const resolved = resolvePath(this.cwd, path);
349
+ const aborted = abortResult(abortSignal, resolved);
350
+ if (aborted)
351
+ return aborted;
352
+ try {
353
+ return ok(await readFile(resolved, { signal: abortSignal }));
354
+ }
355
+ catch (error) {
356
+ return err(toFileError(error, resolved));
357
+ }
358
+ }
359
+ async writeFile(path, content, abortSignal) {
360
+ const resolved = resolvePath(this.cwd, path);
361
+ const aborted = abortResult(abortSignal, resolved);
362
+ if (aborted)
363
+ return aborted;
364
+ try {
365
+ await mkdir(resolve(resolved, ".."), { recursive: true });
366
+ const afterMkdirAbort = abortResult(abortSignal, resolved);
367
+ if (afterMkdirAbort)
368
+ return afterMkdirAbort;
369
+ await writeFile(resolved, content, { signal: abortSignal });
370
+ return ok(undefined);
371
+ }
372
+ catch (error) {
373
+ return err(toFileError(error, resolved));
374
+ }
375
+ }
376
+ async appendFile(path, content) {
377
+ const resolved = resolvePath(this.cwd, path);
378
+ try {
379
+ await mkdir(resolve(resolved, ".."), { recursive: true });
380
+ await appendFile(resolved, content);
381
+ return ok(undefined);
382
+ }
383
+ catch (error) {
384
+ return err(toFileError(error, resolved));
385
+ }
386
+ }
387
+ async fileInfo(path) {
388
+ const resolved = resolvePath(this.cwd, path);
389
+ try {
390
+ return fileInfoFromStats(resolved, await lstat(resolved));
391
+ }
392
+ catch (error) {
393
+ return err(toFileError(error, resolved));
394
+ }
395
+ }
396
+ async listDir(path, abortSignal) {
397
+ const resolved = resolvePath(this.cwd, path);
398
+ const aborted = abortResult(abortSignal, resolved);
399
+ if (aborted)
400
+ return aborted;
401
+ try {
402
+ const entries = await readdir(resolved, { withFileTypes: true });
403
+ const infos = [];
404
+ for (const entry of entries) {
405
+ const loopAbort = abortResult(abortSignal, resolved);
406
+ if (loopAbort)
407
+ return loopAbort;
408
+ const entryPath = resolve(resolved, entry.name);
409
+ try {
410
+ const info = fileInfoFromStats(entryPath, await lstat(entryPath));
411
+ if (info.ok)
412
+ infos.push(info.value);
413
+ }
414
+ catch (error) {
415
+ return err(toFileError(error, entryPath));
416
+ }
417
+ }
418
+ return ok(infos);
419
+ }
420
+ catch (error) {
421
+ return err(toFileError(error, resolved));
422
+ }
423
+ }
424
+ async canonicalPath(path) {
425
+ const resolved = resolvePath(this.cwd, path);
426
+ try {
427
+ return ok(await realpath(resolved));
428
+ }
429
+ catch (error) {
430
+ return err(toFileError(error, resolved));
431
+ }
432
+ }
433
+ async exists(path) {
434
+ const result = await this.fileInfo(path);
435
+ if (result.ok)
436
+ return ok(true);
437
+ if (result.error.code === "not_found")
438
+ return ok(false);
439
+ return err(result.error);
440
+ }
441
+ async createDir(path, options) {
442
+ const resolved = resolvePath(this.cwd, path);
443
+ try {
444
+ await mkdir(resolved, { recursive: options?.recursive ?? true });
445
+ return ok(undefined);
446
+ }
447
+ catch (error) {
448
+ return err(toFileError(error, resolved));
449
+ }
450
+ }
451
+ async remove(path, options) {
452
+ const resolved = resolvePath(this.cwd, path);
453
+ try {
454
+ await rm(resolved, { recursive: options?.recursive ?? false, force: options?.force ?? false });
455
+ return ok(undefined);
456
+ }
457
+ catch (error) {
458
+ return err(toFileError(error, resolved));
459
+ }
460
+ }
461
+ async createTempDir(prefix = "tmp-") {
462
+ try {
463
+ return ok(await mkdtemp(join(tmpdir(), prefix)));
464
+ }
465
+ catch (error) {
466
+ return err(toFileError(error));
467
+ }
468
+ }
469
+ async createTempFile(options) {
470
+ const dir = await this.createTempDir("tmp-");
471
+ if (!dir.ok)
472
+ return dir;
473
+ const filePath = join(dir.value, `${options?.prefix ?? ""}${randomUUID()}${options?.suffix ?? ""}`);
474
+ try {
475
+ await writeFile(filePath, "");
476
+ return ok(filePath);
477
+ }
478
+ catch (error) {
479
+ return err(toFileError(error, filePath));
480
+ }
481
+ }
482
+ async cleanup() {
483
+ // nothing to clean up for the local node implementation
484
+ }
485
+ }
@@ -0,0 +1,101 @@
1
+ export const COMPACTION_SUMMARY_PREFIX = `The conversation history before this point was compacted into the following summary:
2
+
3
+ <summary>
4
+ `;
5
+ export const COMPACTION_SUMMARY_SUFFIX = `
6
+ </summary>`;
7
+ export const BRANCH_SUMMARY_PREFIX = `The following is a summary of a branch that this conversation came back from:
8
+
9
+ <summary>
10
+ `;
11
+ export const BRANCH_SUMMARY_SUFFIX = `</summary>`;
12
+ export function bashExecutionToText(msg) {
13
+ let text = `Ran \`${msg.command}\`\n`;
14
+ if (msg.output) {
15
+ text += `\`\`\`\n${msg.output}\n\`\`\``;
16
+ }
17
+ else {
18
+ text += "(no output)";
19
+ }
20
+ if (msg.cancelled) {
21
+ text += "\n\n(command cancelled)";
22
+ }
23
+ else if (msg.exitCode !== null && msg.exitCode !== undefined && msg.exitCode !== 0) {
24
+ text += `\n\nCommand exited with code ${msg.exitCode}`;
25
+ }
26
+ if (msg.truncated && msg.fullOutputPath) {
27
+ text += `\n\n[Output truncated. Full output: ${msg.fullOutputPath}]`;
28
+ }
29
+ return text;
30
+ }
31
+ export function createBranchSummaryMessage(summary, fromId, timestamp) {
32
+ return {
33
+ role: "branchSummary",
34
+ summary,
35
+ fromId,
36
+ timestamp: new Date(timestamp).getTime(),
37
+ };
38
+ }
39
+ export function createCompactionSummaryMessage(summary, tokensBefore, timestamp) {
40
+ return {
41
+ role: "compactionSummary",
42
+ summary,
43
+ tokensBefore,
44
+ timestamp: new Date(timestamp).getTime(),
45
+ };
46
+ }
47
+ export function createCustomMessage(customType, content, display, details, timestamp) {
48
+ return {
49
+ role: "custom",
50
+ customType,
51
+ content,
52
+ display,
53
+ details,
54
+ timestamp: new Date(timestamp).getTime(),
55
+ };
56
+ }
57
+ export function convertToLlm(messages) {
58
+ return messages
59
+ .map((m) => {
60
+ switch (m.role) {
61
+ case "bashExecution":
62
+ if (m.excludeFromContext) {
63
+ return undefined;
64
+ }
65
+ return {
66
+ role: "user",
67
+ content: [{ type: "text", text: bashExecutionToText(m) }],
68
+ timestamp: m.timestamp,
69
+ };
70
+ case "custom": {
71
+ const content = typeof m.content === "string" ? [{ type: "text", text: m.content }] : m.content;
72
+ return {
73
+ role: "user",
74
+ content,
75
+ timestamp: m.timestamp,
76
+ };
77
+ }
78
+ case "branchSummary":
79
+ return {
80
+ role: "user",
81
+ content: [{ type: "text", text: BRANCH_SUMMARY_PREFIX + m.summary + BRANCH_SUMMARY_SUFFIX }],
82
+ timestamp: m.timestamp,
83
+ };
84
+ case "compactionSummary":
85
+ return {
86
+ role: "user",
87
+ content: [
88
+ { type: "text", text: COMPACTION_SUMMARY_PREFIX + m.summary + COMPACTION_SUMMARY_SUFFIX },
89
+ ],
90
+ timestamp: m.timestamp,
91
+ };
92
+ case "user":
93
+ case "assistant":
94
+ case "toolResult":
95
+ return m;
96
+ default:
97
+ return undefined;
98
+ }
99
+ })
100
+ .filter((m) => m !== undefined);
101
+ }