@adecore/agents 0.29.3 → 0.31.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.
Files changed (65) hide show
  1. package/dist/chat/acp-backend.d.ts +59 -0
  2. package/dist/chat/acp-backend.js +407 -0
  3. package/dist/chat/acp-protocol.d.ts +131 -0
  4. package/dist/chat/acp-protocol.js +605 -0
  5. package/dist/chat/codex-transport.d.ts +4 -27
  6. package/dist/chat/codex-transport.js +4 -185
  7. package/dist/chat/fixtures/acp-v1-turn.json +240 -0
  8. package/dist/chat/index.d.ts +2 -0
  9. package/dist/chat/index.js +1 -0
  10. package/dist/chat/json-rpc-transport.d.ts +50 -0
  11. package/dist/chat/json-rpc-transport.js +203 -0
  12. package/dist/providers/accounts/accounts.d.ts +10 -6
  13. package/dist/providers/accounts/accounts.js +19 -19
  14. package/dist/providers/accounts/service.d.ts +3 -0
  15. package/dist/providers/accounts/service.js +33 -17
  16. package/dist/providers/accounts/store.d.ts +4 -5
  17. package/dist/providers/accounts/store.js +12 -12
  18. package/dist/providers/acp-provider.d.ts +22 -0
  19. package/dist/providers/acp-provider.js +45 -0
  20. package/dist/providers/catalog.js +1 -0
  21. package/dist/providers/index.d.ts +1 -0
  22. package/dist/providers/index.js +1 -0
  23. package/dist/providers/provider.d.ts +1 -1
  24. package/dist/providers/registry.d.ts +1 -0
  25. package/dist/providers/registry.js +4 -0
  26. package/dist/testing/fake-acp.d.ts +10 -0
  27. package/dist/testing/fake-acp.js +262 -0
  28. package/dist/testing/index.d.ts +1 -0
  29. package/dist/testing/index.js +1 -0
  30. package/dist/usage/index-file.d.ts +1 -1
  31. package/dist/usage/index-file.js +3 -1
  32. package/dist/usage/index.d.ts +2 -1
  33. package/dist/usage/index.js +2 -0
  34. package/dist/usage/limits/monitor.d.ts +3 -1
  35. package/dist/usage/limits/monitor.js +19 -4
  36. package/dist/usage/limits/normalize.d.ts +1 -0
  37. package/dist/usage/limits/normalize.js +2 -1
  38. package/dist/usage/readers/host-log.d.ts +16 -0
  39. package/dist/usage/readers/host-log.js +49 -0
  40. package/dist/usage/roots.js +6 -2
  41. package/dist/usage/scanner.js +13 -2
  42. package/package.json +3 -3
  43. package/src/chat/acp-backend.ts +476 -0
  44. package/src/chat/acp-protocol.ts +763 -0
  45. package/src/chat/codex-transport.ts +6 -207
  46. package/src/chat/fixtures/acp-v1-turn.json +240 -0
  47. package/src/chat/index.ts +2 -0
  48. package/src/chat/json-rpc-transport.ts +253 -0
  49. package/src/providers/accounts/accounts.ts +28 -25
  50. package/src/providers/accounts/service.ts +49 -18
  51. package/src/providers/accounts/store.ts +14 -17
  52. package/src/providers/acp-provider.ts +73 -0
  53. package/src/providers/catalog.ts +1 -0
  54. package/src/providers/index.ts +1 -0
  55. package/src/providers/provider.ts +2 -1
  56. package/src/providers/registry.ts +5 -0
  57. package/src/testing/fake-acp.ts +296 -0
  58. package/src/testing/index.ts +1 -0
  59. package/src/usage/index-file.ts +3 -2
  60. package/src/usage/index.ts +2 -1
  61. package/src/usage/limits/monitor.ts +24 -5
  62. package/src/usage/limits/normalize.ts +2 -1
  63. package/src/usage/readers/host-log.ts +72 -0
  64. package/src/usage/roots.ts +7 -3
  65. package/src/usage/scanner.ts +16 -5
@@ -0,0 +1,59 @@
1
+ import { type ModelSelection } from '@adecore/agent-contracts';
2
+ import { type AcpModeMap, type AcpSessionInfo } from './acp-protocol.ts';
3
+ import type { ApprovalDecision, BackendHost, BackendLaunch, ChatBackend, TurnInput } from './backend.ts';
4
+ export interface AcpClientInfo {
5
+ name: string;
6
+ title?: string;
7
+ version: string;
8
+ }
9
+ export declare const DEFAULT_ACP_CLIENT: AcpClientInfo;
10
+ export interface AcpAgentSpec {
11
+ name: string;
12
+ env?: Record<string, string>;
13
+ modes?: AcpModeMap;
14
+ authMethod?: string | null;
15
+ loginHint?: string;
16
+ client?: AcpClientInfo;
17
+ model?(selection: ModelSelection): string | null;
18
+ onSession?(info: AcpSessionInfo): void;
19
+ }
20
+ export declare class AcpBackend implements ChatBackend {
21
+ private readonly launch;
22
+ private readonly host;
23
+ private readonly spec;
24
+ private readonly client;
25
+ private readonly protocol;
26
+ private transport;
27
+ private capabilities;
28
+ private sessionId;
29
+ private replaying;
30
+ private contextPending;
31
+ private prompt;
32
+ constructor(launch: BackendLaunch, host: BackendHost, spec: AcpAgentSpec);
33
+ get running(): boolean;
34
+ get pid(): number | null;
35
+ start(): Promise<void>;
36
+ private handshake;
37
+ private sessionParams;
38
+ private openSession;
39
+ private authenticated;
40
+ private applyMode;
41
+ private applyModel;
42
+ private applyThoughtLevel;
43
+ private promptBlocks;
44
+ sendTurn(input: TurnInput): void;
45
+ compact(): void;
46
+ interrupt(): void;
47
+ respondApproval(requestId: string, decision: ApprovalDecision): boolean;
48
+ respondQuestion(): boolean;
49
+ declineRequest(requestId: string): boolean;
50
+ stop(): void;
51
+ dispose(): Promise<void>;
52
+ private respond;
53
+ private fail;
54
+ private handleFrame;
55
+ private handleExit;
56
+ private emitAll;
57
+ private emit;
58
+ }
59
+ export declare function createAcpBackend(spec: AcpAgentSpec): (launch: BackendLaunch, host: BackendHost) => ChatBackend;
@@ -0,0 +1,407 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { attachmentImageMime } from '@adecore/agent-contracts';
3
+ import { errorText } from '../error-text.js';
4
+ import { MODE_ORDER } from '../modes.js';
5
+ import { ACP_AUTH_REQUIRED, ACP_PROTOCOL_VERSION, AcpProtocol, acpStopWords, autoAllows, parseInitialize, parseSessionInfo, resolveAcpMode, runtimeModeOf, sessionIdOf } from './acp-protocol.js';
6
+ import { attachmentNote } from './input.js';
7
+ import { JsonRpcError, JsonRpcTransport } from './json-rpc-transport.js';
8
+ export const DEFAULT_ACP_CLIENT = { name: 'agents', title: 'Agents', version: '0.1.0' };
9
+ // What `initialize` and a session request may take; a prompt and a login wait for as long as the agent runs.
10
+ const REQUEST_TIMEOUT_MS = 60_000;
11
+ function nameOf(client) {
12
+ return client.title ?? client.name;
13
+ }
14
+ /*
15
+ * One ACP agent process on JSON-RPC over stdio. `start` runs `initialize` and opens the session
16
+ * (new, or the chat's own resumed or loaded), sets the mode and the model, and only then takes a turn.
17
+ * A turn is one `session/prompt` request and ends on its response. The client advertises no file
18
+ * system and no terminal, so the agent works with its own tools.
19
+ */
20
+ export class AcpBackend {
21
+ launch;
22
+ host;
23
+ spec;
24
+ client;
25
+ protocol;
26
+ transport = null;
27
+ capabilities = null;
28
+ sessionId = '';
29
+ // A loaded session replays its history as updates the thread already holds.
30
+ replaying = false;
31
+ contextPending = null;
32
+ prompt = null;
33
+ constructor(launch, host, spec) {
34
+ this.launch = launch;
35
+ this.host = host;
36
+ this.spec = spec;
37
+ this.client = spec.client ?? DEFAULT_ACP_CLIENT;
38
+ this.protocol = new AcpProtocol(launch.generation, spec.modes ?? {}, acpStopWords(spec.name));
39
+ }
40
+ get running() {
41
+ return this.transport !== null;
42
+ }
43
+ get pid() {
44
+ return this.transport?.pid ?? null;
45
+ }
46
+ async start() {
47
+ if (this.transport) {
48
+ return;
49
+ }
50
+ const transport = new JsonRpcTransport({
51
+ command: this.launch.command,
52
+ cwd: this.launch.cwd,
53
+ env: { ...this.launch.env, ...this.spec.env },
54
+ ...(this.launch.spawn ? { spawn: this.launch.spawn } : {}),
55
+ label: this.spec.name,
56
+ errorName: 'AcpRpcError',
57
+ requestTimeoutMs: REQUEST_TIMEOUT_MS,
58
+ versionField: true,
59
+ onError: (error) => this.fail(error),
60
+ onFrame: (frame) => this.handleFrame(transport, frame),
61
+ onExit: (exitCode, stderr) => this.handleExit(transport, exitCode, stderr)
62
+ });
63
+ this.transport = transport;
64
+ try {
65
+ await this.handshake(transport);
66
+ }
67
+ catch (error) {
68
+ // A chat that failed to start drops this backend, so nothing else would end an agent stuck in its handshake.
69
+ if (this.transport === transport) {
70
+ this.transport = null;
71
+ void transport.dispose();
72
+ }
73
+ throw error;
74
+ }
75
+ }
76
+ async handshake(transport) {
77
+ const capabilities = parseInitialize(await transport.request('initialize', {
78
+ protocolVersion: ACP_PROTOCOL_VERSION,
79
+ clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false },
80
+ clientInfo: { name: this.client.name, title: nameOf(this.client), version: this.client.version }
81
+ }));
82
+ if (capabilities.protocolVersion !== ACP_PROTOCOL_VERSION) {
83
+ throw new Error(`${this.spec.name} speaks ACP version ${capabilities.protocolVersion}; only version ${ACP_PROTOCOL_VERSION} is supported`);
84
+ }
85
+ this.capabilities = capabilities;
86
+ const info = await this.openSession(transport, capabilities);
87
+ const modeId = await this.applyMode(transport, info);
88
+ const model = await this.applyModel(transport, info);
89
+ await this.applyThoughtLevel(transport, info);
90
+ this.spec.onSession?.(info);
91
+ this.emit({
92
+ type: 'session',
93
+ agentSessionId: this.sessionId,
94
+ model,
95
+ effectiveRuntimeMode: this.launch.runtimeMode,
96
+ ...(modeId === null ? {} : { permissionMode: modeId })
97
+ });
98
+ }
99
+ sessionParams() {
100
+ const folders = this.launch.folders ?? [];
101
+ return {
102
+ cwd: this.launch.cwd,
103
+ mcpServers: [],
104
+ ...(folders.length > 0 && this.capabilities?.additionalDirectories ? { additionalDirectories: folders } : {})
105
+ };
106
+ }
107
+ async openSession(transport, capabilities) {
108
+ const resume = this.launch.resume;
109
+ if (resume) {
110
+ const method = capabilities.resumeSession ? 'session/resume' : capabilities.loadSession ? 'session/load' : null;
111
+ if (method === null) {
112
+ this.emit({ type: 'note', level: 'warning', text: `${this.spec.name} cannot continue a session it ran before. Started a new one.` });
113
+ }
114
+ else {
115
+ try {
116
+ this.replaying = method === 'session/load';
117
+ const result = await this.authenticated(transport, method, { sessionId: resume, ...this.sessionParams() });
118
+ this.sessionId = resume;
119
+ this.contextPending = [this.launch.resumeInstructions ?? null, this.launch.resumeNote].filter((part) => part !== null).join('\n\n') || null;
120
+ return parseSessionInfo(result, resume, capabilities.agent);
121
+ }
122
+ catch (error) {
123
+ // A refusal means the agent no longer has the session and a fresh one keeps the chat usable; a silence or an exit says nothing about it.
124
+ if (!(error instanceof JsonRpcError)) {
125
+ throw error;
126
+ }
127
+ this.emit({
128
+ type: 'note',
129
+ level: 'warning',
130
+ text: `${this.spec.name} could not continue its session (${errorText(error)}). Started a new one.`
131
+ });
132
+ }
133
+ finally {
134
+ this.replaying = false;
135
+ }
136
+ }
137
+ }
138
+ const result = await this.authenticated(transport, 'session/new', this.sessionParams());
139
+ const sessionId = sessionIdOf(result);
140
+ if (sessionId === null) {
141
+ throw new Error(`session/new failed: ${this.spec.name} answered without a session id`);
142
+ }
143
+ this.sessionId = sessionId;
144
+ this.contextPending = this.launch.instructions;
145
+ return parseSessionInfo(result, sessionId, capabilities.agent);
146
+ }
147
+ /* A session request, once more after `authenticate` when the agent says it needs a login first. */
148
+ async authenticated(transport, method, params) {
149
+ try {
150
+ return await transport.request(method, params);
151
+ }
152
+ catch (error) {
153
+ if (!(error instanceof JsonRpcError) || error.code !== ACP_AUTH_REQUIRED) {
154
+ throw error;
155
+ }
156
+ const methodId = this.spec.authMethod ?? null;
157
+ const offered = this.capabilities?.authMethods ?? [];
158
+ if (methodId === null || (offered.length > 0 && !offered.includes(methodId))) {
159
+ throw new Error(`${this.spec.name} is not signed in.${this.spec.loginHint ? ` ${this.spec.loginHint}` : ''}`, { cause: error });
160
+ }
161
+ await transport.request('authenticate', { methodId }, { timeoutMs: null });
162
+ return transport.request(method, params);
163
+ }
164
+ }
165
+ /* Puts the session in the agent mode the runtime mode maps to; answers the mode it runs in, or null for an agent without modes. */
166
+ async applyMode(transport, info) {
167
+ const modes = info.modes;
168
+ if (modes === null || modes.modes.length === 0) {
169
+ return null;
170
+ }
171
+ const map = this.spec.modes ?? {};
172
+ const requested = this.launch.runtimeMode;
173
+ const resolved = resolveAcpMode(requested, map, modes.modes.map((mode) => mode.id));
174
+ if (resolved === null) {
175
+ const current = runtimeModeOf(modes.current, map);
176
+ if (current !== null && MODE_ORDER.indexOf(current) > MODE_ORDER.indexOf(requested)) {
177
+ throw new Error(`${this.spec.name} runs in ${modes.current}, which allows more than ${requested}, and offers no narrower mode`);
178
+ }
179
+ return modes.current;
180
+ }
181
+ if (resolved.modeId === modes.current) {
182
+ return modes.current;
183
+ }
184
+ if (modes.configId === null) {
185
+ await transport.request('session/set_mode', { sessionId: this.sessionId, modeId: resolved.modeId });
186
+ }
187
+ else {
188
+ await transport.request('session/set_config_option', { sessionId: this.sessionId, configId: modes.configId, value: resolved.modeId });
189
+ }
190
+ return resolved.modeId;
191
+ }
192
+ /* Switches to the selected model where the agent offers it; answers the model the session runs. */
193
+ async applyModel(transport, info) {
194
+ const models = info.models;
195
+ const wanted = this.spec.model ? this.spec.model(this.launch.selection) : this.launch.selection.model;
196
+ if (models === null || wanted === null || wanted === models.current) {
197
+ return models?.current ?? wanted;
198
+ }
199
+ if (!models.models.some((model) => model.id === wanted)) {
200
+ this.emit({ type: 'note', level: 'warning', text: `${this.spec.name} does not offer ${this.launch.modelName}. It runs ${models.current}.` });
201
+ return models.current;
202
+ }
203
+ try {
204
+ if (models.configId === null) {
205
+ await transport.request('session/set_model', { sessionId: this.sessionId, modelId: wanted });
206
+ }
207
+ else {
208
+ await transport.request('session/set_config_option', { sessionId: this.sessionId, configId: models.configId, value: wanted });
209
+ }
210
+ return wanted;
211
+ }
212
+ catch (error) {
213
+ if (!(error instanceof JsonRpcError)) {
214
+ throw error;
215
+ }
216
+ this.emit({
217
+ type: 'note',
218
+ level: 'warning',
219
+ text: `${this.spec.name} did not switch to ${this.launch.modelName} (${errorText(error)}). It runs ${models.current}.`
220
+ });
221
+ return models.current;
222
+ }
223
+ }
224
+ async applyThoughtLevel(transport, info) {
225
+ const level = info.thoughtLevel;
226
+ const effort = this.launch.selection.options.effort;
227
+ if (level === null || typeof effort !== 'string' || effort === level.current || !level.values.includes(effort)) {
228
+ return;
229
+ }
230
+ await transport.request('session/set_config_option', { sessionId: this.sessionId, configId: level.configId, value: effort }).catch((error) => {
231
+ if (!(error instanceof JsonRpcError)) {
232
+ throw error;
233
+ }
234
+ });
235
+ }
236
+ promptBlocks(input) {
237
+ const sendsImages = this.capabilities?.images === true;
238
+ const images = sendsImages ? input.attachments.filter((attachment) => attachmentImageMime(attachment) !== null) : [];
239
+ const blocks = images.map((attachment) => {
240
+ let data;
241
+ try {
242
+ data = readFileSync(attachment.path).toString('base64');
243
+ }
244
+ catch {
245
+ throw new Error(`Could not read attached image "${attachment.name}". Attach it again and retry.`);
246
+ }
247
+ return { type: 'image', mimeType: attachmentImageMime(attachment), data };
248
+ });
249
+ const named = input.attachments.filter((attachment) => !images.includes(attachment));
250
+ const parts = [this.contextPending, input.preamble, input.text, attachmentNote(named)].filter((part) => part !== null && part !== '');
251
+ this.contextPending = null;
252
+ return [{ type: 'text', text: parts.join('\n\n') }, ...blocks];
253
+ }
254
+ sendTurn(input) {
255
+ const transport = this.transport;
256
+ if (!transport) {
257
+ this.emit({ type: 'failed', message: `session/prompt failed: ${this.spec.name} is not running` });
258
+ return;
259
+ }
260
+ const prompt = this.promptBlocks(input);
261
+ const turn = { promptId: input.promptId, cancelled: false };
262
+ this.prompt = turn;
263
+ transport
264
+ .request('session/prompt', { sessionId: this.sessionId, prompt }, { timeoutMs: null })
265
+ .then((result) => {
266
+ if (this.transport === transport && this.prompt === turn) {
267
+ this.prompt = null;
268
+ this.emitAll(this.protocol.finish(turn.cancelled ? { stopReason: 'cancelled' } : result, turn.promptId));
269
+ }
270
+ })
271
+ .catch((error) => {
272
+ // A process that went has its exit tell the thread; anything else ends this turn alone.
273
+ if (this.transport === transport && transport.alive && this.prompt === turn) {
274
+ this.prompt = null;
275
+ this.emitAll(this.protocol.abort(turn.promptId, turn.cancelled ? null : errorText(error)));
276
+ }
277
+ });
278
+ }
279
+ /* ACP has no call to fold the context; a host that offers it sends the agent's own command as a prompt. */
280
+ compact() {
281
+ this.emit({ type: 'note', level: 'warning', text: `${this.spec.name} cannot fold its context on request.` });
282
+ this.emit({ type: 'turn.done', state: 'done', costUsd: 0 });
283
+ }
284
+ /* Cancels the turn: every permission request still open is answered as cancelled, and the turn ends on the prompt's response. */
285
+ interrupt() {
286
+ const transport = this.transport;
287
+ if (!transport) {
288
+ return;
289
+ }
290
+ if (this.prompt === null) {
291
+ this.emit({ type: 'turn.done', state: 'aborted', costUsd: 0 });
292
+ return;
293
+ }
294
+ this.prompt.cancelled = true;
295
+ try {
296
+ for (const cancelled of this.protocol.cancelPermissions()) {
297
+ transport.respond(cancelled.rpcId, cancelled.result);
298
+ this.emit({ type: 'request.withdrawn', requestId: cancelled.requestId });
299
+ }
300
+ transport.notify('session/cancel', { sessionId: this.sessionId });
301
+ }
302
+ catch (error) {
303
+ this.fail(error);
304
+ }
305
+ }
306
+ respondApproval(requestId, decision) {
307
+ const answer = this.protocol.answer(requestId, decision);
308
+ if (!answer) {
309
+ return false;
310
+ }
311
+ return this.respond(requestId, answer.rpcId, answer.result);
312
+ }
313
+ /* ACP asks no questions of its own; an agent asks in its message and the next prompt answers. */
314
+ respondQuestion() {
315
+ return false;
316
+ }
317
+ declineRequest(requestId) {
318
+ return this.protocol.hasPermission(requestId) && this.respondApproval(requestId, 'deny');
319
+ }
320
+ stop() {
321
+ this.transport?.stop();
322
+ }
323
+ dispose() {
324
+ const transport = this.transport;
325
+ this.transport = null;
326
+ return transport?.dispose() ?? Promise.resolve();
327
+ }
328
+ respond(requestId, rpcId, result) {
329
+ try {
330
+ if (!this.transport) {
331
+ throw new Error(`${this.spec.name} is not running`);
332
+ }
333
+ this.transport.respond(rpcId, result);
334
+ return true;
335
+ }
336
+ catch (error) {
337
+ this.emit({ type: 'request.withdrawn', requestId });
338
+ this.fail(error);
339
+ return false;
340
+ }
341
+ }
342
+ fail(error) {
343
+ this.emit({ type: 'failed', message: errorText(error), processAlive: this.running });
344
+ }
345
+ handleFrame(transport, frame) {
346
+ if (this.transport !== transport) {
347
+ return;
348
+ }
349
+ const id = typeof frame.id === 'number' || typeof frame.id === 'string' ? frame.id : null;
350
+ if (frame.method === 'session/update' && id === null) {
351
+ if (!this.replaying) {
352
+ this.emitAll(this.protocol.update(this.sessionId, frame.params));
353
+ }
354
+ return;
355
+ }
356
+ if (id === null) {
357
+ return;
358
+ }
359
+ if (frame.method !== 'session/request_permission') {
360
+ transport.respondError(id, -32601, `${nameOf(this.client)} does not handle ${String(frame.method)}`);
361
+ return;
362
+ }
363
+ const permission = this.protocol.permission(id, this.sessionId, frame.params);
364
+ if (permission === null) {
365
+ transport.respondError(id, -32602, 'Not a permission request of this session');
366
+ return;
367
+ }
368
+ if (this.prompt?.cancelled) {
369
+ this.emitAll(permission.events.filter((event) => event.type !== 'approval.requested'));
370
+ const answer = this.protocol.cancelPermissions().find((cancelled) => cancelled.requestId === permission.requestId);
371
+ if (answer) {
372
+ transport.respond(answer.rpcId, answer.result);
373
+ }
374
+ return;
375
+ }
376
+ if (autoAllows(this.launch.runtimeMode, permission.kind)) {
377
+ this.emitAll(permission.events.filter((event) => event.type !== 'approval.requested'));
378
+ const answer = this.protocol.answer(permission.requestId, 'allow');
379
+ if (answer) {
380
+ this.respond(permission.requestId, answer.rpcId, answer.result);
381
+ }
382
+ return;
383
+ }
384
+ this.emitAll(permission.events);
385
+ }
386
+ handleExit(transport, exitCode, stderr) {
387
+ if (this.transport !== transport) {
388
+ return;
389
+ }
390
+ this.transport = null;
391
+ this.prompt = null;
392
+ this.protocol.cancelPermissions();
393
+ this.emit({ type: 'exit', exitCode, ...(stderr === null ? {} : { stderr }) });
394
+ }
395
+ emitAll(events) {
396
+ for (const event of events) {
397
+ this.emit(event);
398
+ }
399
+ }
400
+ emit(event) {
401
+ this.host.onEvent(event);
402
+ }
403
+ }
404
+ /* The `createBackend` of a provider whose agent speaks ACP. */
405
+ export function createAcpBackend(spec) {
406
+ return (launch, host) => new AcpBackend(launch, host, spec);
407
+ }
@@ -0,0 +1,131 @@
1
+ import { type ChatFileChange, type RuntimeMode } from '@adecore/agent-contracts';
2
+ import { z } from 'zod';
3
+ import type { ApprovalDecision, BackendEvent } from './backend.ts';
4
+ export declare const ACP_PROTOCOL_VERSION = 1;
5
+ export declare const ACP_AUTH_REQUIRED = -32000;
6
+ declare const DiffSchema: z.ZodObject<{
7
+ type: z.ZodLiteral<"diff">;
8
+ path: z.ZodString;
9
+ oldText: z.ZodOptional<z.ZodNullable<z.ZodString>>;
10
+ newText: z.ZodString;
11
+ }, z.core.$loose>;
12
+ export interface AcpAgentInfo {
13
+ name: string;
14
+ title: string | null;
15
+ version: string;
16
+ }
17
+ export interface AcpAgentCapabilities {
18
+ protocolVersion: number;
19
+ loadSession: boolean;
20
+ resumeSession: boolean;
21
+ additionalDirectories: boolean;
22
+ images: boolean;
23
+ authMethods: string[];
24
+ agent: AcpAgentInfo | null;
25
+ }
26
+ export declare function parseInitialize(result: unknown): AcpAgentCapabilities;
27
+ export interface AcpModel {
28
+ id: string;
29
+ name: string;
30
+ description: string | null;
31
+ }
32
+ export interface AcpModelState {
33
+ current: string;
34
+ models: AcpModel[];
35
+ configId: string | null;
36
+ }
37
+ export interface AcpMode {
38
+ id: string;
39
+ name: string;
40
+ }
41
+ export interface AcpModeState {
42
+ current: string;
43
+ modes: AcpMode[];
44
+ configId: string | null;
45
+ }
46
+ export interface AcpSessionInfo {
47
+ sessionId: string;
48
+ agent: AcpAgentInfo | null;
49
+ models: AcpModelState | null;
50
+ modes: AcpModeState | null;
51
+ thoughtLevel: {
52
+ configId: string;
53
+ current: string;
54
+ values: string[];
55
+ } | null;
56
+ }
57
+ export declare function parseSessionInfo(result: unknown, sessionId: string, agent: AcpAgentInfo | null): AcpSessionInfo;
58
+ export declare function sessionIdOf(result: unknown): string | null;
59
+ export type AcpModeMap = Partial<Record<RuntimeMode, readonly string[]>>;
60
+ export declare function resolveAcpMode(requested: RuntimeMode, map: AcpModeMap, offered: readonly string[]): {
61
+ modeId: string;
62
+ runtimeMode: RuntimeMode;
63
+ } | null;
64
+ export declare function runtimeModeOf(modeId: string, map: AcpModeMap): RuntimeMode | null;
65
+ export declare function autoAllows(mode: RuntimeMode, kind: string | null): boolean;
66
+ export declare function unifiedDiff(oldText: string, newText: string): string | null;
67
+ type Diff = z.infer<typeof DiffSchema>;
68
+ interface ToolState {
69
+ title: string;
70
+ name: string | null;
71
+ kind: string | null;
72
+ status: string | null;
73
+ rawInput: unknown;
74
+ rawOutput: unknown;
75
+ locations: string[];
76
+ diffs: Diff[];
77
+ text: string[];
78
+ shown: string | null;
79
+ settled: boolean;
80
+ }
81
+ export declare function toolRow(tool: Pick<ToolState, 'title' | 'name' | 'kind' | 'rawInput' | 'locations' | 'diffs'>): {
82
+ name: string;
83
+ input: Record<string, unknown>;
84
+ changes?: ChatFileChange[];
85
+ };
86
+ export interface AcpPermission {
87
+ requestId: string;
88
+ rpcId: number | string;
89
+ kind: string | null;
90
+ events: BackendEvent[];
91
+ }
92
+ export interface AcpStopWords {
93
+ maxTokens: string;
94
+ maxTurnRequests: string;
95
+ refusal: string;
96
+ }
97
+ export declare function acpStopWords(agentName: string): AcpStopWords;
98
+ export declare class AcpProtocol {
99
+ private readonly generation;
100
+ private readonly modes;
101
+ private readonly words;
102
+ private readonly tools;
103
+ private readonly permissions;
104
+ private text;
105
+ private thought;
106
+ private counter;
107
+ private costUsd;
108
+ constructor(generation: number, modes: AcpModeMap, words: AcpStopWords);
109
+ update(sessionId: string, params: unknown): BackendEvent[];
110
+ modeEvent(modeId: string): BackendEvent;
111
+ permission(rpcId: number | string, sessionId: string, params: unknown): AcpPermission | null;
112
+ answer(requestId: string, decision: ApprovalDecision): {
113
+ rpcId: number | string;
114
+ result: unknown;
115
+ } | null;
116
+ cancelPermissions(): Array<{
117
+ requestId: string;
118
+ rpcId: number | string;
119
+ result: unknown;
120
+ }>;
121
+ hasPermission(requestId: string): boolean;
122
+ finish(result: unknown, promptId: string | undefined): BackendEvent[];
123
+ abort(promptId: string | undefined, error: string | null): BackendEvent[];
124
+ private messageChunk;
125
+ private thoughtChunk;
126
+ private closeText;
127
+ private closeThought;
128
+ private toolCall;
129
+ private plan;
130
+ }
131
+ export {};