reslop 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -1
- package/README.md +21 -3
- package/lib/agents/acp.js +309 -0
- package/lib/agents/agents.js +166 -370
- package/lib/agents/allow.js +141 -52
- package/lib/agents/allow.json +16 -13
- package/lib/agents/models.js +250 -376
- package/lib/agents/registry.js +437 -0
- package/lib/agents/run.js +234 -0
- package/lib/agents/transcript.js +143 -0
- package/lib/cli.js +2 -1
- package/lib/common/process.js +26 -1
- package/lib/dashboard/git.js +11 -1
- package/lib/dashboard/reports.js +80 -0
- package/lib/dashboard/watch.js +43 -8
- package/lib/dashboard/workspace.js +19 -20
- package/lib/dashboard/worktrees.js +145 -0
- package/lib/git/deps.js +29 -0
- package/lib/git/dir.js +50 -0
- package/lib/git/git.js +1 -1
- package/lib/git/linked.js +152 -0
- package/lib/git/local.js +179 -0
- package/lib/git/status.js +126 -0
- package/lib/git/worktree.js +137 -31
- package/lib/input/actions.js +83 -2
- package/lib/input/editor.js +9 -0
- package/lib/input/keys.js +39 -3
- package/lib/input/line-edit.js +8 -3
- package/lib/render/agents.js +64 -36
- package/lib/render/branches.js +55 -27
- package/lib/render/field.js +19 -5
- package/lib/render/footer.js +14 -4
- package/lib/render/frame.js +16 -2
- package/lib/render/log.js +5 -4
- package/lib/render/menu.js +103 -6
- package/lib/render/name-edit.js +31 -0
- package/lib/render/repos.js +52 -8
- package/lib/render/status.js +22 -4
- package/lib/report/render.js +4 -7
- package/lib/report/run.js +16 -23
- package/lib/review/agent-instructions.md +13 -8
- package/lib/review/format.js +0 -6
- package/lib/runs/process.js +3 -3
- package/lib/runs/runs.js +27 -4
- package/lib/session/agents/agents.js +227 -194
- package/lib/session/agents/away.js +48 -0
- package/lib/session/agents/job.js +23 -86
- package/lib/session/agents/jobs.js +14 -27
- package/lib/session/agents/options.js +45 -101
- package/lib/session/agents/plans.js +53 -0
- package/lib/session/agents/view.js +34 -0
- package/lib/session/branches.js +18 -2
- package/lib/session/compose.js +20 -6
- package/lib/session/dispatch.js +43 -3
- package/lib/session/editor-pointer.js +98 -0
- package/lib/session/files-pane.js +3 -0
- package/lib/session/import.js +12 -1
- package/lib/session/lifecycle.js +70 -10
- package/lib/session/navigation.js +5 -0
- package/lib/session/npm/history.js +3 -1
- package/lib/session/npm/npm.js +111 -13
- package/lib/session/ops.js +3 -0
- package/lib/session/pointer.js +304 -46
- package/lib/session/session.js +17 -2
- package/lib/session/tasks.js +1 -1
- package/lib/session/terminal.js +2 -2
- package/lib/session/text-menu.js +50 -0
- package/lib/session/view.js +19 -2
- package/lib/session/workspace.js +179 -38
- package/lib/session/worktrees.js +280 -0
- package/lib/term/select.js +2 -1
- package/lib/term/themes/dark.json +4 -0
- package/lib/term/themes/light.json +4 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased][unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.3.2][] - 2026-10-15
|
|
6
|
+
|
|
7
|
+
- Communicate with agents over the Agent Client Protocol instead of CLI
|
|
8
|
+
- Listed models from the agent session, with effort, context, fast
|
|
9
|
+
- Answer agent permission requests
|
|
10
|
+
- Installing a missing agent's and ACP for them
|
|
11
|
+
- Add Google Antigravity's support
|
|
12
|
+
- Started a new agent session or continued the selected one
|
|
13
|
+
- Showed a plan heading or task as the session caption
|
|
14
|
+
- Support git worktrees at branches screen
|
|
15
|
+
- Added pop-up menus on tables and UI controls
|
|
16
|
+
- Add new hotkeys for better UX
|
|
17
|
+
- Improve command and agent background runners
|
|
18
|
+
|
|
19
|
+
## [0.3.1][] - 2026-10-13
|
|
20
|
+
|
|
21
|
+
- Allow agents to write to work with git
|
|
22
|
+
- Add default URL for tasks import in UI
|
|
23
|
+
- Implement session continue for supported agents
|
|
24
|
+
- Fix scroller in agents sessions list
|
|
25
|
+
- Multiple fixes in background agents and command runner
|
|
26
|
+
- Sync the lockfile when a `package.json` change is staged
|
|
27
|
+
- Update agent instructions to make commits
|
|
28
|
+
- Support Ctrl+Backspace word deletion in editors
|
|
29
|
+
- Support Shift-cursor selection in the tasks editor
|
|
30
|
+
|
|
5
31
|
## [0.3.0][] - 2026-10-12
|
|
6
32
|
|
|
7
33
|
- Execute review results and plan in cli agents
|
|
@@ -187,7 +213,9 @@
|
|
|
187
213
|
- Feedback and todos that produce a repair plan for an agent
|
|
188
214
|
- Intra-line highlighting in unified, mixed, and side-by-side layouts
|
|
189
215
|
|
|
190
|
-
[unreleased]: https://github.com/tshemsedinov/reslop/compare/v0.3.
|
|
216
|
+
[unreleased]: https://github.com/tshemsedinov/reslop/compare/v0.3.2...HEAD
|
|
217
|
+
[0.3.2]: https://github.com/tshemsedinov/reslop/compare/v0.3.1...v0.3.2
|
|
218
|
+
[0.3.1]: https://github.com/tshemsedinov/reslop/compare/v0.3.0...v0.3.1
|
|
191
219
|
[0.3.0]: https://github.com/tshemsedinov/reslop/compare/v0.2.7...v0.3.0
|
|
192
220
|
[0.2.7]: https://github.com/tshemsedinov/reslop/compare/v0.2.6...v0.2.7
|
|
193
221
|
[0.2.6]: https://github.com/tshemsedinov/reslop/compare/v0.2.5...v0.2.6
|
package/README.md
CHANGED
|
@@ -31,8 +31,8 @@ Review → Plan → Repair → Verify
|
|
|
31
31
|
- Review, renew, audit and update npm dependencies in `package.json` and the lockfile
|
|
32
32
|
- Auto-update reslop patch and minor releases; confirm a new major
|
|
33
33
|
- Work with git, npm, and fetch in the background; run tests in the background while you review
|
|
34
|
-
- Execute the plan in local
|
|
35
|
-
- Agent defaults
|
|
34
|
+
- Execute the plan in local agents over ACP: pick agent, model, and effort; watch progress; sessions, history, and continue or new runs
|
|
35
|
+
- Agent defaults in `.reslop` (grouped settings sections)
|
|
36
36
|
- Optional line numbers in diff and file views (`Ctrl+L`)
|
|
37
37
|
|
|
38
38
|
## Install
|
|
@@ -47,6 +47,8 @@ Requires Node.js `>=18.15.0`.
|
|
|
47
47
|
## Usage
|
|
48
48
|
|
|
49
49
|
- `reslop` live dashboard of this repository; press a block key to open its screen
|
|
50
|
+
- `reslop folder` tiles of the repositories in a folder, each with its git worktrees
|
|
51
|
+
- git worktrees of a repository are tiles above its branches when there are several: `←` `→` select a tile, `↑` `↓` go through the branches, `enter` or a click shows that worktree, `insert` and `delete` create and remove one
|
|
50
52
|
- `reslop path/file` show only that path or file (starts on the file list)
|
|
51
53
|
- `reslop 7ac260c` show that commit (read-only)
|
|
52
54
|
- `reslop https://github.com/metarhia/metacom/pull/555` GitHub/GitLab PR/MR
|
|
@@ -64,7 +66,7 @@ Prefix each program in an npm script with `reslop t --`. Keep `&&` between those
|
|
|
64
66
|
|
|
65
67
|
The report is Markdown: failures, diagnostics, and a short summary. Passing results are omitted and similar problems are grouped. The reduced report is saved as `.log/<name>.log` and the raw output as `.log/<name>.raw`. The report names the raw file for an agent to read when a detail is missing. Set `RESLOP_OUTPUT=raw` to pass the command through unchanged. The review screen runs scripts in raw mode and applies its own filter once.
|
|
66
68
|
|
|
67
|
-
|
|
69
|
+
`npm t` runs lint and the tests that do not start a process. Run a test file that starts git, node, or npm with `node --test <file>`. New reviews include both commands in the agent instructions.
|
|
68
70
|
|
|
69
71
|
`reslop t` records each run as `.log/<name>.json` beside those files so that the dashboard can show commands started by an agent in another terminal.
|
|
70
72
|
|
|
@@ -76,6 +78,22 @@ Reviews go in `.plan/YYYY-MM-DD-NN.md` with frontmatter `status`:
|
|
|
76
78
|
|
|
77
79
|
`ready` and `pending` in older reviews are read as `editing`.
|
|
78
80
|
|
|
81
|
+
## Agents
|
|
82
|
+
|
|
83
|
+
reslop runs a plan in an agent over the [Agent Client Protocol](https://agentclientprotocol.com). An agent is listed as available when the command that serves ACP is on `PATH`:
|
|
84
|
+
|
|
85
|
+
| Agent | Command | Install |
|
|
86
|
+
| ----------- | -------------------- | ------------------------------------------------ |
|
|
87
|
+
| Claude Code | `claude-agent-acp` | `npm i -g @agentclientprotocol/claude-agent-acp` |
|
|
88
|
+
| Codex | `codex-acp` | `npm i -g @agentclientprotocol/codex-acp` |
|
|
89
|
+
| Cursor | `cursor-agent acp` | comes with the Cursor CLI |
|
|
90
|
+
| OpenCode | `opencode acp` | `npm i -g opencode-ai` |
|
|
91
|
+
| Antigravity | `agy_acp_server.par` | downloaded from the official ACP registry |
|
|
92
|
+
|
|
93
|
+
A missing agent shows what it needs, in the warning color; `i` on that row proposes installing it. npm adapters are installed globally, while Google Antigravity's official ACP runtime is downloaded from the ACP registry into reslop's user data directory. reslop also discovers that runtime on `PATH` and in Zed's registry directory. After confirmation, reslop lists the agent as soon as the install finishes and does not restart. [docs/acp-adapters.md](docs/acp-adapters.md) describes the integrations.
|
|
94
|
+
|
|
95
|
+
Models are the ones the agent lists; efforts, context sizes, and fast mode are the ones it offers for the picked model. When a run ends, the session is set back to what it was, so the defaults of the agent's own CLI stay as they were. A run has nobody to ask, so reslop answers permission requests itself: edits inside the repository and `npm`, `npx`, `node`, `reslop`, and `git` commands, piped through read-only filters such as `grep` or `tail`, are allowed; everything else is refused.
|
|
96
|
+
|
|
79
97
|
## Future
|
|
80
98
|
|
|
81
99
|
- Search, blame, and file history in the review
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('node:child_process');
|
|
4
|
+
const { jsonParse, isHashObject } = require('metautil');
|
|
5
|
+
|
|
6
|
+
const PROTOCOL_VERSION = 1;
|
|
7
|
+
const METHOD_NOT_FOUND = -32601;
|
|
8
|
+
const INTERNAL_ERROR = -32603;
|
|
9
|
+
const AUTH_REQUIRED = -32000;
|
|
10
|
+
const STDERR_KEEP = 2000;
|
|
11
|
+
const SECRET_AUTH = ['env_var', 'terminal'];
|
|
12
|
+
|
|
13
|
+
const errorText = (error) => {
|
|
14
|
+
const data = isHashObject(error.data) ? error.data : {};
|
|
15
|
+
const detail = data.message ?? data.details ?? error.data;
|
|
16
|
+
const text = typeof detail === 'string' ? detail.trim() : '';
|
|
17
|
+
return text || `${error.message ?? 'agent error'}`;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
class AcpError extends Error {
|
|
21
|
+
constructor(error) {
|
|
22
|
+
super(errorText(error));
|
|
23
|
+
this.code = error.code;
|
|
24
|
+
this.data = error.data;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const rpcError = (message, code) => Object.assign(new Error(message), { code });
|
|
29
|
+
|
|
30
|
+
const methodMissing = (method) =>
|
|
31
|
+
rpcError(`Method not found: ${method}`, METHOD_NOT_FOUND);
|
|
32
|
+
|
|
33
|
+
const exitMessage = (status, stderr) => {
|
|
34
|
+
const code = typeof status === 'number' ? ` with code ${status}` : '';
|
|
35
|
+
const tail = stderr.trim();
|
|
36
|
+
return tail ? `agent exited${code}\n${tail}` : `agent exited${code}`;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// JSON-RPC over the stdio of one agent process, a message per line
|
|
40
|
+
class AcpConnection {
|
|
41
|
+
constructor(handlers = {}) {
|
|
42
|
+
this.onNotify = handlers.notify ?? (() => {});
|
|
43
|
+
this.onRequest = handlers.request ?? null;
|
|
44
|
+
this.child = null;
|
|
45
|
+
this.pending = new Map();
|
|
46
|
+
this.nextId = 1;
|
|
47
|
+
this.buffer = '';
|
|
48
|
+
this.stderr = '';
|
|
49
|
+
this.failure = null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
start(cmd, args, options) {
|
|
53
|
+
const stdio = ['pipe', 'pipe', 'pipe'];
|
|
54
|
+
const child = spawn(cmd, args, { ...options, stdio });
|
|
55
|
+
this.child = child;
|
|
56
|
+
child.stdout.setEncoding('utf8');
|
|
57
|
+
child.stdout.on('data', (chunk) => this.read(chunk));
|
|
58
|
+
child.stderr.setEncoding('utf8');
|
|
59
|
+
child.stderr.on('data', (chunk) => {
|
|
60
|
+
this.stderr = `${this.stderr}${chunk}`.slice(-STDERR_KEEP);
|
|
61
|
+
});
|
|
62
|
+
// A closed pipe is reported by the exit of the process
|
|
63
|
+
child.stdin.on('error', () => {});
|
|
64
|
+
child.on('error', (error) => this.fail(error));
|
|
65
|
+
child.on('close', (status) => {
|
|
66
|
+
this.fail(new Error(exitMessage(status, this.stderr)));
|
|
67
|
+
});
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
fail(error) {
|
|
72
|
+
if (this.failure) return;
|
|
73
|
+
this.failure = error;
|
|
74
|
+
for (const waiter of this.pending.values()) waiter.reject(error);
|
|
75
|
+
this.pending.clear();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
send(message) {
|
|
79
|
+
const stdin = this.child && this.child.stdin;
|
|
80
|
+
if (this.failure || !stdin || !stdin.writable) return;
|
|
81
|
+
stdin.write(`${JSON.stringify({ jsonrpc: '2.0', ...message })}\n`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
request(method, params) {
|
|
85
|
+
if (this.failure) return Promise.reject(this.failure);
|
|
86
|
+
const id = this.nextId;
|
|
87
|
+
this.nextId += 1;
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
this.pending.set(id, { resolve, reject });
|
|
90
|
+
this.send({ id, method, params });
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
notify(method, params) {
|
|
95
|
+
this.send({ method, params });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
read(chunk) {
|
|
99
|
+
const lines = `${this.buffer}${chunk}`.split('\n');
|
|
100
|
+
this.buffer = lines.pop();
|
|
101
|
+
for (const line of lines) {
|
|
102
|
+
const message = line.trim() ? jsonParse(line) : null;
|
|
103
|
+
if (isHashObject(message)) this.dispatch(message);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
dispatch(message) {
|
|
108
|
+
const { id, method } = message;
|
|
109
|
+
if (typeof method !== 'string') return void this.settle(message);
|
|
110
|
+
if (id === undefined || id === null) {
|
|
111
|
+
return void this.onNotify(method, message.params ?? {});
|
|
112
|
+
}
|
|
113
|
+
this.answer(id, method, message.params ?? {});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
settle(message) {
|
|
117
|
+
const waiter = this.pending.get(message.id);
|
|
118
|
+
if (!waiter) return;
|
|
119
|
+
this.pending.delete(message.id);
|
|
120
|
+
if (message.error) waiter.reject(new AcpError(message.error));
|
|
121
|
+
else waiter.resolve(message.result ?? {});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async answer(id, method, params) {
|
|
125
|
+
try {
|
|
126
|
+
if (!this.onRequest) throw methodMissing(method);
|
|
127
|
+
const result = await this.onRequest(method, params);
|
|
128
|
+
this.send({ id, result: result ?? {} });
|
|
129
|
+
} catch (error) {
|
|
130
|
+
const code = typeof error.code === 'number' ? error.code : INTERNAL_ERROR;
|
|
131
|
+
this.send({ id, error: { code, message: error.message } });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const authMethod = (init) => {
|
|
137
|
+
const methods = Array.isArray(init.authMethods) ? init.authMethods : [];
|
|
138
|
+
const open = (method) => {
|
|
139
|
+
if (!isHashObject(method) || typeof method.id !== 'string') return false;
|
|
140
|
+
if (SECRET_AUTH.includes(method.type)) return false;
|
|
141
|
+
const meta = isHashObject(method._meta) ? method._meta : {};
|
|
142
|
+
return !Object.keys(meta).some((key) => /api.?key/i.test(key));
|
|
143
|
+
};
|
|
144
|
+
return methods.find(open) ?? null;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const sessionState = (sessionId, result) => ({
|
|
148
|
+
sessionId,
|
|
149
|
+
configOptions: Array.isArray(result.configOptions)
|
|
150
|
+
? result.configOptions
|
|
151
|
+
: [],
|
|
152
|
+
models: isHashObject(result.models) ? result.models : null,
|
|
153
|
+
modes: isHashObject(result.modes) ? result.modes : null,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// One agent session: opened or continued, configured, then prompted
|
|
157
|
+
class AcpSession {
|
|
158
|
+
constructor(connection, cwd, info) {
|
|
159
|
+
this.connection = connection;
|
|
160
|
+
this.cwd = cwd;
|
|
161
|
+
this.info = info;
|
|
162
|
+
this.init = {};
|
|
163
|
+
this.state = sessionState('', {});
|
|
164
|
+
this.replaying = false;
|
|
165
|
+
this.undone = [];
|
|
166
|
+
this.onAuth = () => {};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
get id() {
|
|
170
|
+
return this.state.sessionId;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
request(method, params = {}) {
|
|
174
|
+
const sessionId = this.id;
|
|
175
|
+
return this.connection.request(method, { sessionId, ...params });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async handshake() {
|
|
179
|
+
this.init = await this.connection.request('initialize', {
|
|
180
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
181
|
+
clientCapabilities: {
|
|
182
|
+
fs: { readTextFile: false, writeTextFile: false },
|
|
183
|
+
terminal: false,
|
|
184
|
+
// Cursor lists the settings of a model only to a client that asks
|
|
185
|
+
_meta: { parameterizedModelPicker: true },
|
|
186
|
+
},
|
|
187
|
+
clientInfo: this.info,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Signing in can open a browser, so only a run is allowed to start it
|
|
192
|
+
async signedIn(call, login) {
|
|
193
|
+
try {
|
|
194
|
+
return await call();
|
|
195
|
+
} catch (error) {
|
|
196
|
+
const asked = login && error.code === AUTH_REQUIRED;
|
|
197
|
+
const method = asked ? authMethod(this.init) : null;
|
|
198
|
+
if (!method) throw error;
|
|
199
|
+
this.onAuth(method);
|
|
200
|
+
await this.connection.request('authenticate', { methodId: method.id });
|
|
201
|
+
return call();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
continueMethod() {
|
|
206
|
+
const caps = this.init.agentCapabilities ?? {};
|
|
207
|
+
const session = caps.sessionCapabilities ?? {};
|
|
208
|
+
if (isHashObject(session.resume)) return 'session/resume';
|
|
209
|
+
if (caps.loadSession === true) return 'session/load';
|
|
210
|
+
throw new Error('agent cannot continue a session');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async open(sessionId = '', login = false) {
|
|
214
|
+
await this.handshake();
|
|
215
|
+
const params = { cwd: this.cwd, mcpServers: [] };
|
|
216
|
+
if (!sessionId) {
|
|
217
|
+
const start = () => this.connection.request('session/new', params);
|
|
218
|
+
const result = await this.signedIn(start, login);
|
|
219
|
+
this.state = sessionState(`${result.sessionId ?? ''}`, result);
|
|
220
|
+
return this.state;
|
|
221
|
+
}
|
|
222
|
+
const method = this.continueMethod();
|
|
223
|
+
const resume = () =>
|
|
224
|
+
this.connection.request(method, { ...params, sessionId });
|
|
225
|
+
// A loaded session replays its history as updates before it answers
|
|
226
|
+
this.replaying = true;
|
|
227
|
+
try {
|
|
228
|
+
const result = await this.signedIn(resume, login);
|
|
229
|
+
this.state = sessionState(sessionId, result);
|
|
230
|
+
} finally {
|
|
231
|
+
this.replaying = false;
|
|
232
|
+
}
|
|
233
|
+
return this.state;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// The request that puts back what a change is about to replace
|
|
237
|
+
undoOf(step) {
|
|
238
|
+
const { state } = this;
|
|
239
|
+
const { configId, modelId, modeId } = step.params;
|
|
240
|
+
const option = state.configOptions.find(
|
|
241
|
+
(item) => isHashObject(item) && item.id === configId,
|
|
242
|
+
);
|
|
243
|
+
const params = {};
|
|
244
|
+
if (option) Object.assign(params, { configId, value: option.currentValue });
|
|
245
|
+
if (modelId && state.models) params.modelId = state.models.currentModelId;
|
|
246
|
+
if (modeId && state.modes) params.modeId = state.modes.currentModeId;
|
|
247
|
+
return Object.keys(params).length ? { method: step.method, params } : null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async apply(step) {
|
|
251
|
+
const result = await this.request(step.method, step.params);
|
|
252
|
+
const state = { ...this.state };
|
|
253
|
+
if (Array.isArray(result.configOptions)) {
|
|
254
|
+
state.configOptions = result.configOptions;
|
|
255
|
+
}
|
|
256
|
+
const { modelId, modeId } = step.params;
|
|
257
|
+
if (modelId && state.models) {
|
|
258
|
+
state.models = { ...state.models, currentModelId: modelId };
|
|
259
|
+
}
|
|
260
|
+
if (modeId && state.modes) {
|
|
261
|
+
state.modes = { ...state.modes, currentModeId: modeId };
|
|
262
|
+
}
|
|
263
|
+
this.state = state;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async change(step) {
|
|
267
|
+
if (!step) return;
|
|
268
|
+
const undo = this.undoOf(step);
|
|
269
|
+
await this.apply(step);
|
|
270
|
+
if (undo) this.undone.push(undo);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// An agent can keep what a session was set to as the defaults of the user
|
|
274
|
+
async revert() {
|
|
275
|
+
const steps = this.undone.reverse();
|
|
276
|
+
this.undone = [];
|
|
277
|
+
for (const step of steps) {
|
|
278
|
+
try {
|
|
279
|
+
await this.apply(step);
|
|
280
|
+
} catch {
|
|
281
|
+
// an agent that is gone has nothing left to put back
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
absorb(update) {
|
|
287
|
+
if (update.sessionUpdate !== 'config_option_update') return;
|
|
288
|
+
if (!Array.isArray(update.configOptions)) return;
|
|
289
|
+
this.state = { ...this.state, configOptions: update.configOptions };
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
cancel() {
|
|
293
|
+
const sessionId = this.id;
|
|
294
|
+
if (sessionId) this.connection.notify('session/cancel', { sessionId });
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
prompt(text) {
|
|
298
|
+
return this.request('session/prompt', {
|
|
299
|
+
prompt: [{ type: 'text', text }],
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
module.exports = {
|
|
305
|
+
AcpConnection,
|
|
306
|
+
AcpSession,
|
|
307
|
+
methodMissing,
|
|
308
|
+
authMethod,
|
|
309
|
+
};
|