@garuda-agent/garuda 0.14.1 → 0.16.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 (38) hide show
  1. package/CHANGELOG.md +49 -1
  2. package/README.md +78 -8
  3. package/dist/acpCommand-AJJBKAE5.js +191 -0
  4. package/dist/chunk-23FXBNBB.js +46 -0
  5. package/dist/chunk-3EQU2GUX.js +27 -0
  6. package/dist/{chunk-YIZWYUBG.js → chunk-44QN5SWZ.js} +2 -2
  7. package/dist/chunk-4JSUUDFQ.js +118 -0
  8. package/dist/{chunk-KWRJREUH.js → chunk-B25VL4RU.js} +1 -42
  9. package/dist/{chunk-U4OQVT2T.js → chunk-B6FA4I7Q.js} +2 -2
  10. package/dist/chunk-EIXUSVFX.js +116 -0
  11. package/dist/{chunk-YM6T4J5V.js → chunk-ENLDPN4S.js} +8 -111
  12. package/dist/{chunk-D5O5CIWK.js → chunk-GXZACJUI.js} +8 -25
  13. package/dist/{chunk-QOW5TK4L.js → chunk-HE2K657U.js} +9 -7
  14. package/dist/chunk-NP6RIC25.js +331 -0
  15. package/dist/chunk-NXJ4XLB5.js +19 -0
  16. package/dist/chunk-R7I2NBPX.js +71 -0
  17. package/dist/{chunk-ZGZ2ZADL.js → chunk-TUUEBHG2.js} +2 -2
  18. package/dist/{chunk-W7BPYQMW.js → chunk-ULAJPMY7.js} +1 -67
  19. package/dist/{chunk-35GMLLJW.js → chunk-USCUXCAU.js} +2 -2
  20. package/dist/{chunk-ITITLNAR.js → chunk-WIJL72ZE.js} +150 -234
  21. package/dist/chunk-XOK5OZOC.js +9 -0
  22. package/dist/cli/index.js +68 -39
  23. package/dist/{create-Y5SXJUXO.js → create-VXIH2GUZ.js} +6 -5
  24. package/dist/{evalCommand-5TG2CKZY.js → evalCommand-OX2LQYEK.js} +35 -18
  25. package/dist/{inkChat-3J26R24L.js → inkChat-YMMMGU7Y.js} +16 -10
  26. package/dist/{install-XWLU22OH.js → install-EWDSPFFZ.js} +4 -3
  27. package/dist/{jobCommand-IYWZWTSI.js → jobCommand-ACSGSBQ4.js} +9 -7
  28. package/dist/{launchd-2FL5UN2U.js → launchd-SSX362VG.js} +5 -4
  29. package/dist/{lspCommand-TJEZBV2E.js → lspCommand-JRXKRSL7.js} +4 -3
  30. package/dist/{manager-EBTYO2GR.js → manager-AWU4NHHK.js} +4 -3
  31. package/dist/{nightCommand-DUNWJASI.js → nightCommand-65MUEV32.js} +9 -7
  32. package/dist/{remove-AENULZZE.js → remove-6GDSTAIO.js} +4 -3
  33. package/dist/{run-66Y6R2KU.js → run-ML6GSPN4.js} +5 -3
  34. package/dist/server-KHHNHUYV.js +694 -0
  35. package/dist/setupCommand-VA7D5MZK.js +303 -0
  36. package/package.json +2 -1
  37. package/dist/chunk-6TWBYMY7.js +0 -9
  38. package/dist/chunk-V7WPGQLB.js +0 -207
package/CHANGELOG.md CHANGED
@@ -1,5 +1,52 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.16.0 (2026-10-06)
4
+
5
+ Setup for editors and the ACP Registry (design: [docs/lld/setup.md](docs/lld/setup.md)).
6
+
7
+ - Stored provider keys: Garuda reads `~/.garuda/credentials` (JSON, key name → key) when no
8
+ environment variable sets the key. It reads the file only when it is a regular file (not a link),
9
+ owned by you, with mode 0600; else it ignores the file with a warning. The key never enters
10
+ Garuda's environment, and the redactor knows it. `garuda setup` writes this file.
11
+ - Commands in the OS sandbox cannot read `~/.garuda/credentials` (macOS and Linux).
12
+ - `"default"` in `~/.garuda/models.json`: the model when neither `--model` nor `GARUDA_MODEL` names
13
+ one.
14
+ - The redactor removes every provider key that Garuda keeps, also one whose variable name does not
15
+ look secret (a custom provider's `apiKeyEnv`).
16
+ - `garuda setup`: choose a provider and a model, enter the key once (hidden input), check it with
17
+ one free request to the provider, then store the model as the default and the key in
18
+ `~/.garuda/credentials`. `--show` shows where each key comes from (masked); `--forget [NAME]`
19
+ removes stored keys. With no terminal, it stops with a message.
20
+ - The messages for a missing model or key name `garuda setup`.
21
+ - `garuda acp`: Terminal Auth. An editor that can run it (`auth.terminal`) gets a "Set up Garuda"
22
+ method; it runs `garuda acp setup` in a terminal. A session with no model or no key fails with
23
+ ACP's "auth required", so the editor offers the setup. The setup is read again for each new
24
+ session: no restart after `garuda setup`.
25
+ - Native Windows: `garuda acp` sessions and `garuda setup` stop with a message to use WSL.
26
+ - Docs: `garuda setup` in the quick start, the landing page, the README and the Editors page; editor
27
+ entries need no API key in their settings. Tested live in Zed 1.22: with no model and no key, Zed
28
+ offered the setup, ran it in a terminal, and the next thread worked with no restart.
29
+ - `-p --output-format stream-json`: `apiKeySource` in `system/init` is `credentials` for a stored
30
+ Claude key.
31
+
32
+ ## 0.15.0 (2026-10-06)
33
+
34
+ Garuda in your editor. Version 0.15.0 is not the old "0.15" branch label of the status table (code
35
+ intelligence), which shipped in 0.14.0.
36
+
37
+ ### Editors
38
+
39
+ - `garuda acp`: Garuda as an agent for editors over the Agent Client Protocol (v1): VS Code (with
40
+ the "ACP Client" extension), Zed, JetBrains, Neovim, Emacs. One runtime per editor session, with
41
+ the same permission engine, OS sandbox, team policy, hooks and audit log as the terminal.
42
+ - Approval questions name their tool call (`callId`), and file writes carry the whole change, so an
43
+ editor shows each question next to the right call, with its own diff view.
44
+ - A stop in the editor names what it ended: each stopped tool call shows as failed, with a line
45
+ such as "Garuda: $ sleep 60 cancelled." (or "the turn was cancelled." when no call ran). A call
46
+ that has not reported within 3 s of the stop ends as failed, so none stays "running".
47
+ - Docs: the website's Editors page (VS Code with ACP Client, Zed, other ACP editors). Tested live
48
+ in VS Code and Zed on macOS.
49
+
3
50
  ## 0.14.1 (2026-10)
4
51
 
5
52
  The first version on npm (`@garuda-agent/garuda`) and Homebrew (`madhu-sv/garuda/garuda`). It fixes
@@ -10,7 +57,8 @@ of the other areas are closed. Each fix has a test that fails without it.
10
57
 
11
58
  - npm: `npm install -g @garuda-agent/garuda`. Published from the release workflow with npm trusted
12
59
  publishing, with provenance.
13
- - Homebrew: `brew install madhu-sv/garuda/garuda`.
60
+ - Homebrew: `brew tap madhu-sv/garuda`, `brew trust --formula madhu-sv/garuda/garuda`, then
61
+ `brew install garuda`.
14
62
 
15
63
  ### Security
16
64
 
package/README.md CHANGED
@@ -4,8 +4,10 @@
4
4
 
5
5
  Website and docs: https://madhu-sv.github.io/garuda/
6
6
 
7
- Garuda is a terminal coding agent. This is version 0.14.1: the 0.14–0.17 branches after the merge gate
8
- and Garuda's own review (see the notes under the status table). Release notes: [CHANGELOG.md](CHANGELOG.md).
7
+ Garuda is a terminal coding agent. This is version 0.16.0: `garuda setup` (choose a model and store
8
+ its key once) and the sign-in from editors, on top of 0.15.0 (Garuda in your editor over the Agent
9
+ Client Protocol, `garuda acp`) and 0.14.1 (the 0.14–0.17 branches after the merge gate and Garuda's own
10
+ review; see the notes under the status table). Release notes: [CHANGELOG.md](CHANGELOG.md).
9
11
  Design documents: [docs/](docs/README.md) (architecture, high-level design, low-level design per component).
10
12
  The requirements doc defines the scope. Code, tests and commits refer to its IDs (F1–F26, N1–N8).
11
13
 
@@ -34,6 +36,11 @@ The requirements doc defines the scope. Code, tests and commits refer to its IDs
34
36
  | 0.15: multi-language AST code intelligence (Python, Java, TS/JS, call graph, blast radius, /callers, /defs, /impact, ast_query) | Done (0.14.0) |
35
37
  | 0.16: language plugins (Go and Rust built in, user plugins in ~/.garuda/languages, /languages; project plugins not loaded) | Done (0.14.0) |
36
38
  | 0.17: Mixture-of-Experts subagents (opt-in), team policy and audit log (W5) | Done (0.14.0) |
39
+ | Editors over the Agent Client Protocol (`garuda acp`: VS Code with an extension, Zed, JetBrains, Neovim, Emacs) | Done (0.15.0) |
40
+ | `garuda setup` (model and stored key) and the editor sign-in (ACP Terminal Auth) | Done (0.16.0) |
41
+
42
+ The rows labelled 0.14–0.17 are branch labels, not versions: the "0.15" and "0.16" branches (code
43
+ intelligence, language plugins) shipped in 0.14.0. The last two rows are versions 0.15.0 and 0.16.0.
37
44
 
38
45
  The 0.14–0.17 rows were built on stacked branches that were never released. They ship together as
39
46
  0.14.0 after the merge gate: the high findings of the branch review are fixed (policy source, audit
@@ -45,7 +52,7 @@ headings below keep the branch labels (0.14–0.17).
45
52
  Before 0.14.0, Garuda reviewed its own code with Fable 5.1, one area at a time (knowledge,
46
53
  permissions, tools, sandbox, audit, loop, agents), in plan mode. 0.14.1 adds the cli and extensions
47
54
  areas and fixes the open findings of all nine areas (see [CHANGELOG.md](CHANGELOG.md)). Known limits
48
- in 0.14.1:
55
+ in 0.16.0:
49
56
 
50
57
  - G09: project language plugins are not loaded (no consent flow for their imports yet).
51
58
  - Subagents: each child has its own step and token budget (capped by the team policy); it does not
@@ -57,14 +64,28 @@ in 0.14.1:
57
64
  `~/.m2/wrapper`) hold programs that a build runs later outside the sandbox. They stay writable, because
58
65
  `./gradlew` needs them in the sandbox; a sandboxed command could change them.
59
66
  - The jdtls download is checked against the `.sha256` file of the same server, not a pinned hash.
67
+ - Editors (`garuda acp`): Garuda's session history is not in the editor (no `session/load`), the
68
+ editor's MCP servers are not started, Garuda reads saved files (not unsaved buffers), the terminal's
69
+ own commands (`/undo`, `/diff` …) are not available, and an edit is approved as a whole (no hunks).
70
+ - Stored keys are in a file (`~/.garuda/credentials`, mode 0600, hidden from the sandbox), not in the
71
+ OS keychain yet. Keep the file out of backups and dotfile sync.
72
+ - Native Windows is not supported; use WSL.
60
73
 
61
74
  ## Use
62
75
 
63
76
  Install (0.14.1 and later) with Homebrew or npm. You need Node 22 or later; Homebrew installs it.
64
77
 
65
78
  ```sh
66
- brew install madhu-sv/garuda/garuda # Homebrew (macOS and Linux)
67
- npm install -g @garuda-agent/garuda # or npm
79
+ npm install -g @garuda-agent/garuda
80
+ ```
81
+
82
+ Or with Homebrew (macOS and Linux). Homebrew loads a formula from a third-party tap only after you
83
+ trust it, so trust the Garuda formula first:
84
+
85
+ ```sh
86
+ brew tap madhu-sv/garuda
87
+ brew trust --formula madhu-sv/garuda/garuda
88
+ brew install garuda
68
89
  ```
69
90
 
70
91
  Or build from source. You need Node 22 or later, pnpm 10 and git. Node 25 and later do not include
@@ -82,8 +103,7 @@ pnpm link --global # optional: puts `garuda` on your PATH (run `pnpm setup` onc
82
103
  Then:
83
104
 
84
105
  ```sh
85
- export ANTHROPIC_API_KEY=...
86
- export GARUDA_MODEL=<model-id>
106
+ garuda setup # once: pick a provider and model, enter the key
87
107
  garuda # chat in the current folder
88
108
  garuda -p "Explain what this repo does" # run one task and exit
89
109
  echo "Explain this repo" | garuda # the same, from stdin
@@ -91,6 +111,13 @@ echo "Explain this repo" | garuda # the same, from stdin
91
111
 
92
112
  From a source build without `pnpm link`, use `node dist/cli/index.js` in place of `garuda`.
93
113
 
114
+ `garuda setup` (0.16) asks for the provider, the model and the key (hidden input), checks the key
115
+ with one free request to the provider, and stores the model as `"default"` in
116
+ `~/.garuda/models.json` and the key in `~/.garuda/credentials` (mode 0600; commands in the sandbox
117
+ cannot read it). `garuda setup --show` shows the model and where each key comes from (masked);
118
+ `garuda setup --forget [NAME]` removes stored keys. Environment variables still work and win:
119
+ `ANTHROPIC_API_KEY`, `GARUDA_MODEL`. Do not sync or back up `~/.garuda/credentials` to other places.
120
+
94
121
  Tools: read_file, glob and grep run with no question.
95
122
  write_file, edit_file and bash show a diff or the command first. You pick: allow once, allow for this session, or deny.
96
123
  Commands run in an OS sandbox when your machine has one (see Sandbox below). Without a sandbox,
@@ -134,13 +161,15 @@ Set the context window, price and output limit per model, and add providers, in
134
161
  ```json
135
162
  {
136
163
  "providers": { "lab": { "type": "openai-compatible", "baseUrl": "https://llm.example.com/v1", "apiKeyEnv": "LAB_KEY" } },
137
- "models": { "ollama/qwen3-coder:30b": { "contextWindow": 65536, "maxTokens": 8192 } }
164
+ "models": { "ollama/qwen3-coder:30b": { "contextWindow": 65536, "maxTokens": 8192 } },
165
+ "default": "ollama/qwen3-coder:30b"
138
166
  }
139
167
  ```
140
168
 
141
169
  - Only this file in your home folder can define providers; a project cannot send your code elsewhere.
142
170
  Keys come from environment variables. Plain http works only to this machine (or with
143
171
  `"allowInsecureHttp": true`).
172
+ - `"default"` is the model when neither `--model` nor `GARUDA_MODEL` names one.
144
173
  - Some small models write a tool call as JSON text. When the whole reply is such a call to a known
145
174
  tool, Garuda runs it as a real call, with the usual approvals. For a model that also writes
146
175
  sentences around the call (for example `qwen2.5-coder:7b`), set `"textToolCalls": "lines"` for that
@@ -236,6 +265,47 @@ Set the context window, price and output limit per model, and add providers, in
236
265
 
237
266
  Model text goes to stdout; tool activity and notes go to stderr. So `garuda -p "…" > answer.md` keeps only the answer.
238
267
 
268
+ ## Editors (ACP, 0.15; sign-in 0.16)
269
+
270
+ `garuda acp` runs Garuda as an agent for editors that speak the Agent Client Protocol. The editor
271
+ starts it, shows the conversation, the tool calls and the questions, and Garuda does the work with
272
+ the same permission engine, OS sandbox, team policy, hooks and audit log as in the terminal.
273
+
274
+ Run `garuda setup` once in a terminal (see Use): then the editor needs no key in its settings.
275
+
276
+ VS Code, with the extension "ACP Client" (`formulahendry.acp-client`), in the user settings
277
+ (Cmd+Shift+P, **Preferences: Open User Settings (JSON)**). Use the full path from `which garuda`:
278
+ VS Code started from the Dock does not get your shell's `PATH`.
279
+
280
+ ```json
281
+ {
282
+ "acp.agents": {
283
+ "Garuda": { "command": "/opt/homebrew/bin/garuda", "args": ["acp"] }
284
+ }
285
+ }
286
+ ```
287
+
288
+ Zed: the same command and argument under `agent_servers` (with `"type": "custom"`). With no model
289
+ or key, Zed offers **Set up Garuda** (ACP Terminal Auth, 0.16): it runs `garuda acp setup` in a
290
+ terminal, and the next thread works. The website's
291
+ [Editors](https://madhu-sv.github.io/garuda/docs/editors/) page has the steps and what the editor
292
+ shows.
293
+
294
+ - Each edit asks in the editor, with the editor's diff view; "Allow for this session" lasts for this
295
+ session only. Keep an extension's auto-approve off: it answers Garuda's questions for you.
296
+ - The editor's stop button ends the turn: a running command stops, and a waiting question is a deny.
297
+ Each call that the stop ended shows as failed, with a line such as "Garuda: $ sleep 60 cancelled."
298
+ A command that exits with an error shows as done (the model reads the error); a refused call shows
299
+ as failed.
300
+ - Build and plan modes are in the editor's mode menu; custom commands and skills are in its `/` menu.
301
+ Built-in chat commands (`/undo`, `/diff` ...) are only in the terminal in this version.
302
+ - Garuda reads and writes files and runs commands itself; it does not use the editor's file or
303
+ terminal access, so it reads the saved file, not an unsaved buffer. It does not start MCP servers
304
+ that the editor sends (its own `~/.garuda/mcp.json` and the project's work as usual).
305
+ - Project settings that loosen safety apply only after a yes in a terminal (`garuda` in that folder).
306
+
307
+ Design: [docs/lld/acp.md](docs/lld/acp.md).
308
+
239
309
  ## Init: set up a folder
240
310
 
241
311
  `garuda init` (or `/init` in the chat) sets up the current folder:
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ FileSessionStore,
4
+ Runtime,
5
+ auditDirFor
6
+ } from "./chunk-WIJL72ZE.js";
7
+ import "./chunk-G2AX7MGS.js";
8
+ import "./chunk-A2QAIRQL.js";
9
+ import "./chunk-ULAJPMY7.js";
10
+ import {
11
+ chooseModel,
12
+ keepProviderKeys,
13
+ loadModelsConfig,
14
+ providerKeySource,
15
+ resolveModel
16
+ } from "./chunk-NP6RIC25.js";
17
+ import "./chunk-EIXUSVFX.js";
18
+ import "./chunk-R7I2NBPX.js";
19
+ import {
20
+ loadSearchConfig
21
+ } from "./chunk-B25VL4RU.js";
22
+ import "./chunk-23FXBNBB.js";
23
+ import {
24
+ VERSION
25
+ } from "./chunk-XOK5OZOC.js";
26
+ import "./chunk-5DEWP3YX.js";
27
+ import "./chunk-ZAE25JM2.js";
28
+ import "./chunk-ATW7BNIG.js";
29
+ import "./chunk-PZS3V6P3.js";
30
+ import "./chunk-OUCV3FCT.js";
31
+ import "./chunk-GXZACJUI.js";
32
+ import "./chunk-WMOLXTBU.js";
33
+ import "./chunk-3EQU2GUX.js";
34
+ import "./chunk-HUIK3WXO.js";
35
+ import "./chunk-3NJNVZTK.js";
36
+
37
+ // src/cli/acpCommand.ts
38
+ import { homedir } from "os";
39
+ import { Readable } from "stream";
40
+ var WINDOWS_MESSAGE = "Garuda supports macOS and Linux. On Windows, run Garuda in WSL (Windows Subsystem for Linux).";
41
+ async function acpCommand(options) {
42
+ if (options.action !== void 0) {
43
+ if (options.action !== "setup") {
44
+ process.stderr.write(`Unknown argument "${options.action}". Use: garuda acp [setup]
45
+ `);
46
+ return 1;
47
+ }
48
+ const { setupCommand, terminalIO } = await import("./setupCommand-VA7D5MZK.js");
49
+ return setupCommand({}, { io: await terminalIO() });
50
+ }
51
+ const output = protocolOutput(process.stdout, process.stderr);
52
+ const input = Readable.toWeb(process.stdin);
53
+ const { acpServer, setupNeeded } = await import("./server-KHHNHUYV.js");
54
+ const server = acpServer({
55
+ version: VERSION,
56
+ // Not on native Windows: the setup there could not keep a key private (no 0600).
57
+ ...process.platform === "win32" ? {} : { terminalAuth: { args: ["setup"] } },
58
+ createRuntime: async ({ root, approver, onEvent, onNotice }) => {
59
+ const setup = await prepare(options);
60
+ if ("problem" in setup) {
61
+ throw setup.setupNeeded ? setupNeeded(setup.problem) : new Error(setup.problem);
62
+ }
63
+ return createRuntime(setup, { root, approver, onEvent, onNotice });
64
+ }
65
+ });
66
+ process.on("exit", () => server.shutdown());
67
+ const stop = () => {
68
+ server.shutdown();
69
+ process.exit(143);
70
+ };
71
+ process.once("SIGTERM", stop);
72
+ await server.serve(output, input);
73
+ await server.close();
74
+ return 0;
75
+ }
76
+ function protocolOutput(stdout, stderr) {
77
+ const write = stdout.write.bind(stdout);
78
+ const toStderr = stderr.write.bind(stderr);
79
+ stdout.write = ((...args) => toStderr(...args));
80
+ return new WritableStream({
81
+ write: (chunk) => new Promise((resolve, reject) => {
82
+ write(chunk, (error) => error ? reject(error) : resolve());
83
+ })
84
+ });
85
+ }
86
+ var SETUP_HINT = "Run `garuda setup` in a terminal, or use the editor's sign-in for Garuda.";
87
+ async function prepare(options, from = {}) {
88
+ const fail = (problem, setupNeeded = false) => ({ problem, setupNeeded });
89
+ if ((from.platform ?? process.platform) === "win32") return fail(WINDOWS_MESSAGE);
90
+ const home = from.home ?? homedir();
91
+ const models = await loadModelsConfig(home);
92
+ if (models.problem !== void 0) return fail(models.problem);
93
+ const spec = chooseModel(models.config, options.model, process.env.GARUDA_MODEL);
94
+ if (!spec) return fail(`Garuda has no model. ${SETUP_HINT}`, true);
95
+ let resolved;
96
+ let sub;
97
+ try {
98
+ resolved = resolveModel(spec, models.config);
99
+ const subSpec = process.env.GARUDA_SUBAGENT_MODEL;
100
+ if (subSpec) sub = resolveModel(subSpec, models.config);
101
+ } catch (error) {
102
+ return fail(error.message);
103
+ }
104
+ const search = await loadSearchConfig(home);
105
+ const credentialsWarning = await keepProviderKeys(
106
+ [resolved.def.apiKeyEnv, sub?.def.apiKeyEnv, "ANTHROPIC_API_KEY"],
107
+ home
108
+ );
109
+ if (credentialsWarning !== void 0) process.stderr.write(`garuda acp: ${credentialsWarning}
110
+ `);
111
+ const keyName = resolved.def.type === "anthropic" ? resolved.def.apiKeyEnv ?? "ANTHROPIC_API_KEY" : resolved.def.apiKeyEnv;
112
+ const sdkToken = resolved.def.type === "anthropic" && (process.env.ANTHROPIC_AUTH_TOKEN ?? "") !== "";
113
+ if (keyName !== void 0 && providerKeySource(keyName) === void 0 && !sdkToken) {
114
+ const why = credentialsWarning === void 0 ? "" : ` ${credentialsWarning}`;
115
+ return fail(`Garuda has no key for ${resolved.spec} (${keyName}).${why} ${SETUP_HINT}`, true);
116
+ }
117
+ const { loadTeamPolicy } = await import("./policy-EJPLEEXP.js");
118
+ let team;
119
+ try {
120
+ team = await loadTeamPolicy("managed" in from ? { home, managed: from.managed } : { home });
121
+ } catch (error) {
122
+ return fail(`Team policy: ${error.message}`);
123
+ }
124
+ return {
125
+ resolved,
126
+ ...sub === void 0 ? {} : { sub },
127
+ models: models.config,
128
+ ...team === void 0 ? {} : { team: { policy: team.policy, sources: team.sources } },
129
+ search,
130
+ ...credentialsWarning === void 0 ? {} : { credentialsWarning }
131
+ };
132
+ }
133
+ function createRuntime(setup, input) {
134
+ const { resolved, sub, models, team, search } = setup;
135
+ return Runtime.create({
136
+ root: input.root,
137
+ modelId: resolved.spec,
138
+ ...team === void 0 ? {} : { policy: team.policy, policySources: team.sources },
139
+ audit: { dir: auditDirFor(input.root) },
140
+ model: () => resolved.create(),
141
+ modelInfo: resolved.info,
142
+ ...resolved.maxTokens === void 0 ? {} : { maxTokens: resolved.maxTokens },
143
+ ...sub === void 0 ? {} : { subagentModel: { spec: sub.spec, model: () => sub.create(), info: sub.info } },
144
+ approver: input.approver,
145
+ store: new FileSessionStore(input.root),
146
+ undo: {},
147
+ skills: {},
148
+ languages: {},
149
+ // The editor cannot answer a question before session/new returns: only project settings that
150
+ // the user approved in a terminal apply; the rest is left out with a notice (docs/lld/acp.md).
151
+ projectSettings: { home: homedir(), ask: false },
152
+ ...search.config === void 0 && search.claude === void 0 ? {} : {
153
+ search: {
154
+ ...search.config === void 0 ? {} : { config: search.config },
155
+ ...search.claude === void 0 ? {} : { claude: search.claude },
156
+ ...search.use === void 0 ? {} : { use: search.use },
157
+ home: homedir()
158
+ }
159
+ },
160
+ agents: {
161
+ resolveModel: (spec) => {
162
+ const r = resolveModel(spec, models);
163
+ return { spec: r.spec, model: () => r.create(), info: r.info };
164
+ }
165
+ },
166
+ models: {
167
+ resolve: (spec) => {
168
+ const r = resolveModel(spec, models);
169
+ return {
170
+ spec: r.spec,
171
+ model: () => r.create(),
172
+ info: r.info,
173
+ ...r.maxTokens === void 0 ? {} : { maxTokens: r.maxTokens }
174
+ };
175
+ },
176
+ configured: Object.keys(models.models)
177
+ },
178
+ ...input.onEvent === void 0 ? {} : { onEvent: input.onEvent },
179
+ onNotice: input.onNotice
180
+ }).then((runtime) => {
181
+ if (setup.credentialsWarning !== void 0) input.onNotice(setup.credentialsWarning);
182
+ return runtime;
183
+ });
184
+ }
185
+ export {
186
+ WINDOWS_MESSAGE,
187
+ acpCommand,
188
+ prepare,
189
+ protocolOutput
190
+ };
191
+ //# sourceMappingURL=acpCommand-AJJBKAE5.js.map
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/commands/builtins.ts
4
+ var BUILTIN_COMMANDS = [
5
+ "help",
6
+ "usage",
7
+ "session",
8
+ "sessions",
9
+ "models",
10
+ "export",
11
+ "compact",
12
+ "diff",
13
+ "schedule",
14
+ "jobs",
15
+ "where",
16
+ "defs",
17
+ "refs",
18
+ "callers",
19
+ "impact",
20
+ "map",
21
+ "mcp",
22
+ "hooks",
23
+ "lsp",
24
+ "languages",
25
+ "experts",
26
+ "audit",
27
+ "commands",
28
+ "agents",
29
+ "plan",
30
+ "build",
31
+ "new",
32
+ "init",
33
+ "editor",
34
+ "details",
35
+ "thinking",
36
+ "search",
37
+ "undo",
38
+ "redo",
39
+ "exit",
40
+ "quit"
41
+ ];
42
+
43
+ export {
44
+ BUILTIN_COMMANDS
45
+ };
46
+ //# sourceMappingURL=chunk-23FXBNBB.js.map
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/model/types.ts
4
+ var EFFORTS = ["low", "medium", "high", "xhigh", "max"];
5
+ var ZERO_USAGE = {
6
+ inputTokens: 0,
7
+ outputTokens: 0,
8
+ cacheReadTokens: 0,
9
+ cacheWriteTokens: 0
10
+ };
11
+ function addUsage(a, b) {
12
+ const searches = (a.webSearches ?? 0) + (b.webSearches ?? 0);
13
+ return {
14
+ inputTokens: a.inputTokens + b.inputTokens,
15
+ outputTokens: a.outputTokens + b.outputTokens,
16
+ cacheReadTokens: a.cacheReadTokens + b.cacheReadTokens,
17
+ cacheWriteTokens: a.cacheWriteTokens + b.cacheWriteTokens,
18
+ ...searches === 0 ? {} : { webSearches: searches }
19
+ };
20
+ }
21
+
22
+ export {
23
+ EFFORTS,
24
+ ZERO_USAGE,
25
+ addUsage
26
+ };
27
+ //# sourceMappingURL=chunk-3EQU2GUX.js.map
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  JobGitError,
4
4
  git
5
- } from "./chunk-ZGZ2ZADL.js";
5
+ } from "./chunk-TUUEBHG2.js";
6
6
 
7
7
  // src/jobs/worktree.ts
8
8
  import { createHash } from "crypto";
@@ -135,4 +135,4 @@ export {
135
135
  commitJob,
136
136
  jobChanges
137
137
  };
138
- //# sourceMappingURL=chunk-YIZWYUBG.js.map
138
+ //# sourceMappingURL=chunk-44QN5SWZ.js.map
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/approver.ts
4
+ import { styleText } from "util";
5
+ var TerminalApprover = class {
6
+ /** Set by the CLI: it aborts the current turn. */
7
+ onInterrupt = () => {
8
+ };
9
+ /** One question at a time (0.14.1, review): two prompts at once mixed their keys. */
10
+ ask(request, signal) {
11
+ const next = this.asking === 0 ? this.askNow(request, signal) : this.queue.then(() => this.askNow(request, signal));
12
+ this.asking++;
13
+ this.queue = next.catch(() => {
14
+ }).finally(() => {
15
+ this.asking--;
16
+ });
17
+ return next;
18
+ }
19
+ asking = 0;
20
+ queue = Promise.resolve();
21
+ async askNow(request, signal) {
22
+ signal.throwIfAborted();
23
+ const out = process.stderr;
24
+ out.write(`
25
+ ${header(request)}
26
+ ${colorPreview(request)}
27
+ `);
28
+ if (!process.stdin.isTTY) {
29
+ out.write("No terminal to ask for approval, so Garuda denies this call.\n");
30
+ return "deny";
31
+ }
32
+ const sessionLabel = request.labels?.session ?? (request.target.kind === "command" ? "Yes, and allow this exact command for this session" : request.target.kind === "url" ? `Yes, and allow ${request.target.host} for this session` : `Yes, and allow all ${request.tool} calls for this session`);
33
+ const { select } = await import("@inquirer/prompts");
34
+ try {
35
+ return await select(
36
+ {
37
+ message: request.question ?? "Allow?",
38
+ choices: [
39
+ { name: request.labels?.once ?? "Yes, once", value: "once" },
40
+ { name: sessionLabel, value: "session" },
41
+ { name: request.labels?.deny ?? "No, deny", value: "deny" }
42
+ ].filter((c) => request.choices?.includes(c.value) ?? true)
43
+ },
44
+ { signal, output: out }
45
+ );
46
+ } catch (error) {
47
+ if (error.name === "ExitPromptError") this.onInterrupt();
48
+ throw error;
49
+ }
50
+ }
51
+ };
52
+ var SwitchApprover = class {
53
+ constructor(current) {
54
+ this.current = current;
55
+ }
56
+ current;
57
+ /** Called before each question: the chat's notifier (0.6). */
58
+ onAsk = () => {
59
+ };
60
+ ask(request, signal) {
61
+ this.onAsk(request);
62
+ return this.current.ask(request, signal);
63
+ }
64
+ };
65
+ function visible(text) {
66
+ return text.replace(/[\u0000-\u0008\u000b-\u001f]/g, (c) => String.fromCodePoint(9216 + c.charCodeAt(0))).replace(/\u007f/g, "\u2421").replace(
67
+ /[\u0080-\u009f\u200b-\u200f\u202a-\u202e\u2060-\u2064\u2066-\u2069\ufeff\u{e0000}-\u{e007f}]/gu,
68
+ (c) => `[U+${(c.codePointAt(0) ?? 0).toString(16).toUpperCase().padStart(4, "0")}]`
69
+ );
70
+ }
71
+ function hasHidden(text) {
72
+ return visible(text) !== text;
73
+ }
74
+ var HIDDEN_WARNING = "! This holds hidden or control characters, shown as \u240D, \u241B or [U+\u2026]. Read it with care.";
75
+ function headerText(request) {
76
+ const { tool, target, isolation } = request;
77
+ if (request.title !== void 0) return visible(request.title);
78
+ if (target.kind === "command") {
79
+ const where = isolation === "none" ? "on your machine, no sandbox" : target.outsideSandbox ? "OUTSIDE the sandbox: network on, writes anywhere" : `sandbox: ${isolation}`;
80
+ return `${tool} wants to run a command (${where}):`;
81
+ }
82
+ if (target.kind === "input") return `${tool} wants to run with this input:`;
83
+ if (target.kind === "url") return `${tool} wants to fetch from ${visible(target.host)}:`;
84
+ return `${tool} wants to change ${visible(target.path)}:`;
85
+ }
86
+ function header(request) {
87
+ return styleText("bold", headerText(request));
88
+ }
89
+ function colorPreview({ target, preview }) {
90
+ const warning = hasHidden(preview) ? `
91
+ ${styleText("yellow", ` ${HIDDEN_WARNING}`)}` : "";
92
+ if (target.kind === "command") return `${styleText("cyan", ` $ ${visible(preview)}`)}${warning}`;
93
+ if (target.kind === "url") return `${visible(preview)}${warning}`;
94
+ return `${colorDiff(visible(preview))}${warning}`;
95
+ }
96
+ function colorDiff(text) {
97
+ return text.split("\n").map((line) => {
98
+ if (line.startsWith("+++") || line.startsWith("---") || line.startsWith("diff --git"))
99
+ return styleText("bold", line);
100
+ if (line.startsWith("+")) return styleText("green", line);
101
+ if (line.startsWith("-")) return styleText("red", line);
102
+ if (line.startsWith("@@")) return styleText("cyan", line);
103
+ return line;
104
+ }).join("\n");
105
+ }
106
+
107
+ export {
108
+ TerminalApprover,
109
+ SwitchApprover,
110
+ visible,
111
+ hasHidden,
112
+ HIDDEN_WARNING,
113
+ headerText,
114
+ header,
115
+ colorPreview,
116
+ colorDiff
117
+ };
118
+ //# sourceMappingURL=chunk-4JSUUDFQ.js.map
@@ -3,46 +3,6 @@ import {
3
3
  isLoopbackHost
4
4
  } from "./chunk-ATW7BNIG.js";
5
5
 
6
- // src/commands/builtins.ts
7
- var BUILTIN_COMMANDS = [
8
- "help",
9
- "usage",
10
- "session",
11
- "sessions",
12
- "models",
13
- "export",
14
- "compact",
15
- "diff",
16
- "schedule",
17
- "jobs",
18
- "where",
19
- "defs",
20
- "refs",
21
- "callers",
22
- "impact",
23
- "map",
24
- "mcp",
25
- "hooks",
26
- "lsp",
27
- "languages",
28
- "experts",
29
- "audit",
30
- "commands",
31
- "agents",
32
- "plan",
33
- "build",
34
- "new",
35
- "init",
36
- "editor",
37
- "details",
38
- "thinking",
39
- "search",
40
- "undo",
41
- "redo",
42
- "exit",
43
- "quit"
44
- ];
45
-
46
6
  // src/web/search.ts
47
7
  import { mkdir, readFile, writeFile } from "fs/promises";
48
8
  import { homedir } from "os";
@@ -273,11 +233,10 @@ async function readCapped(response, max) {
273
233
  }
274
234
 
275
235
  export {
276
- BUILTIN_COMMANDS,
277
236
  MAX_RESULTS,
278
237
  providerLabel,
279
238
  loadSearchConfig,
280
239
  saveSearchUse,
281
240
  search
282
241
  };
283
- //# sourceMappingURL=chunk-KWRJREUH.js.map
242
+ //# sourceMappingURL=chunk-B25VL4RU.js.map