paseo-acp-agy 1.2.10 → 1.2.12

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.
@@ -610,7 +610,25 @@ export class ACPServer extends EventEmitter {
610
610
  }
611
611
  };
612
612
  try {
613
- const resultEvent = await session.process.sendPrompt(promptText, onStepUpdate);
613
+ let resultEvent;
614
+ try {
615
+ resultEvent = await session.process.sendPrompt(promptText, onStepUpdate);
616
+ }
617
+ catch (firstErr) {
618
+ const firstMsg = firstErr.message || "";
619
+ if (stepCount === 0 &&
620
+ !session.isCancelled &&
621
+ firstMsg.includes("agy process exited prematurely")) {
622
+ logger.warn("agy process exited before streaming any steps; retrying prompt with fresh process", {
623
+ sessionId,
624
+ error: firstMsg,
625
+ });
626
+ resultEvent = await session.process.sendPrompt(promptText, onStepUpdate);
627
+ }
628
+ else {
629
+ throw firstErr;
630
+ }
631
+ }
614
632
  const turnUsage = resultEvent.result?.usage;
615
633
  const numTurns = resultEvent.result?.num_turns || stepCount || 1;
616
634
  // Usage is billable even when the turn terminates with ERROR
@@ -642,12 +660,14 @@ export class ACPServer extends EventEmitter {
642
660
  }
643
661
  }
644
662
  catch (promptErr) {
645
- if (session.isCancelled) {
663
+ const errorMsg = promptErr.message || "Turn execution failed";
664
+ if (session.isCancelled ||
665
+ errorMsg.includes("cancelled") ||
666
+ errorMsg.includes("Turn was cancelled")) {
646
667
  if (!isNotification)
647
668
  this.sendSuccess(id, { stopReason: "cancelled" });
648
669
  }
649
670
  else {
650
- const errorMsg = promptErr.message || "Turn execution failed";
651
671
  logger.error("Error executing prompt turn", { error: errorMsg, sessionId });
652
672
  this.sendNotification(ACP_METHODS.SESSION_UPDATE, {
653
673
  sessionId: session.id,
@@ -399,6 +399,7 @@ export class AntigravityProcess extends EventEmitter {
399
399
  child.stderr.setEncoding("utf-8");
400
400
  let stdoutBuffer = "";
401
401
  let stderrBuffer = "";
402
+ const recentStderrLines = [];
402
403
  child.stdout.on("data", (chunk) => {
403
404
  this.emit("activity");
404
405
  stdoutBuffer += chunk;
@@ -424,8 +425,13 @@ export class AntigravityProcess extends EventEmitter {
424
425
  const lines = stderrBuffer.split("\n");
425
426
  stderrBuffer = lines.pop() || "";
426
427
  for (const line of lines) {
427
- if (line.trim())
428
- logger.warn("agy stderr: " + line.trim());
428
+ const trimmed = line.trim();
429
+ if (trimmed) {
430
+ logger.warn("agy stderr: " + trimmed);
431
+ recentStderrLines.push(trimmed);
432
+ if (recentStderrLines.length > 20)
433
+ recentStderrLines.shift();
434
+ }
429
435
  }
430
436
  });
431
437
  child.on("error", (err) => {
@@ -438,9 +444,24 @@ export class AntigravityProcess extends EventEmitter {
438
444
  });
439
445
  child.on("close", (code, signal) => {
440
446
  logger.info("agy process exited", { code, signal, pid: child.pid });
447
+ if (stderrBuffer.trim()) {
448
+ recentStderrLines.push(stderrBuffer.trim());
449
+ if (recentStderrLines.length > 20)
450
+ recentStderrLines.shift();
451
+ }
452
+ const stderrDiagnostic = recentStderrLines.join(" ").trim();
453
+ const exitDiagnostic = stderrDiagnostic
454
+ ? `agy process exited prematurely with code ${code} signal ${signal}: ${stderrDiagnostic}`
455
+ : `agy process exited prematurely with code ${code} signal ${signal}`;
441
456
  if (this.currentTurnPromise?.child === child) {
442
- this.currentTurnPromise.reject(new Error(`agy process exited prematurely with code ${code} signal ${signal}`));
457
+ const turn = this.currentTurnPromise;
443
458
  this.currentTurnPromise = null;
459
+ if (turn.cancelled) {
460
+ turn.reject(new Error("Turn was cancelled"));
461
+ }
462
+ else {
463
+ turn.reject(new Error(exitDiagnostic));
464
+ }
444
465
  }
445
466
  // The leader can die while a tool subprocess remains in its process
446
467
  // group. Keep the group identity long enough to reap those descendants.
@@ -742,6 +763,11 @@ export class AntigravityProcess extends EventEmitter {
742
763
  logger.info("Cancelling active agy turn", { hasChild: Boolean(active.child) });
743
764
  if (active.child) {
744
765
  const child = active.child;
766
+ // Immediately detach this.child so subsequent turns will NOT attach to this dying process!
767
+ if (this.child === child) {
768
+ this.child = null;
769
+ this.initialInfo = null;
770
+ }
745
771
  this.signalProcessTree(child, "SIGINT");
746
772
  setTimeout(() => {
747
773
  if (this.isProcessTreeAlive(child)) {
@@ -13,69 +13,100 @@ export function getAgyAppDataDir() {
13
13
  }
14
14
  return path.join(os.homedir(), ".gemini", "antigravity-cli");
15
15
  }
16
+ function querySummariesDb(dbPath, query, params = []) {
17
+ // 1. Try node:sqlite DatabaseSync (Node 22+)
18
+ try {
19
+ const DatabaseSync = require("node:sqlite")?.DatabaseSync;
20
+ if (DatabaseSync) {
21
+ const db = new DatabaseSync(dbPath, { readOnly: true });
22
+ return db.prepare(query).all(...params) || [];
23
+ }
24
+ }
25
+ catch { }
26
+ // 2. Try sqlite3 CLI fallback (macOS, Linux with Node < 22)
27
+ try {
28
+ const { execFileSync } = require("node:child_process");
29
+ let finalQuery = query;
30
+ if (params.length === 1 && typeof params[0] === "string") {
31
+ finalQuery = query.replace("?", `'${params[0].replace(/'/g, "''")}'`);
32
+ }
33
+ const stdout = execFileSync("sqlite3", ["-json", dbPath, finalQuery], {
34
+ encoding: "utf-8",
35
+ timeout: 5000,
36
+ stdio: ["pipe", "pipe", "ignore"],
37
+ });
38
+ if (stdout && stdout.trim()) {
39
+ return JSON.parse(stdout) || [];
40
+ }
41
+ }
42
+ catch (err) {
43
+ logger.debug("Failed to query conversation summaries via sqlite3 CLI", {
44
+ error: err.message,
45
+ });
46
+ }
47
+ return [];
48
+ }
16
49
  export function loadNativeAntigravitySessions(cwd) {
17
50
  const results = [];
18
51
  const appDataDir = getAgyAppDataDir();
19
52
  const dbPath = path.join(appDataDir, "conversation_summaries.db");
20
53
  const normalizedTargetCwd = cwd ? path.resolve(cwd).toLowerCase() : undefined;
21
54
  if (fs.existsSync(dbPath)) {
22
- let DatabaseSync = null;
23
55
  try {
24
- DatabaseSync = require("node:sqlite")?.DatabaseSync;
25
- }
26
- catch { }
27
- if (DatabaseSync) {
28
- try {
29
- const db = new DatabaseSync(dbPath, { readOnly: true });
30
- const rows = db
31
- .prepare(`SELECT conversation_id, title, preview, last_modified_time, workspace_uris
32
- FROM conversation_summaries
33
- ORDER BY last_modified_time DESC
34
- LIMIT 100`)
35
- .all();
36
- for (const row of rows) {
37
- if (!row.conversation_id)
38
- continue;
39
- let primaryCwd = process.cwd();
40
- let matchesCwd = false;
41
- if (row.workspace_uris) {
42
- try {
43
- const uris = JSON.parse(row.workspace_uris);
44
- if (Array.isArray(uris) && uris.length > 0) {
45
- const filePaths = uris
46
- .map((u) => u.replace(/^file:\/\//i, ""))
47
- .filter(Boolean);
48
- if (filePaths.length > 0) {
49
- primaryCwd = filePaths[0];
56
+ const rows = querySummariesDb(dbPath, `SELECT conversation_id, title, preview, last_modified_time, workspace_uris
57
+ FROM conversation_summaries
58
+ ORDER BY last_modified_time DESC
59
+ LIMIT 100`);
60
+ for (const row of rows) {
61
+ if (!row.conversation_id)
62
+ continue;
63
+ let primaryCwd = process.cwd();
64
+ let matchesCwd = false;
65
+ if (row.workspace_uris) {
66
+ try {
67
+ const uris = JSON.parse(row.workspace_uris);
68
+ if (Array.isArray(uris) && uris.length > 0) {
69
+ const filePaths = uris
70
+ .map((u) => {
71
+ const cleaned = u.replace(/^file:\/\//i, "");
72
+ try {
73
+ return path.resolve(decodeURIComponent(cleaned));
50
74
  }
51
- if (normalizedTargetCwd) {
52
- matchesCwd = filePaths.some((p) => path.resolve(p).toLowerCase() === normalizedTargetCwd);
75
+ catch {
76
+ return path.resolve(cleaned);
53
77
  }
78
+ })
79
+ .filter(Boolean);
80
+ if (filePaths.length > 0) {
81
+ primaryCwd = filePaths[0];
82
+ }
83
+ if (normalizedTargetCwd) {
84
+ matchesCwd = filePaths.some((p) => p.toLowerCase() === normalizedTargetCwd);
54
85
  }
55
86
  }
56
- catch { }
57
- }
58
- if (normalizedTargetCwd && !matchesCwd) {
59
- continue;
60
87
  }
61
- const rawDate = row.last_modified_time;
62
- const dateIso = rawDate ? new Date(rawDate).toISOString() : undefined;
63
- results.push({
64
- sessionId: row.conversation_id,
65
- cwd: primaryCwd,
66
- title: (row.title && row.title.trim()) ||
67
- (row.preview && row.preview.trim()) ||
68
- "Antigravity Session",
69
- updatedAt: dateIso,
70
- });
88
+ catch { }
71
89
  }
72
- }
73
- catch (err) {
74
- logger.debug("Failed to read native conversation summaries from sqlite", {
75
- error: err.message,
90
+ if (normalizedTargetCwd && !matchesCwd) {
91
+ continue;
92
+ }
93
+ const rawDate = row.last_modified_time;
94
+ const dateIso = rawDate ? new Date(rawDate).toISOString() : undefined;
95
+ results.push({
96
+ sessionId: row.conversation_id,
97
+ cwd: primaryCwd,
98
+ title: (row.title && row.title.trim()) ||
99
+ (row.preview && row.preview.trim()) ||
100
+ "Antigravity Session",
101
+ updatedAt: dateIso,
76
102
  });
77
103
  }
78
104
  }
105
+ catch (err) {
106
+ logger.debug("Failed to read native conversation summaries from sqlite", {
107
+ error: err.message,
108
+ });
109
+ }
79
110
  }
80
111
  // Fallback: Only scan brainDir if no conversations were found in DB
81
112
  if (results.length === 0) {
@@ -119,46 +150,41 @@ export function getNativeAntigravitySession(conversationId) {
119
150
  const appDataDir = getAgyAppDataDir();
120
151
  const dbPath = path.join(appDataDir, "conversation_summaries.db");
121
152
  if (fs.existsSync(dbPath)) {
122
- let DatabaseSync = null;
123
153
  try {
124
- DatabaseSync = require("node:sqlite")?.DatabaseSync;
125
- }
126
- catch { }
127
- if (DatabaseSync) {
128
- try {
129
- const db = new DatabaseSync(dbPath, { readOnly: true });
130
- const row = db
131
- .prepare(`SELECT conversation_id, title, preview, last_modified_time, workspace_uris
132
- FROM conversation_summaries
133
- WHERE conversation_id = ?`)
134
- .get(conversationId);
135
- if (row) {
136
- let primaryCwd = process.cwd();
137
- if (row.workspace_uris) {
138
- try {
139
- const uris = JSON.parse(row.workspace_uris);
140
- if (Array.isArray(uris) && uris.length > 0) {
141
- const p = uris[0].replace(/^file:\/\//i, "");
142
- if (p)
143
- primaryCwd = p;
154
+ const rows = querySummariesDb(dbPath, `SELECT conversation_id, title, preview, last_modified_time, workspace_uris
155
+ FROM conversation_summaries
156
+ WHERE conversation_id = ?`, [conversationId]);
157
+ const row = rows[0];
158
+ if (row) {
159
+ let primaryCwd = process.cwd();
160
+ if (row.workspace_uris) {
161
+ try {
162
+ const uris = JSON.parse(row.workspace_uris);
163
+ if (Array.isArray(uris) && uris.length > 0) {
164
+ const cleaned = uris[0].replace(/^file:\/\//i, "");
165
+ try {
166
+ primaryCwd = path.resolve(decodeURIComponent(cleaned));
167
+ }
168
+ catch {
169
+ primaryCwd = path.resolve(cleaned);
144
170
  }
145
171
  }
146
- catch { }
147
172
  }
148
- return {
149
- sessionId: row.conversation_id,
150
- cwd: primaryCwd,
151
- title: (row.title && row.title.trim()) ||
152
- (row.preview && row.preview.trim()) ||
153
- "Antigravity Session",
154
- updatedAt: row.last_modified_time
155
- ? new Date(row.last_modified_time).toISOString()
156
- : undefined,
157
- };
173
+ catch { }
158
174
  }
175
+ return {
176
+ sessionId: row.conversation_id,
177
+ cwd: primaryCwd,
178
+ title: (row.title && row.title.trim()) ||
179
+ (row.preview && row.preview.trim()) ||
180
+ "Antigravity Session",
181
+ updatedAt: row.last_modified_time
182
+ ? new Date(row.last_modified_time).toISOString()
183
+ : undefined,
184
+ };
159
185
  }
160
- catch { }
161
186
  }
187
+ catch { }
162
188
  }
163
189
  // Fallback check transcript file
164
190
  const brainDir = path.join(appDataDir, "brain", conversationId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paseo-acp-agy",
3
- "version": "1.2.10",
3
+ "version": "1.2.12",
4
4
  "description": "ACP (Agent Client Protocol) provider for Google Antigravity in Paseo and Zed",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",