paseo-acp-agy 1.1.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.
@@ -0,0 +1,575 @@
1
+ import readline from "node:readline";
2
+ import { logger } from "./logger.js";
3
+ import { ACP_METHODS, AVAILABLE_MODES, fetchAvailableModels, buildConfigOptionsForModel, extractPromptText, mapToolNameToKind, calculateUsageCostUsd, roundUsageCostUsd, getModelContextWindow, fetchAntigravityUsage, } from "./protocol.js";
4
+ import { SessionManager } from "./session.js";
5
+ import { executeSlashCommand, AVAILABLE_SLASH_COMMANDS } from "./slash-commands.js";
6
+ import { getShortVersion } from "./version.js";
7
+ function splitModelAndEffort(modelInput) {
8
+ if (!modelInput)
9
+ return {};
10
+ for (const effort of ["high", "medium", "low"]) {
11
+ const suffix = `-${effort}`;
12
+ if (modelInput.endsWith(suffix)) {
13
+ return { model: modelInput.slice(0, -suffix.length), effort };
14
+ }
15
+ }
16
+ return { model: modelInput };
17
+ }
18
+ export class ACPServer {
19
+ sessionManager;
20
+ input;
21
+ output;
22
+ binaryPath;
23
+ rl = null;
24
+ isRunning = false;
25
+ constructor(options = {}) {
26
+ this.input = options.input || process.stdin;
27
+ this.output = options.output || process.stdout;
28
+ this.binaryPath = options.binaryPath || process.env.AGY_BIN_PATH || "agy";
29
+ this.sessionManager =
30
+ options.sessionManager || new SessionManager({ defaultBinaryPath: this.binaryPath });
31
+ }
32
+ start() {
33
+ if (this.isRunning)
34
+ return;
35
+ this.isRunning = true;
36
+ this.rl = readline.createInterface({
37
+ input: this.input,
38
+ output: undefined,
39
+ terminal: false,
40
+ });
41
+ this.rl.on("line", (line) => {
42
+ this.handleLine(line).catch((err) => {
43
+ logger.error("Error processing line in ACP server", { error: err.message });
44
+ });
45
+ });
46
+ this.rl.on("close", () => {
47
+ logger.info("ACP input stream closed, shutting down server");
48
+ this.stop().catch((err) => {
49
+ logger.error("Error stopping ACP server on close", { error: err.message });
50
+ });
51
+ });
52
+ logger.info("ACP Server started");
53
+ }
54
+ send(msg) {
55
+ this.output.write(JSON.stringify(msg) + "\n");
56
+ }
57
+ sendNotification(method, params) {
58
+ const notification = { jsonrpc: "2.0", method, params };
59
+ this.send(notification);
60
+ }
61
+ sendSuccess(id, result) {
62
+ const response = { jsonrpc: "2.0", id, result };
63
+ this.send(response);
64
+ }
65
+ sendError(id, code, message, data) {
66
+ const response = {
67
+ jsonrpc: "2.0",
68
+ id,
69
+ error: { code, message, ...(data !== undefined ? { data } : {}) },
70
+ };
71
+ this.send(response);
72
+ }
73
+ publishCommands(sessionId) {
74
+ this.sendNotification(ACP_METHODS.SESSION_UPDATE, {
75
+ sessionId,
76
+ update: {
77
+ sessionUpdate: "available_commands_update",
78
+ availableCommands: AVAILABLE_SLASH_COMMANDS,
79
+ },
80
+ });
81
+ }
82
+ async sessionState(session, forceModels = false) {
83
+ const availableModels = await fetchAvailableModels(this.binaryPath, forceModels);
84
+ return {
85
+ sessionId: session.id,
86
+ modes: {
87
+ availableModes: AVAILABLE_MODES,
88
+ currentModeId: session.mode,
89
+ },
90
+ models: {
91
+ availableModels,
92
+ currentModelId: session.model,
93
+ },
94
+ configOptions: buildConfigOptionsForModel(session.model, session.effort, availableModels),
95
+ };
96
+ }
97
+ requireSession(sessionId) {
98
+ return this.sessionManager.getSession(sessionId) || null;
99
+ }
100
+ async validateModel(modelId) {
101
+ const models = await fetchAvailableModels(this.binaryPath);
102
+ return { models, model: models.find((candidate) => candidate.modelId === modelId) || null };
103
+ }
104
+ turnUsagePayload(session, turnUsage, executingModel) {
105
+ const input = turnUsage?.input_tokens ?? 0;
106
+ const output = turnUsage?.output_tokens ?? 0;
107
+ const cached = turnUsage?.cache_read_tokens ?? 0;
108
+ const thought = turnUsage?.thinking_tokens;
109
+ const total = turnUsage?.total_tokens ?? (input + output);
110
+ const maxTokens = getModelContextWindow(executingModel || session.model);
111
+ const usedTokens = session.usage.contextWindowUsedTokens;
112
+ const costUsd = roundUsageCostUsd(session.usage.totalCostUsd);
113
+ return {
114
+ inputTokens: input,
115
+ outputTokens: output,
116
+ cachedReadTokens: cached,
117
+ ...(thought !== undefined ? { thoughtTokens: thought } : {}),
118
+ totalTokens: total,
119
+ totalCostUsd: costUsd,
120
+ cost: { amount: costUsd, currency: "USD" },
121
+ size: maxTokens,
122
+ used: usedTokens,
123
+ contextWindowMaxTokens: maxTokens,
124
+ contextWindowUsedTokens: usedTokens,
125
+ };
126
+ }
127
+ async handleLine(rawLine) {
128
+ const trimmed = rawLine.trim();
129
+ if (!trimmed)
130
+ return;
131
+ let parsed;
132
+ try {
133
+ parsed = JSON.parse(trimmed);
134
+ }
135
+ catch {
136
+ logger.warn("Invalid JSON received on ACP stdio");
137
+ this.sendError(null, -32700, "Parse error: invalid JSON");
138
+ return;
139
+ }
140
+ const method = String(parsed.method || "");
141
+ const id = parsed.id;
142
+ const isNotification = id === undefined || id === null;
143
+ const params = (parsed.params || {});
144
+ logger.debug("Received ACP message", { method, isNotification, id });
145
+ try {
146
+ switch (method) {
147
+ case ACP_METHODS.INITIALIZE: {
148
+ const clientInfo = (params.clientInfo || {});
149
+ logger.info("ACP Client connected", { clientInfo });
150
+ if (!isNotification) {
151
+ const shortVersion = getShortVersion();
152
+ this.sendSuccess(id, {
153
+ protocolVersion: 1,
154
+ agentInfo: { name: "agy-acp", version: shortVersion },
155
+ serverInfo: { name: "agy-acp", version: shortVersion },
156
+ agentCapabilities: {
157
+ loadSession: false,
158
+ sessionCapabilities: { resume: {} },
159
+ },
160
+ });
161
+ }
162
+ break;
163
+ }
164
+ case ACP_METHODS.SESSION_NEW:
165
+ case ACP_METHODS.SESSION_NEW_ALIAS: {
166
+ const cwd = typeof params.cwd === "string" ? params.cwd : undefined;
167
+ const requestedModel = typeof params.model === "string" ? params.model : undefined;
168
+ const parsedModel = splitModelAndEffort(requestedModel);
169
+ const mode = typeof params.mode === "string" ? params.mode : undefined;
170
+ const session = this.sessionManager.createSession({
171
+ cwd,
172
+ model: parsedModel.model,
173
+ effort: parsedModel.effort,
174
+ mode,
175
+ binaryPath: this.binaryPath,
176
+ });
177
+ if (!isNotification) {
178
+ this.sendSuccess(id, await this.sessionState(session, true));
179
+ this.publishCommands(session.id);
180
+ }
181
+ break;
182
+ }
183
+ case ACP_METHODS.SESSION_LOAD:
184
+ case ACP_METHODS.SESSION_LOAD_ALIAS: {
185
+ if (!isNotification) {
186
+ this.sendError(id, -32601, "session/load is not supported because agy-acp does not replay prior history; use session/resume");
187
+ }
188
+ break;
189
+ }
190
+ case ACP_METHODS.SESSION_RESUME:
191
+ case ACP_METHODS.SESSION_RESUME_ALIAS: {
192
+ const sessionId = String(params.sessionId || "");
193
+ const cwd = typeof params.cwd === "string" ? params.cwd : undefined;
194
+ if (!sessionId) {
195
+ if (!isNotification)
196
+ this.sendError(id, -32602, "sessionId is required");
197
+ break;
198
+ }
199
+ let session = this.sessionManager.getSession(sessionId);
200
+ let created = false;
201
+ try {
202
+ if (!session) {
203
+ session = this.sessionManager.createSession({
204
+ id: sessionId,
205
+ cwd,
206
+ binaryPath: this.binaryPath,
207
+ });
208
+ created = true;
209
+ }
210
+ await session.ensureReadyForResume();
211
+ if (!isNotification) {
212
+ this.sendSuccess(id, await this.sessionState(session));
213
+ this.publishCommands(session.id);
214
+ }
215
+ }
216
+ catch (err) {
217
+ if (created || session) {
218
+ await this.sessionManager.closeSession(sessionId).catch(() => false);
219
+ }
220
+ logger.warn("Failed to resume ACP session", {
221
+ sessionId,
222
+ error: err.message,
223
+ });
224
+ if (!isNotification) {
225
+ this.sendError(id, -32602, `Unable to resume session ${sessionId}`, {
226
+ reason: err.message,
227
+ });
228
+ }
229
+ }
230
+ break;
231
+ }
232
+ case ACP_METHODS.SESSION_PROMPT:
233
+ case ACP_METHODS.SESSION_PROMPT_ALIAS: {
234
+ const sessionId = String(params.sessionId || "");
235
+ const session = this.requireSession(sessionId);
236
+ if (!session) {
237
+ if (!isNotification)
238
+ this.sendError(id, -32602, `Session not found: ${sessionId}`);
239
+ break;
240
+ }
241
+ if (!session.tryBeginPromptOperation()) {
242
+ if (!isNotification) {
243
+ this.sendError(id, -32000, "A prompt operation is already in progress on this session");
244
+ }
245
+ break;
246
+ }
247
+ // Snapshot the model that will actually execute this turn. A model
248
+ // change received while the turn is running only applies after the
249
+ // Antigravity process restarts for the next turn.
250
+ const executingModel = session.model;
251
+ try {
252
+ session.touch();
253
+ const promptText = extractPromptText(params.prompt);
254
+ logger.info("Processing ACP prompt", {
255
+ sessionId,
256
+ promptLength: promptText.length,
257
+ model: executingModel,
258
+ effort: session.effort,
259
+ });
260
+ const slashResult = await executeSlashCommand(promptText, session, this.binaryPath);
261
+ if (slashResult.handled) {
262
+ if (session.isCancelled) {
263
+ if (!isNotification)
264
+ this.sendSuccess(id, { stopReason: "cancelled" });
265
+ break;
266
+ }
267
+ if (slashResult.response) {
268
+ this.sendNotification(ACP_METHODS.SESSION_UPDATE, {
269
+ sessionId: session.id,
270
+ update: {
271
+ sessionUpdate: "agent_message_chunk",
272
+ content: { type: "text", text: slashResult.response },
273
+ },
274
+ });
275
+ }
276
+ if (!isNotification)
277
+ this.sendSuccess(id, { stopReason: "end_turn" });
278
+ break;
279
+ }
280
+ const onStepUpdate = (event) => {
281
+ const step = event.step_update;
282
+ if (!step)
283
+ return;
284
+ if (step.step_type === "agent_response" && step.text_delta) {
285
+ this.sendNotification(ACP_METHODS.SESSION_UPDATE, {
286
+ sessionId: session.id,
287
+ update: {
288
+ sessionUpdate: "agent_message_chunk",
289
+ content: { type: "text", text: step.text_delta },
290
+ },
291
+ });
292
+ }
293
+ else if (step.step_type === "thought" && step.text_delta) {
294
+ this.sendNotification(ACP_METHODS.SESSION_UPDATE, {
295
+ sessionId: session.id,
296
+ update: {
297
+ sessionUpdate: "agent_thought_chunk",
298
+ content: { type: "text", text: step.text_delta },
299
+ },
300
+ });
301
+ }
302
+ else if (step.step_type === "tool" && step.tool_info) {
303
+ const toolCallId = `tool_${step.step_index}`;
304
+ const toolName = step.tool_name || step.tool_info.name || "tool";
305
+ const toolKind = mapToolNameToKind(toolName);
306
+ if (step.state === "ACTIVE") {
307
+ this.sendNotification(ACP_METHODS.SESSION_UPDATE, {
308
+ sessionId: session.id,
309
+ update: {
310
+ sessionUpdate: "tool_call",
311
+ toolCallId,
312
+ title: toolName,
313
+ kind: toolKind,
314
+ rawInput: step.tool_info.parameters || {},
315
+ },
316
+ });
317
+ }
318
+ else if (step.state === "DONE") {
319
+ this.sendNotification(ACP_METHODS.SESSION_UPDATE, {
320
+ sessionId: session.id,
321
+ update: {
322
+ sessionUpdate: "tool_call_update",
323
+ toolCallId,
324
+ status: "completed",
325
+ rawOutput: step.tool_info.output ?? "",
326
+ },
327
+ });
328
+ }
329
+ else if (step.state === "ERROR") {
330
+ this.sendNotification(ACP_METHODS.SESSION_UPDATE, {
331
+ sessionId: session.id,
332
+ update: {
333
+ sessionUpdate: "tool_call_update",
334
+ toolCallId,
335
+ status: "failed",
336
+ rawOutput: step.tool_info.error?.message || "Tool execution error",
337
+ },
338
+ });
339
+ }
340
+ }
341
+ if (step.usage) {
342
+ const turnCost = calculateUsageCostUsd(executingModel, step.usage.input_tokens || 0, step.usage.output_tokens || 0, step.usage.cache_read_tokens || 0);
343
+ const inputTokens = step.usage.input_tokens ?? 0;
344
+ const outputTokens = step.usage.output_tokens ?? 0;
345
+ const cachedReadTokens = step.usage.cache_read_tokens ?? 0;
346
+ const thoughtTokens = step.usage.thinking_tokens;
347
+ const totalTokens = step.usage.total_tokens ?? (inputTokens + outputTokens);
348
+ const totalCostUsd = roundUsageCostUsd(session.usage.totalCostUsd + turnCost);
349
+ const contextWindowMaxTokens = getModelContextWindow(executingModel);
350
+ const contextWindowUsedTokens = inputTokens + outputTokens;
351
+ this.sendNotification(ACP_METHODS.SESSION_UPDATE, {
352
+ sessionId: session.id,
353
+ update: {
354
+ sessionUpdate: "usage_update",
355
+ size: contextWindowMaxTokens,
356
+ used: contextWindowUsedTokens,
357
+ cost: { amount: totalCostUsd, currency: "USD" },
358
+ inputTokens,
359
+ outputTokens,
360
+ cachedReadTokens,
361
+ ...(thoughtTokens !== undefined ? { thoughtTokens } : {}),
362
+ totalTokens,
363
+ totalCostUsd,
364
+ contextWindowMaxTokens,
365
+ contextWindowUsedTokens,
366
+ },
367
+ });
368
+ }
369
+ };
370
+ try {
371
+ const resultEvent = await session.process.sendPrompt(promptText, onStepUpdate);
372
+ const turnUsage = resultEvent.result?.usage;
373
+ // Usage is billable even when the turn terminates with ERROR
374
+ // after model/tool work. Record it before branching on status.
375
+ session.recordTurnUsage(turnUsage, executingModel);
376
+ const usagePayload = this.turnUsagePayload(session, turnUsage, executingModel);
377
+ if (session.isCancelled) {
378
+ if (!isNotification) {
379
+ this.sendSuccess(id, { stopReason: "cancelled", usage: usagePayload });
380
+ }
381
+ }
382
+ else if (resultEvent.result?.status === "ERROR") {
383
+ const errorMessage = resultEvent.result.error || "Turn failed with an error";
384
+ logger.error("Turn result reported error", { error: errorMessage, sessionId });
385
+ this.sendNotification(ACP_METHODS.SESSION_UPDATE, {
386
+ sessionId: session.id,
387
+ update: {
388
+ sessionUpdate: "agent_message_chunk",
389
+ content: { type: "text", text: `\n\n**Error:** ${errorMessage}\n` },
390
+ },
391
+ });
392
+ if (!isNotification) {
393
+ this.sendSuccess(id, { stopReason: "refusal", usage: usagePayload });
394
+ }
395
+ }
396
+ else if (!isNotification) {
397
+ this.sendSuccess(id, { stopReason: "end_turn", usage: usagePayload });
398
+ }
399
+ }
400
+ catch (promptErr) {
401
+ if (session.isCancelled) {
402
+ if (!isNotification)
403
+ this.sendSuccess(id, { stopReason: "cancelled" });
404
+ }
405
+ else {
406
+ const errorMsg = promptErr.message || "Turn execution failed";
407
+ logger.error("Error executing prompt turn", { error: errorMsg, sessionId });
408
+ this.sendNotification(ACP_METHODS.SESSION_UPDATE, {
409
+ sessionId: session.id,
410
+ update: {
411
+ sessionUpdate: "agent_message_chunk",
412
+ content: { type: "text", text: `\n\n**Error:** ${errorMsg}\n` },
413
+ },
414
+ });
415
+ if (!isNotification)
416
+ this.sendSuccess(id, { stopReason: "refusal" });
417
+ }
418
+ }
419
+ }
420
+ finally {
421
+ session.endPromptOperation();
422
+ }
423
+ break;
424
+ }
425
+ case ACP_METHODS.SESSION_CANCEL:
426
+ case ACP_METHODS.SESSION_CANCEL_ALIAS: {
427
+ const sessionId = String(params.sessionId || "");
428
+ const session = this.requireSession(sessionId);
429
+ if (session) {
430
+ logger.info("Cancelling session prompt", { sessionId });
431
+ session.cancelTurn();
432
+ }
433
+ if (!isNotification)
434
+ this.sendSuccess(id, {});
435
+ break;
436
+ }
437
+ case ACP_METHODS.SESSION_CLOSE:
438
+ case ACP_METHODS.SESSION_CLOSE_ALIAS: {
439
+ const sessionId = String(params.sessionId || "");
440
+ await this.sessionManager.closeSession(sessionId);
441
+ if (!isNotification)
442
+ this.sendSuccess(id, {});
443
+ break;
444
+ }
445
+ case ACP_METHODS.SESSION_SET_MODE:
446
+ case ACP_METHODS.SESSION_SET_MODE_ALIAS: {
447
+ const sessionId = String(params.sessionId || "");
448
+ const modeId = String(params.modeId || params.mode || "default");
449
+ const session = this.requireSession(sessionId);
450
+ if (!session) {
451
+ if (!isNotification)
452
+ this.sendError(id, -32602, `Session not found: ${sessionId}`);
453
+ break;
454
+ }
455
+ if (!AVAILABLE_MODES.some((mode) => mode.id === modeId)) {
456
+ if (!isNotification)
457
+ this.sendError(id, -32602, `Unsupported mode: ${modeId}`);
458
+ break;
459
+ }
460
+ session.setMode(modeId);
461
+ if (!isNotification)
462
+ this.sendSuccess(id, {});
463
+ break;
464
+ }
465
+ case ACP_METHODS.SESSION_SET_MODEL:
466
+ case ACP_METHODS.SESSION_SET_MODEL_ALIAS: {
467
+ const sessionId = String(params.sessionId || "");
468
+ const session = this.requireSession(sessionId);
469
+ if (!session) {
470
+ if (!isNotification)
471
+ this.sendError(id, -32602, `Session not found: ${sessionId}`);
472
+ break;
473
+ }
474
+ const requested = String(params.modelId || params.model || "");
475
+ const parsedModel = splitModelAndEffort(requested);
476
+ if (!parsedModel.model) {
477
+ if (!isNotification)
478
+ this.sendError(id, -32602, "modelId is required");
479
+ break;
480
+ }
481
+ const { models, model } = await this.validateModel(parsedModel.model);
482
+ if (!model) {
483
+ if (!isNotification)
484
+ this.sendError(id, -32602, `Unsupported model: ${parsedModel.model}`);
485
+ break;
486
+ }
487
+ if (parsedModel.effort && !model.supportedEfforts.includes(parsedModel.effort)) {
488
+ if (!isNotification) {
489
+ this.sendError(id, -32602, `Model ${model.modelId} does not support effort ${parsedModel.effort}`);
490
+ }
491
+ break;
492
+ }
493
+ if (parsedModel.effort)
494
+ session.setEffort(parsedModel.effort);
495
+ session.setModel(model.modelId);
496
+ const configOptions = buildConfigOptionsForModel(session.model, session.effort, models);
497
+ if (!isNotification)
498
+ this.sendSuccess(id, { configOptions });
499
+ break;
500
+ }
501
+ case ACP_METHODS.SESSION_SET_CONFIG_OPTION:
502
+ case ACP_METHODS.SESSION_SET_CONFIG_OPTION_ALIAS: {
503
+ const sessionId = String(params.sessionId || "");
504
+ const configId = String(params.configId || "");
505
+ const value = String(params.value || "");
506
+ const session = this.requireSession(sessionId);
507
+ if (!session) {
508
+ if (!isNotification)
509
+ this.sendError(id, -32602, `Session not found: ${sessionId}`);
510
+ break;
511
+ }
512
+ const models = await fetchAvailableModels(this.binaryPath);
513
+ if (configId === "thought_level" || configId === "effort") {
514
+ const model = models.find((candidate) => candidate.modelId === session.model);
515
+ if (!model || !model.supportedEfforts.includes(value)) {
516
+ if (!isNotification) {
517
+ this.sendError(id, -32602, `Model ${session.model} does not support effort ${value}`);
518
+ }
519
+ break;
520
+ }
521
+ session.setEffort(value);
522
+ logger.info("Updated reasoning effort for session", { sessionId, effort: value });
523
+ }
524
+ else if (configId === "model") {
525
+ const model = models.find((candidate) => candidate.modelId === value);
526
+ if (!model) {
527
+ if (!isNotification)
528
+ this.sendError(id, -32602, `Unsupported model: ${value}`);
529
+ break;
530
+ }
531
+ session.setModel(value);
532
+ logger.info("Updated model for session via config option", { sessionId, model: value });
533
+ }
534
+ else {
535
+ if (!isNotification)
536
+ this.sendError(id, -32602, `Unsupported config option: ${configId}`);
537
+ break;
538
+ }
539
+ const configOptions = buildConfigOptionsForModel(session.model, session.effort, models);
540
+ if (!isNotification)
541
+ this.sendSuccess(id, { configOptions });
542
+ break;
543
+ }
544
+ case "provider/usage":
545
+ case "antigravity/usage":
546
+ case "session/usage": {
547
+ const usage = await fetchAntigravityUsage(this.binaryPath);
548
+ if (!isNotification)
549
+ this.sendSuccess(id, usage);
550
+ break;
551
+ }
552
+ default:
553
+ if (!isNotification)
554
+ this.sendError(id, -32601, `Method not found: ${method}`);
555
+ }
556
+ }
557
+ catch (handlerErr) {
558
+ logger.error("Internal handler error", { method, error: handlerErr.message });
559
+ if (!isNotification) {
560
+ this.sendError(id, -32603, "Internal error: " + handlerErr.message);
561
+ }
562
+ }
563
+ }
564
+ async stop() {
565
+ if (!this.isRunning)
566
+ return;
567
+ this.isRunning = false;
568
+ if (this.rl) {
569
+ this.rl.close();
570
+ this.rl = null;
571
+ }
572
+ await this.sessionManager.closeAll();
573
+ logger.info("ACP Server stopped");
574
+ }
575
+ }
@@ -0,0 +1,52 @@
1
+ import { EventEmitter } from "node:events";
2
+ import { AgyInitEvent, AgyStepUpdateEvent, AgyResultEvent } from "./protocol.js";
3
+ import { PermissionSettings } from "./permissions.js";
4
+ export interface AntigravityProcessOptions {
5
+ binaryPath?: string;
6
+ cwd?: string;
7
+ model?: string;
8
+ effort?: string;
9
+ mode?: string;
10
+ conversationId?: string;
11
+ permissions?: PermissionSettings;
12
+ env?: Record<string, string>;
13
+ }
14
+ export declare class AntigravityProcess extends EventEmitter {
15
+ private child;
16
+ private binaryPath;
17
+ private cwd;
18
+ private model?;
19
+ private effort?;
20
+ private mode?;
21
+ private conversationId?;
22
+ private permissions;
23
+ private env;
24
+ private isClosed;
25
+ private needsRestart;
26
+ private transitionPromise;
27
+ private currentTurnPromise;
28
+ initialInfo: AgyInitEvent["init"] | null;
29
+ constructor(options?: AntigravityProcessOptions);
30
+ get isRunning(): boolean;
31
+ get isExecutingTurn(): boolean;
32
+ get currentConversationId(): string | undefined;
33
+ get currentModel(): string | undefined;
34
+ get currentEffort(): string | undefined;
35
+ get currentMode(): string | undefined;
36
+ setModel(model: string): void;
37
+ setEffort(effort: string): void;
38
+ setMode(mode: string): void;
39
+ setConversationId(conversationId?: string): void;
40
+ private scheduleRestart;
41
+ private isProcessTreeAlive;
42
+ private signalProcessTree;
43
+ private waitForProcessTreeExit;
44
+ private terminateChild;
45
+ private applyPendingRestart;
46
+ start(): Promise<void>;
47
+ private handleEvent;
48
+ ensureReady(expectedConversationId?: string, timeoutMs?: number): Promise<void>;
49
+ sendPrompt(text: string, onStepUpdate?: (event: AgyStepUpdateEvent) => void): Promise<AgyResultEvent>;
50
+ cancelCurrentTurn(): boolean;
51
+ close(): Promise<void>;
52
+ }