amness 0.0.0-stage → 0.1.2

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 (235) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +237 -2
  3. package/dist/cli/acpcli.d.ts +7 -0
  4. package/dist/cli/acpcli.js +14 -0
  5. package/dist/cli/amness.d.ts +2 -0
  6. package/dist/cli/amness.js +694 -0
  7. package/dist/cli/benchcli.d.ts +5 -0
  8. package/dist/cli/benchcli.js +160 -0
  9. package/dist/cli/config.d.ts +79 -0
  10. package/dist/cli/config.js +111 -0
  11. package/dist/cli/demo.d.ts +13 -0
  12. package/dist/cli/demo.js +44 -0
  13. package/dist/cli/detect.d.ts +2 -0
  14. package/dist/cli/detect.js +20 -0
  15. package/dist/cli/evalcli.d.ts +15 -0
  16. package/dist/cli/evalcli.js +198 -0
  17. package/dist/cli/format.d.ts +5 -0
  18. package/dist/cli/format.js +55 -0
  19. package/dist/cli/headless.d.ts +2 -0
  20. package/dist/cli/headless.js +169 -0
  21. package/dist/cli/keystore.d.ts +9 -0
  22. package/dist/cli/keystore.js +108 -0
  23. package/dist/cli/model.d.ts +11 -0
  24. package/dist/cli/model.js +19 -0
  25. package/dist/cli/plugincli.d.ts +2 -0
  26. package/dist/cli/plugincli.js +176 -0
  27. package/dist/cli/schedulecli.d.ts +2 -0
  28. package/dist/cli/schedulecli.js +66 -0
  29. package/dist/cli/state.d.ts +11 -0
  30. package/dist/cli/state.js +19 -0
  31. package/dist/cli/tui/ansi.d.ts +38 -0
  32. package/dist/cli/tui/ansi.js +142 -0
  33. package/dist/cli/tui/app.d.ts +328 -0
  34. package/dist/cli/tui/app.js +1358 -0
  35. package/dist/cli/tui/blocks.d.ts +151 -0
  36. package/dist/cli/tui/blocks.js +128 -0
  37. package/dist/cli/tui/brand.d.ts +9 -0
  38. package/dist/cli/tui/brand.js +16 -0
  39. package/dist/cli/tui/canvas.d.ts +92 -0
  40. package/dist/cli/tui/canvas.js +199 -0
  41. package/dist/cli/tui/cards.d.ts +44 -0
  42. package/dist/cli/tui/cards.js +215 -0
  43. package/dist/cli/tui/clipboard.d.ts +5 -0
  44. package/dist/cli/tui/clipboard.js +29 -0
  45. package/dist/cli/tui/commands.d.ts +10 -0
  46. package/dist/cli/tui/commands.js +650 -0
  47. package/dist/cli/tui/complete.d.ts +39 -0
  48. package/dist/cli/tui/complete.js +113 -0
  49. package/dist/cli/tui/custom.d.ts +9 -0
  50. package/dist/cli/tui/custom.js +34 -0
  51. package/dist/cli/tui/diffview.d.ts +25 -0
  52. package/dist/cli/tui/diffview.js +130 -0
  53. package/dist/cli/tui/editor.d.ts +60 -0
  54. package/dist/cli/tui/editor.js +230 -0
  55. package/dist/cli/tui/full.d.ts +86 -0
  56. package/dist/cli/tui/full.js +630 -0
  57. package/dist/cli/tui/keys.d.ts +31 -0
  58. package/dist/cli/tui/keys.js +134 -0
  59. package/dist/cli/tui/markdown.d.ts +37 -0
  60. package/dist/cli/tui/markdown.js +155 -0
  61. package/dist/cli/tui/mentions.d.ts +15 -0
  62. package/dist/cli/tui/mentions.js +48 -0
  63. package/dist/cli/tui/modal.d.ts +26 -0
  64. package/dist/cli/tui/modal.js +107 -0
  65. package/dist/cli/tui/overlays.d.ts +98 -0
  66. package/dist/cli/tui/overlays.js +237 -0
  67. package/dist/cli/tui/pager.d.ts +15 -0
  68. package/dist/cli/tui/pager.js +66 -0
  69. package/dist/cli/tui/pricing.d.ts +9 -0
  70. package/dist/cli/tui/pricing.js +23 -0
  71. package/dist/cli/tui/rich.d.ts +16 -0
  72. package/dist/cli/tui/rich.js +72 -0
  73. package/dist/cli/tui/screen.d.ts +30 -0
  74. package/dist/cli/tui/screen.js +62 -0
  75. package/dist/cli/tui/session.d.ts +77 -0
  76. package/dist/cli/tui/session.js +103 -0
  77. package/dist/cli/tui/sidebar.d.ts +84 -0
  78. package/dist/cli/tui/sidebar.js +114 -0
  79. package/dist/cli/tui/text.d.ts +15 -0
  80. package/dist/cli/tui/text.js +94 -0
  81. package/dist/cli/tui/theme.d.ts +59 -0
  82. package/dist/cli/tui/theme.js +148 -0
  83. package/dist/cli/tui/themes.d.ts +56 -0
  84. package/dist/cli/tui/themes.js +80 -0
  85. package/dist/cli/tui/tokens.d.ts +39 -0
  86. package/dist/cli/tui/tokens.js +71 -0
  87. package/dist/cli/tui/view.d.ts +60 -0
  88. package/dist/cli/tui/view.js +178 -0
  89. package/dist/cli/ui.d.ts +34 -0
  90. package/dist/cli/ui.js +224 -0
  91. package/dist/src/acp.d.ts +28 -0
  92. package/dist/src/acp.js +111 -0
  93. package/dist/src/agents.d.ts +23 -0
  94. package/dist/src/agents.js +71 -0
  95. package/dist/src/anthropic.d.ts +30 -0
  96. package/dist/src/anthropic.js +91 -0
  97. package/dist/src/approver.d.ts +15 -0
  98. package/dist/src/approver.js +40 -0
  99. package/dist/src/bench.d.ts +67 -0
  100. package/dist/src/bench.js +201 -0
  101. package/dist/src/bundle.d.ts +36 -0
  102. package/dist/src/bundle.js +55 -0
  103. package/dist/src/chaos.d.ts +15 -0
  104. package/dist/src/chaos.js +26 -0
  105. package/dist/src/code.d.ts +187 -0
  106. package/dist/src/code.js +590 -0
  107. package/dist/src/codeindex.d.ts +65 -0
  108. package/dist/src/codeindex.js +300 -0
  109. package/dist/src/deep.d.ts +62 -0
  110. package/dist/src/deep.js +177 -0
  111. package/dist/src/diff.d.ts +8 -0
  112. package/dist/src/diff.js +74 -0
  113. package/dist/src/dynamic.d.ts +42 -0
  114. package/dist/src/dynamic.js +133 -0
  115. package/dist/src/embed.d.ts +8 -0
  116. package/dist/src/embed.js +25 -0
  117. package/dist/src/evalcases.d.ts +11 -0
  118. package/dist/src/evalcases.js +196 -0
  119. package/dist/src/evals.d.ts +197 -0
  120. package/dist/src/evals.js +340 -0
  121. package/dist/src/gitflow.d.ts +27 -0
  122. package/dist/src/gitflow.js +56 -0
  123. package/dist/src/grade.d.ts +42 -0
  124. package/dist/src/grade.js +96 -0
  125. package/dist/src/guidance.d.ts +15 -0
  126. package/dist/src/guidance.js +68 -0
  127. package/dist/src/hardcases.d.ts +2 -0
  128. package/dist/src/hardcases.js +94 -0
  129. package/dist/src/harness.d.ts +411 -0
  130. package/dist/src/harness.js +725 -0
  131. package/dist/src/hooks.d.ts +44 -0
  132. package/dist/src/hooks.js +116 -0
  133. package/dist/src/images.d.ts +13 -0
  134. package/dist/src/images.js +27 -0
  135. package/dist/src/index.d.ts +57 -0
  136. package/dist/src/index.js +57 -0
  137. package/dist/src/ledger.d.ts +58 -0
  138. package/dist/src/ledger.js +78 -0
  139. package/dist/src/live.d.ts +16 -0
  140. package/dist/src/live.js +80 -0
  141. package/dist/src/lsp.d.ts +31 -0
  142. package/dist/src/lsp.js +147 -0
  143. package/dist/src/memory.d.ts +128 -0
  144. package/dist/src/memory.js +390 -0
  145. package/dist/src/metrics.d.ts +3 -0
  146. package/dist/src/metrics.js +42 -0
  147. package/dist/src/mining.d.ts +49 -0
  148. package/dist/src/mining.js +126 -0
  149. package/dist/src/openai.d.ts +50 -0
  150. package/dist/src/openai.js +307 -0
  151. package/dist/src/parallel.d.ts +66 -0
  152. package/dist/src/parallel.js +135 -0
  153. package/dist/src/patch.d.ts +45 -0
  154. package/dist/src/patch.js +219 -0
  155. package/dist/src/permissions.d.ts +46 -0
  156. package/dist/src/permissions.js +245 -0
  157. package/dist/src/plugins.d.ts +148 -0
  158. package/dist/src/plugins.js +225 -0
  159. package/dist/src/pluginscan.d.ts +8 -0
  160. package/dist/src/pluginscan.js +20 -0
  161. package/dist/src/procs.d.ts +41 -0
  162. package/dist/src/procs.js +94 -0
  163. package/dist/src/profiles.d.ts +36 -0
  164. package/dist/src/profiles.js +61 -0
  165. package/dist/src/progress.d.ts +29 -0
  166. package/dist/src/progress.js +75 -0
  167. package/dist/src/prompts.d.ts +10 -0
  168. package/dist/src/prompts.js +28 -0
  169. package/dist/src/ratelimit.d.ts +34 -0
  170. package/dist/src/ratelimit.js +116 -0
  171. package/dist/src/replay.d.ts +25 -0
  172. package/dist/src/replay.js +99 -0
  173. package/dist/src/review.d.ts +20 -0
  174. package/dist/src/review.js +49 -0
  175. package/dist/src/route.d.ts +9 -0
  176. package/dist/src/route.js +30 -0
  177. package/dist/src/router.d.ts +61 -0
  178. package/dist/src/router.js +126 -0
  179. package/dist/src/sandbox.d.ts +13 -0
  180. package/dist/src/sandbox.js +68 -0
  181. package/dist/src/schedule.d.ts +20 -0
  182. package/dist/src/schedule.js +59 -0
  183. package/dist/src/schema.d.ts +13 -0
  184. package/dist/src/schema.js +36 -0
  185. package/dist/src/secrets.d.ts +10 -0
  186. package/dist/src/secrets.js +41 -0
  187. package/dist/src/shell.d.ts +34 -0
  188. package/dist/src/shell.js +95 -0
  189. package/dist/src/skills.d.ts +20 -0
  190. package/dist/src/skills.js +112 -0
  191. package/dist/src/snapshot.d.ts +20 -0
  192. package/dist/src/snapshot.js +93 -0
  193. package/dist/src/stats.d.ts +24 -0
  194. package/dist/src/stats.js +62 -0
  195. package/dist/src/strategies.d.ts +16 -0
  196. package/dist/src/strategies.js +35 -0
  197. package/dist/src/suites.d.ts +30 -0
  198. package/dist/src/suites.js +112 -0
  199. package/dist/src/trace.d.ts +63 -0
  200. package/dist/src/trace.js +153 -0
  201. package/dist/src/tune.d.ts +38 -0
  202. package/dist/src/tune.js +49 -0
  203. package/dist/src/web.d.ts +19 -0
  204. package/dist/src/web.js +56 -0
  205. package/dist/src/worktree.d.ts +24 -0
  206. package/dist/src/worktree.js +85 -0
  207. package/docs/agent-loop.md +222 -0
  208. package/docs/architecture.md +228 -0
  209. package/docs/bench.md +292 -0
  210. package/docs/budgets.md +185 -0
  211. package/docs/build-your-own-harness.md +424 -0
  212. package/docs/cookbook.md +375 -0
  213. package/docs/development.md +150 -0
  214. package/docs/evaluation.md +450 -0
  215. package/docs/features.md +1320 -0
  216. package/docs/getting-started.md +205 -0
  217. package/docs/headless.md +188 -0
  218. package/docs/images/approval.png +0 -0
  219. package/docs/images/chat.png +0 -0
  220. package/docs/images/daylight.png +0 -0
  221. package/docs/images/loop.png +0 -0
  222. package/docs/images/palette.png +0 -0
  223. package/docs/images/running.png +0 -0
  224. package/docs/images/subagent-done.png +0 -0
  225. package/docs/images/subagent-running.png +0 -0
  226. package/docs/images/welcome.png +0 -0
  227. package/docs/index.md +25 -0
  228. package/docs/library.md +267 -0
  229. package/docs/plugins.md +691 -0
  230. package/docs/providers.md +175 -0
  231. package/docs/safety.md +250 -0
  232. package/docs/usage.md +348 -0
  233. package/docs/why.md +155 -0
  234. package/docs/writing-guide.md +39 -0
  235. package/package.json +66 -4
@@ -0,0 +1,44 @@
1
+ export type HookEvent = "SessionStart" | "UserPromptSubmit" | "PreToolUse" | "PostToolUse" | "PreCompact" | "Stop" | "PreModel" | "PostModel";
2
+ export declare const HOOK_EVENTS: HookEvent[];
3
+ export type HookDef = {
4
+ match?: string;
5
+ command: string; /** seconds, default 10 */
6
+ timeout?: number; /** a crash or timeout blocks instead of being ignored */
7
+ failClosed?: boolean;
8
+ };
9
+ export type HookConfig = Partial<Record<HookEvent, HookDef[]>>;
10
+ export type HookResult = {
11
+ block?: string;
12
+ context?: string;
13
+ args?: Record<string, unknown>; /** PreModel only: the tools that may be called in this turn (a call to any other is refused with an explanation) */
14
+ tools?: string[]; /** PreModel only: a text-only reply to this request is not the final answer; add this message and ask for the next step (for a planning phase) */
15
+ then?: string;
16
+ warnings: string[];
17
+ };
18
+ export type HookRunner = {
19
+ run(event: HookEvent, payload: Record<string, unknown>): Promise<HookResult>;
20
+ count(): number;
21
+ };
22
+ /** The environment a hook sees: no API keys, tokens or passwords. */
23
+ export declare function scrubbedEnv(env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
24
+ export declare class Hooks implements HookRunner {
25
+ private cfg;
26
+ private cwd;
27
+ private env;
28
+ constructor(cfg: HookConfig, cwd: string, env?: NodeJS.ProcessEnv);
29
+ /** Where project hooks live, and a fingerprint of their content (to remember that the user approved exactly this file). */
30
+ static projectFile(cwd: string): string;
31
+ static fingerprint(cwd: string): string | undefined;
32
+ /** User-level hooks always load; project hooks only when `trustProject` (a cloned repo must not run commands on your machine unasked). */
33
+ static load(cwd: string, o?: {
34
+ trustProject?: boolean;
35
+ userDir?: string;
36
+ }): Hooks;
37
+ count(): number;
38
+ list(): {
39
+ event: HookEvent;
40
+ def: HookDef;
41
+ }[];
42
+ run(event: HookEvent, payload: Record<string, unknown>): Promise<HookResult>;
43
+ private exec;
44
+ }
@@ -0,0 +1,116 @@
1
+ // Lifecycle hooks: your own commands, run at fixed points of the agent loop, that can block a step, rewrite a tool call, or add context.
2
+ // Same vocabulary as Claude Code and Codex, so a hook script written for one is easy to move.
3
+ import { spawn } from "node:child_process";
4
+ import { createHash } from "node:crypto";
5
+ import { existsSync, readFileSync } from "node:fs";
6
+ import { homedir } from "node:os";
7
+ import { join } from "node:path";
8
+ import { shellInvocation } from "./shell.js";
9
+ export const HOOK_EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "PreCompact", "Stop", "PreModel", "PostModel"];
10
+ /** The environment a hook sees: no API keys, tokens or passwords. */
11
+ export function scrubbedEnv(env = process.env) {
12
+ const out = {};
13
+ for (const [k, v] of Object.entries(env))
14
+ if (!/(KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH)/i.test(k))
15
+ out[k] = v;
16
+ return out;
17
+ }
18
+ const readCfg = (file) => {
19
+ if (!existsSync(file))
20
+ return {};
21
+ const j = JSON.parse(readFileSync(file, "utf8")), out = {};
22
+ for (const ev of HOOK_EVENTS) {
23
+ const list = j[ev];
24
+ if (list === undefined)
25
+ continue;
26
+ if (!Array.isArray(list))
27
+ throw new Error(`${file}: "${ev}" must be a list of { command, match?, timeout? }`);
28
+ for (const d of list)
29
+ if (!d || typeof d.command !== "string")
30
+ throw new Error(`${file}: every "${ev}" hook needs a "command" string`);
31
+ out[ev] = list;
32
+ }
33
+ return out;
34
+ };
35
+ export class Hooks {
36
+ cfg;
37
+ cwd;
38
+ env;
39
+ constructor(cfg, cwd, env = scrubbedEnv()) { this.cfg = cfg; this.cwd = cwd; this.env = env; }
40
+ /** Where project hooks live, and a fingerprint of their content (to remember that the user approved exactly this file). */
41
+ static projectFile(cwd) { return join(cwd, ".amness", "hooks.json"); }
42
+ static fingerprint(cwd) { const f = Hooks.projectFile(cwd); return existsSync(f) ? createHash("sha256").update(readFileSync(f)).digest("hex").slice(0, 16) : undefined; }
43
+ /** User-level hooks always load; project hooks only when `trustProject` (a cloned repo must not run commands on your machine unasked). */
44
+ static load(cwd, o = {}) {
45
+ const user = readCfg(join(o.userDir ?? process.env.AMNESS_HOME ?? join(homedir(), ".amness"), "hooks.json")), project = o.trustProject ? readCfg(Hooks.projectFile(cwd)) : {};
46
+ const merged = {};
47
+ for (const ev of HOOK_EVENTS) {
48
+ const l = [...(user[ev] ?? []), ...(project[ev] ?? [])];
49
+ if (l.length)
50
+ merged[ev] = l;
51
+ }
52
+ return new Hooks(merged, cwd);
53
+ }
54
+ count() { return HOOK_EVENTS.reduce((n, ev) => n + (this.cfg[ev]?.length ?? 0), 0); }
55
+ list() { return HOOK_EVENTS.flatMap((event) => (this.cfg[event] ?? []).map((def) => ({ event, def }))); }
56
+ async run(event, payload) {
57
+ const res = { warnings: [] };
58
+ for (const def of this.cfg[event] ?? []) {
59
+ if (def.match && typeof payload.tool === "string" && !new RegExp(`^(?:${def.match})$`).test(payload.tool))
60
+ continue;
61
+ const r = await this.exec(def, { event, cwd: this.cwd, ...payload });
62
+ if (r.warning) {
63
+ if (def.failClosed) {
64
+ res.block = r.warning;
65
+ break;
66
+ }
67
+ res.warnings.push(r.warning);
68
+ continue;
69
+ }
70
+ if (r.block !== undefined) {
71
+ res.block = r.block;
72
+ break;
73
+ }
74
+ if (r.context)
75
+ res.context = (res.context ? res.context + "\n" : "") + r.context;
76
+ if (r.args)
77
+ res.args = r.args;
78
+ }
79
+ return res;
80
+ }
81
+ exec(def, input) {
82
+ return new Promise((done) => {
83
+ const inv = shellInvocation(def.command), p = spawn(inv.file, inv.args, { windowsHide: true, cwd: this.cwd, env: this.env, stdio: ["pipe", "pipe", "pipe"] });
84
+ let out = "", err = "", settled = false;
85
+ const finish = (v) => { if (!settled) {
86
+ settled = true;
87
+ clearTimeout(timer);
88
+ done(v);
89
+ } };
90
+ const timer = setTimeout(() => { p.kill("SIGKILL"); finish({ warning: `hook \`${def.command}\` timed out after ${def.timeout ?? 10}s` }); }, (def.timeout ?? 10) * 1000);
91
+ p.stdout.on("data", (d) => { if (out.length < 20_000)
92
+ out += d; });
93
+ p.stderr.on("data", (d) => { if (err.length < 20_000)
94
+ err += d; });
95
+ p.on("error", (e) => finish({ warning: `hook \`${def.command}\` could not start: ${e.message}` }));
96
+ p.stdin.on("error", () => { });
97
+ p.stdin.end(JSON.stringify(input));
98
+ p.on("close", (code) => {
99
+ if (code === 2)
100
+ return finish({ block: (err.trim() || out.trim() || `blocked by hook \`${def.command}\``).slice(0, 2000) });
101
+ if (code !== 0)
102
+ return finish({ warning: `hook \`${def.command}\` exited with ${code}${err.trim() ? ": " + err.trim().slice(0, 300) : ""}` });
103
+ const text = out.trim();
104
+ if (!text)
105
+ return finish({});
106
+ try {
107
+ const j = JSON.parse(text);
108
+ if (j && typeof j === "object")
109
+ return finish(j.decision === "block" ? { block: j.reason ?? "blocked by hook" } : { context: j.context, args: j.args && typeof j.args === "object" ? j.args : undefined });
110
+ }
111
+ catch { /* plain text */ }
112
+ finish({ context: text.slice(0, 4000) });
113
+ });
114
+ });
115
+ }
116
+ }
@@ -0,0 +1,13 @@
1
+ export type Image = {
2
+ mime: string;
3
+ data: string;
4
+ };
5
+ export declare const MAX_IMAGE_BYTES: number, MAX_IMAGES = 4;
6
+ export declare const isImagePath: (p: string) => boolean;
7
+ /** Read an image file as base64. Throws a readable error when it is too big or not an image. */
8
+ export declare function readImage(path: string): Image;
9
+ /**
10
+ * Does this model take images? OpenAI and Claude models do; for local servers it is a guess from the model name (llava, vision, -vl, gemma3, minicpm-v, pixtral, ...),
11
+ * so a text-only model gets a clear message instead of an obscure server error.
12
+ */
13
+ export declare function visionLikely(provider: string, model: string): boolean;
@@ -0,0 +1,27 @@
1
+ // Image input: read a screenshot or picture from disk into a form the model adapters can send.
2
+ import { readFileSync, statSync } from "node:fs";
3
+ import { extname } from "node:path";
4
+ const MIME = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".webp": "image/webp" };
5
+ export const MAX_IMAGE_BYTES = 5 * 1024 * 1024, MAX_IMAGES = 4;
6
+ export const isImagePath = (p) => extname(p).toLowerCase() in MIME;
7
+ /** Read an image file as base64. Throws a readable error when it is too big or not an image. */
8
+ export function readImage(path) {
9
+ const mime = MIME[extname(path).toLowerCase()];
10
+ if (!mime)
11
+ throw new Error(`${path}: not a supported image (png, jpg, gif, webp)`);
12
+ const size = statSync(path).size;
13
+ if (size > MAX_IMAGE_BYTES)
14
+ throw new Error(`${path}: ${(size / 1048576).toFixed(1)} MB is over the ${MAX_IMAGE_BYTES / 1048576} MB limit`);
15
+ return { mime, data: readFileSync(path).toString("base64") };
16
+ }
17
+ /**
18
+ * Does this model take images? OpenAI and Claude models do; for local servers it is a guess from the model name (llava, vision, -vl, gemma3, minicpm-v, pixtral, ...),
19
+ * so a text-only model gets a clear message instead of an obscure server error.
20
+ */
21
+ export function visionLikely(provider, model) {
22
+ if (provider === "anthropic")
23
+ return true;
24
+ if (provider === "openai")
25
+ return !/^(gpt-3|gpt-4(-0|-32k)?$|o1-mini|o1-preview|text-|davinci)/i.test(model);
26
+ return /llava|vision|[-_:.]vl\b|[-_]vl[-_:]|gemma-?3|minicpm-v|pixtral|llama-?3\.2-(11|90)b|moondream|bakllava|internvl|qwen.*vl|glm-4v|mistral-small-?3/i.test(model);
27
+ }
@@ -0,0 +1,57 @@
1
+ export * from "./harness.ts";
2
+ export * from "./schema.ts";
3
+ export * from "./openai.ts";
4
+ export * from "./web.ts";
5
+ export * from "./deep.ts";
6
+ export * from "./code.ts";
7
+ export * from "./evals.ts";
8
+ export * from "./dynamic.ts";
9
+ export * from "./anthropic.ts";
10
+ export * from "./diff.ts";
11
+ export * from "./memory.ts";
12
+ export * from "./bundle.ts";
13
+ export * from "./live.ts";
14
+ export * from "./stats.ts";
15
+ export * from "./evalcases.ts";
16
+ export * from "./embed.ts";
17
+ export * from "./guidance.ts";
18
+ export * from "./hooks.ts";
19
+ export * from "./permissions.ts";
20
+ export * from "./trace.ts";
21
+ export * from "./procs.ts";
22
+ export * from "./ledger.ts";
23
+ export * from "./progress.ts";
24
+ export * from "./review.ts";
25
+ export * from "./patch.ts";
26
+ export * from "./sandbox.ts";
27
+ export * from "./skills.ts";
28
+ export * from "./worktree.ts";
29
+ export * from "./agents.ts";
30
+ export * from "./route.ts";
31
+ export * from "./codeindex.ts";
32
+ export * from "./replay.ts";
33
+ export * from "./mining.ts";
34
+ export * from "./suites.ts";
35
+ export * from "./router.ts";
36
+ export * from "./profiles.ts";
37
+ export * from "./secrets.ts";
38
+ export * from "./snapshot.ts";
39
+ export * from "./gitflow.ts";
40
+ export * from "./lsp.ts";
41
+ export * from "./tune.ts";
42
+ export * from "./parallel.ts";
43
+ export * from "./approver.ts";
44
+ export * from "./images.ts";
45
+ export * from "./acp.ts";
46
+ export * from "./schedule.ts";
47
+ export * from "./shell.ts";
48
+ export * from "./hardcases.ts";
49
+ export * from "./ratelimit.ts";
50
+ export * from "./plugins.ts";
51
+ export * from "./strategies.ts";
52
+ export * from "./metrics.ts";
53
+ export * from "./chaos.ts";
54
+ export * from "./prompts.ts";
55
+ export * from "./pluginscan.ts";
56
+ export * from "./grade.ts";
57
+ export * from "./bench.ts";
@@ -0,0 +1,57 @@
1
+ export * from "./harness.js";
2
+ export * from "./schema.js";
3
+ export * from "./openai.js";
4
+ export * from "./web.js";
5
+ export * from "./deep.js";
6
+ export * from "./code.js";
7
+ export * from "./evals.js";
8
+ export * from "./dynamic.js";
9
+ export * from "./anthropic.js";
10
+ export * from "./diff.js";
11
+ export * from "./memory.js";
12
+ export * from "./bundle.js";
13
+ export * from "./live.js";
14
+ export * from "./stats.js";
15
+ export * from "./evalcases.js";
16
+ export * from "./embed.js";
17
+ export * from "./guidance.js";
18
+ export * from "./hooks.js";
19
+ export * from "./permissions.js";
20
+ export * from "./trace.js";
21
+ export * from "./procs.js";
22
+ export * from "./ledger.js";
23
+ export * from "./progress.js";
24
+ export * from "./review.js";
25
+ export * from "./patch.js";
26
+ export * from "./sandbox.js";
27
+ export * from "./skills.js";
28
+ export * from "./worktree.js";
29
+ export * from "./agents.js";
30
+ export * from "./route.js";
31
+ export * from "./codeindex.js";
32
+ export * from "./replay.js";
33
+ export * from "./mining.js";
34
+ export * from "./suites.js";
35
+ export * from "./router.js";
36
+ export * from "./profiles.js";
37
+ export * from "./secrets.js";
38
+ export * from "./snapshot.js";
39
+ export * from "./gitflow.js";
40
+ export * from "./lsp.js";
41
+ export * from "./tune.js";
42
+ export * from "./parallel.js";
43
+ export * from "./approver.js";
44
+ export * from "./images.js";
45
+ export * from "./acp.js";
46
+ export * from "./schedule.js";
47
+ export * from "./shell.js";
48
+ export * from "./hardcases.js";
49
+ export * from "./ratelimit.js";
50
+ export * from "./plugins.js";
51
+ export * from "./strategies.js";
52
+ export * from "./metrics.js";
53
+ export * from "./chaos.js";
54
+ export * from "./prompts.js";
55
+ export * from "./pluginscan.js";
56
+ export * from "./grade.js";
57
+ export * from "./bench.js";
@@ -0,0 +1,58 @@
1
+ export type LedgerEdit = {
2
+ path: string;
3
+ added: number;
4
+ removed: number;
5
+ created: boolean;
6
+ seq: number;
7
+ diff?: string;
8
+ };
9
+ export type LedgerCmd = {
10
+ cmd: string;
11
+ code: number | null;
12
+ test: boolean;
13
+ build: boolean;
14
+ failedOutput: boolean;
15
+ seq: number;
16
+ };
17
+ export type Audit = {
18
+ changed: string[];
19
+ testsAfterLastEdit: boolean | undefined;
20
+ notes: string[];
21
+ };
22
+ export declare const TEST_CMD: RegExp;
23
+ export declare const BUILD_CMD: RegExp;
24
+ export declare class Ledger {
25
+ edits: LedgerEdit[];
26
+ cmds: LedgerCmd[];
27
+ private seq;
28
+ private extraTest?;
29
+ constructor(o?: {
30
+ testCommand?: string;
31
+ });
32
+ reset(): void;
33
+ onEdit(e: {
34
+ path: string;
35
+ added: number;
36
+ removed: number;
37
+ created: boolean;
38
+ diff?: string;
39
+ }): void;
40
+ /** Every change made so far as diffs (capped), for a reviewer. */
41
+ diffs(max?: number): string;
42
+ /** The last few commands with their outcome. */
43
+ recentCommands(n?: number): string;
44
+ /** Record a shell command from its tool result (`exit=N\n...`). */
45
+ onCommand(cmd: string, result: string): void;
46
+ private lastEdit;
47
+ private ok;
48
+ /** A passing test run (or build) that happened after the last edit. */
49
+ private passedAfterEdit;
50
+ private ranAny;
51
+ private shellWrote;
52
+ /** Problems with the claims in `summary`, judged against what was recorded. `gated`: a verify command already proved the result. */
53
+ check(summary: string, o?: {
54
+ gated?: boolean;
55
+ }): string[];
56
+ /** A short factual record of the run, for the user. */
57
+ audit(task: string): Audit;
58
+ }
@@ -0,0 +1,78 @@
1
+ export const TEST_CMD = /\b(npm\s+(run\s+)?t(est)?\b|yarn\s+test|pnpm\s+(run\s+)?test|npx\s+(jest|vitest|mocha|ava|tap)|jest|vitest|mocha|pytest|python3?\s+-m\s+(pytest|unittest)|go\s+test|cargo\s+(test|nextest)|mvn\s+(-\S+\s+)*test|gradle\w*\s+test|dotnet\s+test|make\s+(test|check)|rspec|phpunit|node\s+(--test\b|\S*test\S*\.[cm]?js)|bun\s+test|deno\s+test|ctest|swift\s+test)\b/i;
2
+ export const BUILD_CMD = /\b(tsc\b|npm\s+run\s+(build|typecheck|lint|check)|yarn\s+(build|lint)|cargo\s+(build|check|clippy)|go\s+(build|vet)|make\b|mvn\s+(-\S+\s+)*(compile|package)|gradle\w*\s+build|eslint|ruff|mypy|pyright|next\s+build|vite\s+build|webpack)\b/i;
3
+ const FAIL_OUTPUT = /(?:^|\n)\s*(?:FAIL\b|✖|not ok\b)|\b[1-9]\d*\s+(?:tests?\s+)?(?:failed|failing)\b|\b(?:failed|failing):\s*[1-9]|Tests:\s+\d+\s+failed|AssertionError|[1-9]\d*\s+errors?\b/i;
4
+ const SHELL_WRITE = /\bsed\s+-i|>\s*[\w./~-]|\btee\b|\bmv\b|\bcp\b|\bgit\s+(apply|checkout|restore|stash|reset|mv)\b|\bpatch\b|\bperl\s+-p?i|\btouch\b|\bmkdir\b/;
5
+ const NEG = /\b(?:not|n't|no|never|without|fail\w*|broken|still)\b[^.\n]{0,25}$/i;
6
+ export class Ledger {
7
+ edits = [];
8
+ cmds = [];
9
+ seq = 0;
10
+ extraTest;
11
+ constructor(o = {}) { if (o.testCommand)
12
+ this.extraTest = new RegExp(o.testCommand.trim().split(/\s+/).map((w) => w.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("\\s+")); }
13
+ reset() { this.edits = []; this.cmds = []; this.seq = 0; }
14
+ onEdit(e) { this.edits.push({ path: e.path, added: e.added, removed: e.removed, created: e.created, seq: ++this.seq, diff: e.diff?.slice(0, 6000) }); }
15
+ /** Every change made so far as diffs (capped), for a reviewer. */
16
+ diffs(max = 24_000) { let out = ""; for (const e of this.edits) {
17
+ const d = `### ${e.path}${e.created ? " (new)" : ""}\n${e.diff ?? `+${e.added} -${e.removed}`}\n`;
18
+ if (out.length + d.length > max) {
19
+ out += `[... ${this.edits.length} edits in total; the rest are not shown]\n`;
20
+ break;
21
+ }
22
+ out += d;
23
+ } return out; }
24
+ /** The last few commands with their outcome. */
25
+ recentCommands(n = 8) { return this.cmds.slice(-n).map((c) => `- ${c.cmd.slice(0, 120)} → exit ${c.code ?? "?"}${c.failedOutput ? " (failures in output)" : ""}`).join("\n"); }
26
+ /** Record a shell command from its tool result (`exit=N\n...`). */
27
+ onCommand(cmd, result) {
28
+ const m = /^exit=(\d+)/.exec(result), code = m ? Number(m[1]) : /^exit=/.test(result) ? null : null;
29
+ this.cmds.push({ cmd, code: m ? code : null, test: TEST_CMD.test(cmd) || !!this.extraTest?.test(cmd), build: BUILD_CMD.test(cmd), failedOutput: FAIL_OUTPUT.test(result), seq: ++this.seq });
30
+ }
31
+ lastEdit() { return this.edits.at(-1)?.seq ?? 0; }
32
+ ok(c) { return c.code === 0 && !c.failedOutput; }
33
+ /** A passing test run (or build) that happened after the last edit. */
34
+ passedAfterEdit(kind) { const le = this.lastEdit(); return this.cmds.some((c) => c[kind] && c.seq > le && this.ok(c)); }
35
+ ranAny(kind) { return this.cmds.some((c) => c[kind]); }
36
+ shellWrote() { return this.cmds.some((c) => SHELL_WRITE.test(c.cmd)); }
37
+ /** Problems with the claims in `summary`, judged against what was recorded. `gated`: a verify command already proved the result. */
38
+ check(summary, o = {}) {
39
+ const problems = [], s = summary;
40
+ const claimed = (re) => { for (const m of s.matchAll(new RegExp(re.source, re.flags.includes("g") ? re.flags : re.flags + "g"))) {
41
+ if (!NEG.test(s.slice(Math.max(0, m.index - 30), m.index)))
42
+ return true;
43
+ } return false; };
44
+ const testClaim = /\b(?:all\s+)?(?:unit\s+|integration\s+)?(?:tests?|specs?|test suite|suite)\b[^.\n]{0,40}?\b(?:pass(?:es|ed|ing)?|green|succe(?:ed|eds|eded|ssful)|ok)\b|\b(?:pass(?:es|ed|ing)?)\b[^.\n]{0,15}\b(?:tests?|suite)\b|\b\d+\s*\/\s*\d+\s+(?:tests?\s+)?pass/i;
45
+ const buildClaim = /\b(?:build|compil\w+|type-?check\w*|lint\w*)\b[^.\n]{0,30}?\b(?:pass(?:es|ed)?|succe\w+|clean|ok|works?|without errors)\b/i;
46
+ if (!o.gated && claimed(testClaim)) {
47
+ if (!this.ranAny("test"))
48
+ problems.push("you say the tests pass, but no test command was run in this session. Run the tests, or remove the claim.");
49
+ else if (!this.passedAfterEdit("test"))
50
+ problems.push(this.edits.length && this.cmds.some((c) => c.test && !this.ok(c) && c.seq > this.lastEdit()) ? "you say the tests pass, but the last test run after your final edit failed." : "you say the tests pass, but no test run succeeded after your last edit. Run them again.");
51
+ }
52
+ if (!o.gated && claimed(buildClaim) && !this.passedAfterEdit("build") && !this.passedAfterEdit("test"))
53
+ problems.push("you say the build/typecheck/lint is clean, but no such command succeeded after your last edit.");
54
+ if (!this.shellWrote()) {
55
+ const verbs = /\b(?:fix(?:ed)?|updat(?:ed?)|chang(?:ed?)|add(?:ed)?|implement(?:ed)?|modif(?:y|ied)|edit(?:ed)?|renam(?:ed?)|creat(?:ed?)|remov(?:ed?)|delet(?:ed?)|refactor(?:ed)?|rewr(?:ote|itten))\b[^.;()\n]{0,30}?`?([\w@./-]+\.[A-Za-z]{1,5})`?/gi;
56
+ const touched = new Set(this.edits.map((e) => e.path.replace(/^\.\//, "")));
57
+ const missing = [...new Set([...s.matchAll(verbs)].map((m) => m[1].replace(/^\.\//, "")))].filter((p) => p.includes("/") || /\.(?:[cm]?[jt]sx?|py|go|rs|java|rb|php|css|html|json|md|ya?ml|toml|sh|c|cc|cpp|h)$/.test(p)).filter((p) => ![...touched].some((t) => t === p || t.endsWith("/" + p) || p.endsWith("/" + t)));
58
+ if (missing.length)
59
+ problems.push(`the summary says ${missing.slice(0, 3).join(", ")} ${missing.length === 1 ? "was" : "were"} changed, but no edit to ${missing.length === 1 ? "it" : "them"} was recorded.`);
60
+ }
61
+ return problems;
62
+ }
63
+ /** A short factual record of the run, for the user. */
64
+ audit(task) {
65
+ const changed = [...new Set(this.edits.map((e) => e.path))], notes = [];
66
+ const tests = changed.filter((p) => /(^|\/)(tests?|__tests__|spec)\/|\.(test|spec)\.[a-z]+$|_test\.(go|py)$/i.test(p));
67
+ if (tests.length && !/\b(tests?|specs?)\b/i.test(task))
68
+ notes.push(`test files were modified although the task did not mention tests: ${tests.slice(0, 3).join(", ")}`);
69
+ if (changed.length > 8)
70
+ notes.push(`${changed.length} files changed, which is a lot for one task: check the diff`);
71
+ const testsAfter = this.edits.length ? (this.ranAny("test") ? this.passedAfterEdit("test") : undefined) : undefined;
72
+ if (this.edits.length && testsAfter === undefined)
73
+ notes.push("no test command was run");
74
+ else if (testsAfter === false)
75
+ notes.push("the last test run after the final edit did not pass");
76
+ return { changed, testsAfterLastEdit: testsAfter, notes };
77
+ }
78
+ }
@@ -0,0 +1,16 @@
1
+ import type { Event } from "./harness.ts";
2
+ /**
3
+ * Live view of a run in the browser: file diffs as they land, tool timeline, token meter.
4
+ * Serves on 127.0.0.1 only and rejects foreign Host headers (DNS-rebinding guard). Events are rendered with textContent,
5
+ * never as HTML, because diffs contain untrusted file contents.
6
+ */
7
+ export declare function startLive(o?: {
8
+ port?: number;
9
+ title?: string;
10
+ }): Promise<{
11
+ url: string;
12
+ onEvent(e: Event & {
13
+ agent?: string;
14
+ }): void;
15
+ close: () => Promise<void>;
16
+ }>;
@@ -0,0 +1,80 @@
1
+ import { createServer } from "node:http";
2
+ /**
3
+ * Live view of a run in the browser: file diffs as they land, tool timeline, token meter.
4
+ * Serves on 127.0.0.1 only and rejects foreign Host headers (DNS-rebinding guard). Events are rendered with textContent,
5
+ * never as HTML, because diffs contain untrusted file contents.
6
+ */
7
+ export async function startLive(o = {}) {
8
+ const history = [], clients = new Set();
9
+ const server = createServer((req, res) => {
10
+ const host = (req.headers.host ?? "").replace(/:\d+$/, "");
11
+ if (host !== "127.0.0.1" && host !== "localhost") {
12
+ res.writeHead(403).end("forbidden");
13
+ return;
14
+ }
15
+ if (req.url === "/events") {
16
+ res.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-cache", connection: "keep-alive" });
17
+ history.forEach((d) => res.write(`data: ${d}\n\n`));
18
+ clients.add(res);
19
+ req.on("close", () => clients.delete(res));
20
+ }
21
+ else if (req.url === "/" || req.url?.startsWith("/?")) {
22
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8", "content-security-policy": "default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; connect-src 'self'" });
23
+ res.end(PAGE.replace("__TITLE__", (o.title ?? "Amness live").replace(/[<>&"]/g, "")));
24
+ }
25
+ else
26
+ res.writeHead(404).end();
27
+ });
28
+ await new Promise((ok, fail) => { server.once("error", fail); server.listen(o.port ?? 0, "127.0.0.1", ok); });
29
+ const url = `http://127.0.0.1:${server.address().port}`;
30
+ return {
31
+ url,
32
+ onEvent(e) {
33
+ const d = JSON.stringify({ ...e, t: Date.now(), ...("diff" in e && { diff: e.diff.slice(0, 30_000) }), ...("result" in e && { result: e.result.slice(0, 400) }) });
34
+ history.push(d);
35
+ if (history.length > 1000)
36
+ history.shift();
37
+ clients.forEach((c) => c.write(`data: ${d}\n\n`));
38
+ },
39
+ close: () => new Promise((ok) => { clients.forEach((c) => c.end()); server.close(() => ok()); server.closeAllConnections?.(); }),
40
+ };
41
+ }
42
+ const PAGE = `<!doctype html><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>__TITLE__</title>
43
+ <style>
44
+ :root{--bg:#0f1115;--fg:#e6e8ee;--mut:#8b93a7;--card:#171a21;--bd:#262b36;--add:#1f6f43;--addfg:#8ff0b3;--del:#7a2630;--delfg:#ffb3bd;--hunk:#7aa2ff;--acc:#7aa2ff}
45
+ @media(prefers-color-scheme:light){:root{--bg:#f6f7f9;--fg:#1b1f27;--mut:#667085;--card:#fff;--bd:#e3e6ec;--add:#d6f5e1;--addfg:#0b5d2a;--del:#fde0e3;--delfg:#8a1c2b;--hunk:#2a55c5;--acc:#2a55c5}}
46
+ *{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--fg);font:14px/1.45 system-ui,sans-serif}
47
+ header{display:flex;gap:16px;align-items:center;padding:10px 16px;border-bottom:1px solid var(--bd);position:sticky;top:0;background:var(--bg)}
48
+ header b{font-size:15px}.dot{width:9px;height:9px;border-radius:50%;background:#e5a50a}.dot.on{background:#2ea043}.dot.off{background:#6e7681}
49
+ .m{color:var(--mut)}main{display:grid;grid-template-columns:minmax(260px,32%) 1fr;gap:12px;padding:12px 16px}
50
+ @media(max-width:800px){main{grid-template-columns:1fr}}
51
+ .col{display:flex;flex-direction:column;gap:8px;min-width:0}h2{font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:var(--mut);margin:4px 0}
52
+ .ev{padding:6px 10px;border:1px solid var(--bd);border-radius:8px;background:var(--card);word-break:break-word}.ev.bad{border-color:#b4414c}.ev.warn{border-color:#b08800}
53
+ .ev small{color:var(--mut)}.f{border:1px solid var(--bd);border-radius:10px;background:var(--card);overflow:hidden}
54
+ .f>div{padding:8px 12px;font-weight:600;display:flex;gap:10px;align-items:center;border-bottom:1px solid var(--bd);position:sticky;top:48px;background:var(--card)}
55
+ pre{margin:0;padding:6px 0;overflow:auto;font:12.5px/1.5 ui-monospace,Menlo,monospace}pre span{display:block;padding:0 12px;white-space:pre}
56
+ .a{background:var(--add);color:var(--addfg)}.d{background:var(--del);color:var(--delfg)}.h{color:var(--hunk)}.c{color:var(--mut)}
57
+ label{margin-left:auto;display:flex;gap:6px;align-items:center}
58
+ </style>
59
+ <header><span class="dot" id=dot></span><b>__TITLE__</b><span class=m id=stat>waiting for events…</span><label class=m><input type=checkbox id=follow checked>follow</label></header>
60
+ <main><div class=col><h2>Timeline</h2><div class=col id=tl></div></div><div class=col><h2>Changes</h2><div class=col id=files><span class=m>No edits yet.</span></div></div></main>
61
+ <script>
62
+ const $=id=>document.getElementById(id),tl=$("tl"),files=$("files");let steps=0,tokens=0,edits=0,cards={};
63
+ const el=(t,c,x)=>{const e=document.createElement(t);if(c)e.className=c;if(x!=null)e.textContent=x;return e};
64
+ function ev(text,cls,sub){const e=el("div","ev "+(cls||""),text);if(sub){e.append(el("br"),el("small",0,sub))}tl.append(e);return e}
65
+ function card(p){if(cards[p])return cards[p];if(files.firstChild&&!files.firstChild.className)files.textContent="";const f=el("div","f"),h=el("div",0,p),s=el("span","m","");h.append(s);f.append(h);const pre=el("pre");f.append(pre);files.append(f);return cards[p]={f,s,pre,a:0,r:0}}
66
+ function on(e){
67
+ const who=e.agent&&e.agent!=="lead"?"["+e.agent+"] ":"";
68
+ if(e.kind==="turn"){steps=e.step+1;tokens=e.tokens}
69
+ else if(e.kind==="tool"){const bad=/^(ERROR|DENIED|REJECTED|exit=[^0])/.test(e.result);let a="";try{const j=JSON.parse(e.args);a=j.cmd||j.path||j.query||j.url||j.description||""}catch{}ev(who+e.name+" "+String(a).slice(0,90),bad?"bad":"",bad?e.result.slice(0,140):"")}
70
+ else if(e.kind==="edit"){edits++;const c=card(e.path);c.a+=e.added;c.r+=e.removed;c.s.textContent=(e.created?"new ":"")+"+"+c.a+" −"+c.r;
71
+ const sep=el("span","c","── edit "+edits+" ──");c.pre.append(sep);
72
+ for(const l of e.diff.split("\\n")){if(/^(---|\\+\\+\\+)/.test(l))continue;c.pre.append(el("span",l[0]==="+"?"a":l[0]==="-"?"d":l[0]==="@"?"h":"",l))}
73
+ ev("✎ "+e.path,"","+"+e.added+" −"+e.removed)}
74
+ else if(e.kind==="rollover")ev("⟳ context rolled over","warn",e.detail);
75
+ else if(e.kind==="finish"){ev("✔ finished","",e.summary);$("dot").className="dot off"}
76
+ else if(e.detail)ev(e.kind+": "+e.detail.slice(0,160),["denied","budget","rejected"].includes(e.kind)?"bad":"warn");
77
+ $("stat").textContent=steps+" turns · "+tokens.toLocaleString()+" tokens · "+edits+" edits";
78
+ if($("follow").checked)scrollTo(0,document.body.scrollHeight)}
79
+ const es=new EventSource("/events");es.onopen=()=>$("dot").className="dot on";es.onerror=()=>$("dot").className="dot";es.onmessage=m=>on(JSON.parse(m.data));
80
+ </script>`;
@@ -0,0 +1,31 @@
1
+ export type LspServer = {
2
+ name: string;
3
+ command: string;
4
+ args: string[];
5
+ extensions: string[];
6
+ languageId: (ext: string) => string;
7
+ };
8
+ export declare const DEFAULT_SERVERS: LspServer[];
9
+ export type Diagnostic = {
10
+ line: number;
11
+ col: number;
12
+ message: string;
13
+ severity: number;
14
+ source?: string;
15
+ };
16
+ export declare class Diagnostics {
17
+ private clients;
18
+ private root;
19
+ private servers;
20
+ private missing;
21
+ constructor(root: string, o?: {
22
+ servers?: LspServer[];
23
+ });
24
+ /** Which server would handle this file, if one is installed. */
25
+ serverFor(path: string): LspServer | undefined;
26
+ errors(path: string): Promise<Diagnostic[] | undefined>;
27
+ /** Text to append to an edit result: the errors the language server reports for these files, or "" when there are none (or no server). */
28
+ report(paths: string[]): Promise<string>;
29
+ dispose(): Promise<void>;
30
+ installed(): string[];
31
+ }