@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
|
@@ -71,6 +71,8 @@ export class ChatSession {
|
|
|
71
71
|
// The last turn the main agent spoke in; a turn opened only for a subagent's request gets no `result`.
|
|
72
72
|
spokenTurnId = null;
|
|
73
73
|
titleTimer = null;
|
|
74
|
+
/* Checkpoints still asking git, which `dispose` waits for: the host may remove the folder next. */
|
|
75
|
+
checkpointWork = new Set();
|
|
74
76
|
// A name is asked for once per chat this host holds; the turn count keeps it once across restarts.
|
|
75
77
|
naming = false;
|
|
76
78
|
// Set while the host goes down: a CLI dying with it must not end the turn a restart takes up again.
|
|
@@ -1126,7 +1128,7 @@ export class ChatSession {
|
|
|
1126
1128
|
this.frozen = true;
|
|
1127
1129
|
this.backend?.stop();
|
|
1128
1130
|
}
|
|
1129
|
-
/* Ends the CLI and everything it started; settles once it exited or was sent the SIGKILL. */
|
|
1131
|
+
/* Ends the CLI and everything it started; settles once it exited or was sent the SIGKILL and no checkpoint asks git any more. */
|
|
1130
1132
|
dispose() {
|
|
1131
1133
|
this.uiReplies.clear();
|
|
1132
1134
|
this.clearStartWarning();
|
|
@@ -1138,7 +1140,7 @@ export class ChatSession {
|
|
|
1138
1140
|
const ended = this.backend?.dispose() ?? Promise.resolve();
|
|
1139
1141
|
this.backend = null;
|
|
1140
1142
|
this.starting = null;
|
|
1141
|
-
return ended;
|
|
1143
|
+
return Promise.all([ended, ...this.checkpointWork]).then(() => undefined);
|
|
1142
1144
|
}
|
|
1143
1145
|
/* What a turn changed against its checkpoint: the stored answer, or one taken now while it runs. */
|
|
1144
1146
|
async turnDiff(turnId) {
|
|
@@ -1412,15 +1414,13 @@ export class ChatSession {
|
|
|
1412
1414
|
return Promise.resolve();
|
|
1413
1415
|
}
|
|
1414
1416
|
const cwd = this.thread.info.cwd;
|
|
1415
|
-
return Promise.all([checkpoints.take(cwd), checkpoints.head?.(cwd) ?? null])
|
|
1416
|
-
.then(([tree, head]) => {
|
|
1417
|
+
return this.trackCheckpoint(Promise.all([checkpoints.take(cwd), checkpoints.head?.(cwd) ?? null]).then(([tree, head]) => {
|
|
1417
1418
|
const turn = this.thread.get(turnId);
|
|
1418
1419
|
if (tree === null || turn?.kind !== 'turn' || turn.state !== 'running' || this.info.activeTurnId !== turnId) {
|
|
1419
1420
|
return;
|
|
1420
1421
|
}
|
|
1421
1422
|
this.emit([this.thread.upsert({ ...turn, checkpoint: tree, ...(head === null ? {} : { checkpointHead: head }) })]);
|
|
1422
|
-
})
|
|
1423
|
-
.catch(() => undefined);
|
|
1423
|
+
}));
|
|
1424
1424
|
}
|
|
1425
1425
|
/* 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. */
|
|
1426
1426
|
settleCheckpoint(turnId) {
|
|
@@ -1430,8 +1430,7 @@ export class ChatSession {
|
|
|
1430
1430
|
}
|
|
1431
1431
|
const { checkpoints } = this.options;
|
|
1432
1432
|
const cwd = this.thread.info.cwd;
|
|
1433
|
-
void Promise.all([checkpoints.settle(cwd, turn.checkpoint), checkpoints.head?.(cwd) ?? null])
|
|
1434
|
-
.then(([answer, head]) => {
|
|
1433
|
+
void this.trackCheckpoint(Promise.all([checkpoints.settle(cwd, turn.checkpoint), checkpoints.head?.(cwd) ?? null]).then(([answer, head]) => {
|
|
1435
1434
|
const settled = this.thread.get(turnId);
|
|
1436
1435
|
if (answer === null || settled?.kind !== 'turn') {
|
|
1437
1436
|
return;
|
|
@@ -1445,8 +1444,13 @@ export class ChatSession {
|
|
|
1445
1444
|
})
|
|
1446
1445
|
]);
|
|
1447
1446
|
this.options.persist();
|
|
1448
|
-
})
|
|
1449
|
-
|
|
1447
|
+
}));
|
|
1448
|
+
}
|
|
1449
|
+
/* Keeps the work in `checkpointWork` until it settled; a failure leaves the turn without a checkpoint. */
|
|
1450
|
+
trackCheckpoint(work) {
|
|
1451
|
+
const tracked = work.catch(() => undefined).finally(() => this.checkpointWork.delete(tracked));
|
|
1452
|
+
this.checkpointWork.add(tracked);
|
|
1453
|
+
return tracked;
|
|
1450
1454
|
}
|
|
1451
1455
|
/*
|
|
1452
1456
|
* What the agent has to hear before this prompt: what the chat kept for it and what the host says.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import type { SpawnChatProcess } from './chat-process.ts';
|
|
2
|
+
import { JsonRpcTransport, type JsonRpcFrame } from './json-rpc-transport.ts';
|
|
3
|
+
export type CodexFrame = JsonRpcFrame;
|
|
3
4
|
export interface CodexClientInfo {
|
|
4
5
|
name: string;
|
|
5
6
|
title: string;
|
|
@@ -15,31 +16,7 @@ interface CodexTransportOptions {
|
|
|
15
16
|
onError?(error: Error): void;
|
|
16
17
|
onExit(exitCode: number | null, stderr: string | null): void;
|
|
17
18
|
}
|
|
18
|
-
export declare class CodexTransport {
|
|
19
|
-
private readonly child;
|
|
20
|
-
private readonly options;
|
|
21
|
-
private readonly pending;
|
|
22
|
-
private nextId;
|
|
23
|
-
private closed;
|
|
24
|
-
private inputClosed;
|
|
19
|
+
export declare class CodexTransport extends JsonRpcTransport {
|
|
25
20
|
constructor(options: CodexTransportOptions);
|
|
26
|
-
get pid(): number;
|
|
27
|
-
get alive(): boolean;
|
|
28
|
-
request(method: string, params: unknown): Promise<unknown>;
|
|
29
|
-
notify(method: string, params: unknown): void;
|
|
30
|
-
respond(id: number | string, result: unknown): void;
|
|
31
|
-
respondError(id: number | string, code: number, message: string): void;
|
|
32
|
-
end(): void;
|
|
33
|
-
kill(signal?: 'SIGTERM' | 'SIGKILL'): void;
|
|
34
|
-
stop(): void;
|
|
35
|
-
dispose(): Promise<void>;
|
|
36
|
-
private write;
|
|
37
|
-
private broken;
|
|
38
|
-
private failed;
|
|
39
|
-
private readLine;
|
|
40
|
-
private readLines;
|
|
41
|
-
private handleLine;
|
|
42
|
-
private handleExit;
|
|
43
|
-
private rejectPending;
|
|
44
21
|
}
|
|
45
22
|
export {};
|
|
@@ -1,191 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ChatChild, MAX_LINE_LENGTH, readStreamLines } from './chat-process.js';
|
|
1
|
+
import { JsonRpcTransport } from './json-rpc-transport.js';
|
|
3
2
|
export const DEFAULT_CODEX_CLIENT = { name: 'agents', title: 'Agents', version: '0.1.0' };
|
|
4
|
-
class CodexRpcError extends Error {
|
|
5
|
-
code;
|
|
6
|
-
constructor(method, error) {
|
|
7
|
-
const detail = typeof error === 'object' && error !== null ? error : {};
|
|
8
|
-
super(`${method} failed: ${typeof detail.message === 'string' ? detail.message : JSON.stringify(error)}`);
|
|
9
|
-
this.name = 'CodexRpcError';
|
|
10
|
-
this.code = typeof detail.code === 'number' ? detail.code : null;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
3
|
// The app-server answers every request it takes at once and does the work in notifications, so a silent one is stuck.
|
|
14
4
|
const REQUEST_TIMEOUT_MS = 60_000;
|
|
15
|
-
/*
|
|
16
|
-
|
|
17
|
-
* a request carries an id and gets a result or an error back under the same id, a notification
|
|
18
|
-
* has no id. The app-server also sends requests of its own (approvals), answered with `respond`.
|
|
19
|
-
*/
|
|
20
|
-
export class CodexTransport {
|
|
21
|
-
child;
|
|
22
|
-
options;
|
|
23
|
-
pending = new Map();
|
|
24
|
-
nextId = 1;
|
|
25
|
-
closed = false;
|
|
26
|
-
// Set once stdin ended or broke; a write after an EPIPE only raises the next one.
|
|
27
|
-
inputClosed = false;
|
|
5
|
+
/* One `codex app-server` process. Its frames leave out the `jsonrpc` field, and an error it answers with is named `CodexRpcError`. */
|
|
6
|
+
export class CodexTransport extends JsonRpcTransport {
|
|
28
7
|
constructor(options) {
|
|
29
|
-
|
|
30
|
-
this.child = new ChatChild({
|
|
31
|
-
command: options.command,
|
|
32
|
-
cwd: options.cwd,
|
|
33
|
-
env: options.env,
|
|
34
|
-
...(options.spawn ? { spawn: options.spawn } : {}),
|
|
35
|
-
onError: (error) => this.broken(error),
|
|
36
|
-
onExit: (exitCode, stderr) => this.handleExit(exitCode, stderr)
|
|
37
|
-
});
|
|
38
|
-
void this.readLines();
|
|
39
|
-
}
|
|
40
|
-
get pid() {
|
|
41
|
-
return this.child.pid;
|
|
42
|
-
}
|
|
43
|
-
get alive() {
|
|
44
|
-
return !this.closed;
|
|
45
|
-
}
|
|
46
|
-
request(method, params) {
|
|
47
|
-
return new Promise((resolve, reject) => {
|
|
48
|
-
if (this.closed) {
|
|
49
|
-
reject(new Error(`${method} failed: Codex is not running`));
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
const id = this.nextId++;
|
|
53
|
-
const timer = setTimeout(() => {
|
|
54
|
-
this.pending.delete(id);
|
|
55
|
-
reject(new Error(`${method} failed: Codex did not answer in ${REQUEST_TIMEOUT_MS / 1000} seconds`));
|
|
56
|
-
}, REQUEST_TIMEOUT_MS);
|
|
57
|
-
timer.unref?.();
|
|
58
|
-
this.pending.set(id, { resolve, reject, method, timer });
|
|
59
|
-
try {
|
|
60
|
-
this.write({ id, method, params });
|
|
61
|
-
}
|
|
62
|
-
catch (error) {
|
|
63
|
-
clearTimeout(timer);
|
|
64
|
-
this.pending.delete(id);
|
|
65
|
-
reject(error);
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
notify(method, params) {
|
|
70
|
-
this.write({ method, params });
|
|
71
|
-
}
|
|
72
|
-
respond(id, result) {
|
|
73
|
-
this.write({ id, result });
|
|
74
|
-
}
|
|
75
|
-
respondError(id, code, message) {
|
|
76
|
-
this.write({ id, error: { code, message } });
|
|
77
|
-
}
|
|
78
|
-
/* Closes stdin; the app-server ends on its own once it has nothing more to say. */
|
|
79
|
-
end() {
|
|
80
|
-
this.inputClosed = true;
|
|
81
|
-
try {
|
|
82
|
-
this.child.process.stdin.end();
|
|
83
|
-
}
|
|
84
|
-
catch {
|
|
85
|
-
// Already closed by the other side.
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
kill(signal = 'SIGTERM') {
|
|
89
|
-
this.child.process.kill(signal);
|
|
90
|
-
}
|
|
91
|
-
stop() {
|
|
92
|
-
this.end();
|
|
93
|
-
this.child.endAfterGrace();
|
|
94
|
-
}
|
|
95
|
-
dispose() {
|
|
96
|
-
return this.child.end();
|
|
97
|
-
}
|
|
98
|
-
write(frame) {
|
|
99
|
-
if (this.closed || this.inputClosed) {
|
|
100
|
-
throw new Error('Codex stdin is closed');
|
|
101
|
-
}
|
|
102
|
-
try {
|
|
103
|
-
this.child.process.stdin.write(`${JSON.stringify(frame)}\n`);
|
|
104
|
-
this.child.process.stdin.flush();
|
|
105
|
-
}
|
|
106
|
-
catch (error) {
|
|
107
|
-
this.inputClosed = true;
|
|
108
|
-
throw error;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
/* A pipe that failed takes no more writes; a frame a consumer choked on leaves it as it was. */
|
|
112
|
-
broken(error) {
|
|
113
|
-
this.inputClosed = true;
|
|
114
|
-
this.failed(error);
|
|
115
|
-
}
|
|
116
|
-
failed(error) {
|
|
117
|
-
const failure = new Error(`Codex transport failed: ${errorText(error)}`);
|
|
118
|
-
this.rejectPending(() => failure);
|
|
119
|
-
try {
|
|
120
|
-
this.options.onError?.(failure);
|
|
121
|
-
}
|
|
122
|
-
catch (reportError) {
|
|
123
|
-
console.error('Reporting a Codex transport failure failed:', errorText(reportError));
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
readLine(line) {
|
|
127
|
-
try {
|
|
128
|
-
this.handleLine(line);
|
|
129
|
-
}
|
|
130
|
-
catch (error) {
|
|
131
|
-
this.failed(error);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
async readLines() {
|
|
135
|
-
try {
|
|
136
|
-
await readStreamLines(this.child.process.stdout, (line) => this.readLine(line), () => this.failed(new Error(`Codex wrote a frame of more than ${MAX_LINE_LENGTH / 1_000_000} million characters, which was dropped`)));
|
|
137
|
-
}
|
|
138
|
-
catch (error) {
|
|
139
|
-
if (!this.closed) {
|
|
140
|
-
this.broken(error);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
handleLine(line) {
|
|
145
|
-
if (this.closed || line.trim() === '') {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
let frame;
|
|
149
|
-
try {
|
|
150
|
-
frame = JSON.parse(line);
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
if (typeof frame !== 'object' || frame === null || Array.isArray(frame)) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
const message = frame;
|
|
159
|
-
if (typeof message.method === 'string') {
|
|
160
|
-
this.options.onFrame(message);
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
const settle = typeof message.id === 'number' ? this.pending.get(message.id) : undefined;
|
|
164
|
-
if (!settle) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
this.pending.delete(message.id);
|
|
168
|
-
clearTimeout(settle.timer);
|
|
169
|
-
if (message.error !== undefined) {
|
|
170
|
-
settle.reject(new CodexRpcError(settle.method, message.error));
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
settle.resolve(message.result);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
handleExit(exitCode, stderr) {
|
|
177
|
-
if (this.closed) {
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
this.closed = true;
|
|
181
|
-
this.rejectPending((method) => new Error(`${method} failed: Codex exited`));
|
|
182
|
-
this.options.onExit(exitCode, stderr);
|
|
183
|
-
}
|
|
184
|
-
rejectPending(reason) {
|
|
185
|
-
for (const settle of this.pending.values()) {
|
|
186
|
-
clearTimeout(settle.timer);
|
|
187
|
-
settle.reject(reason(settle.method));
|
|
188
|
-
}
|
|
189
|
-
this.pending.clear();
|
|
8
|
+
super({ ...options, label: 'Codex', errorName: 'CodexRpcError', requestTimeoutMs: REQUEST_TIMEOUT_MS });
|
|
190
9
|
}
|
|
191
10
|
}
|
|
@@ -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/dist/chat/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { type AcpAgentSpec, type AcpClientInfo, createAcpBackend } from './acp-backend.ts';
|
|
2
|
+
export type { AcpAgentInfo, AcpModel, AcpModelState, AcpMode, AcpModeState, AcpSessionInfo, AcpModeMap } from './acp-protocol.ts';
|
|
1
3
|
export { AttachmentStore } from './attachment-store.ts';
|
|
2
4
|
export type { BackendLaunch, TurnInput, ApprovalDecision, BackendEvent, BackendHost, ChatBackend } from './backend.ts';
|
|
3
5
|
export { runningInBackground, type BackgroundWork } from './background-work.ts';
|
package/dist/chat/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Public API: exports.test.ts snapshots this list, so a name added or dropped here is an API change.
|
|
2
|
+
export { createAcpBackend } from './acp-backend.js';
|
|
2
3
|
export { AttachmentStore } from './attachment-store.js';
|
|
3
4
|
export { runningInBackground } from './background-work.js';
|
|
4
5
|
export { BookmarkStore } from './bookmark-store.js';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type SpawnChatProcess } from './chat-process.ts';
|
|
2
|
+
export type JsonRpcFrame = Record<string, unknown>;
|
|
3
|
+
export interface JsonRpcTransportOptions {
|
|
4
|
+
command: string[];
|
|
5
|
+
cwd: string;
|
|
6
|
+
env: Record<string, string>;
|
|
7
|
+
spawn?: SpawnChatProcess;
|
|
8
|
+
label: string;
|
|
9
|
+
errorName?: string;
|
|
10
|
+
requestTimeoutMs: number | null;
|
|
11
|
+
versionField?: boolean;
|
|
12
|
+
onFrame(frame: JsonRpcFrame): void;
|
|
13
|
+
onError?(error: Error): void;
|
|
14
|
+
onExit(exitCode: number | null, stderr: string | null): void;
|
|
15
|
+
}
|
|
16
|
+
export interface JsonRpcRequestOptions {
|
|
17
|
+
timeoutMs?: number | null;
|
|
18
|
+
}
|
|
19
|
+
export declare class JsonRpcError extends Error {
|
|
20
|
+
readonly code: number | null;
|
|
21
|
+
readonly data: unknown;
|
|
22
|
+
constructor(name: string, method: string, error: unknown);
|
|
23
|
+
}
|
|
24
|
+
export declare class JsonRpcTransport {
|
|
25
|
+
private readonly child;
|
|
26
|
+
private readonly options;
|
|
27
|
+
private readonly pending;
|
|
28
|
+
private nextId;
|
|
29
|
+
private closed;
|
|
30
|
+
private inputClosed;
|
|
31
|
+
constructor(options: JsonRpcTransportOptions);
|
|
32
|
+
get pid(): number;
|
|
33
|
+
get alive(): boolean;
|
|
34
|
+
request(method: string, params: unknown, options?: JsonRpcRequestOptions): Promise<unknown>;
|
|
35
|
+
notify(method: string, params: unknown): void;
|
|
36
|
+
respond(id: number | string, result: unknown): void;
|
|
37
|
+
respondError(id: number | string, code: number, message: string): void;
|
|
38
|
+
end(): void;
|
|
39
|
+
kill(signal?: 'SIGTERM' | 'SIGKILL'): void;
|
|
40
|
+
stop(): void;
|
|
41
|
+
dispose(): Promise<void>;
|
|
42
|
+
private write;
|
|
43
|
+
private broken;
|
|
44
|
+
private failed;
|
|
45
|
+
private readLine;
|
|
46
|
+
private readLines;
|
|
47
|
+
private handleLine;
|
|
48
|
+
private handleExit;
|
|
49
|
+
private rejectPending;
|
|
50
|
+
}
|