@xingjisoft/neutrino 0.1.4 → 0.1.6
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 +413 -163
- package/assets/prompt.txt +4 -0
- package/dist/agents.js +114 -15
- package/dist/app.js +442 -30
- package/dist/bin.js +177 -5
- package/dist/colors.js +2 -0
- package/dist/compact.js +19 -0
- package/dist/config.js +121 -28
- package/dist/cwd.js +54 -0
- package/dist/header.js +8 -0
- package/dist/llm/approval.js +543 -0
- package/dist/llm/client.js +14 -0
- package/dist/llm/risk.js +377 -0
- package/dist/llm/tools.js +37 -21
- package/dist/persistence.js +51 -0
- package/dist/screens/agentHistory.js +7 -4
- package/dist/screens/chat.js +709 -49
- package/dist/screens/history.js +9 -7
- package/dist/screens/settings.js +131 -14
- package/dist/screens/start.js +50 -16
- package/dist/selection.js +60 -0
- package/dist/state.js +1 -0
- package/dist/terminal/input.js +34 -5
- package/dist/terminal/renderer.js +58 -12
- package/dist/terminal/restore.js +148 -0
- package/dist/ui.js +16 -0
- package/dist/web/auth.js +82 -0
- package/dist/web/hub.js +180 -0
- package/dist/web/server.js +688 -0
- package/dist/web/session.js +572 -0
- package/dist/web/settings.js +101 -0
- package/dist/width.js +20 -0
- package/package.json +6 -4
- package/web/README.md +210 -0
- package/web/app.js +1100 -0
- package/web/index.html +90 -0
- package/web/settings.js +193 -0
- package/web/style.css +1088 -0
- package/web/turns.js +52 -0
package/README.md
CHANGED
|
@@ -1,163 +1,413 @@
|
|
|
1
|
-
# XINGJI Neutrino
|
|
2
|
-
|
|
3
|
-
A CLI coding agent that lives in your terminal. Neutrino gives you an interactive chat interface backed by LLMs, with built-in tool use (running commands, reading and writing files) so it can help you work on real projects.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **Interactive terminal UI** — logo screen, chat, model select, history, and settings screens.
|
|
8
|
-
- **Full mouse support** — hover to highlight, click to select/open/position the cursor, wheel to scroll, right-click to go back (like OpenCode).
|
|
9
|
-
- **Select & copy** — drag the mouse over any text (agent output, the input box, menus, …) to select it; releasing the button copies the selection to the clipboard (via OSC 52, with a native fallback).
|
|
10
|
-
- **Paste-friendly input** — like OpenCode, a pasted block has its newlines turned into spaces so it stays one line, and only a manually typed `Enter` submits the message.
|
|
11
|
-
- **Faithful log rendering** — text in the chat log keeps its indentation, blank lines and column alignment; when a long tool-output line wraps, continuation lines stay indented instead of snapping back to the left margin.
|
|
12
|
-
- **Scrollable context log** — a vertical scrollbar is drawn on the right edge of the chat (and the agent-conversation viewer) whenever the conversation is taller than the view. Scroll with the wheel, `PgUp`/`PgDn` or `↑`/`↓`, or grab the scrollbar with the mouse and drag it like in a GUI.
|
|
13
|
-
- **LLM providers** — ships with DeepSeek, OpenAI, and Anthropic presets; works with any OpenAI-compatible endpoint.
|
|
14
|
-
- **Tool calling** — the agent can run shell commands, read files, write files, and list directories in your working directory.
|
|
15
|
-
- **Streaming responses** — token-by-token output with pause/resume (`ESC ESC`).
|
|
16
|
-
- **Intensity control** — tune `temperature`/`top_p` from `Low` to `Ultra`.
|
|
17
|
-
- **Session history** —
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
| `
|
|
59
|
-
|
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
62
|
-
| `Ctrl+
|
|
63
|
-
| `
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
|
77
|
-
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
- `
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
1
|
+
# XINGJI Neutrino
|
|
2
|
+
|
|
3
|
+
A CLI coding agent that lives in your terminal. Neutrino gives you an interactive chat interface backed by LLMs, with built-in tool use (running commands, reading and writing files) so it can help you work on real projects.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Interactive terminal UI** — logo screen, chat, model select, history, and settings screens.
|
|
8
|
+
- **Full mouse support** — hover to highlight, click to select/open/position the cursor, wheel to scroll, right-click to go back (like OpenCode).
|
|
9
|
+
- **Select & copy** — drag the mouse over any text (agent output, the input box, menus, …) to select it; releasing the button copies the selection to the clipboard (via OSC 52, with a native fallback). Holding the drag against the top or bottom edge of the log scrolls it, so one selection can run for as long as the whole conversation.
|
|
10
|
+
- **Paste-friendly input** — like OpenCode, a pasted block has its newlines turned into spaces so it stays one line, and only a manually typed `Enter` submits the message.
|
|
11
|
+
- **Faithful log rendering** — text in the chat log keeps its indentation, blank lines and column alignment; when a long tool-output line wraps, continuation lines stay indented instead of snapping back to the left margin.
|
|
12
|
+
- **Scrollable context log** — a vertical scrollbar is drawn on the right edge of the chat (and the agent-conversation viewer) whenever the conversation is taller than the view. Scroll with the wheel, `PgUp`/`PgDn` or `↑`/`↓`, or grab the scrollbar with the mouse and drag it like in a GUI.
|
|
13
|
+
- **LLM providers** — ships with DeepSeek, OpenAI, and Anthropic presets; works with any OpenAI-compatible endpoint.
|
|
14
|
+
- **Tool calling** — the agent can run shell commands, read files, write files, and list directories in your working directory.
|
|
15
|
+
- **Streaming responses** — token-by-token output with pause/resume (`ESC ESC`).
|
|
16
|
+
- **Intensity control** — tune `temperature`/`top_p` from `Low` to `Ultra`.
|
|
17
|
+
- **Session history** — messages are saved before a turn starts, streamed replies are checkpointed every second, and tool calls/results are saved immediately. The history screen lists conversations started in the current working directory. Failed saves are shown in the terminal and browser UI.
|
|
18
|
+
- **Auto-folding turns** — as soon as the model finishes a reply, that round collapses into one summary line (prompt, answer preview, message count) in both the terminal and the browser. Click it to read the complete round again; short exchanges stay open, because hiding two lines helps nobody. A turn stopped with `ESC ESC` is paused, not finished, so it stays open. Nothing is dropped: the transcript, the saved session and the model's context keep every message.
|
|
19
|
+
- **Complete conversation history** — context compression keeps every original message and tool result on disk. The earlier portion starts collapsed in the terminal and browser; click **Earlier conversation** to expand it, including in reopened history. Model requests use a separate summary plus recent messages.
|
|
20
|
+
- **Token usage stats** — click the `ⓘ` in the chat header to see prompt/completion/total token counts for the current session.
|
|
21
|
+
- **Agent history browser** — view past sessions from other CLI agents (Claude Code, Codex, OpenCode) directly from the history screen.
|
|
22
|
+
- **Approval gate** — risky commands and paths outside the working directory need your sign-off. The header turns orange and reads **Request Approval**; press `Y` or `N`. Delete-class commands always ask, unless you explicitly granted full trust, and there is an opt-in reviewer agent for automatic decisions.
|
|
23
|
+
- **Browser UI on any port** — mount a local website that lists every saved conversation grouped by working directory, streams the running one in real time, and lets you pause/resume it, send messages, switch provider/model/intensity, and edit settings from the browser.
|
|
24
|
+
|
|
25
|
+
## Requirements
|
|
26
|
+
|
|
27
|
+
- Node.js **>= 18.0.0**
|
|
28
|
+
- An interactive terminal (TTY)
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
npm install -g @xingjisoft/neutrino
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or run from source:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
git clone <repo-url> neutrino
|
|
40
|
+
cd neutrino
|
|
41
|
+
npm install
|
|
42
|
+
npm run build
|
|
43
|
+
npm link
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
neutrino
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Type a message on the start screen and press `Enter` to begin a new chat.
|
|
53
|
+
|
|
54
|
+
### Keys
|
|
55
|
+
|
|
56
|
+
| Keys | Action |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `Enter` | Send message / confirm (only a manually typed Enter sends) |
|
|
59
|
+
| `←`/`→`, `↑`/`↓` | Move focus / navigate |
|
|
60
|
+
| `Ctrl+E` | Open model config |
|
|
61
|
+
| `Ctrl+B` | Back to menu (from chat) |
|
|
62
|
+
| `Ctrl+L` | Clear conversation (from chat) |
|
|
63
|
+
| `Esc Esc` | Pause / resume streaming (a paused turn stays open: only a finished reply folds) |
|
|
64
|
+
| `Esc` | Clear an active selection |
|
|
65
|
+
| `PgUp` / `PgDn` | Scroll chat history |
|
|
66
|
+
| `Ctrl+C` | Do nothing while text is selected; otherwise pause the running conversation or show the exit hint at the top |
|
|
67
|
+
| `Ctrl+C` × 3 | Quit after three consecutive presses, no more than 1.5 seconds apart (another key, paste, click or scroll resets the count) |
|
|
68
|
+
| `Ctrl+Shift+C` | Quit immediately (requires the terminal to forward the shortcut) |
|
|
69
|
+
| `Ctrl+A` | Toggle Request Approval / Auto Approval in a conversation |
|
|
70
|
+
| `Y` | Approve the pending request (while the approval card is up) |
|
|
71
|
+
| `N` / `Esc` | Deny the pending request |
|
|
72
|
+
| `Ctrl+Q` | Quit |
|
|
73
|
+
|
|
74
|
+
### Mouse
|
|
75
|
+
|
|
76
|
+
| Action | Effect |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| Hover over an item | Highlight / select it in lists and menus |
|
|
79
|
+
| Click an item | Select and confirm (open a screen, load a session, apply a setting…) |
|
|
80
|
+
| Click the input box | Focus it and place the cursor at the clicked position |
|
|
81
|
+
| Click a folded turn | Expand the complete round, or fold it again |
|
|
82
|
+
| Click `⚙ tool` | Expand or collapse a tool call's output |
|
|
83
|
+
| Click and drag | Select text under the rectangle; releasing copies it to the clipboard |
|
|
84
|
+
| Drag onto the first/last row of the log | Scroll the log a line at a time (faster the longer you hold it) and keep growing the selection past the screen |
|
|
85
|
+
| Click the header (left of `ⓘ`) | Open the model selector |
|
|
86
|
+
| Click `ⓘ` (chat header) | Toggle token usage stats |
|
|
87
|
+
| Drag the scrollbar | Grab the thumb on the right edge of the chat/log and drag it to scroll |
|
|
88
|
+
| Scroll wheel | Scroll the chat/log, or move through lists |
|
|
89
|
+
| Right-click | Go back (like `Esc`) |
|
|
90
|
+
|
|
91
|
+
The selection highlight is drawn over whatever is on screen, so it works uniformly on the chat log, the input box and every menu. Selected text is copied with trailing whitespace trimmed per line (shell-style), so it can be pasted straight back into a terminal. UI chrome (such as the scrollbar) is skipped when copying and highlighting, so a copy never picks up the bar or the padding in front of it.
|
|
92
|
+
|
|
93
|
+
A drag that scrolls keeps its starting point anchored to the line it began on,
|
|
94
|
+
not to the row it began on: the mark stays on the edge that is scrolling, the
|
|
95
|
+
highlight stops at the log's edge instead of spilling into the header or the
|
|
96
|
+
input box, and the copy includes the lines that scrolled out of view. Scroll back
|
|
97
|
+
and the selection shrinks again; press `Esc` to drop it.
|
|
98
|
+
|
|
99
|
+
## Configuration
|
|
100
|
+
|
|
101
|
+
Settings are stored under `~/.neutrino/`:
|
|
102
|
+
|
|
103
|
+
- `model.json` — provider and model definitions (edit to add your own endpoints).
|
|
104
|
+
- `config.json` — app configuration (selected provider/model/intensity, theme,
|
|
105
|
+
language, approval mode, `auto_approve_extreme_risk`, `sound_alerts`).
|
|
106
|
+
- `session/` — saved conversation sessions, one JSON file per session.
|
|
107
|
+
|
|
108
|
+
Set your API key in the model select screen or directly in `model.json`.
|
|
109
|
+
|
|
110
|
+
A few environment variables override flags:
|
|
111
|
+
|
|
112
|
+
| Variable | Effect |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| `NEUTRINO_WEB_PORT` | Web UI port: like `--web-port` in TUI mode, and the port in `neutrino web`. |
|
|
115
|
+
| `NEUTRINO_WEB_HOST` | Interface to bind, like `--host`. |
|
|
116
|
+
|
|
117
|
+
### Approvals
|
|
118
|
+
|
|
119
|
+
Every tool call is classified before it runs.
|
|
120
|
+
|
|
121
|
+
| Class | Examples | Default behaviour |
|
|
122
|
+
| --- | --- | --- |
|
|
123
|
+
| Routine | `npm run build`, `git status`, reading or writing inside the project | Runs without asking |
|
|
124
|
+
| High | `sudo`, `chmod`, package installs, `git push`, `curl`, inline `python -c`, any path outside the working directory | Asks the user |
|
|
125
|
+
| Extreme | Delete-class: `rm`, `del`, `shred`, `mkfs`, `git clean -f`, `git reset --hard`, `find -delete`, `DROP TABLE`, … | Always asks the user |
|
|
126
|
+
|
|
127
|
+
While a call waits for an answer the top bar turns orange and reads
|
|
128
|
+
**Request Approval**, and an orange card floats above the input box with the
|
|
129
|
+
command, **why the agent says it needs it**, why it was flagged, and
|
|
130
|
+
`[ Y ] Approve` / `[ N ] Deny`. Approving runs the call; denying returns a note
|
|
131
|
+
to the model so it can pick another route instead of retrying.
|
|
132
|
+
|
|
133
|
+
Every tool call carries a `reason` argument, and a call that needs approval but
|
|
134
|
+
states none does not run: the gate refuses it before you are interrupted or a
|
|
135
|
+
reviewer is paid for, and the model is told to resend the same call with a
|
|
136
|
+
reason. The reason is the agent's own claim, shown apart from the arguments.
|
|
137
|
+
A vague, imperfect or seemingly unrelated reason is not itself a safety hazard;
|
|
138
|
+
reviewers judge the actual call's consequences and explicit user restrictions.
|
|
139
|
+
|
|
140
|
+
Two phrases in your message change the policy for that conversation:
|
|
141
|
+
|
|
142
|
+
| Phrase in the prompt | Effect |
|
|
143
|
+
| --- | --- |
|
|
144
|
+
| `无需审批` | High-risk calls run without asking. Delete-class calls still ask. |
|
|
145
|
+
| `我已知悉风险,同意给予完整权限,无需任何审批` | Nothing asks, including delete-class calls. |
|
|
146
|
+
|
|
147
|
+
The one-row **Request Approval / Auto Approval** button above the main menu's
|
|
148
|
+
version label sets and remembers the default for new conversations. Each terminal
|
|
149
|
+
conversation has its own button above the input (or **Ctrl+A**), and each browser
|
|
150
|
+
conversation has a switch in its controls. The conversation's choice is saved
|
|
151
|
+
with its history and synced between terminal and browser. Writing `自动审批` or
|
|
152
|
+
`自动批准` in a prompt no longer enables automatic review.
|
|
153
|
+
|
|
154
|
+
With **Auto Approval** enabled, each pending tool call receives **two independent
|
|
155
|
+
safety reviews**. A review round passes when its average safety score is at least
|
|
156
|
+
60/100 and no concrete safety hazard or failed review blocks it. Task relevance
|
|
157
|
+
has zero weight and no ordinary minimum: even a score of 0 does not by itself
|
|
158
|
+
prevent a safe call from running, and doubts about purpose must not lower safety.
|
|
159
|
+
|
|
160
|
+
Only an extreme, obvious task mismatch can veto on relevance: both reviewers
|
|
161
|
+
must independently mark it, give a relevance score of at most 5/100, and provide
|
|
162
|
+
specific evidence comparing the task and proposed action. One suspicion, weak
|
|
163
|
+
justification, or uncertainty about the task is insufficient.
|
|
164
|
+
|
|
165
|
+
The policy favors ordinary implementation and verification steps, including
|
|
166
|
+
scoped edits, dependency installation, public network checks and temporary test
|
|
167
|
+
files. Concrete serious hazards and explicit user prohibitions block approval;
|
|
168
|
+
a failed, timed-out or malformed review also prevents automatic execution.
|
|
169
|
+
Every assessment and the safety result are included in the decision, per round
|
|
170
|
+
for delete-class calls.
|
|
171
|
+
|
|
172
|
+
Each turn captures its literal user request and earlier user messages separately
|
|
173
|
+
from summaries and tool output, so short follow-ups keep their task context.
|
|
174
|
+
Queued messages take effect on the next turn. Denied tool results include the
|
|
175
|
+
reviewed user request and recent user requests for diagnosis.
|
|
176
|
+
|
|
177
|
+
Automatic approval of delete-class commands is behind a switch in **Settings →
|
|
178
|
+
Extreme-risk auto-approval**, which is **off by default**: while it is off,
|
|
179
|
+
delete-class calls always go to a human even in **Auto Approval** mode.
|
|
180
|
+
|
|
181
|
+
With the switch on, a delete-class call is reviewed **three separate times**.
|
|
182
|
+
Each round is the usual pair of independent reviewers; the rounds run one after
|
|
183
|
+
another and no round is told what an earlier one concluded. Every round is
|
|
184
|
+
judged on its own scores and the call only runs if **all three** approve, so the
|
|
185
|
+
first round that denies or fails ends the sequence: a deletion never gets a
|
|
186
|
+
second chance to pass. The switches are layered, so turning **Auto Approval**
|
|
187
|
+
off still sends every delete-class request to you. The reviewer model defaults
|
|
188
|
+
to the chat model and can be changed in **Settings → Approval reviewer model**.
|
|
189
|
+
|
|
190
|
+
### Sound alerts
|
|
191
|
+
|
|
192
|
+
A short cue plays when a turn finishes and when an approval request appears, in
|
|
193
|
+
both the terminal and the web UI, so a long task or a blocking approval is
|
|
194
|
+
noticeable from another window. Approval requests ring twice; finished turns
|
|
195
|
+
ring once. Toggle it in **Settings → Sound alerts** (TUI or web), or set
|
|
196
|
+
`"sound_alerts": false` in `config.json`.
|
|
197
|
+
|
|
198
|
+
Browsers refuse to start audio before a user gesture, so the web UI stays silent
|
|
199
|
+
until the page has been clicked or a key has been pressed at least once.
|
|
200
|
+
|
|
201
|
+
### Browsing other agents' history
|
|
202
|
+
|
|
203
|
+
From the history screen, select **Open other Agent's history** to view past sessions recorded by other CLI agents installed on your machine:
|
|
204
|
+
|
|
205
|
+
- **Claude Code** — reads `~/.claude/projects/**/*.jsonl`.
|
|
206
|
+
- **Codex** — reads `~/.codex/sessions` and `~/.codex/archived_sessions`.
|
|
207
|
+
- **OpenCode** — reads the local SQLite database (`opencode.db`) via `node:sqlite` (requires Node.js **22.13+**; silently unavailable on older versions).
|
|
208
|
+
|
|
209
|
+
Only sessions that were started in the current working directory are listed, so the history stays focused on the project you are in. These sessions are read-only; you can open them for review but not edit or delete.
|
|
210
|
+
|
|
211
|
+
## Web UI
|
|
212
|
+
|
|
213
|
+
Neutrino can mount a small website on a port of your choice. It groups every
|
|
214
|
+
saved conversation by working directory (showing the five newest per path with
|
|
215
|
+
the rest collapsed), folds every finished turn into one summary line that opens
|
|
216
|
+
on a click, streams a running conversation live, and lets you pause or
|
|
217
|
+
resume it and send new messages from the browser.
|
|
218
|
+
|
|
219
|
+
A saved transcript opens read-only, because nothing is running for it. Press
|
|
220
|
+
**Continue here** (or simply type in the composer) and the server resumes it
|
|
221
|
+
in-process — same model, working directory and token counters — so you can keep
|
|
222
|
+
chatting from the browser with no terminal attached. Turns are written back to
|
|
223
|
+
the same session file.
|
|
224
|
+
|
|
225
|
+
Use the selectors above a conversation to change its provider, model or
|
|
226
|
+
intensity, then press **Apply to conversation** (pause an active turn first).
|
|
227
|
+
The **⚙ Settings** button in the sidebar edits defaults for new conversations,
|
|
228
|
+
context compression and approval settings. Conversation choices are saved with
|
|
229
|
+
the transcript and do not overwrite the defaults. Configured models and keys
|
|
230
|
+
are shared with the terminal; keys stay on the server.
|
|
231
|
+
|
|
232
|
+
Context auto-compression starts at 80% of the current model's context window.
|
|
233
|
+
Models default to 1,000,000 tokens (compression at 800,000). Adjust the model
|
|
234
|
+
context window in terminal Settings or select the provider/model in Web Settings
|
|
235
|
+
and enter its capacity in tokens. This is saved per provider/model and applies
|
|
236
|
+
to future turns in active conversations. The legacy fixed
|
|
237
|
+
`context_compress_threshold` setting is no longer used.
|
|
238
|
+
|
|
239
|
+
```sh
|
|
240
|
+
# Mount the web UI while the terminal UI runs
|
|
241
|
+
neutrino --web-port 8765
|
|
242
|
+
|
|
243
|
+
# Or serve only the saved history, with no terminal
|
|
244
|
+
neutrino web --port 8765
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Then open <http://127.0.0.1:8765>. The site itself lives in [`web/`](./web) and
|
|
248
|
+
needs no build step; see [`web/README.md`](./web/README.md) for the API.
|
|
249
|
+
|
|
250
|
+
### Reaching it from another device
|
|
251
|
+
|
|
252
|
+
The server binds to loopback, so only this machine can open it. To use it from a
|
|
253
|
+
phone, a tablet or another box on your LAN or VPN, bind an interface others can
|
|
254
|
+
see:
|
|
255
|
+
|
|
256
|
+
```sh
|
|
257
|
+
neutrino web --port 8765 --lan # every interface (0.0.0.0)
|
|
258
|
+
neutrino --web-port 8765 --lan # same, while the TUI runs
|
|
259
|
+
neutrino web --port 8765 --host 100.64.0.7 # one specific address
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Startup prints every address that works, so you can copy the one matching the
|
|
263
|
+
device you are on:
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
Neutrino web UI listening on 0.0.0.0:8765
|
|
267
|
+
Open the address that matches your device:
|
|
268
|
+
http://100.105.130.25:8765/
|
|
269
|
+
http://192.168.1.211:8765/
|
|
270
|
+
http://127.0.0.1:8765/
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Open the address and sign in with your permanent Web password. The initial
|
|
274
|
+
password is **`114514`**. Change it in the browser's **Settings → Web access**
|
|
275
|
+
or in the terminal's **Settings → Web password**. It is saved as a salted hash
|
|
276
|
+
in `~/.neutrino/config.json` and survives restarts.
|
|
277
|
+
|
|
278
|
+
For automatic login, open `http://<host>:8765/?pwd=114514`. The page checks the
|
|
279
|
+
password, remembers the login, and removes `pwd` from the address bar. If it is
|
|
280
|
+
incorrect, the password login dialog lets you try again. URL-encode special
|
|
281
|
+
characters in your password.
|
|
282
|
+
|
|
283
|
+
The browser remembers your login in an HttpOnly cookie for up to one year,
|
|
284
|
+
including across server restarts. Changing the password signs out other
|
|
285
|
+
browsers; the browser that saves the new password stays signed in. Addresses
|
|
286
|
+
and startup output never contain your password or session credentials.
|
|
287
|
+
|
|
288
|
+
- **`--no-auth`** explicitly disables password login.
|
|
289
|
+
- Static files and the login endpoint remain public; all conversation and
|
|
290
|
+
settings APIs require a login, including when accessed through localhost.
|
|
291
|
+
- **Firewall:** the first bind to `0.0.0.0` triggers an allow prompt on Windows
|
|
292
|
+
and macOS. Allow it for private networks, and check that your WLAN is not
|
|
293
|
+
marked "public", where incoming connections are blocked anyway.
|
|
294
|
+
- **`127.0.0.1` remains the default** because the zero-config case should not be
|
|
295
|
+
a network service.
|
|
296
|
+
|
|
297
|
+
## Built-in tools
|
|
298
|
+
|
|
299
|
+
The agent can call the following tools during a conversation:
|
|
300
|
+
|
|
301
|
+
- `run_command` — execute a shell command (120s timeout, output capped).
|
|
302
|
+
- `read_file` — read a text file (capped at 200KB).
|
|
303
|
+
- `write_file` — write content to a file.
|
|
304
|
+
- `list_files` — list directory contents.
|
|
305
|
+
|
|
306
|
+
Every tool takes an additional `reason` argument: one sentence saying what the
|
|
307
|
+
call is for and why it is needed now. It is what the approval card shows and what
|
|
308
|
+
reviewers check the arguments against, so it is required even for calls that
|
|
309
|
+
usually run without asking.
|
|
310
|
+
|
|
311
|
+
## Development
|
|
312
|
+
|
|
313
|
+
```sh
|
|
314
|
+
npm run build # compile TypeScript to dist/
|
|
315
|
+
npm test # build, then run every test below
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Individual tests:
|
|
319
|
+
|
|
320
|
+
```sh
|
|
321
|
+
node scripts/test-paste.mjs # verify pasted newlines are flattened to spaces
|
|
322
|
+
node scripts/test-mouse.mjs # verify mouse escape-sequence parsing
|
|
323
|
+
node scripts/test-cursor.mjs # verify click-to-position-cursor math
|
|
324
|
+
node scripts/test-selection.mjs # verify selection highlighting & text extraction
|
|
325
|
+
node scripts/test-drag-scroll.mjs # verify drag-select auto-scroll: edge scrolling, cross-screen copy
|
|
326
|
+
node scripts/test-copy.mjs # verify a copy never includes the scrollbar or trailing padding
|
|
327
|
+
node scripts/test-scrollbar.mjs # verify the conversation scrollbar size/position and drag maths
|
|
328
|
+
node scripts/test-markdown.mjs # verify markdown/table rendering (incl. ragged rows)
|
|
329
|
+
node scripts/test-wrap.mjs # verify indentation & spacing are preserved when wrapping
|
|
330
|
+
node scripts/test-render.mjs # fuzz the diff renderer + render the chat screen,
|
|
331
|
+
# asserting no stale/residual cells are left behind
|
|
332
|
+
node scripts/test-sanitize.mjs # verify ANSI/control codes never reach the screen
|
|
333
|
+
node scripts/test-drift.mjs # verify the input area self-heals after terminal drift
|
|
334
|
+
node scripts/test-fetch.mjs # verify streamed requests release aborts and retry drops
|
|
335
|
+
node scripts/test-compact.mjs # verify context-compression planning
|
|
336
|
+
node scripts/test-history-fold.mjs # verify full-history retention, folding and resume
|
|
337
|
+
node scripts/test-web-history.mjs # verify browser history disclosures and streaming state
|
|
338
|
+
node scripts/test-turn-fold.mjs # verify finished turns fold in both UIs and keep every message
|
|
339
|
+
node scripts/test-tools.mjs # verify tool-output folding
|
|
340
|
+
node scripts/test-web.mjs # verify the web server, API, SSE stream and path-traversal guards
|
|
341
|
+
node scripts/test-web-settings.mjs # verify model switching, persistence, browser controls and defaults
|
|
342
|
+
node scripts/test-web-settings-http.mjs # verify authenticated settings and model changes over HTTP
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Rendering model
|
|
346
|
+
|
|
347
|
+
The screen is drawn into a `Buffer` of cells which is diffed against the previous frame, so only changed rows are emitted. Rows are erased to the end of the line before being repainted, and the buffer keeps double-width glyphs consistent (writing over one half of a wide character blanks the other half). Together these guarantee that a repainted row can never leave a stale cell behind.
|
|
348
|
+
|
|
349
|
+
Because only *changed* rows are emitted, a row whose contents happen to be identical from one frame to the next is never rewritten — so if the terminal shifts its own contents behind our back (ConPTY reflowing the buffer mid-resize, a size-race repaint, a stray scroll) that row would stay stale. Two defences cover this: the persistent chrome (input box, tabs, key hints) is repainted on every frame via `Screen.alwaysRepaintRows()`, and the app periodically re-emits the whole cached frame (`Renderer.repaint()`) as a safety net. Both are no-ops when nothing drifted.
|
|
350
|
+
|
|
351
|
+
### Text hygiene
|
|
352
|
+
|
|
353
|
+
A cell's glyph is copied straight into the output stream, so a cell holding a *control* character becomes a command the terminal executes — an ESC sequence can move the cursor, clear the screen or leave the alternate buffer, and a TAB shifts the rest of the row. Tool output (ANSI colours from `ls --color`, `git`, `npm`, progress bars, …), file contents and pasted text all carry such bytes, so they are neutralised in three places:
|
|
354
|
+
|
|
355
|
+
1. `src/sanitize.ts` — `sanitizeText()` removes whole escape sequences (not just the ESC byte, which would leave `[31m` behind as literal text), expands tabs so measured widths match what the terminal draws, and drops any remaining control code. It is applied where untrusted text enters the display (chat messages, tool results, pasted input).
|
|
356
|
+
2. `Buffer` — writing a control character stores a space instead, preserving the column width the caller measured, so no cell can ever hold one.
|
|
357
|
+
3. `Renderer` — a last-resort check when emitting a row, so the byte stream cannot carry a control code even if a cell were populated another way.
|
|
358
|
+
|
|
359
|
+
### Network resilience
|
|
360
|
+
|
|
361
|
+
Model responses arrive over a streamed connection that can drop at any point. A single blip should not end the session, so three cases are handled:
|
|
362
|
+
|
|
363
|
+
- **The request never goes out** (`fetch` rejects) — retried a couple of times with a short backoff, bounded by a connect timeout.
|
|
364
|
+
- **The stream dies before the first byte** — the server or a proxy closed the connection mid-flight (`TypeError: terminated`, `other side closed`, …). Nothing has reached the user, so the request is simply re-issued.
|
|
365
|
+
- **The stream dies after output has started** — retrying would duplicate text, so the error is surfaced instead, with the real reason expanded out of the `error.cause` chain (`fetch failed` alone says nothing useful).
|
|
366
|
+
|
|
367
|
+
Any stream the consumer walks away from (pause, or a newer turn taking over) always has its body cancelled, so sockets are not leaked.
|
|
368
|
+
|
|
369
|
+
### Crash safety
|
|
370
|
+
|
|
371
|
+
The TUI takes over the terminal: raw mode, the alternate screen and mouse
|
|
372
|
+
reporting. If the process ever dies without undoing those, the shell inherits a
|
|
373
|
+
terminal that types `[<b>;<x>;<y>M` mouse reports at every cursor move.
|
|
374
|
+
|
|
375
|
+
So the teardown sequence is registered three ways — normal quit, a
|
|
376
|
+
`process.on("exit")` hook that writes synchronously to fd 1, and
|
|
377
|
+
`uncaughtException` / `unhandledRejection` handlers. Normal quit uses the same
|
|
378
|
+
synchronous teardown, so pending frame output cannot swallow the cleanup.
|
|
379
|
+
Catchable exits also flush the latest conversation snapshot, including a partial
|
|
380
|
+
reply. Uncaught errors are appended to `~/.neutrino/crash.log` and exit with
|
|
381
|
+
status 1. SIGHUP and Windows Ctrl+Break also restore and checkpoint before exit.
|
|
382
|
+
|
|
383
|
+
Session snapshots are written to a temporary file in the session directory,
|
|
384
|
+
synced, then atomically renamed over the previous snapshot. A failed write
|
|
385
|
+
preserves the previous history, reports an error in the UI and crash log, and
|
|
386
|
+
is retried at the next checkpoint. Partial replies are replaced by the completed
|
|
387
|
+
reply in the saved snapshot, so reopening history does not duplicate them.
|
|
388
|
+
|
|
389
|
+
Key handlers and the render loop are also wrapped: they run inside `stdin`
|
|
390
|
+
callbacks and timers, where a throw is an uncaught exception that used to end
|
|
391
|
+
the session. A failed frame is now retried, and only a screen that cannot be
|
|
392
|
+
drawn at all stops the app.
|
|
393
|
+
|
|
394
|
+
An uncatchable kill (SIGKILL, TerminateProcess or a runtime/OS failure) cannot
|
|
395
|
+
run cleanup. Already-written snapshots survive; with a writable disk, streamed
|
|
396
|
+
output since the last one-second checkpoint may be lost. If a shell is stuck
|
|
397
|
+
in mouse mode, run this in Bash:
|
|
398
|
+
|
|
399
|
+
```sh
|
|
400
|
+
printf '\033[?2004l\033[?1000l\033[?1002l\033[?1003l\033[?1006l\033[?1015l\033[?25h\033[0m\033[?1049l'
|
|
401
|
+
stty sane
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
In PowerShell:
|
|
405
|
+
|
|
406
|
+
```powershell
|
|
407
|
+
$esc = [char]27
|
|
408
|
+
[Console]::Write("${esc}[?2004l${esc}[?1000l${esc}[?1002l${esc}[?1003l${esc}[?1006l${esc}[?1015l${esc}[?25h${esc}[0m${esc}[?1049l")
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## License
|
|
412
|
+
|
|
413
|
+
Apache-2.0. See [LICENSE](./LICENSE).
|
package/assets/prompt.txt
CHANGED
|
@@ -16,5 +16,9 @@ Guidelines:
|
|
|
16
16
|
- When suggesting changes, explain the reasoning.
|
|
17
17
|
- Use the tools available to you to explore the codebase before making changes.
|
|
18
18
|
- Verify your changes by running tests and linting when possible.
|
|
19
|
+
- Every tool call must carry a `reason` argument: one honest sentence saying what
|
|
20
|
+
the call is for and why it is needed for the current request. It is what the
|
|
21
|
+
user sees when the call needs approval, and a call that needs approval but gives
|
|
22
|
+
no reason is refused instead of run.
|
|
19
23
|
|
|
20
24
|
Language: Always respond in the same language the user writes in.
|