chivgent 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +136 -907
- package/README.zh-CN.md +25 -2
- package/dist/agent.d.ts +14 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +195 -28
- package/dist/agent.js.map +1 -1
- package/dist/cli-options.d.ts +2 -0
- package/dist/cli-options.d.ts.map +1 -1
- package/dist/cli-options.js +12 -2
- package/dist/cli-options.js.map +1 -1
- package/dist/cli-runtime.d.ts +24 -0
- package/dist/cli-runtime.d.ts.map +1 -0
- package/dist/cli-runtime.js +64 -0
- package/dist/cli-runtime.js.map +1 -0
- package/dist/cli.js +20 -72
- package/dist/cli.js.map +1 -1
- package/dist/context/compaction.d.ts +5 -2
- package/dist/context/compaction.d.ts.map +1 -1
- package/dist/context/compaction.js +58 -32
- package/dist/context/compaction.js.map +1 -1
- package/dist/context/context-manager.d.ts +2 -0
- package/dist/context/context-manager.d.ts.map +1 -1
- package/dist/context/context-manager.js +4 -3
- package/dist/context/context-manager.js.map +1 -1
- package/dist/context/project-instructions.d.ts +17 -0
- package/dist/context/project-instructions.d.ts.map +1 -0
- package/dist/context/project-instructions.js +191 -0
- package/dist/context/project-instructions.js.map +1 -0
- package/dist/evals/baseline.d.ts +41 -0
- package/dist/evals/baseline.d.ts.map +1 -0
- package/dist/evals/baseline.js +121 -0
- package/dist/evals/baseline.js.map +1 -0
- package/dist/evals/cli.js +27 -1
- package/dist/evals/cli.js.map +1 -1
- package/dist/evals/parse-args.d.ts +2 -0
- package/dist/evals/parse-args.d.ts.map +1 -1
- package/dist/evals/parse-args.js +5 -0
- package/dist/evals/parse-args.js.map +1 -1
- package/dist/evals/report.d.ts +2 -0
- package/dist/evals/report.d.ts.map +1 -1
- package/dist/evals/report.js +12 -8
- package/dist/evals/report.js.map +1 -1
- package/dist/evals/runner.d.ts +3 -0
- package/dist/evals/runner.d.ts.map +1 -1
- package/dist/evals/runner.js +5 -1
- package/dist/evals/runner.js.map +1 -1
- package/dist/evals/task.d.ts.map +1 -1
- package/dist/evals/task.js +1 -0
- package/dist/evals/task.js.map +1 -1
- package/dist/evals/tools.d.ts +4 -1
- package/dist/evals/tools.d.ts.map +1 -1
- package/dist/evals/tools.js +11 -4
- package/dist/evals/tools.js.map +1 -1
- package/dist/events.d.ts +7 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/patch/parse.d.ts +26 -0
- package/dist/patch/parse.d.ts.map +1 -0
- package/dist/patch/parse.js +117 -0
- package/dist/patch/parse.js.map +1 -0
- package/dist/prompts.d.ts +2 -2
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +4 -3
- package/dist/prompts.js.map +1 -1
- package/dist/repl.d.ts +12 -1
- package/dist/repl.d.ts.map +1 -1
- package/dist/repl.js +305 -4
- package/dist/repl.js.map +1 -1
- package/dist/session-store.d.ts +6 -2
- package/dist/session-store.d.ts.map +1 -1
- package/dist/session-store.js +5 -1
- package/dist/session-store.js.map +1 -1
- package/dist/session.d.ts +1 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +38 -12
- package/dist/session.js.map +1 -1
- package/dist/shell/approval.d.ts +15 -0
- package/dist/shell/approval.d.ts.map +1 -0
- package/dist/shell/approval.js +25 -0
- package/dist/shell/approval.js.map +1 -0
- package/dist/shell/docker.d.ts +18 -0
- package/dist/shell/docker.d.ts.map +1 -0
- package/dist/shell/docker.js +83 -0
- package/dist/shell/docker.js.map +1 -0
- package/dist/shell/local.d.ts +1 -1
- package/dist/shell/local.d.ts.map +1 -1
- package/dist/shell/local.js +1 -1
- package/dist/shell/local.js.map +1 -1
- package/dist/tools/apply-patch.d.ts +18 -0
- package/dist/tools/apply-patch.d.ts.map +1 -0
- package/dist/tools/apply-patch.js +53 -0
- package/dist/tools/apply-patch.js.map +1 -0
- package/dist/tools/bash.d.ts +4 -0
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +28 -3
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/parallel.d.ts +14 -0
- package/dist/tools/parallel.d.ts.map +1 -0
- package/dist/tools/parallel.js +20 -0
- package/dist/tools/parallel.js.map +1 -0
- package/dist/tools/tool.d.ts +7 -0
- package/dist/tools/tool.d.ts.map +1 -1
- package/dist/tools/tool.js +7 -1
- package/dist/tools/tool.js.map +1 -1
- package/dist/tools/update-plan.d.ts +57 -0
- package/dist/tools/update-plan.d.ts.map +1 -0
- package/dist/tools/update-plan.js +99 -0
- package/dist/tools/update-plan.js.map +1 -0
- package/dist/tui/activity.d.ts +17 -0
- package/dist/tui/activity.d.ts.map +1 -0
- package/dist/tui/activity.js +188 -0
- package/dist/tui/activity.js.map +1 -0
- package/dist/tui/diff.d.ts +14 -0
- package/dist/tui/diff.d.ts.map +1 -0
- package/dist/tui/diff.js +57 -0
- package/dist/tui/diff.js.map +1 -0
- package/dist/tui/editor-input.d.ts +53 -0
- package/dist/tui/editor-input.d.ts.map +1 -0
- package/dist/tui/editor-input.js +353 -0
- package/dist/tui/editor-input.js.map +1 -0
- package/dist/tui/editor.d.ts +72 -0
- package/dist/tui/editor.d.ts.map +1 -0
- package/dist/tui/editor.js +321 -0
- package/dist/tui/editor.js.map +1 -0
- package/dist/tui/git-diff.d.ts +7 -0
- package/dist/tui/git-diff.d.ts.map +1 -0
- package/dist/tui/git-diff.js +48 -0
- package/dist/tui/git-diff.js.map +1 -0
- package/dist/tui/input.d.ts +14 -0
- package/dist/tui/input.d.ts.map +1 -1
- package/dist/tui/input.js +77 -1
- package/dist/tui/input.js.map +1 -1
- package/dist/tui/live.d.ts +8 -0
- package/dist/tui/live.d.ts.map +1 -1
- package/dist/tui/live.js +37 -5
- package/dist/tui/live.js.map +1 -1
- package/dist/tui/markdown.d.ts +7 -0
- package/dist/tui/markdown.d.ts.map +1 -0
- package/dist/tui/markdown.js +55 -0
- package/dist/tui/markdown.js.map +1 -0
- package/dist/tui/menu.js +1 -1
- package/dist/tui/review.d.ts +27 -0
- package/dist/tui/review.d.ts.map +1 -0
- package/dist/tui/review.js +114 -0
- package/dist/tui/review.js.map +1 -0
- package/dist/tui/slash.d.ts +11 -0
- package/dist/tui/slash.d.ts.map +1 -0
- package/dist/tui/slash.js +13 -0
- package/dist/tui/slash.js.map +1 -0
- package/dist/tui/state.d.ts +5 -0
- package/dist/tui/state.d.ts.map +1 -1
- package/dist/tui/state.js +10 -1
- package/dist/tui/state.js.map +1 -1
- package/dist/tui/syntax.d.ts +6 -0
- package/dist/tui/syntax.d.ts.map +1 -0
- package/dist/tui/syntax.js +48 -0
- package/dist/tui/syntax.js.map +1 -0
- package/dist/tui/view.d.ts +1 -1
- package/dist/tui/view.d.ts.map +1 -1
- package/dist/tui/view.js +21 -9
- package/dist/tui/view.js.map +1 -1
- package/dist/tui/welcome.d.ts +5 -0
- package/dist/tui/welcome.d.ts.map +1 -1
- package/dist/tui/welcome.js +35 -20
- package/dist/tui/welcome.js.map +1 -1
- package/dist/workspace/patch.d.ts +11 -0
- package/dist/workspace/patch.d.ts.map +1 -0
- package/dist/workspace/patch.js +124 -0
- package/dist/workspace/patch.js.map +1 -0
- package/dist/workspace/types.d.ts +7 -0
- package/dist/workspace/types.d.ts.map +1 -1
- package/dist/workspace/types.js.map +1 -1
- package/dist/workspace.d.ts +2 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +5 -0
- package/dist/workspace.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,960 +1,189 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/chivgent-banner-dark.svg">
|
|
4
|
+
<img src="docs/assets/chivgent-banner-light.svg" alt="chivgent: terminal coding agent. Read-only by default, Docker shell, OpenAI-compatible." width="100%">
|
|
5
|
+
</picture>
|
|
6
|
+
</p>
|
|
7
|
+
|
|
1
8
|
# chivgent
|
|
2
9
|
|
|
3
10
|
[English](README.md) | [简体中文](README.zh-CN.md)
|
|
4
11
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-

|
|
8
|
-

|
|
12
|
+
[](https://www.npmjs.com/package/chivgent)
|
|
9
13
|
[](https://github.com/chivopic/chivgent/actions/workflows/ci.yml)
|
|
10
|
-
|
|
11
|
-

|
|
12
|
-
|
|
13
|
-
`chivgent` connects a Provider-independent agent loop to LLM APIs, tools, and a
|
|
14
|
-
workspace boundary. It can discover files, search source text, read bounded
|
|
15
|
-
file ranges, and optionally edit files or run commands. It works with OpenAI,
|
|
16
|
-
DeepSeek, and compatible Chat Completions endpoints.
|
|
17
|
-
|
|
18
|
-
The project is intentionally compact: it is designed to make the mechanics of
|
|
19
|
-
tool calling, conversation state, Provider adapters, and loop termination easy
|
|
20
|
-
to study before adding production-harness complexity.
|
|
21
|
-
|
|
22
|
-
## Features
|
|
23
|
-
|
|
24
|
-
- A real multi-turn agent loop: model -> tool call -> tool result -> model.
|
|
25
|
-
- Provider-independent runtime messages and tool contracts.
|
|
26
|
-
- A Provider registry: adding a Provider is a declaration, not a CLI change.
|
|
27
|
-
- API keys resolved from `--api-key`, the environment, then an optional file.
|
|
28
|
-
- OpenAI support through the Responses API.
|
|
29
|
-
- DeepSeek support through a reusable OpenAI-compatible Chat Completions client.
|
|
30
|
-
- Custom OpenAI-compatible endpoints through the TUI, CLI options, or environment.
|
|
31
|
-
- A rolling TUI with inline slash-command suggestions and a Provider picker.
|
|
32
|
-
- An interactive session with slash commands, or a single-shot question.
|
|
33
|
-
- Sessions that persist as JSON lines and can be resumed in a later process.
|
|
34
|
-
- A context manager that summarises old turns to stay inside the window.
|
|
35
|
-
- A `--json` event stream for scripting and other front ends.
|
|
36
|
-
- Streamed answers rendered from a typed runtime event stream.
|
|
37
|
-
- Interruptible runs: Ctrl+C stops the current answer; at an idle prompt it exits.
|
|
38
|
-
- Per-attempt Provider timeouts and bounded exponential-backoff retries.
|
|
39
|
-
- Deterministic project discovery through `list_files` and literal `search_text`.
|
|
40
|
-
- Ranged `read_file` output with continuation hints and bounded tool results.
|
|
41
|
-
- Read-only by default; `--allow-writes` adds `write_file` and `edit_file`.
|
|
42
|
-
- An opt-in `bash` tool behind `--allow-shell`, with streamed output.
|
|
43
|
-
- Token usage from the Provider, per turn, per run, per eval task.
|
|
44
|
-
- An eval suite that measures whether the agent completes tasks, not just runs.
|
|
45
|
-
- Remote sessions: one process holds a session, others attach over a local socket.
|
|
46
|
-
- Several clients can watch one session; any of them can interrupt the run.
|
|
47
|
-
- Extensions that add tools, slash commands, event handlers and prompt text.
|
|
48
|
-
- Project extensions gated by a per-directory trust decision, taken before any
|
|
49
|
-
module is imported and refused outright when there is no terminal to ask.
|
|
50
|
-
- Commands run in their own process group, so cancelling kills the whole tree.
|
|
51
|
-
- Command output is truncated from the tail; the full text goes to a temp file.
|
|
52
|
-
- Exact-match `edit_file` that refuses missing or ambiguous edits.
|
|
53
|
-
- Edits preserve the file's own byte order mark and CRLF line endings.
|
|
54
|
-
- Atomic writes: a crash mid-write leaves the original file intact.
|
|
55
|
-
- Safe workspace access with traversal and symlink-escape protection.
|
|
56
|
-
- Root `.gitignore`, generated-directory, and sensitive-path filtering.
|
|
57
|
-
- Tool argument validation, explicit tool errors, and a bounded turn limit.
|
|
58
|
-
- A packaged Node.js CLI with no framework dependency.
|
|
59
|
-
- Unit tests that do not spend API credits.
|
|
14
|
+

|
|
60
15
|
|
|
61
|
-
|
|
16
|
+
chivgent is a terminal coding agent you can read end to end. It asks an LLM
|
|
17
|
+
(OpenAI, DeepSeek, or any OpenAI-compatible endpoint) about the project in your
|
|
18
|
+
current directory, using read-only file tools by default. Writes and shell
|
|
19
|
+
commands are separate opt-in flags, and shell commands run in an offline Docker
|
|
20
|
+
container with per-command approval.
|
|
21
|
+
|
|
22
|
+
It is small on purpose: a TypeScript CLI with four runtime dependencies, where
|
|
23
|
+
the agent loop, Provider adapters, context compaction, and tool sandboxing are
|
|
24
|
+
each a few hundred lines you can study.
|
|
62
25
|
|
|
63
|
-
|
|
26
|
+
**Use it if** you want a coding agent whose every mechanism you can inspect and
|
|
27
|
+
change, or a reference implementation for building your own.
|
|
28
|
+
**Look elsewhere if** you want the most capable agent for daily work, a hosted
|
|
29
|
+
product, a fine-grained permission system, or native Windows shell support.
|
|
64
30
|
|
|
65
|
-
|
|
66
|
-
- npm
|
|
67
|
-
- An API key for OpenAI, DeepSeek, or a compatible Provider
|
|
31
|
+
## Quick start
|
|
68
32
|
|
|
69
|
-
|
|
33
|
+
You need Node.js 22 or newer and an API key for one Provider.
|
|
70
34
|
|
|
71
35
|
```bash
|
|
72
36
|
npm install -g chivgent
|
|
73
37
|
chivgent --version
|
|
74
38
|
```
|
|
75
39
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
chivgent update --check # Check without installing
|
|
80
|
-
chivgent update # Install the newer version, then restart chivgent
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
For older releases without this command, run `npm install -g chivgent@latest`
|
|
84
|
-
once. The updater checks only when requested. Source checkouts, local dependencies
|
|
85
|
-
and temporary `npx` runs receive instructions for their installation type.
|
|
86
|
-
See [updating chivgent](docs/updating.md) for details and troubleshooting.
|
|
87
|
-
|
|
88
|
-
To install the current source checkout instead:
|
|
89
|
-
|
|
90
|
-
```bash
|
|
91
|
-
git clone https://github.com/chivopic/chivgent.git
|
|
92
|
-
cd chivgent
|
|
93
|
-
npm install
|
|
94
|
-
npm run build
|
|
95
|
-
npm install -g .
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### Ask about a project
|
|
99
|
-
|
|
100
|
-
Run `chivgent` from the project you want it to inspect.
|
|
101
|
-
|
|
102
|
-
With OpenAI:
|
|
40
|
+
This prints the installed version, such as `1.0.0`. Then, from the project you want to ask
|
|
41
|
+
about:
|
|
103
42
|
|
|
104
43
|
```bash
|
|
105
44
|
export OPENAI_API_KEY="your-api-key"
|
|
106
|
-
chivgent "What does
|
|
45
|
+
chivgent "What does this project do, and where is its entry point?"
|
|
107
46
|
```
|
|
108
47
|
|
|
109
|
-
|
|
48
|
+
The answer streams to stdout. Tool activity (`list_files`, `search_text`,
|
|
49
|
+
`read_file`) is shown on stderr. Nothing in the project is changed.
|
|
50
|
+
|
|
51
|
+
Prefer DeepSeek or another Provider? Swap the key and add `--provider`:
|
|
110
52
|
|
|
111
53
|
```bash
|
|
112
54
|
export DEEPSEEK_API_KEY="your-api-key"
|
|
113
55
|
chivgent --provider deepseek "Explain the architecture in src/"
|
|
114
56
|
```
|
|
115
57
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
export OPENAI_API_KEY="your-provider-api-key"
|
|
120
|
-
export OPENAI_BASE_URL="https://api.vendor.example/v1"
|
|
121
|
-
export OPENAI_MODEL="vendor-model"
|
|
122
|
-
|
|
123
|
-
chivgent --provider openai-compatible "Explain the architecture in src/"
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
Start an interactive session by running `chivgent` with no question:
|
|
127
|
-
|
|
128
|
-
```bash
|
|
129
|
-
chivgent
|
|
130
|
-
› What does src/agent.ts do?
|
|
131
|
-
› Where is that loop tested?
|
|
132
|
-
› /exit
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
The conversation is kept across prompts, so follow-up questions do not repeat
|
|
136
|
-
the earlier context. Resume it later with `chivgent --continue` (or
|
|
137
|
-
`chivgent --resume <id>`; `chivgent --sessions` lists what is recorded).
|
|
138
|
-
|
|
139
|
-
Answers stream to stdout as the model produces them, so stdout stays pipeable.
|
|
140
|
-
Tool activity, retries, and run status go to stderr; Provider failures produce a
|
|
141
|
-
non-zero exit code. Use `--no-stream` for one final write, and `--quiet` to hide
|
|
142
|
-
tool activity.
|
|
143
|
-
|
|
144
|
-
## CLI reference
|
|
145
|
-
|
|
146
|
-
```text
|
|
147
|
-
chivgent [options] "question" Answer one question and exit
|
|
148
|
-
chivgent [options] Start an interactive session
|
|
149
|
-
|
|
150
|
-
Options:
|
|
151
|
-
--provider NAME openai, deepseek, openai-compatible, openrouter, groq, xai,
|
|
152
|
-
moonshot (default: openai)
|
|
153
|
-
--model MODEL Provider model override
|
|
154
|
-
--max-turns N Tool-calling turn limit (default: 8, 16 with --allow-writes
|
|
155
|
-
or --allow-shell)
|
|
156
|
-
--no-stream Wait for the full answer instead of streaming tokens
|
|
157
|
-
-q, --quiet Hide tool activity on stderr
|
|
158
|
-
--json Write the run as JSON lines instead of rendered text
|
|
159
|
-
-c, --continue Resume the most recent session for this workspace
|
|
160
|
-
--resume ID Resume a specific session
|
|
161
|
-
--api-key KEY API key for this run; prefer an environment variable
|
|
162
|
-
--sessions List recorded sessions and exit
|
|
163
|
-
--allow-writes Let the agent create and change files (default: read-only)
|
|
164
|
-
--allow-shell Let the agent run shell commands. This implies write access:
|
|
165
|
-
a shell is not bound by the workspace. Unix only.
|
|
166
|
-
--serve Expose this session on a local socket and keep running
|
|
167
|
-
--connect TARGET Attach to a served session, by id or socket path
|
|
168
|
-
--servers List the servers still answering, then exit
|
|
169
|
-
--no-extensions Do not load any extension, and do not ask about trust
|
|
170
|
-
--extensions List loaded extensions and what they register, then exit
|
|
171
|
-
--forget-trust Forget the trust decision covering this workspace, then exit
|
|
172
|
-
--context-window N Token budget for the context (default: 128000)
|
|
173
|
-
--no-compaction Send the whole transcript instead of summarising old turns
|
|
174
|
-
--no-session Do not record this run
|
|
175
|
-
-h, --help Show help
|
|
176
|
-
-v, --version Show version
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
In an interactive session, `/help` lists the slash commands, including
|
|
180
|
-
`/provider`, `/model`, `/endpoint`, `/login`, `/session`, `/tools`, `/clear`, and `/exit`.
|
|
181
|
-
In the TUI, typing `/` opens an inline command menu; `/provider` opens a Provider picker
|
|
182
|
-
with Up/Down and Enter. Selecting a Provider without a saved key immediately asks for
|
|
183
|
-
its API key. Use `/login` later to replace a key. Ctrl+C stops a running answer;
|
|
184
|
-
at an idle prompt it exits with code 130. Ctrl+D also leaves the session.
|
|
185
|
-
|
|
186
|
-
Exit codes: `0` answered, `1` configuration or Provider failure, `2` turn limit
|
|
187
|
-
reached, `130` interrupted with Ctrl+C.
|
|
188
|
-
|
|
189
|
-
### Signing in
|
|
190
|
-
|
|
191
|
-
The quickest way in is to start chivgent and let it ask:
|
|
192
|
-
|
|
193
|
-
```text
|
|
194
|
-
$ chivgent --tui
|
|
195
|
-
v1.0.0
|
|
196
|
-
openai / gpt-5.6
|
|
197
|
-
Start with /provider → choose a Provider
|
|
198
|
-
› /provider deepseek
|
|
199
|
-
Selected deepseek / deepseek-v4-flash. API key needed for deepseek.
|
|
200
|
-
API key for deepseek (hidden, saved locally; Enter skips):
|
|
201
|
-
Stored the key for deepseek. Ready for your next prompt.
|
|
202
|
-
›
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
An interactive session starts even without a key. Choose a Provider in the TUI
|
|
206
|
-
and enter its key when asked; the next prompt can use it immediately. An already
|
|
207
|
-
configured Provider skips this step. `/login` remains available to replace a key.
|
|
208
|
-
|
|
209
|
-
The key is not echoed as you type, and `auth.json` is written owner-only. It is
|
|
210
|
-
not verified against the Provider when stored; the first prompt after it is what
|
|
211
|
-
proves it works.
|
|
212
|
-
|
|
213
|
-
A non-interactive run still fails fast when no key is configured: a pipeline has
|
|
214
|
-
nobody to ask.
|
|
215
|
-
|
|
216
|
-
You can switch Providers and models without restarting or losing history:
|
|
217
|
-
|
|
218
|
-
```text
|
|
219
|
-
› /provider
|
|
220
|
-
› /provider deepseek
|
|
221
|
-
› /model deepseek-v4-pro
|
|
222
|
-
› /provider openrouter
|
|
223
|
-
› /model anthropic/claude-sonnet-4
|
|
224
|
-
› /provider openai-compatible
|
|
225
|
-
› /model vendor-model
|
|
226
|
-
› /endpoint https://api.vendor.example/v1
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
In the TUI, a newly selected Provider asks for its key if none is configured.
|
|
230
|
-
For Providers that also need a model or endpoint, the key can be stored first.
|
|
231
|
-
|
|
232
|
-
`--api-key` remains scoped to the Provider selected at startup; it is never
|
|
233
|
-
reused for a different Provider.
|
|
234
|
-
Switching from OpenAI to a custom compatible endpoint also does not reuse
|
|
235
|
-
`OPENAI_API_KEY`; the TUI asks for that endpoint's own key.
|
|
236
|
-
|
|
237
|
-
### Provider configuration
|
|
238
|
-
|
|
239
|
-
| Provider | API key | Model environment variable | Default model | API style |
|
|
240
|
-
| --- | --- | --- | --- | --- |
|
|
241
|
-
| OpenAI | `OPENAI_API_KEY` | `OPENAI_MODEL` | `gpt-5.6` | Responses API |
|
|
242
|
-
| DeepSeek | `DEEPSEEK_API_KEY` | `DEEPSEEK_MODEL` | `deepseek-v4-flash` | OpenAI-compatible Chat Completions |
|
|
243
|
-
| Custom compatible | `OPENAI_API_KEY` | `OPENAI_MODEL` | Required | OpenAI-compatible Chat Completions |
|
|
244
|
-
| OpenRouter | `OPENROUTER_API_KEY` | `OPENROUTER_MODEL` | Required | OpenAI-compatible Chat Completions |
|
|
245
|
-
| Groq | `GROQ_API_KEY` | `GROQ_MODEL` | Required | OpenAI-compatible Chat Completions |
|
|
246
|
-
| xAI | `XAI_API_KEY` | `XAI_MODEL` | Required | OpenAI-compatible Chat Completions |
|
|
247
|
-
| Moonshot | `MOONSHOT_API_KEY` | `MOONSHOT_MODEL` | Required | OpenAI-compatible Chat Completions |
|
|
248
|
-
|
|
249
|
-
An explicit `--model` value takes precedence over the Provider-specific model
|
|
250
|
-
environment variable. Custom compatible Providers also require
|
|
251
|
-
`OPENAI_BASE_URL`. Sessions are written under `CHIVGENT_HOME` (default
|
|
252
|
-
`~/.chivgent`).
|
|
253
|
-
|
|
254
|
-
Providers are declared in a registry rather than branched on in the CLI, so
|
|
255
|
-
`--help` and `--provider` validation are generated from the same list that
|
|
256
|
-
creates the client.
|
|
257
|
-
|
|
258
|
-
#### API key resolution
|
|
259
|
-
|
|
260
|
-
Keys are resolved in this order, first match wins:
|
|
261
|
-
|
|
262
|
-
1. `--api-key` for a single run
|
|
263
|
-
2. the Provider's environment variable
|
|
264
|
-
3. `<CHIVGENT_HOME>/auth.json`
|
|
58
|
+
Run `chivgent` with no question for an interactive session. If no key is set,
|
|
59
|
+
the session still starts: type `/provider`, pick one, and enter its key when
|
|
60
|
+
asked. Keys are saved owner-only in `~/.chivgent/auth.json`.
|
|
265
61
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
without editing anything.
|
|
62
|
+
**Try it without a key or network:** from a source checkout, `npm run demo:tui`
|
|
63
|
+
runs the TUI against a stub Provider and does not touch the workspace.
|
|
269
64
|
|
|
270
|
-
|
|
65
|
+
## What it can do, and what it needs from you
|
|
271
66
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
Neither `$VAR` expansion nor `!command` substitution is supported: letting a
|
|
280
|
-
config file spawn a process is a large attack surface for a small convenience.
|
|
281
|
-
chivgent warns when the file is readable by other users; keep it at `chmod 600`.
|
|
282
|
-
|
|
283
|
-
```bash
|
|
284
|
-
chivgent --provider openai --model gpt-5.6 "Explain package.json"
|
|
285
|
-
chivgent --provider deepseek --model deepseek-v4-pro "Explain package.json"
|
|
286
|
-
chivgent --provider openai-compatible --model vendor-model "Explain package.json"
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
## Architecture
|
|
290
|
-
|
|
291
|
-
```text
|
|
292
|
-
+-> OpenAI Responses API
|
|
293
|
-
User -> CLI -> Agent -> LLMClient |
|
|
294
|
-
| +-> OpenAI-compatible Chat -> DeepSeek / custom
|
|
295
|
-
|
|
|
296
|
-
+-> Tool Registry -> list_files / search_text / read_file -> Workspace
|
|
297
|
-
| write_file / edit_file (--allow-writes)
|
|
298
|
-
| bash (--allow-shell) -> ShellOperations
|
|
299
|
-
```
|
|
67
|
+
| Capability | Flag | What it can touch |
|
|
68
|
+
| --- | --- | --- |
|
|
69
|
+
| Discover, search, and read files | default | The current directory only. `.gitignore`d, generated, and credential paths (such as `.env`, `id_rsa`) are skipped. |
|
|
70
|
+
| Create and edit files (`write_file`, `edit_file`, `apply_patch`) | `--allow-writes` | Files inside the workspace. Edits are exact-match and atomic. |
|
|
71
|
+
| Run shell commands (`bash`) | `--allow-shell` | A fresh `node:22-alpine` Docker container with no network, the workspace mounted at `/workspace`, and no API keys. Each command needs a `y` to run. Docker is required; there is no fallback to the host. |
|
|
72
|
+
| Load project extensions | trust prompt on first run | Anything you can: extensions are in-process JavaScript. |
|
|
300
73
|
|
|
301
|
-
|
|
302
|
-
|
|
74
|
+
Costs: chivgent calls the Provider you choose with your key, so you pay that
|
|
75
|
+
Provider's normal token prices. It reports token counts per turn and per
|
|
76
|
+
session (`/session`) but never converts them to money.
|
|
303
77
|
|
|
304
|
-
|
|
305
|
-
Agent Message[] -> Provider adapter -> Provider request
|
|
306
|
-
<- Provider response
|
|
307
|
-
AssistantMessage <- normalized result
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
This prevents the Agent, tools, and CLI from depending on one vendor's message
|
|
311
|
-
format.
|
|
312
|
-
|
|
313
|
-
### Context management
|
|
314
|
-
|
|
315
|
-
A session records everything that happened. The context manager decides what
|
|
316
|
-
is worth sending for one request. Keeping those apart is what lets a long
|
|
317
|
-
session stay inside a fixed context window.
|
|
318
|
-
|
|
319
|
-
```text
|
|
320
|
-
Full transcript ──────────────→ Session store (what happened)
|
|
321
|
-
│
|
|
322
|
-
↓
|
|
323
|
-
ContextManager
|
|
324
|
-
│ token estimate vs. contextWindow - reserveTokens
|
|
325
|
-
↓
|
|
326
|
-
summary + recent messages ────→ Provider (what the model sees)
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
When the estimate exceeds the budget, older messages are summarised into a
|
|
330
|
-
single message and recent turns are kept verbatim. Three details matter:
|
|
331
|
-
|
|
332
|
-
- **File lists are derived from tool calls, not from the summary.** A summary
|
|
333
|
-
can forget or invent a path. For a coding agent, "which files did I read and
|
|
334
|
-
change" is the part that most needs to survive intact, so it is collected
|
|
335
|
-
from the `read_file`, `write_file`, and `edit_file` calls themselves.
|
|
336
|
-
- **Tool calls are never separated from their results.** A split point that
|
|
337
|
-
would orphan a tool result moves forward past the whole group.
|
|
338
|
-
- **Compaction discards the Provider continuation.** A Provider that chains
|
|
339
|
-
history server-side replays its own copy and ignores the messages sent with
|
|
340
|
-
it, so keeping the continuation would send back the history just removed.
|
|
341
|
-
|
|
342
|
-
Token counts are estimated from character length rather than with a real
|
|
343
|
-
tokenizer, which would be model-specific and a large dependency for a number
|
|
344
|
-
that only decides *when* to compact. The reserve budget absorbs the error.
|
|
345
|
-
|
|
346
|
-
A single tool result larger than the whole budget cannot be compacted away;
|
|
347
|
-
bound tool output instead. Compaction is disabled with `--no-compaction`.
|
|
348
|
-
|
|
349
|
-
### Runtime events
|
|
350
|
-
|
|
351
|
-
The Agent Loop reports what it is doing through a typed event stream instead of
|
|
352
|
-
printing anything itself. One run emits:
|
|
353
|
-
|
|
354
|
-
```text
|
|
355
|
-
agent_start
|
|
356
|
-
turn_start -> message_start -> message_update* -> message_end
|
|
357
|
-
tool_execution_start -> tool_execution_end (once per tool call)
|
|
358
|
-
turn_end
|
|
359
|
-
...
|
|
360
|
-
agent_end (completed | max_turns | aborted | error)
|
|
361
|
-
```
|
|
78
|
+
## Providers
|
|
362
79
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
80
|
+
| Provider | `--provider` | API key variable | Default model |
|
|
81
|
+
| --- | --- | --- | --- |
|
|
82
|
+
| OpenAI (Responses API) | `openai` (default) | `OPENAI_API_KEY` | `gpt-5.6` |
|
|
83
|
+
| DeepSeek | `deepseek` | `DEEPSEEK_API_KEY` | `deepseek-v4-flash` |
|
|
84
|
+
| Any OpenAI-compatible endpoint | `openai-compatible` | `OPENAI_API_KEY` + `OPENAI_BASE_URL` | set `OPENAI_MODEL` |
|
|
85
|
+
| OpenRouter | `openrouter` | `OPENROUTER_API_KEY` | set `OPENROUTER_MODEL` |
|
|
86
|
+
| Groq | `groq` | `GROQ_API_KEY` | set `GROQ_MODEL` |
|
|
87
|
+
| xAI | `xai` | `XAI_API_KEY` | set `XAI_MODEL` |
|
|
88
|
+
| Moonshot | `moonshot` | `MOONSHOT_API_KEY` | set `MOONSHOT_MODEL` |
|
|
368
89
|
|
|
369
|
-
`
|
|
370
|
-
|
|
90
|
+
`--model` overrides the model variable. Keys resolve in this order: `--api-key`,
|
|
91
|
+
then the environment variable, then `~/.chivgent/auth.json` (literal keys only;
|
|
92
|
+
no `$VAR` expansion or command substitution). A custom `OPENAI_BASE_URL`
|
|
93
|
+
receives your key and prompts, so point it only at endpoints you trust.
|
|
371
94
|
|
|
372
|
-
|
|
95
|
+
Compatible endpoints must implement `POST /chat/completions` with function tool
|
|
96
|
+
calling. Changing only the base URL does not guarantee full compatibility; see
|
|
97
|
+
[architecture](docs/architecture.md#openai-compatible-providers).
|
|
373
98
|
|
|
374
|
-
|
|
375
|
-
`baseURL`, credentials, and model. CLI users do not need to edit code:
|
|
99
|
+
## Everyday use
|
|
376
100
|
|
|
377
101
|
```bash
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
102
|
+
chivgent # interactive session
|
|
103
|
+
chivgent --continue # resume the latest session here
|
|
104
|
+
chivgent --allow-writes "Rename sayHi to greet everywhere"
|
|
105
|
+
chivgent --allow-shell "Run the tests and explain the failure"
|
|
106
|
+
chivgent --json "List the CLI flags" > run.jsonl # machine-readable events
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
- **Interactive sessions** keep history across prompts and are saved as JSON
|
|
110
|
+
lines under `~/.chivgent/sessions` (`--no-session` to skip). `/help` lists
|
|
111
|
+
slash commands such as `/provider`, `/model`, `/login`, `/session`, `/diff`,
|
|
112
|
+
and `/exit`. Ctrl+C stops a running answer; at an idle prompt it exits.
|
|
113
|
+
- **`--tui`** adds a live status region and a multi-line editor with history,
|
|
114
|
+
an inline `/` command menu, and `/gitdiff` review. See the
|
|
115
|
+
[TUI guide](docs/tui-v5.md).
|
|
116
|
+
- **Long sessions** stay inside the context window: older turns are summarised,
|
|
117
|
+
while the list of files read and changed is kept from the tool calls
|
|
118
|
+
themselves. See [architecture](docs/architecture.md#context-management).
|
|
119
|
+
- **Exit codes:** `0` answered, `1` configuration or Provider failure, `2` turn
|
|
120
|
+
limit reached, `130` interrupted.
|
|
121
|
+
- **Updating:** `chivgent update --check`, then `chivgent update`. See
|
|
122
|
+
[updating](docs/updating.md).
|
|
123
|
+
|
|
124
|
+
`chivgent --help` prints every flag.
|
|
381
125
|
|
|
382
|
-
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
`OPENAI_BASE_URL` must point to the Provider's OpenAI-compatible API root. The
|
|
386
|
-
Provider must implement `POST /chat/completions` and function tool calling.
|
|
387
|
-
|
|
388
|
-
When adding a named Provider in source code, use the same adapter:
|
|
389
|
-
|
|
390
|
-
```ts
|
|
391
|
-
const client = new OpenAICompatibleChatClient({
|
|
392
|
-
apiKey: process.env.VENDOR_API_KEY!,
|
|
393
|
-
baseURL: "https://api.vendor.example/v1",
|
|
394
|
-
model: "vendor-model",
|
|
395
|
-
continuationTag: "vendor-chat",
|
|
396
|
-
});
|
|
397
|
-
```
|
|
126
|
+
## Security model
|
|
398
127
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
list.ts Directory walking
|
|
440
|
-
search.ts Literal text search
|
|
441
|
-
write.ts Atomic whole-file writes and exact edits
|
|
442
|
-
providers/
|
|
443
|
-
registry.ts Provider registry
|
|
444
|
-
usage.ts Reading and adding up Provider token counts
|
|
445
|
-
deferred-client.ts A client whose Provider can arrive later
|
|
446
|
-
definitions.ts Built-in Provider declarations
|
|
447
|
-
client.ts Credential resolution into an LLM client
|
|
448
|
-
openai.ts OpenAI Responses adapter
|
|
449
|
-
openai-compatible-chat.ts Shared Chat Completions adapter
|
|
450
|
-
deepseek.ts DeepSeek configuration wrapper
|
|
451
|
-
tools/
|
|
452
|
-
tool.ts Tool contract
|
|
453
|
-
output.ts Shared 64 KiB tool-output boundary
|
|
454
|
-
list-files.ts Deterministic project-tree discovery
|
|
455
|
-
search-text.ts Bounded literal source search
|
|
456
|
-
read-file.ts Ranged text-file reader
|
|
457
|
-
write-file.ts Whole-file create and replace
|
|
458
|
-
edit-file.ts Exact unique-match edit
|
|
459
|
-
bash.ts Shell command execution
|
|
460
|
-
prompts.ts The instructions the agent runs under
|
|
461
|
-
evals/
|
|
462
|
-
task.ts Task definitions and their validation
|
|
463
|
-
fixture.ts A fresh workspace copy per attempt
|
|
464
|
-
graders.ts Deterministic graders
|
|
465
|
-
runner.ts Runs a task N times and aggregates
|
|
466
|
-
report.ts Table and JSON output
|
|
467
|
-
parse-args.ts Eval CLI arguments
|
|
468
|
-
cli.ts npm run eval
|
|
469
|
-
remote/
|
|
470
|
-
protocol.ts Message shapes and version negotiation
|
|
471
|
-
framing.ts JSON Lines framing with a bounded buffer
|
|
472
|
-
server.ts Serves one session on a Unix socket
|
|
473
|
-
client.ts Attaches to a served session
|
|
474
|
-
repl.ts The interactive loop for an attached client
|
|
475
|
-
socket-path.ts Socket paths, staleness, and discovery
|
|
476
|
-
extensions/
|
|
477
|
-
trust.ts Per-directory trust store
|
|
478
|
-
decide-trust.ts The prompt, and the refusal when nobody can answer
|
|
479
|
-
discover.ts Where extensions live and which files count
|
|
480
|
-
api.ts What an extension may register
|
|
481
|
-
registry.ts Collects registrations, rejects conflicts
|
|
482
|
-
loader.ts Imports modules and contains their failures
|
|
483
|
-
shell/
|
|
484
|
-
types.ts Execution contract and shell error types
|
|
485
|
-
config.ts Shell resolution and the Windows guard
|
|
486
|
-
local.ts Local spawn backend
|
|
487
|
-
process.ts Process-group kill and exit handling
|
|
488
|
-
output.ts Bounded streaming output accumulator
|
|
489
|
-
truncate.ts Tail truncation for command output
|
|
490
|
-
sanitize.ts Control-character filtering
|
|
491
|
-
tests/ Provider, loop, and workspace tests
|
|
492
|
-
evals/ Eval tasks and their fixtures
|
|
493
|
-
docs/ Architecture and learning notes
|
|
494
|
-
```
|
|
128
|
+
chivgent states its boundaries rather than pretending to enforce fine-grained
|
|
129
|
+
ones. Capabilities are coarse, session-level switches.
|
|
130
|
+
|
|
131
|
+
- File tools reject paths outside the workspace, including `..` traversal and
|
|
132
|
+
symlink escapes, and deny common credential and private-key paths. Tool output
|
|
133
|
+
is capped at 64 KiB.
|
|
134
|
+
- `--allow-shell` runs each command in a new offline container (dropped
|
|
135
|
+
capabilities, read-only root, non-root user, resource limits) and asks before
|
|
136
|
+
each one. Headless and served sessions deny commands unless you also pass
|
|
137
|
+
`--approve-all-shell`. Inside the container, commands can still change or
|
|
138
|
+
delete anything in the mounted project, and Docker is not proof against
|
|
139
|
+
kernel or VM escapes. Details: [shell sandbox](docs/shell-sandbox.md).
|
|
140
|
+
- Writes and trusted extensions are **not** inside the container. Trusting a
|
|
141
|
+
project's extensions means letting its authors run code as you, so chivgent
|
|
142
|
+
asks once per directory and never loads them when there is no terminal to ask.
|
|
143
|
+
- `auth.json`, session logs, and `trust.json` are written owner-only but in
|
|
144
|
+
plain text. Session logs contain prompts, answers, and file excerpts.
|
|
145
|
+
- `--serve` exposes a session on a Unix socket in an owner-only directory.
|
|
146
|
+
Anyone who can open the socket gets every capability the server started with.
|
|
147
|
+
There is no TCP listener; use SSH forwarding for another machine.
|
|
148
|
+
|
|
149
|
+
Work on committed code, and use a disposable copy for repositories you don't
|
|
150
|
+
trust.
|
|
151
|
+
|
|
152
|
+
## Going deeper
|
|
153
|
+
|
|
154
|
+
- [Architecture](docs/architecture.md): agent loop, runtime events, context
|
|
155
|
+
compaction, Provider adapters, the TUI live region, and the source layout.
|
|
156
|
+
- [Evals and token usage](docs/evals.md): ten deterministic task suites that
|
|
157
|
+
measure pass rates over repeated attempts, separate from the free, offline
|
|
158
|
+
unit tests.
|
|
159
|
+
- [Extensions and remote sessions](docs/extensions-and-remote.md): adding
|
|
160
|
+
tools and slash commands, project trust, and attaching several terminals to
|
|
161
|
+
one session.
|
|
162
|
+
- [Patch format](docs/apply-patch.md), [scoped `AGENTS.md`](docs/scoped-agents-md.md),
|
|
163
|
+
[parallel read tools](docs/parallel-read-tools.md),
|
|
164
|
+
[plan tracking](docs/agent-plan-progress.md).
|
|
165
|
+
- Design notes for each build stage, from
|
|
166
|
+
[stage 1](docs/stage-1-minimal-agent.md) to [stage 14](docs/stage-14-tui.md),
|
|
167
|
+
and the [release process](docs/releasing.md).
|
|
495
168
|
|
|
496
169
|
## Development
|
|
497
170
|
|
|
498
171
|
```bash
|
|
172
|
+
git clone https://github.com/chivopic/chivgent.git
|
|
173
|
+
cd chivgent
|
|
499
174
|
npm install
|
|
500
175
|
npm run check
|
|
501
176
|
npm test
|
|
502
177
|
npm run build
|
|
503
178
|
```
|
|
504
179
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
npm run release:check
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
Build a locally installable tarball:
|
|
512
|
-
|
|
513
|
-
```bash
|
|
514
|
-
npm pack
|
|
515
|
-
npm install -g ./chivgent-0.6.0.tgz
|
|
516
|
-
```
|
|
517
|
-
|
|
518
|
-
Tests use scripted or mocked LLM clients. A real API smoke test is deliberately
|
|
519
|
-
manual so the default test suite never consumes credits.
|
|
520
|
-
|
|
521
|
-
## Evals
|
|
522
|
-
|
|
523
|
-
The 300-odd unit tests check that the code runs the way it was written. They say
|
|
524
|
-
nothing about whether this is a good agent. `npm run eval` answers that:
|
|
525
|
-
|
|
526
|
-
```bash
|
|
527
|
-
npm run eval # every task in evals/
|
|
528
|
-
npm run eval -- --task rename-symbol --attempts 10
|
|
529
|
-
npm run eval -- --allow-writes --allow-shell --json report.json
|
|
530
|
-
DEEPSEEK_API_KEY=sk-... npm run eval -- --provider deepseek
|
|
531
|
-
```
|
|
532
|
-
|
|
533
|
-
`--api-key` works too, but prefer the environment variable here: npm echoes the
|
|
534
|
-
fully expanded command before running it, so a key on the command line lands in
|
|
535
|
-
the clear in any captured log.
|
|
536
|
-
|
|
537
|
-
```text
|
|
538
|
-
task pass turns tokens tools p50
|
|
539
|
-
find-auth-logic 4/5 2.0 3.4k list_files,search_text,... 6.1s
|
|
540
|
-
rename-symbol 3/5 5.4 12.1k read_file,edit_file 9.8s
|
|
541
|
-
|
|
542
|
-
overall 7/10 (70%) 58.3k tokens
|
|
543
|
-
|
|
544
|
-
rename-symbol attempt 2,5: not-used-tool name="write_file" — called write_file 1 time(s)
|
|
545
|
-
```
|
|
546
|
-
|
|
547
|
-
The `tokens` column is what the Provider reported, so a change can be judged on
|
|
548
|
-
both axes at once: a prompt that lifts the pass rate from 70% to 75% while
|
|
549
|
-
tripling the tokens is usually a bad trade, and without the second number that
|
|
550
|
-
looks like a pure win.
|
|
551
|
-
|
|
552
|
-
**An eval is not a test.** A model is nondeterministic, so one run of a task is a
|
|
553
|
-
coin flip: passing and failing are properties of the model-and-harness
|
|
554
|
-
combination, not of that run. The unit is therefore a pass rate over N attempts,
|
|
555
|
-
and evals never gate CI — a merge gate that costs money and goes red six times in
|
|
556
|
-
ten trains everyone to ignore CI. `npm test` stays free, offline and
|
|
557
|
-
deterministic.
|
|
558
|
-
|
|
559
|
-
A task is a directory under `evals/`: a `task.json` and a `fixture/` tree copied
|
|
560
|
-
into a fresh temporary workspace for every attempt.
|
|
561
|
-
|
|
562
|
-
```jsonc
|
|
563
|
-
{
|
|
564
|
-
"prompt": "Rename the function sayHi to greet everywhere, including callers.",
|
|
565
|
-
"capabilities": ["writes"],
|
|
566
|
-
"attempts": 5,
|
|
567
|
-
"graders": [
|
|
568
|
-
{ "type": "file-contains", "path": "src/greet.ts", "text": "export function greet(" },
|
|
569
|
-
{ "type": "file-excludes", "path": "src/greet.ts", "text": "sayHi" },
|
|
570
|
-
{ "type": "used-tool", "name": "edit_file" },
|
|
571
|
-
{ "type": "not-used-tool", "name": "write_file" }
|
|
572
|
-
]
|
|
573
|
-
}
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
Graders are deterministic — file contents, tool calls, answer patterns, turn
|
|
577
|
-
counts. There is no LLM judge: a judge is itself a nondeterministic instrument,
|
|
578
|
-
and measuring a nondeterministic subject with one compounds the error until "the
|
|
579
|
-
agent got worse" cannot be told apart from "the judge felt different today".
|
|
580
|
-
|
|
581
|
-
Those last two graders are the point. Rewriting the whole file also produces the
|
|
582
|
-
right contents, and an eval that scores results alone would give it full marks.
|
|
583
|
-
For a coding agent, tool misuse is the more common and the more interesting
|
|
584
|
-
failure, so tasks assert on how the work was done.
|
|
585
|
-
|
|
586
|
-
A grader naming a tool the task never grants is refused when the task loads.
|
|
587
|
-
`not-used-tool: write_file` on a read-only task cannot fail — it asserts the
|
|
588
|
-
model did not call something it was never given — and its mirror image cannot
|
|
589
|
-
pass. Both are task-definition mistakes, and both are caught before the run
|
|
590
|
-
spends anything. One such grader shipped for two releases before this check
|
|
591
|
-
existed.
|
|
592
|
-
|
|
593
|
-
Three graders exist because a set of tool names cannot answer the question they
|
|
594
|
-
answer. `tool-never-failed` catches a guessed path, which shows up as a failed
|
|
595
|
-
read that a deduplicated name list hides — it is sound only where a failed call
|
|
596
|
-
means the model was wrong, so it fits `read_file` and not `bash`, which reports
|
|
597
|
-
any non-zero exit as an error and is *supposed* to fail on a task that runs a
|
|
598
|
-
failing test. `max-tool-calls` budgets calls rather than turns, because a model
|
|
599
|
-
may issue any number of calls in one turn: "did it follow the imports or read
|
|
600
|
-
the whole project" is a call count. `file-unchanged` compares against the
|
|
601
|
-
fixture, so "don't touch what you weren't asked to touch" becomes a verdict
|
|
602
|
-
rather than an intention — and it needs no second copy of the file in
|
|
603
|
-
`task.json` to drift out of date. The JSON report carries `toolCalls`, every
|
|
604
|
-
call in order with whether it succeeded, alongside the `toolsUsed` set the table
|
|
605
|
-
prints.
|
|
606
|
-
|
|
607
|
-
The nine tasks split into what the agent found and what it changed. Four exist
|
|
608
|
-
because the first real baseline passed 29 of 30 attempts and therefore measured
|
|
609
|
-
nothing: `needle-in-many-files` (40 files with uninformative names, so listing
|
|
610
|
-
gives no signal), `trace-the-default` (a value overridden twice, where stopping
|
|
611
|
-
at the first hop yields a plausible wrong number), `decoy-config` (two
|
|
612
|
-
near-identical modules, one of them dead), and `wrong-test` (a failing test
|
|
613
|
-
whose expectation contradicts the contract documented in the source — bending
|
|
614
|
-
the source to fit it is the trap).
|
|
615
|
-
|
|
616
|
-
A task that scores 0/N or N/N across different models is not measuring anything
|
|
617
|
-
and should be made harder, fixed, or dropped. Passing is not evidence of quality
|
|
618
|
-
when everything passes.
|
|
619
|
-
|
|
620
|
-
## Token usage
|
|
621
|
-
|
|
622
|
-
Providers report what each call cost, and chivgent now keeps it: per turn on
|
|
623
|
-
`message_end`, per run on `agent_end` and the run result, per attempt in the
|
|
624
|
-
eval report, and cumulatively in `/session`.
|
|
625
|
-
|
|
626
|
-
```text
|
|
627
|
-
› /session
|
|
628
|
-
id: 2026-09-07T...
|
|
629
|
-
workspace: /home/me/project
|
|
630
|
-
prompts: 4
|
|
631
|
-
messages: 18
|
|
632
|
-
tokens: 38.2k total, 35.9k in, 2.3k out, 12.0k cached
|
|
633
|
-
```
|
|
634
|
-
|
|
635
|
-
**Tokens only, never money.** Converting to a currency needs a model-to-price
|
|
636
|
-
table, and that table goes stale silently when a Provider changes its rates —
|
|
637
|
-
producing a figure that looks precise and is wrong, which is worse than no
|
|
638
|
-
figure because nobody questions a number with a decimal point in it. Token
|
|
639
|
-
counts are what the Provider itself reported and do not expire.
|
|
640
|
-
|
|
641
|
-
A total is marked incomplete when some call reported nothing, rather than
|
|
642
|
-
counting it as zero: a missing figure stays visibly missing instead of quietly
|
|
643
|
-
understating the total. Compaction's own summarising call is included, since it
|
|
644
|
-
trades a call now against smaller inputs later and that trade cannot be judged
|
|
645
|
-
with half of it hidden.
|
|
646
|
-
|
|
647
|
-
Streamed Chat Completions only report usage when asked, so chivgent sends
|
|
648
|
-
`stream_options: { include_usage: true }`. A self-hosted OpenAI-compatible
|
|
649
|
-
endpoint that predates that field may reject the request; `--no-stream` is the
|
|
650
|
-
fallback.
|
|
651
|
-
|
|
652
|
-
## The live region
|
|
653
|
-
|
|
654
|
-
`--tui` draws a status region above the prompt while a run is in progress: the
|
|
655
|
-
turn, whether the model is thinking or running tools, which tools are running
|
|
656
|
-
with what they are aimed at and their newest output, elapsed time, and the
|
|
657
|
-
running token count. Long single-line replies keep their newest text visible;
|
|
658
|
-
long paths retain the filename and tool progress. On narrow terminals, the
|
|
659
|
-
status and stop shortcut take priority.
|
|
660
|
-
|
|
661
|
-
```text
|
|
662
|
-
Let me look at how sessions are stored.
|
|
663
|
-
read_file src/session.ts (2s)
|
|
664
|
-
search_text (1s) src/store.ts:41: export class SessionStore
|
|
665
|
-
running tools · turn 2/8 · 11s · 4.2k tokens · ctrl+c to stop
|
|
666
|
-
```
|
|
667
|
-
|
|
668
|
-
It is the first consumer of the event stream that runs backwards. Every other
|
|
669
|
-
one — streamed output, the session log, extensions, the remote client — is
|
|
670
|
-
append-only: an event arrives, it is printed, it is forgotten. This one has to
|
|
671
|
-
rebuild *what is happening now* from the same stream and keep it right as more
|
|
672
|
-
arrives, which splits into three pieces:
|
|
673
|
-
|
|
674
|
-
```text
|
|
675
|
-
reduce(state, event, now) -> ViewState pure
|
|
676
|
-
view(state, {width, now}) -> string[] pure
|
|
677
|
-
paint(previous, next) the only part that touches a terminal
|
|
678
|
-
```
|
|
679
|
-
|
|
680
|
-
Both pure pieces are tested without a terminal, which is where the real
|
|
681
|
-
guarantees live. One asymmetry in the event vocabulary is what makes the state
|
|
682
|
-
model load-bearing: `message_update` carries a **delta**, `tool_execution_update`
|
|
683
|
-
carries a truncated **snapshot**. Appending a snapshot stacks output that was
|
|
684
|
-
never produced — the window has already had its front dropped — and replacing on
|
|
685
|
-
a delta leaves the last token and loses the answer. An append-only renderer
|
|
686
|
-
never has to know the difference, because it just prints whatever arrives. Two
|
|
687
|
-
tests fail when either is written the wrong way round.
|
|
688
|
-
|
|
689
|
-
It does not take the alternate screen buffer. That would cost the scrollback,
|
|
690
|
-
and getting it back means reimplementing scrolling, search and selection —
|
|
691
|
-
thousands of lines that teach terminal graphics rather than agent architecture.
|
|
692
|
-
A finished turn is flushed to the terminal's own scrollback and dropped from
|
|
693
|
-
the state, so a long session never grows it. Input stays with readline for the
|
|
694
|
-
same reason: a line editor is not the interesting problem here.
|
|
695
|
-
|
|
696
|
-
Painting rewrites only the lines whose content changed, so a ticking timer
|
|
697
|
-
costs one line rather than a repaint. A resize invalidates that baseline — the
|
|
698
|
-
terminal has reflowed what is on screen — and is the one moment a full repaint
|
|
699
|
-
is correct rather than lazy.
|
|
700
|
-
|
|
701
|
-
`--tui` needs a terminal on stdin and stderr and an interactive session; it
|
|
702
|
-
refuses rather than falling back silently, since a switch that quietly does
|
|
703
|
-
nothing makes a mistyped command look like it worked. It is not the default
|
|
704
|
-
yet. The startup wordmark shows the Provider, model, workspace and next action;
|
|
705
|
-
use `/session` for the session id. Type `/` to open the inline command menu
|
|
706
|
-
(including extension commands); Up/Down selects, Tab completes, and Enter runs.
|
|
707
|
-
Outside a menu, Up/Down browses input history. During a run ordinary typing is ignored; Ctrl+C cancels immediately.
|
|
708
|
-
At an idle prompt Ctrl+C exits, clearing the draft and returning the terminal
|
|
709
|
-
to the shell. Ctrl+D on an empty line also exits.
|
|
710
|
-
Input is single-line: only the first submitted line in an input batch is accepted.
|
|
711
|
-
Wait for the prompt before sending another question.
|
|
712
|
-
After cancellation the prompt is ready again and partial text stays in scrollback.
|
|
713
|
-
A completion line records the outcome, elapsed time and reported token usage.
|
|
714
|
-
|
|
715
|
-
Chinese, combining characters and joined emoji are clipped by terminal columns;
|
|
716
|
-
short terminals keep the status visible. Resizing repaints the existing region.
|
|
717
|
-
All prompts and UI output go to stderr; stdout receives final answers only, so
|
|
718
|
-
`chivgent --tui > answers.txt` keeps the terminal interactive while saving answers.
|
|
719
|
-
|
|
720
|
-
```sh
|
|
721
|
-
npm run build
|
|
722
|
-
node dist/cli.js --tui
|
|
723
|
-
npm run demo:tui # Offline interactive demo; no API key or network required
|
|
724
|
-
```
|
|
725
|
-
|
|
726
|
-
The demo uses a stub Provider and simulated tool progress; it does not modify the
|
|
727
|
-
workspace. See [the TUI guide](docs/tui.md) for controls and verification.
|
|
728
|
-
|
|
729
|
-
## Remote sessions
|
|
730
|
-
|
|
731
|
-
A session normally lives and dies with the terminal that started it. `--serve`
|
|
732
|
-
keeps it in one process and lets others attach:
|
|
733
|
-
|
|
734
|
-
```bash
|
|
735
|
-
# One terminal
|
|
736
|
-
chivgent --serve --allow-writes
|
|
737
|
-
# chivgent 0.12.0 serving session 2026-09-06T...
|
|
738
|
-
# socket: ~/.chivgent/sockets/2026-09-06T....sock
|
|
739
|
-
# capabilities: --allow-writes
|
|
740
|
-
|
|
741
|
-
# Another terminal
|
|
742
|
-
chivgent --servers # list what is running
|
|
743
|
-
chivgent --connect <id> # attach interactively
|
|
744
|
-
chivgent --connect <id> "question" # ask once and leave
|
|
745
|
-
```
|
|
746
|
-
|
|
747
|
-
A client holds no state: it sends prompts and renders the event stream the
|
|
748
|
-
server broadcasts. Several clients can attach at once — the extra ones watch the
|
|
749
|
-
same run as it happens, and any of them can interrupt it, because the run
|
|
750
|
-
belongs to the session rather than to whoever started it. A prompt sent while
|
|
751
|
-
another is running is refused rather than queued, and a client that disconnects
|
|
752
|
-
mid-run does not cancel it.
|
|
753
|
-
|
|
754
|
-
Attaching does not replay history; the session log already holds that. The
|
|
755
|
-
protocol is JSON Lines over a Unix socket, one object per line, the same shape
|
|
756
|
-
`--json` writes.
|
|
757
|
-
|
|
758
|
-
**Whoever can reach the socket has everything the server was started with.** If
|
|
759
|
-
it was started with `--allow-shell`, anyone who can connect can make the model
|
|
760
|
-
run commands, which is why the server prints its capabilities on startup: the
|
|
761
|
-
person attaching cannot see the flags you typed. The boundary is filesystem
|
|
762
|
-
permissions — sockets live in an owner-only directory under `CHIVGENT_HOME` —
|
|
763
|
-
and there is no TCP listener. For another machine, forward the socket over SSH
|
|
764
|
-
so that authentication is SSH's job.
|
|
765
|
-
|
|
766
|
-
## Extensions
|
|
767
|
-
|
|
768
|
-
An extension is an ES module that default-exports a function. It is called once
|
|
769
|
-
at startup with an API that can add a tool, add a slash command, subscribe to
|
|
770
|
-
runtime events, and append to the system prompt.
|
|
771
|
-
|
|
772
|
-
```js
|
|
773
|
-
// .chivgent/extensions/word-count.js
|
|
774
|
-
export default function (api) {
|
|
775
|
-
api.registerTool({
|
|
776
|
-
name: "word_count",
|
|
777
|
-
description: "Count the words in a workspace file.",
|
|
778
|
-
inputSchema: {
|
|
779
|
-
type: "object",
|
|
780
|
-
properties: { path: { type: "string" } },
|
|
781
|
-
required: ["path"],
|
|
782
|
-
additionalProperties: false,
|
|
783
|
-
},
|
|
784
|
-
async execute(args, context) {
|
|
785
|
-
const file = await context.workspace.readTextFile(args.path);
|
|
786
|
-
return {
|
|
787
|
-
content: `${file.content.split(/\s+/).filter(Boolean).length} words`,
|
|
788
|
-
isError: false,
|
|
789
|
-
};
|
|
790
|
-
},
|
|
791
|
-
});
|
|
792
|
-
|
|
793
|
-
api.contributeSystemPrompt("Use word_count instead of reading a file to count words.");
|
|
794
|
-
}
|
|
795
|
-
```
|
|
796
|
-
|
|
797
|
-
Extensions are loaded from two places:
|
|
798
|
-
|
|
799
|
-
| Location | Loaded |
|
|
800
|
-
| --- | --- |
|
|
801
|
-
| `<CHIVGENT_HOME>/extensions/` | Always: it is your own machine's configuration |
|
|
802
|
-
| `<workspace>/.chivgent/extensions/` | Only after you trust the project |
|
|
803
|
-
|
|
804
|
-
Both accept `name.js` and `name/index.js`; nesting stops there. Plain JavaScript
|
|
805
|
-
only — compiling TypeScript yourself is cheaper than making chivgent carry a
|
|
806
|
-
TypeScript loader. A name already taken by a built-in tool or command is refused,
|
|
807
|
-
so an extension cannot shadow `read_file` or `/clear`, and a broken extension is
|
|
808
|
-
reported and skipped rather than taking chivgent down with it.
|
|
809
|
-
|
|
810
|
-
`chivgent --extensions` lists what loaded and what each one registered.
|
|
811
|
-
|
|
812
|
-
### Project trust
|
|
813
|
-
|
|
814
|
-
The first time you run chivgent in a project that ships extensions, it describes
|
|
815
|
-
what they are and asks once. The answer is stored in `<CHIVGENT_HOME>/trust.json`
|
|
816
|
-
against the resolved directory path, and matched by nearest ancestor, so trusting
|
|
817
|
-
`~/work` covers every repository under it. `--forget-trust` removes the decision
|
|
818
|
-
covering the current workspace.
|
|
819
|
-
|
|
820
|
-
**Trusting a project means letting its authors run code as you.** An extension
|
|
821
|
-
executes inside the chivgent process: it is not bound by the workspace, it does
|
|
822
|
-
not need `--allow-shell` to run a command, and it can read your environment
|
|
823
|
-
including the API key. That is why the question is asked before anything is
|
|
824
|
-
imported, and why the answer is no whenever there is nobody to ask — a CI job or
|
|
825
|
-
a piped run never loads a checkout's extensions on its own.
|
|
826
|
-
|
|
827
|
-
## Security model
|
|
828
|
-
|
|
829
|
-
- API keys come from `--api-key`, an environment variable, or an optional
|
|
830
|
-
`auth.json`, in that order, and must never be committed.
|
|
831
|
-
- `auth.json` stores keys in plain text. It is opt-in for that reason, and
|
|
832
|
-
chivgent warns when its permissions let other users read it.
|
|
833
|
-
- `/login` writes it owner-only and does not echo what you type, but the key is
|
|
834
|
-
still stored in plain text; it is a convenience, not a secret manager.
|
|
835
|
-
- The auth file accepts literal keys only; it cannot expand environment
|
|
836
|
-
variables or run shell commands.
|
|
837
|
-
- A custom `OPENAI_BASE_URL` receives the configured API key and prompts; use
|
|
838
|
-
only endpoints you trust.
|
|
839
|
-
- Workspace tools are read-only unless `--allow-writes` is passed; `write_file`
|
|
840
|
-
and `edit_file` are not registered at all without it.
|
|
841
|
-
- The `bash` tool needs `--allow-shell`, which is separate from `--allow-writes`
|
|
842
|
-
and never implied by it. Granting it grants far more: a shell can change or
|
|
843
|
-
delete anything the user running chivgent can, inside the workspace or not,
|
|
844
|
-
and none of the workspace limits below apply to it.
|
|
845
|
-
- Commands are spawned in their own process group and killed as a group, so
|
|
846
|
-
cancelling a run does not leave descendants behind.
|
|
847
|
-
- Commands inherit chivgent's environment, which includes the API key it is
|
|
848
|
-
using. A model with `--allow-shell` can read that key and anything else the
|
|
849
|
-
environment holds. Run it with an environment that carries only what the task
|
|
850
|
-
needs.
|
|
851
|
-
- Truncated command output is written to a temp file so the model can read the
|
|
852
|
-
rest. The file is owner-only, but it is not deleted when the run ends: it can
|
|
853
|
-
hold anything the command printed. Clear the temp directory after sensitive
|
|
854
|
-
work.
|
|
855
|
-
- Session logs record command output as well as file excerpts once
|
|
856
|
-
`--allow-shell` is on.
|
|
857
|
-
- Project extensions are loaded only after an explicit, recorded trust decision,
|
|
858
|
-
and never without a terminal to ask at. An extension runs in-process with your
|
|
859
|
-
permissions and is bound by none of the workspace limits above, so trusting a
|
|
860
|
-
project is the same order of authority as `--allow-shell`, reached by cloning
|
|
861
|
-
a repository rather than by typing a flag.
|
|
862
|
-
- User extensions under `<CHIVGENT_HOME>/extensions/` are always loaded; that
|
|
863
|
-
directory is your own configuration.
|
|
864
|
-
- Extensions cannot take the name of a built-in tool or command.
|
|
865
|
-
- `trust.json`, like the session log and the auth file, is written owner-only.
|
|
866
|
-
- A served session is reachable by anyone who can open its socket, and they get
|
|
867
|
-
every capability the server was started with. Sockets live in an owner-only
|
|
868
|
-
directory; the directory is the real gate, because a socket file exists
|
|
869
|
-
briefly with default permissions before it can be restricted.
|
|
870
|
-
- chivgent never listens on TCP. Use SSH forwarding to reach another machine.
|
|
871
|
-
- Writes resolve the deepest existing ancestor and reject a symlink at any
|
|
872
|
-
segment, so a planted link cannot redirect a write out of the workspace.
|
|
873
|
-
- Writes are staged in a sibling temp file and renamed into place, so an
|
|
874
|
-
interrupted write cannot truncate an existing file.
|
|
875
|
-
- `edit_file` refuses an edit whose `old_text` is missing or matches more than
|
|
876
|
-
once, so an imprecise edit fails instead of changing the wrong line.
|
|
877
|
-
- Paths must remain inside the current workspace.
|
|
878
|
-
- Real-path checks block `..` traversal and symlink escapes.
|
|
879
|
-
- File size and binary-content checks limit unsafe reads.
|
|
880
|
-
- Discovery respects the root `.gitignore` and fixed generated-directory ignores.
|
|
881
|
-
- Common credential and private-key paths are denied across all workspace tools.
|
|
882
|
-
- Tool results are limited to 64 KiB; reads, scans, depth, and result counts are bounded.
|
|
883
|
-
- Tool inputs are untrusted and validated before execution.
|
|
884
|
-
- The agent stops after a bounded number of model turns.
|
|
885
|
-
- Session logs under `~/.chivgent/sessions` contain prompts, answers, and tool
|
|
886
|
-
results, including file excerpts. Use `--no-session` in sensitive workspaces,
|
|
887
|
-
and treat the log directory like the project it describes.
|
|
888
|
-
- Session ids are validated before they become file paths.
|
|
889
|
-
|
|
890
|
-
This is an educational MVP, not a hardened sandbox. There is no permission
|
|
891
|
-
system: capabilities are coarse, session-level switches, and neither
|
|
892
|
-
`--allow-writes` nor `--allow-shell` asks for per-action confirmation. That is
|
|
893
|
-
deliberate. Once a shell exists, a command allowlist is bypassed by a single
|
|
894
|
-
`sh -c`, and a prompt on every action only trains people to approve without
|
|
895
|
-
reading, so chivgent states the boundary instead of pretending to enforce one.
|
|
896
|
-
|
|
897
|
-
Use these switches on work you have committed. If you need a real boundary,
|
|
898
|
-
put the whole process in a container and give that container only what the task
|
|
899
|
-
needs. Review the code and threat model before pointing chivgent at a sensitive
|
|
900
|
-
project.
|
|
901
|
-
|
|
902
|
-
## Roadmap
|
|
903
|
-
|
|
904
|
-
- [x] Minimal tool-calling agent loop
|
|
905
|
-
- [x] Safe `read_file` tool
|
|
906
|
-
- [x] OpenAI and DeepSeek Providers
|
|
907
|
-
- [x] Reusable OpenAI-compatible Chat Completions adapter
|
|
908
|
-
- [x] Custom OpenAI-compatible CLI Provider
|
|
909
|
-
- [x] Project discovery tools: `list_files`, `search_text`, and ranged `read_file`
|
|
910
|
-
- [x] Streaming output and runtime events
|
|
911
|
-
- [x] Persistent multi-turn sessions
|
|
912
|
-
- [x] Context-window management and compaction
|
|
913
|
-
- [x] Opt-in `write_file` and `edit_file` behind `--allow-writes`
|
|
914
|
-
- [x] Provider registry and credential resolution chain
|
|
915
|
-
- [x] `bash` tool with streaming output, behind `--allow-shell`
|
|
916
|
-
- [x] Extensions and project trust
|
|
917
|
-
- [x] Remote sessions over a local socket
|
|
918
|
-
- [x] An eval suite with deterministic graders and pass rates
|
|
919
|
-
- [x] Token usage from the Provider, aggregated per run and per eval
|
|
920
|
-
- [x] A live status region driven by the event stream (`--tui`)
|
|
921
|
-
|
|
922
|
-
Per-command confirmation prompts and command allowlists are deliberately not
|
|
923
|
-
planned. Once a shell tool exists, `bash` can do anything `write_file` can and
|
|
924
|
-
more, so an allowlist is bypassed by a single `sh -c` and a per-command prompt
|
|
925
|
-
only trains people to approve without reading. Capabilities are coarse,
|
|
926
|
-
session-level switches; the real boundary is a container.
|
|
927
|
-
|
|
928
|
-
## Documentation
|
|
929
|
-
|
|
930
|
-
- [Stage 1: Minimal Agent design](docs/stage-1-minimal-agent.md)
|
|
931
|
-
- [DeepSeek Provider design](docs/deepseek-provider.md)
|
|
932
|
-
- [Stage 2: Project Discovery implementation design](docs/stage-2-project-discovery.md)
|
|
933
|
-
- [Stage 3: Runtime Events and Streaming design](docs/stage-3-runtime-events.md)
|
|
934
|
-
- [Stage 4: Sessions and interactive mode design](docs/stage-4-sessions.md)
|
|
935
|
-
- [Stage 5: Write tools and the workspace split](docs/stage-5-write-tools.md)
|
|
936
|
-
- [Stage 6: Provider registry and credential chain](docs/stage-6-provider-registry.md)
|
|
937
|
-
- [Stage 7: Context budget and compaction](docs/stage-7-context-management.md)
|
|
938
|
-
- [Stage 8: Shell tool and streaming subprocesses](docs/stage-8-shell-tool.md)
|
|
939
|
-
- [Stage 9: Extensions and project trust](docs/stage-9-extensions.md)
|
|
940
|
-
- [Stage 10: Remote sessions](docs/stage-10-remote-sessions.md)
|
|
941
|
-
- [Stage 11: Evals](docs/stage-11-evals.md)
|
|
942
|
-
- [Stage 12: Token usage](docs/stage-12-usage.md)
|
|
943
|
-
- [Release process](docs/releasing.md)
|
|
944
|
-
|
|
945
|
-
## Contributing
|
|
946
|
-
|
|
947
|
-
Issues and focused pull requests are welcome. Before submitting a change, run:
|
|
948
|
-
|
|
949
|
-
```bash
|
|
950
|
-
npm run check
|
|
951
|
-
npm test
|
|
952
|
-
npm run build
|
|
953
|
-
```
|
|
180
|
+
Tests use scripted LLM clients and never spend API credits.
|
|
181
|
+
`npm run release:check` runs the full gate, including an `npm pack` dry run.
|
|
182
|
+
To try the checkout as a global command: `npm install -g .`
|
|
954
183
|
|
|
955
|
-
|
|
956
|
-
|
|
184
|
+
Issues and focused pull requests are welcome. Keep Provider-specific types in
|
|
185
|
+
`src/providers/` so the agent runtime stays independent of vendor SDKs.
|
|
957
186
|
|
|
958
187
|
## License
|
|
959
188
|
|
|
960
|
-
|
|
189
|
+
[MIT](LICENSE)
|