@adecore/agents 0.29.2 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat/acp-backend.d.ts +59 -0
- package/dist/chat/acp-backend.js +407 -0
- package/dist/chat/acp-protocol.d.ts +131 -0
- package/dist/chat/acp-protocol.js +605 -0
- package/dist/chat/chat-session.d.ts +2 -0
- package/dist/chat/chat-session.js +14 -10
- package/dist/chat/codex-transport.d.ts +4 -27
- package/dist/chat/codex-transport.js +4 -185
- package/dist/chat/fixtures/acp-v1-turn.json +240 -0
- package/dist/chat/index.d.ts +2 -0
- package/dist/chat/index.js +1 -0
- package/dist/chat/json-rpc-transport.d.ts +50 -0
- package/dist/chat/json-rpc-transport.js +203 -0
- package/dist/providers/accounts/accounts.d.ts +10 -6
- package/dist/providers/accounts/accounts.js +19 -19
- package/dist/providers/accounts/service.d.ts +3 -0
- package/dist/providers/accounts/service.js +20 -13
- package/dist/providers/accounts/store.d.ts +4 -5
- package/dist/providers/accounts/store.js +12 -12
- package/dist/providers/acp-provider.d.ts +22 -0
- package/dist/providers/acp-provider.js +45 -0
- package/dist/providers/catalog.js +1 -0
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.js +1 -0
- package/dist/providers/registry.d.ts +1 -0
- package/dist/providers/registry.js +4 -0
- package/dist/testing/fake-acp.d.ts +10 -0
- package/dist/testing/fake-acp.js +262 -0
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.js +1 -0
- package/package.json +3 -3
- package/src/chat/acp-backend.ts +476 -0
- package/src/chat/acp-protocol.ts +763 -0
- package/src/chat/chat-session.ts +17 -8
- package/src/chat/codex-transport.ts +6 -207
- package/src/chat/fixtures/acp-v1-turn.json +240 -0
- package/src/chat/index.ts +2 -0
- package/src/chat/json-rpc-transport.ts +253 -0
- package/src/providers/accounts/accounts.ts +28 -25
- package/src/providers/accounts/service.ts +37 -14
- package/src/providers/accounts/store.ts +14 -17
- package/src/providers/acp-provider.ts +73 -0
- package/src/providers/catalog.ts +1 -0
- package/src/providers/index.ts +1 -0
- package/src/providers/registry.ts +5 -0
- package/src/testing/fake-acp.ts +296 -0
- package/src/testing/index.ts +1 -0
package/src/chat/chat-session.ts
CHANGED
|
@@ -198,6 +198,8 @@ export class ChatSession {
|
|
|
198
198
|
// The last turn the main agent spoke in; a turn opened only for a subagent's request gets no `result`.
|
|
199
199
|
private spokenTurnId: string | null = null;
|
|
200
200
|
private titleTimer: ReturnType<typeof setTimeout> | null = null;
|
|
201
|
+
/* Checkpoints still asking git, which `dispose` waits for: the host may remove the folder next. */
|
|
202
|
+
private readonly checkpointWork = new Set<Promise<void>>();
|
|
201
203
|
// A name is asked for once per chat this host holds; the turn count keeps it once across restarts.
|
|
202
204
|
private naming = false;
|
|
203
205
|
// Set while the host goes down: a CLI dying with it must not end the turn a restart takes up again.
|
|
@@ -1341,7 +1343,7 @@ export class ChatSession {
|
|
|
1341
1343
|
this.backend?.stop();
|
|
1342
1344
|
}
|
|
1343
1345
|
|
|
1344
|
-
/* Ends the CLI and everything it started; settles once it exited or was sent the SIGKILL. */
|
|
1346
|
+
/* Ends the CLI and everything it started; settles once it exited or was sent the SIGKILL and no checkpoint asks git any more. */
|
|
1345
1347
|
dispose(): Promise<void> {
|
|
1346
1348
|
this.uiReplies.clear();
|
|
1347
1349
|
this.clearStartWarning();
|
|
@@ -1353,7 +1355,7 @@ export class ChatSession {
|
|
|
1353
1355
|
const ended = this.backend?.dispose() ?? Promise.resolve();
|
|
1354
1356
|
this.backend = null;
|
|
1355
1357
|
this.starting = null;
|
|
1356
|
-
return ended;
|
|
1358
|
+
return Promise.all([ended, ...this.checkpointWork]).then(() => undefined);
|
|
1357
1359
|
}
|
|
1358
1360
|
|
|
1359
1361
|
/* What a turn changed against its checkpoint: the stored answer, or one taken now while it runs. */
|
|
@@ -1645,15 +1647,15 @@ export class ChatSession {
|
|
|
1645
1647
|
return Promise.resolve();
|
|
1646
1648
|
}
|
|
1647
1649
|
const cwd = this.thread.info.cwd;
|
|
1648
|
-
return
|
|
1649
|
-
.then(([tree, head]) => {
|
|
1650
|
+
return this.trackCheckpoint(
|
|
1651
|
+
Promise.all([checkpoints.take(cwd), checkpoints.head?.(cwd) ?? null]).then(([tree, head]) => {
|
|
1650
1652
|
const turn = this.thread.get(turnId);
|
|
1651
1653
|
if (tree === null || turn?.kind !== 'turn' || turn.state !== 'running' || this.info.activeTurnId !== turnId) {
|
|
1652
1654
|
return;
|
|
1653
1655
|
}
|
|
1654
1656
|
this.emit([this.thread.upsert({ ...turn, checkpoint: tree, ...(head === null ? {} : { checkpointHead: head }) })]);
|
|
1655
1657
|
})
|
|
1656
|
-
|
|
1658
|
+
);
|
|
1657
1659
|
}
|
|
1658
1660
|
|
|
1659
1661
|
/* The diff of a turn that just ended, so a reload shows it without asking git again, and the tree a fork after it starts from. */
|
|
@@ -1664,8 +1666,8 @@ export class ChatSession {
|
|
|
1664
1666
|
}
|
|
1665
1667
|
const { checkpoints } = this.options;
|
|
1666
1668
|
const cwd = this.thread.info.cwd;
|
|
1667
|
-
void
|
|
1668
|
-
.then(([answer, head]) => {
|
|
1669
|
+
void this.trackCheckpoint(
|
|
1670
|
+
Promise.all([checkpoints.settle(cwd, turn.checkpoint), checkpoints.head?.(cwd) ?? null]).then(([answer, head]) => {
|
|
1669
1671
|
const settled = this.thread.get(turnId);
|
|
1670
1672
|
if (answer === null || settled?.kind !== 'turn') {
|
|
1671
1673
|
return;
|
|
@@ -1680,7 +1682,14 @@ export class ChatSession {
|
|
|
1680
1682
|
]);
|
|
1681
1683
|
this.options.persist();
|
|
1682
1684
|
})
|
|
1683
|
-
|
|
1685
|
+
);
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
/* Keeps the work in `checkpointWork` until it settled; a failure leaves the turn without a checkpoint. */
|
|
1689
|
+
private trackCheckpoint(work: Promise<void>): Promise<void> {
|
|
1690
|
+
const tracked = work.catch(() => undefined).finally(() => this.checkpointWork.delete(tracked));
|
|
1691
|
+
this.checkpointWork.add(tracked);
|
|
1692
|
+
return tracked;
|
|
1684
1693
|
}
|
|
1685
1694
|
|
|
1686
1695
|
/*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { SpawnChatProcess } from './chat-process.ts';
|
|
2
|
+
import { JsonRpcTransport, type JsonRpcFrame } from './json-rpc-transport.ts';
|
|
3
3
|
|
|
4
|
-
export type CodexFrame =
|
|
4
|
+
export type CodexFrame = JsonRpcFrame;
|
|
5
5
|
|
|
6
6
|
/* How the host names itself in the `initialize` every app-server is opened with. */
|
|
7
7
|
export interface CodexClientInfo {
|
|
@@ -17,218 +17,17 @@ interface CodexTransportOptions {
|
|
|
17
17
|
cwd: string;
|
|
18
18
|
env: Record<string, string>;
|
|
19
19
|
spawn?: SpawnChatProcess;
|
|
20
|
-
// Notifications and server requests; responses to our own requests settle their promise instead.
|
|
21
20
|
onFrame(frame: CodexFrame): void;
|
|
22
21
|
onError?(error: Error): void;
|
|
23
|
-
// `stderr` is the tail of what the app-server wrote there, for an exit with an error code only.
|
|
24
22
|
onExit(exitCode: number | null, stderr: string | null): void;
|
|
25
23
|
}
|
|
26
24
|
|
|
27
|
-
class CodexRpcError extends Error {
|
|
28
|
-
readonly code: number | null;
|
|
29
|
-
|
|
30
|
-
constructor(method: string, error: unknown) {
|
|
31
|
-
const detail = typeof error === 'object' && error !== null ? (error as { message?: unknown; code?: unknown }) : {};
|
|
32
|
-
super(`${method} failed: ${typeof detail.message === 'string' ? detail.message : JSON.stringify(error)}`);
|
|
33
|
-
this.name = 'CodexRpcError';
|
|
34
|
-
this.code = typeof detail.code === 'number' ? detail.code : null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
type Settle = { resolve(value: unknown): void; reject(reason: Error): void; method: string; timer: ReturnType<typeof setTimeout> };
|
|
39
|
-
|
|
40
25
|
// The app-server answers every request it takes at once and does the work in notifications, so a silent one is stuck.
|
|
41
26
|
const REQUEST_TIMEOUT_MS = 60_000;
|
|
42
27
|
|
|
43
|
-
/*
|
|
44
|
-
|
|
45
|
-
* a request carries an id and gets a result or an error back under the same id, a notification
|
|
46
|
-
* has no id. The app-server also sends requests of its own (approvals), answered with `respond`.
|
|
47
|
-
*/
|
|
48
|
-
export class CodexTransport {
|
|
49
|
-
private readonly child: ChatChild;
|
|
50
|
-
private readonly options: CodexTransportOptions;
|
|
51
|
-
private readonly pending = new Map<number, Settle>();
|
|
52
|
-
private nextId = 1;
|
|
53
|
-
private closed = false;
|
|
54
|
-
// Set once stdin ended or broke; a write after an EPIPE only raises the next one.
|
|
55
|
-
private inputClosed = false;
|
|
56
|
-
|
|
28
|
+
/* One `codex app-server` process. Its frames leave out the `jsonrpc` field, and an error it answers with is named `CodexRpcError`. */
|
|
29
|
+
export class CodexTransport extends JsonRpcTransport {
|
|
57
30
|
constructor(options: CodexTransportOptions) {
|
|
58
|
-
|
|
59
|
-
this.child = new ChatChild({
|
|
60
|
-
command: options.command,
|
|
61
|
-
cwd: options.cwd,
|
|
62
|
-
env: options.env,
|
|
63
|
-
...(options.spawn ? { spawn: options.spawn } : {}),
|
|
64
|
-
onError: (error) => this.broken(error),
|
|
65
|
-
onExit: (exitCode, stderr) => this.handleExit(exitCode, stderr)
|
|
66
|
-
});
|
|
67
|
-
void this.readLines();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
get pid(): number {
|
|
71
|
-
return this.child.pid;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
get alive(): boolean {
|
|
75
|
-
return !this.closed;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
request(method: string, params: unknown): Promise<unknown> {
|
|
79
|
-
return new Promise((resolve, reject) => {
|
|
80
|
-
if (this.closed) {
|
|
81
|
-
reject(new Error(`${method} failed: Codex is not running`));
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
const id = this.nextId++;
|
|
85
|
-
const timer = setTimeout(() => {
|
|
86
|
-
this.pending.delete(id);
|
|
87
|
-
reject(new Error(`${method} failed: Codex did not answer in ${REQUEST_TIMEOUT_MS / 1000} seconds`));
|
|
88
|
-
}, REQUEST_TIMEOUT_MS);
|
|
89
|
-
timer.unref?.();
|
|
90
|
-
this.pending.set(id, { resolve, reject, method, timer });
|
|
91
|
-
try {
|
|
92
|
-
this.write({ id, method, params });
|
|
93
|
-
} catch (error) {
|
|
94
|
-
clearTimeout(timer);
|
|
95
|
-
this.pending.delete(id);
|
|
96
|
-
reject(error);
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
notify(method: string, params: unknown): void {
|
|
102
|
-
this.write({ method, params });
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
respond(id: number | string, result: unknown): void {
|
|
106
|
-
this.write({ id, result });
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
respondError(id: number | string, code: number, message: string): void {
|
|
110
|
-
this.write({ id, error: { code, message } });
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/* Closes stdin; the app-server ends on its own once it has nothing more to say. */
|
|
114
|
-
end(): void {
|
|
115
|
-
this.inputClosed = true;
|
|
116
|
-
try {
|
|
117
|
-
this.child.process.stdin.end();
|
|
118
|
-
} catch {
|
|
119
|
-
// Already closed by the other side.
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
kill(signal: 'SIGTERM' | 'SIGKILL' = 'SIGTERM'): void {
|
|
124
|
-
this.child.process.kill(signal);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
stop(): void {
|
|
128
|
-
this.end();
|
|
129
|
-
this.child.endAfterGrace();
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
dispose(): Promise<void> {
|
|
133
|
-
return this.child.end();
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
private write(frame: CodexFrame): void {
|
|
137
|
-
if (this.closed || this.inputClosed) {
|
|
138
|
-
throw new Error('Codex stdin is closed');
|
|
139
|
-
}
|
|
140
|
-
try {
|
|
141
|
-
this.child.process.stdin.write(`${JSON.stringify(frame)}\n`);
|
|
142
|
-
this.child.process.stdin.flush();
|
|
143
|
-
} catch (error) {
|
|
144
|
-
this.inputClosed = true;
|
|
145
|
-
throw error;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/* A pipe that failed takes no more writes; a frame a consumer choked on leaves it as it was. */
|
|
150
|
-
private broken(error: unknown): void {
|
|
151
|
-
this.inputClosed = true;
|
|
152
|
-
this.failed(error);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
private failed(error: unknown): void {
|
|
156
|
-
const failure = new Error(`Codex transport failed: ${errorText(error)}`);
|
|
157
|
-
this.rejectPending(() => failure);
|
|
158
|
-
try {
|
|
159
|
-
this.options.onError?.(failure);
|
|
160
|
-
} catch (reportError) {
|
|
161
|
-
console.error('Reporting a Codex transport failure failed:', errorText(reportError));
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
private readLine(line: string): void {
|
|
166
|
-
try {
|
|
167
|
-
this.handleLine(line);
|
|
168
|
-
} catch (error) {
|
|
169
|
-
this.failed(error);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
private async readLines(): Promise<void> {
|
|
174
|
-
try {
|
|
175
|
-
await readStreamLines(
|
|
176
|
-
this.child.process.stdout,
|
|
177
|
-
(line) => this.readLine(line),
|
|
178
|
-
() => this.failed(new Error(`Codex wrote a frame of more than ${MAX_LINE_LENGTH / 1_000_000} million characters, which was dropped`))
|
|
179
|
-
);
|
|
180
|
-
} catch (error) {
|
|
181
|
-
if (!this.closed) {
|
|
182
|
-
this.broken(error);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
private handleLine(line: string): void {
|
|
188
|
-
if (this.closed || line.trim() === '') {
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
let frame: unknown;
|
|
192
|
-
try {
|
|
193
|
-
frame = JSON.parse(line);
|
|
194
|
-
} catch {
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
if (typeof frame !== 'object' || frame === null || Array.isArray(frame)) {
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
const message = frame as CodexFrame;
|
|
201
|
-
if (typeof message.method === 'string') {
|
|
202
|
-
this.options.onFrame(message);
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
const settle = typeof message.id === 'number' ? this.pending.get(message.id) : undefined;
|
|
206
|
-
if (!settle) {
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
this.pending.delete(message.id as number);
|
|
210
|
-
clearTimeout(settle.timer);
|
|
211
|
-
if (message.error !== undefined) {
|
|
212
|
-
settle.reject(new CodexRpcError(settle.method, message.error));
|
|
213
|
-
} else {
|
|
214
|
-
settle.resolve(message.result);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
private handleExit(exitCode: number | null, stderr: string | null): void {
|
|
219
|
-
if (this.closed) {
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
this.closed = true;
|
|
223
|
-
this.rejectPending((method) => new Error(`${method} failed: Codex exited`));
|
|
224
|
-
this.options.onExit(exitCode, stderr);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
private rejectPending(reason: (method: string) => Error): void {
|
|
228
|
-
for (const settle of this.pending.values()) {
|
|
229
|
-
clearTimeout(settle.timer);
|
|
230
|
-
settle.reject(reason(settle.method));
|
|
231
|
-
}
|
|
232
|
-
this.pending.clear();
|
|
31
|
+
super({ ...options, label: 'Codex', errorName: 'CodexRpcError', requestTimeoutMs: REQUEST_TIMEOUT_MS });
|
|
233
32
|
}
|
|
234
33
|
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "ACP protocol version 1",
|
|
3
|
+
"source": "Written by hand from the schema at agentclientprotocol.com: one turn of an agent that thinks, answers, runs a command behind a permission request, edits a file, keeps a plan and reports usage. Frames go from the agent to the client; `response` is the client's answer where one is due. Unknown fields and an unknown update kind are there on purpose.",
|
|
4
|
+
"sessionId": "sess_7f2a",
|
|
5
|
+
"initialize": {
|
|
6
|
+
"jsonrpc": "2.0",
|
|
7
|
+
"id": 1,
|
|
8
|
+
"result": {
|
|
9
|
+
"protocolVersion": 1,
|
|
10
|
+
"agentCapabilities": {
|
|
11
|
+
"loadSession": true,
|
|
12
|
+
"promptCapabilities": { "image": true, "audio": false, "embeddedContext": true },
|
|
13
|
+
"mcpCapabilities": { "http": false, "sse": false },
|
|
14
|
+
"sessionCapabilities": { "additionalDirectories": {} },
|
|
15
|
+
"futureCapability": { "anything": true }
|
|
16
|
+
},
|
|
17
|
+
"authMethods": [{ "id": "api-key", "name": "API key", "description": "Reads a key from the environment" }, { "broken": true }],
|
|
18
|
+
"agentInfo": { "name": "example-agent", "title": "Example Agent", "version": "1.4.0" }
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"sessionNew": {
|
|
22
|
+
"jsonrpc": "2.0",
|
|
23
|
+
"id": 2,
|
|
24
|
+
"result": {
|
|
25
|
+
"sessionId": "sess_7f2a",
|
|
26
|
+
"modes": {
|
|
27
|
+
"currentModeId": "default",
|
|
28
|
+
"availableModes": [
|
|
29
|
+
{ "id": "default", "name": "Ask before changes" },
|
|
30
|
+
{ "id": "accept-edits", "name": "Accept edits" },
|
|
31
|
+
{ "id": "plan", "name": "Plan" },
|
|
32
|
+
{ "name": "missing an id" }
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
"configOptions": [
|
|
36
|
+
{
|
|
37
|
+
"id": "model",
|
|
38
|
+
"name": "Model",
|
|
39
|
+
"category": "model",
|
|
40
|
+
"type": "select",
|
|
41
|
+
"currentValue": "example-fast",
|
|
42
|
+
"options": [
|
|
43
|
+
{ "group": "fast", "name": "Fast", "options": [{ "value": "example-fast", "name": "Example Fast" }] },
|
|
44
|
+
{ "group": "deep", "name": "Deep", "options": [{ "value": "example-deep", "name": "Example Deep" }] }
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "reasoning",
|
|
49
|
+
"name": "Reasoning",
|
|
50
|
+
"category": "thought_level",
|
|
51
|
+
"type": "select",
|
|
52
|
+
"currentValue": "medium",
|
|
53
|
+
"options": [
|
|
54
|
+
{ "value": "low", "name": "Low" },
|
|
55
|
+
{ "value": "medium", "name": "Medium" },
|
|
56
|
+
{ "value": "high", "name": "High" }
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
{ "id": "web", "name": "Web access", "type": "boolean", "currentValue": true }
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"frames": [
|
|
64
|
+
{
|
|
65
|
+
"jsonrpc": "2.0",
|
|
66
|
+
"method": "session/update",
|
|
67
|
+
"params": {
|
|
68
|
+
"sessionId": "sess_7f2a",
|
|
69
|
+
"update": {
|
|
70
|
+
"sessionUpdate": "available_commands_update",
|
|
71
|
+
"availableCommands": [
|
|
72
|
+
{ "name": "review", "description": "Review the changes", "input": null },
|
|
73
|
+
{ "name": "init", "description": "Write an instructions file" },
|
|
74
|
+
{ "description": "missing a name" }
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"jsonrpc": "2.0",
|
|
81
|
+
"method": "session/update",
|
|
82
|
+
"params": { "sessionId": "sess_7f2a", "update": { "sessionUpdate": "agent_thought_chunk", "content": { "type": "text", "text": "The test fails on " } } }
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"jsonrpc": "2.0",
|
|
86
|
+
"method": "session/update",
|
|
87
|
+
"params": { "sessionId": "sess_7f2a", "update": { "sessionUpdate": "agent_thought_chunk", "content": { "type": "text", "text": "an off-by-one." } } }
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"jsonrpc": "2.0",
|
|
91
|
+
"method": "session/update",
|
|
92
|
+
"params": { "sessionId": "sess_7f2a", "update": { "sessionUpdate": "agent_message_chunk", "messageId": "msg_1", "content": { "type": "text", "text": "Let me run " } } }
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"jsonrpc": "2.0",
|
|
96
|
+
"method": "session/update",
|
|
97
|
+
"params": { "sessionId": "sess_7f2a", "update": { "sessionUpdate": "agent_message_chunk", "messageId": "msg_1", "content": { "type": "text", "text": "the tests." } } }
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"jsonrpc": "2.0",
|
|
101
|
+
"method": "session/update",
|
|
102
|
+
"params": { "sessionId": "sess_7f2a", "update": { "sessionUpdate": "agent_message_chunk", "content": { "type": "image", "data": "iVBORw0KGgo=", "mimeType": "image/png" } } }
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"jsonrpc": "2.0",
|
|
106
|
+
"method": "session/update",
|
|
107
|
+
"params": {
|
|
108
|
+
"sessionId": "sess_7f2a",
|
|
109
|
+
"update": {
|
|
110
|
+
"sessionUpdate": "tool_call",
|
|
111
|
+
"toolCallId": "call_001",
|
|
112
|
+
"title": "Run the test suite",
|
|
113
|
+
"kind": "execute",
|
|
114
|
+
"status": "pending",
|
|
115
|
+
"rawInput": { "command": ["bun", "test"], "timeout": 120 }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"jsonrpc": "2.0",
|
|
121
|
+
"id": 0,
|
|
122
|
+
"method": "session/request_permission",
|
|
123
|
+
"params": {
|
|
124
|
+
"sessionId": "sess_7f2a",
|
|
125
|
+
"toolCall": { "toolCallId": "call_001" },
|
|
126
|
+
"options": [
|
|
127
|
+
{ "optionId": "allow", "name": "Allow", "kind": "allow_once" },
|
|
128
|
+
{ "optionId": "allow-bun", "name": "Always allow bun", "kind": "allow_always" },
|
|
129
|
+
{ "optionId": "deny", "name": "Deny", "kind": "reject_once" }
|
|
130
|
+
]
|
|
131
|
+
},
|
|
132
|
+
"response": { "decision": "allow", "result": { "outcome": { "outcome": "selected", "optionId": "allow" } } }
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"jsonrpc": "2.0",
|
|
136
|
+
"method": "session/update",
|
|
137
|
+
"params": { "sessionId": "sess_7f2a", "update": { "sessionUpdate": "tool_call_update", "toolCallId": "call_001", "status": "in_progress" } }
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"jsonrpc": "2.0",
|
|
141
|
+
"method": "session/update",
|
|
142
|
+
"params": {
|
|
143
|
+
"sessionId": "sess_7f2a",
|
|
144
|
+
"update": {
|
|
145
|
+
"sessionUpdate": "tool_call_update",
|
|
146
|
+
"toolCallId": "call_001",
|
|
147
|
+
"status": "failed",
|
|
148
|
+
"content": [
|
|
149
|
+
{ "type": "content", "content": { "type": "text", "text": "1 fail" } },
|
|
150
|
+
{ "type": "content", "content": { "type": "text", "text": "expected 3, received 4" } }
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"jsonrpc": "2.0",
|
|
157
|
+
"method": "session/update",
|
|
158
|
+
"params": {
|
|
159
|
+
"sessionId": "sess_7f2a",
|
|
160
|
+
"update": {
|
|
161
|
+
"sessionUpdate": "plan",
|
|
162
|
+
"entries": [
|
|
163
|
+
{ "content": "Find the off-by-one", "priority": "high", "status": "completed" },
|
|
164
|
+
{ "content": "Fix the loop bound", "priority": "high", "status": "in_progress" },
|
|
165
|
+
{ "content": "Run the tests again", "priority": "medium", "status": "pending" }
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"jsonrpc": "2.0",
|
|
172
|
+
"method": "session/update",
|
|
173
|
+
"params": {
|
|
174
|
+
"sessionId": "sess_7f2a",
|
|
175
|
+
"update": {
|
|
176
|
+
"sessionUpdate": "tool_call",
|
|
177
|
+
"toolCallId": "call_002",
|
|
178
|
+
"title": "Edit src/range.ts",
|
|
179
|
+
"kind": "edit",
|
|
180
|
+
"status": "pending",
|
|
181
|
+
"locations": [{ "path": "/work/demo/src/range.ts", "line": 12 }]
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"jsonrpc": "2.0",
|
|
187
|
+
"method": "session/update",
|
|
188
|
+
"params": {
|
|
189
|
+
"sessionId": "sess_7f2a",
|
|
190
|
+
"update": {
|
|
191
|
+
"sessionUpdate": "tool_call_update",
|
|
192
|
+
"toolCallId": "call_002",
|
|
193
|
+
"status": "completed",
|
|
194
|
+
"content": [
|
|
195
|
+
{
|
|
196
|
+
"type": "diff",
|
|
197
|
+
"path": "/work/demo/src/range.ts",
|
|
198
|
+
"oldText": "for (let i = 0; i <= end; i++) {\n",
|
|
199
|
+
"newText": "for (let i = 0; i < end; i++) {\n"
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"jsonrpc": "2.0",
|
|
207
|
+
"method": "session/update",
|
|
208
|
+
"params": { "sessionId": "sess_7f2a", "update": { "sessionUpdate": "a_future_update", "payload": { "anything": true } } }
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"jsonrpc": "2.0",
|
|
212
|
+
"method": "session/update",
|
|
213
|
+
"params": { "sessionId": "another_session", "update": { "sessionUpdate": "agent_message_chunk", "content": { "type": "text", "text": "not ours" } } }
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"jsonrpc": "2.0",
|
|
217
|
+
"method": "session/update",
|
|
218
|
+
"params": { "sessionId": "sess_7f2a", "update": { "sessionUpdate": "current_mode_update", "currentModeId": "accept-edits" } }
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"jsonrpc": "2.0",
|
|
222
|
+
"method": "session/update",
|
|
223
|
+
"params": { "sessionId": "sess_7f2a", "update": { "sessionUpdate": "agent_message_chunk", "messageId": "msg_2", "content": { "type": "text", "text": "Fixed the bound." } } }
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"jsonrpc": "2.0",
|
|
227
|
+
"method": "session/update",
|
|
228
|
+
"params": {
|
|
229
|
+
"sessionId": "sess_7f2a",
|
|
230
|
+
"update": { "sessionUpdate": "usage_update", "used": 48210, "size": 200000, "cost": { "amount": 0.0421, "currency": "USD" } }
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"jsonrpc": "2.0",
|
|
235
|
+
"method": "session/update",
|
|
236
|
+
"params": { "sessionId": "sess_7f2a", "update": { "sessionUpdate": "session_info_update", "title": "Fix the range off-by-one" } }
|
|
237
|
+
}
|
|
238
|
+
],
|
|
239
|
+
"promptResponse": { "jsonrpc": "2.0", "id": 3, "result": { "stopReason": "end_turn" } }
|
|
240
|
+
}
|
package/src/chat/index.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// Public API: exports.test.ts snapshots this list, so a name added or dropped here is an API change.
|
|
2
|
+
export { type AcpAgentSpec, type AcpClientInfo, createAcpBackend } from './acp-backend.ts';
|
|
3
|
+
export type { AcpAgentInfo, AcpModel, AcpModelState, AcpMode, AcpModeState, AcpSessionInfo, AcpModeMap } from './acp-protocol.ts';
|
|
2
4
|
export { AttachmentStore } from './attachment-store.ts';
|
|
3
5
|
export type { BackendLaunch, TurnInput, ApprovalDecision, BackendEvent, BackendHost, ChatBackend } from './backend.ts';
|
|
4
6
|
export { runningInBackground, type BackgroundWork } from './background-work.ts';
|