@axhub/genie 0.2.6 → 0.2.7

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 (120) hide show
  1. package/dist/assets/App-BWSqiXAT.js +220 -0
  2. package/dist/assets/App-DrlLKa8f.css +1 -0
  3. package/dist/assets/ReviewApp-nz3mbArg.js +1 -0
  4. package/dist/assets/{_basePickBy-DkiHsp3X.js → _basePickBy-C19AekOu.js} +1 -1
  5. package/dist/assets/{_baseUniq-7ElXb2sX.js → _baseUniq-JsnevLw_.js} +1 -1
  6. package/dist/assets/{arc-CEsS3MdK.js → arc-BLpcuBlf.js} +1 -1
  7. package/dist/assets/architectureDiagram-2XIMDMQ5-CarjBOOv.js +36 -0
  8. package/dist/assets/{blockDiagram-WCTKOSBZ-Cza6M6Ht.js → blockDiagram-WCTKOSBZ-DQBLwsUS.js} +3 -3
  9. package/dist/assets/c4Diagram-IC4MRINW-CGobwBIj.js +10 -0
  10. package/dist/assets/channel-DkFNxV_H.js +1 -0
  11. package/dist/assets/{chunk-4BX2VUAB--HkodwbY.js → chunk-4BX2VUAB-De63kbgc.js} +1 -1
  12. package/dist/assets/{chunk-55IACEB6-CyBuez4e.js → chunk-55IACEB6-DtTDDdM9.js} +1 -1
  13. package/dist/assets/{chunk-FMBD7UC4-CuzG4iAl.js → chunk-FMBD7UC4-DHuwd8tw.js} +1 -1
  14. package/dist/assets/{chunk-JSJVCQXG-BNi8S861.js → chunk-JSJVCQXG-BgytFtmO.js} +1 -1
  15. package/dist/assets/{chunk-KX2RTZJC-D817O-GT.js → chunk-KX2RTZJC-nZdp86aN.js} +1 -1
  16. package/dist/assets/chunk-NQ4KR5QH-CMH6EDP2.js +220 -0
  17. package/dist/assets/{chunk-QZHKN3VN-VMEn-zxh.js → chunk-QZHKN3VN-DvUQ3mnO.js} +1 -1
  18. package/dist/assets/chunk-WL4C6EOR-Dn7db_6t.js +189 -0
  19. package/dist/assets/classDiagram-VBA2DB6C-DtwCEe8S.js +1 -0
  20. package/dist/assets/classDiagram-v2-RAHNMMFH-DtwCEe8S.js +1 -0
  21. package/dist/assets/clone-C0lCEIEO.js +1 -0
  22. package/dist/assets/cose-bilkent-S5V4N54A-DD_nzqsz.js +1 -0
  23. package/dist/assets/cytoscape.esm-5J0xJHOV.js +321 -0
  24. package/dist/assets/{dagre-KLK3FWXG-Bqp7DjEa.js → dagre-KLK3FWXG-CHYIvW47.js} +1 -1
  25. package/dist/assets/diagram-E7M64L7V-TVdvHtGc.js +24 -0
  26. package/dist/assets/{diagram-IFDJBPK2--fHfW6V2.js → diagram-IFDJBPK2-Dzsiln_C.js} +1 -1
  27. package/dist/assets/{diagram-P4PSJMXO-D1kQI5RB.js → diagram-P4PSJMXO-DKnGbUpE.js} +1 -1
  28. package/dist/assets/erDiagram-INFDFZHY-5Kw0bByo.js +70 -0
  29. package/dist/assets/{flowDiagram-PKNHOUZH-DWeNr4yg.js → flowDiagram-PKNHOUZH-BAZ2-jKp.js} +4 -4
  30. package/dist/assets/ganttDiagram-A5KZAMGK-CsADFkcq.js +292 -0
  31. package/dist/assets/{gitGraphDiagram-K3NZZRJ6-B5a8UWjN.js → gitGraphDiagram-K3NZZRJ6-BflpyjGy.js} +1 -1
  32. package/dist/assets/{graph-Cw1rYoD9.js → graph-suelaXFh.js} +1 -1
  33. package/dist/assets/highlighted-body-OFNGDK62-CZrBMazC.js +1 -0
  34. package/dist/assets/index-B01NxbUv.css +1 -0
  35. package/dist/assets/index-DW5pGgQ_.js +2 -0
  36. package/dist/assets/{infoDiagram-LFFYTUFH-D2u70rhN.js → infoDiagram-LFFYTUFH-pfD1FA3p.js} +1 -1
  37. package/dist/assets/ishikawaDiagram-PHBUUO56-ndm9snwO.js +70 -0
  38. package/dist/assets/journeyDiagram-4ABVD52K-HgF2t7z5.js +139 -0
  39. package/dist/assets/{kanban-definition-K7BYSVSG-DbVt0v29.js → kanban-definition-K7BYSVSG-FWinmur1.js} +5 -5
  40. package/dist/assets/{layout-W_tRx4UV.js → layout-vcz43XvZ.js} +1 -1
  41. package/dist/assets/{linear-CcMb2ay-.js → linear-le4gc0vx.js} +1 -1
  42. package/dist/assets/mermaid-GHXKKRXX-CK8m3lad.js +870 -0
  43. package/dist/assets/mindmap-definition-YRQLILUH-CNq9SKj4.js +68 -0
  44. package/dist/assets/{pieDiagram-SKSYHLDU-CDyJaACv.js → pieDiagram-SKSYHLDU-C7PKDh3b.js} +2 -2
  45. package/dist/assets/quadrantDiagram-337W2JSQ-B7FnztNO.js +7 -0
  46. package/dist/assets/requirementDiagram-Z7DCOOCP-Bl_BM2Th.js +73 -0
  47. package/dist/assets/{sankeyDiagram-WA2Y5GQK-Di1ShaMF.js → sankeyDiagram-WA2Y5GQK-4gulcOP4.js} +3 -3
  48. package/dist/assets/sequenceDiagram-2WXFIKYE-VEuJDwyJ.js +145 -0
  49. package/dist/assets/{stateDiagram-RAJIS63D-CVZYMqyW.js → stateDiagram-RAJIS63D-CB4Vl7qM.js} +1 -1
  50. package/dist/assets/stateDiagram-v2-FVOUBMTO-C85ucl39.js +1 -0
  51. package/dist/assets/timeline-definition-YZTLITO2-BPGKhi7f.js +61 -0
  52. package/dist/assets/{treemap-KZPCXAKY-CGG4gx3C.js → treemap-KZPCXAKY-DZSEE6Hz.js} +58 -58
  53. package/dist/assets/vendor-codemirror-CyOKkaQZ.js +31 -0
  54. package/dist/assets/vendor-react-CP4yFTs7.js +8 -0
  55. package/dist/assets/vendor-xterm-DfcmCpbH.js +66 -0
  56. package/dist/assets/{vennDiagram-LZ73GAT5-Dds37L2k.js → vennDiagram-LZ73GAT5-8E_G06fI.js} +4 -4
  57. package/dist/assets/xychartDiagram-JWTSCODW-CbBk50-O.js +7 -0
  58. package/dist/index.html +4 -4
  59. package/package.json +2 -1
  60. package/server/_legacy-providers/README.md +30 -0
  61. package/server/_legacy-providers/claude-sdk.js +956 -0
  62. package/server/_legacy-providers/gemini-cli.js +368 -0
  63. package/server/_legacy-providers/openai-codex.js +705 -0
  64. package/server/_legacy-providers/opencode-cli.js +674 -0
  65. package/server/acp-runtime/client.js +1805 -0
  66. package/server/acp-runtime/client.test.js +688 -0
  67. package/server/acp-runtime/index.js +419 -0
  68. package/server/acp-runtime/registry.js +45 -0
  69. package/server/acp-runtime/session-store.js +254 -0
  70. package/server/acp-runtime/session-store.test.js +89 -0
  71. package/server/channels/runtime/AgentRuntimeAdapter.js +21 -70
  72. package/server/claude-sdk.js +24 -944
  73. package/server/cli.js +4 -2
  74. package/server/external-agent/service.js +52 -63
  75. package/server/gemini-cli.js +23 -360
  76. package/server/index.js +47 -44
  77. package/server/openai-codex.js +24 -698
  78. package/server/opencode-cli.js +70 -640
  79. package/server/routes/agent.js +2 -0
  80. package/server/routes/git.js +3 -20
  81. package/server/routes/session-core.js +44 -10
  82. package/server/session-core/abortSession.js +2 -18
  83. package/server/session-core/eventStore.js +5 -1
  84. package/server/session-core/providerAdapters.js +98 -10
  85. package/server/session-core/runtimeState.js +16 -17
  86. package/server/session-core/runtimeWriter.js +19 -12
  87. package/shared/conversationEvents.js +347 -10
  88. package/shared/conversationEvents.test.js +403 -0
  89. package/dist/assets/App-CYTE30Cf.js +0 -484
  90. package/dist/assets/App-qxJ8_QYu.css +0 -32
  91. package/dist/assets/ReviewApp-BEicSBzW.js +0 -1
  92. package/dist/assets/architectureDiagram-2XIMDMQ5-BubZ7T3U.js +0 -36
  93. package/dist/assets/c4Diagram-IC4MRINW-jhjtOQ12.js +0 -10
  94. package/dist/assets/channel-RmqTALN0.js +0 -1
  95. package/dist/assets/chunk-NQ4KR5QH-DyujyOvx.js +0 -220
  96. package/dist/assets/chunk-WL4C6EOR-CQHHFLvx.js +0 -189
  97. package/dist/assets/classDiagram-VBA2DB6C-wvVV1ggz.js +0 -1
  98. package/dist/assets/classDiagram-v2-RAHNMMFH-wvVV1ggz.js +0 -1
  99. package/dist/assets/clone-oT5aWXpf.js +0 -1
  100. package/dist/assets/cose-bilkent-S5V4N54A-qykDd54p.js +0 -1
  101. package/dist/assets/cytoscape.esm-2ZfV8NB5.js +0 -331
  102. package/dist/assets/diagram-E7M64L7V-BKtx468K.js +0 -24
  103. package/dist/assets/erDiagram-INFDFZHY-DT9YzdNw.js +0 -70
  104. package/dist/assets/ganttDiagram-A5KZAMGK--IgwcUhI.js +0 -292
  105. package/dist/assets/highlighted-body-TPN3WLV5-BCxJHuqY.js +0 -1
  106. package/dist/assets/index-CBuAXA5S.js +0 -2
  107. package/dist/assets/index-CyLWKyxy.css +0 -1
  108. package/dist/assets/ishikawaDiagram-PHBUUO56-Cl8yrezU.js +0 -70
  109. package/dist/assets/journeyDiagram-4ABVD52K-ddP0AMU9.js +0 -139
  110. package/dist/assets/mermaid-O7DHMXV3-BBJqt8pT.js +0 -988
  111. package/dist/assets/mindmap-definition-YRQLILUH-BGhZa7Na.js +0 -68
  112. package/dist/assets/quadrantDiagram-337W2JSQ-BSYuqf0Q.js +0 -7
  113. package/dist/assets/requirementDiagram-Z7DCOOCP-Cfi9YX9H.js +0 -73
  114. package/dist/assets/sequenceDiagram-2WXFIKYE-CYTTG38e.js +0 -145
  115. package/dist/assets/stateDiagram-v2-FVOUBMTO-Bbl0b4-i.js +0 -1
  116. package/dist/assets/timeline-definition-YZTLITO2-B1sdb5mK.js +0 -61
  117. package/dist/assets/vendor-codemirror-Dz7_EqNA.js +0 -39
  118. package/dist/assets/vendor-react-Cpt6D04s.js +0 -59
  119. package/dist/assets/vendor-xterm-DfaPXD3y.js +0 -66
  120. package/dist/assets/xychartDiagram-JWTSCODW-C8QKSyRR.js +0 -7
package/server/cli.js CHANGED
@@ -740,6 +740,8 @@ function showStatus(options = {}) {
740
740
 
741
741
  // Show help
742
742
  function showHelp() {
743
+ const defaultProjectUrl = 'https://github.com/lintendo/AI-Web-UI';
744
+ const defaultIssuesUrl = `${defaultProjectUrl}/issues`;
743
745
  console.log(`
744
746
  ╔═══════════════════════════════════════════════════════════════╗
745
747
  ║ Axhub Genie - Command Line Tool ║
@@ -805,10 +807,10 @@ Environment Variables:
805
807
  AXHUB_GENIE_NO_OPEN Set to true to disable auto-opening the homepage
806
808
 
807
809
  Documentation:
808
- ${packageJson.homepage || 'https://github.com/siteboon/claudecodeui'}
810
+ ${packageJson.homepage || defaultProjectUrl}
809
811
 
810
812
  Report Issues:
811
- ${packageJson.bugs?.url || 'https://github.com/siteboon/claudecodeui/issues'}
813
+ ${packageJson.bugs?.url || defaultIssuesUrl}
812
814
  `);
813
815
  }
814
816
 
@@ -1,10 +1,11 @@
1
1
  import { promises as fs } from 'fs';
2
2
 
3
3
  import { addProjectManually } from '../projects.js';
4
- import { queryClaudeSDK } from '../claude-sdk.js';
5
- import { queryCodex } from '../openai-codex.js';
6
- import { queryGemini } from '../gemini-cli.js';
7
- import { queryOpencode } from '../opencode-cli.js';
4
+ import {
5
+ applyConversationEventToTimelineMessages,
6
+ CONVERSATION_EVENT_KINDS
7
+ } from '../../shared/conversationEvents.js';
8
+ import { executeAgentPrompt } from '../acp-runtime/index.js';
8
9
  import { detectProviderInstallationStatus } from '../routes/cli-auth.js';
9
10
  import {
10
11
  buildAgentCallbackPayload,
@@ -16,7 +17,6 @@ import {
16
17
  } from '../utils/agentCallback.js';
17
18
  import { normalizeExternalImages } from '../utils/agentImages.js';
18
19
  import { resolveWorkingDirectory } from '../utils/defaultWorkingDirectory.js';
19
- import { CODEX_MODELS, GEMINI_MODELS, OPENCODE_MODELS } from '../../shared/modelConstants.js';
20
20
 
21
21
  export const SUPPORTED_EXTERNAL_AGENT_PROVIDERS = ['claude', 'codex', 'gemini', 'opencode'];
22
22
 
@@ -274,7 +274,7 @@ function extractTerminalErrorMessage(payload) {
274
274
  export class CallbackCaptureWriter {
275
275
  constructor() {
276
276
  this.sessionId = null;
277
- this.assistantMessages = [];
277
+ this.timelineMessages = [];
278
278
  this.tokenSummary = createEmptyTokenSummary();
279
279
  this.terminalState = null;
280
280
  this.terminalErrorMessage = null;
@@ -303,24 +303,34 @@ export class CallbackCaptureWriter {
303
303
  };
304
304
  }
305
305
 
306
- if (payload.type === 'claude-response' && payload.data?.type === 'assistant') {
307
- this.assistantMessages.push(payload.data);
308
- }
306
+ if (payload.type === 'conversation-event' && payload.event) {
307
+ this.timelineMessages = applyConversationEventToTimelineMessages(
308
+ this.timelineMessages,
309
+ payload.event,
310
+ payload.event.provider || 'claude'
311
+ );
312
+
313
+ if (payload.event.kind === CONVERSATION_EVENT_KINDS.ERROR) {
314
+ this.terminalState = 'errored';
315
+ this.terminalErrorMessage = payload.event.payload?.message || this.terminalErrorMessage || 'Agent session failed';
316
+ return;
317
+ }
309
318
 
310
- if (
311
- payload.type === 'codex-response' &&
312
- payload.data?.type === 'item_done' &&
313
- payload.data?.itemType === 'agent_message' &&
314
- typeof payload.data?.content === 'string' &&
315
- payload.data.content.trim()
316
- ) {
317
- this.assistantMessages.push({
318
- type: 'assistant',
319
- message: {
320
- role: 'assistant',
321
- content: payload.data.content
319
+ if (payload.event.kind === CONVERSATION_EVENT_KINDS.SESSION_STATE_CHANGED) {
320
+ const nextState = String(payload.event.payload?.state || '').trim().toLowerCase();
321
+ if (nextState === 'completed') {
322
+ this.terminalState = 'completed';
323
+ this.terminalErrorMessage = null;
324
+ } else if (nextState === 'aborted') {
325
+ this.terminalState = 'aborted';
326
+ this.terminalErrorMessage = 'Session aborted';
327
+ } else if (nextState === 'errored') {
328
+ this.terminalState = 'errored';
329
+ this.terminalErrorMessage = payload.event.payload?.message || this.terminalErrorMessage || 'Agent session failed';
322
330
  }
323
- });
331
+ }
332
+
333
+ return;
324
334
  }
325
335
 
326
336
  if (payload.type === 'session-aborted') {
@@ -359,7 +369,16 @@ export class CallbackCaptureWriter {
359
369
  }
360
370
 
361
371
  getAssistantMessages() {
362
- return this.assistantMessages;
372
+ return this.timelineMessages
373
+ .filter((message) => message?.type === 'assistant' && !message?.isToolUse && !message?.isSystemNotice)
374
+ .map((message) => ({
375
+ type: 'assistant',
376
+ message: {
377
+ role: 'assistant',
378
+ content: message.content || ''
379
+ },
380
+ timestamp: message.timestamp || null
381
+ }));
363
382
  }
364
383
 
365
384
  getTotalTokens() {
@@ -418,52 +437,19 @@ async function ensureProjectPathExists(projectPath) {
418
437
  }
419
438
 
420
439
  async function runProviderSession({ provider, message, images, finalProjectPath, sessionId, model, writer }) {
421
- if (provider === 'claude') {
422
- await queryClaudeSDK(message, {
440
+ await executeAgentPrompt({
441
+ agentKey: provider,
442
+ command: message,
443
+ options: {
423
444
  projectPath: finalProjectPath,
424
445
  cwd: finalProjectPath,
425
446
  sessionId,
426
447
  model,
427
448
  permissionMode: 'bypassPermissions',
428
449
  images
429
- }, writer);
430
- return;
431
- }
432
-
433
- if (provider === 'codex') {
434
- await queryCodex(message, {
435
- projectPath: finalProjectPath,
436
- cwd: finalProjectPath,
437
- sessionId,
438
- model: model || CODEX_MODELS.DEFAULT,
439
- permissionMode: 'bypassPermissions',
440
- images
441
- }, writer);
442
- return;
443
- }
444
-
445
- if (provider === 'gemini') {
446
- await queryGemini(message, {
447
- projectPath: finalProjectPath,
448
- cwd: finalProjectPath,
449
- sessionId,
450
- resume: !!sessionId,
451
- model: model || GEMINI_MODELS.DEFAULT,
452
- permissionMode: 'bypassPermissions'
453
- }, writer);
454
- return;
455
- }
456
-
457
- if (provider === 'opencode') {
458
- await queryOpencode(message, {
459
- projectPath: finalProjectPath,
460
- cwd: finalProjectPath,
461
- sessionId,
462
- resume: !!sessionId,
463
- model: model || OPENCODE_MODELS.DEFAULT,
464
- permissionMode: 'bypassPermissions'
465
- }, writer);
466
- }
450
+ },
451
+ writer
452
+ });
467
453
  }
468
454
 
469
455
  export async function runExternalAgentRequest({
@@ -483,6 +469,7 @@ export async function runExternalAgentRequest({
483
469
  const response = {
484
470
  success: true,
485
471
  openOnly: true,
472
+ runtime: 'acp',
486
473
  sessionId: normalized.normalizedSessionId,
487
474
  ...buildSessionNavigation(normalized.normalizedSessionId),
488
475
  isResumed: true,
@@ -491,6 +478,7 @@ export async function runExternalAgentRequest({
491
478
 
492
479
  transportWriter?.send?.({
493
480
  type: 'open-only',
481
+ runtime: 'acp',
494
482
  ...response
495
483
  });
496
484
 
@@ -550,6 +538,7 @@ export async function runExternalAgentRequest({
550
538
  const response = {
551
539
  success: true,
552
540
  sessionId: callbackSessionId,
541
+ runtime: 'acp',
553
542
  ...navigation,
554
543
  isResumed: !!normalized.normalizedSessionId,
555
544
  messages: callbackCaptureWriter.getAssistantMessages(),
@@ -1,368 +1,31 @@
1
- import { spawn } from 'child_process';
2
- import crossSpawn from 'cross-spawn';
3
- import { resolveWorkingDirectory } from './utils/defaultWorkingDirectory.js';
4
-
5
- const spawnFunction = process.platform === 'win32' ? crossSpawn : spawn;
6
-
7
- const activeGeminiProcesses = new Map();
8
-
9
- function collectTextChunks(payload) {
10
- if (!payload) return [];
11
- if (typeof payload === 'string') return payload.trim() ? [payload] : [];
12
-
13
- const chunks = [];
14
-
15
- if (Array.isArray(payload)) {
16
- payload.forEach(item => {
17
- chunks.push(...collectTextChunks(item));
18
- });
19
- return chunks;
20
- }
21
-
22
- if (typeof payload !== 'object') {
23
- return chunks;
24
- }
25
-
26
- const directKeys = ['text', 'response', 'content', 'message', 'delta'];
27
- for (const key of directKeys) {
28
- if (payload[key] !== undefined) {
29
- chunks.push(...collectTextChunks(payload[key]));
30
- }
31
- }
32
-
33
- if (Array.isArray(payload.parts)) {
34
- payload.parts.forEach(part => {
35
- chunks.push(...collectTextChunks(part));
36
- });
37
- }
38
-
39
- if (payload.data && typeof payload.data === 'object') {
40
- chunks.push(...collectTextChunks(payload.data));
41
- }
42
-
43
- return chunks;
44
- }
45
-
46
- function getEventRole(event) {
47
- return (
48
- event?.role ||
49
- event?.author ||
50
- event?.sender ||
51
- event?.message?.role ||
52
- event?.data?.role ||
53
- event?.content?.role ||
54
- null
55
- );
56
- }
57
-
58
- function extractAssistantTextChunks(event, command) {
59
- const role = String(getEventRole(event) || '').toLowerCase();
60
- if (role === 'user') return [];
61
-
62
- const eventType = String(event?.type || event?.event || event?.kind || '').toLowerCase();
63
- const hasAssistantPayload = !!(event?.response || event?.candidates || event?.delta || event?.text || event?.content || event?.message);
64
- if (eventType.includes('prompt') && !hasAssistantPayload) {
65
- return [];
66
- }
67
-
68
- const normalizedPrompt = String(command || '').trim();
69
- return collectTextChunks(event).filter((text) => {
70
- const trimmed = String(text || '').trim();
71
- if (!trimmed) return false;
72
- if (normalizedPrompt && trimmed === normalizedPrompt) return false;
73
- return true;
74
- });
75
- }
76
-
77
- function parseGeminiJsonLine(line) {
78
- const trimmed = line.trim();
79
- if (!trimmed) return null;
80
-
81
- const payload = trimmed.startsWith('data:') ? trimmed.slice(5).trim() : trimmed;
82
- if (!payload) return null;
83
-
84
- return JSON.parse(payload);
85
- }
86
-
87
- function emitTextChunks(ws, sessionId, chunks) {
88
- chunks.forEach((text) => {
89
- if (!text) return;
90
- ws.send({
91
- type: 'claude-response',
92
- data: {
93
- type: 'content_block_delta',
94
- delta: {
95
- type: 'text_delta',
96
- text
97
- }
98
- },
99
- provider: 'gemini',
100
- sessionId
101
- });
102
- });
103
- }
104
-
105
- function formatGeminiProcessError(code, stderrOutput) {
106
- const normalizedStderr = String(stderrOutput || '').trim();
107
- if (normalizedStderr) {
108
- const apiMessageMatch =
109
- normalizedStderr.match(/"message":"([^"]+)"/) ||
110
- normalizedStderr.match(/message:\s*"([^"]+)"/i) ||
111
- normalizedStderr.match(/ApiError:\s*\{.*?"message":"([^"]+)"/i) ||
112
- normalizedStderr.match(/Error when talking to Gemini API[\s\S]*?message":"([^"]+)"/i);
113
-
114
- if (apiMessageMatch?.[1]) {
115
- return apiMessageMatch[1];
116
- }
117
-
118
- const firstMeaningfulLine = normalizedStderr
119
- .split('\n')
120
- .map((line) => line.trim())
121
- .find((line) => line && !line.startsWith('at '));
122
-
123
- if (firstMeaningfulLine) {
124
- return firstMeaningfulLine;
125
- }
126
-
127
- return normalizedStderr;
128
- }
129
-
130
- return `Gemini CLI exited with code ${code}`;
131
- }
132
-
133
- function extractGeminiEventError(event) {
134
- if (!event || typeof event !== 'object') {
135
- return null;
136
- }
137
-
138
- const candidates = [
139
- event?.error?.message,
140
- event?.error?.details,
141
- event?.result?.error?.message,
142
- event?.result?.error,
143
- event?.data?.error?.message,
144
- event?.data?.error,
145
- event?.message
146
- ];
147
-
148
- for (const candidate of candidates) {
149
- const normalized = String(candidate || '').trim();
150
- if (normalized) {
151
- return normalized;
152
- }
153
- }
154
-
155
- return null;
156
- }
157
-
158
- async function queryGemini(command, options = {}, ws) {
159
- return new Promise((resolve, reject) => {
160
- const { sessionId, cwd, projectPath, resume, model, permissionMode } = options;
161
- let capturedSessionId = sessionId;
162
- let sentSessionCreated = false;
163
- let stderrBuffer = '';
164
- let fatalErrorSent = false;
165
- let sawSuccessfulTerminalEvent = false;
166
- let sawErroredTerminalEvent = false;
167
-
168
- const args = ['-y', '@google/gemini-cli'];
169
-
170
- if (sessionId && (resume || !command || !command.trim())) {
171
- args.push('--resume', sessionId);
172
- }
173
-
174
- if (command && command.trim()) {
175
- args.push('--prompt', command);
176
- args.push('--output-format', 'stream-json');
177
- }
178
-
179
- if (model) {
180
- args.push('--model', model);
181
- }
182
-
183
- if (permissionMode === 'bypassPermissions' || permissionMode === 'acceptEdits') {
184
- args.push('--yolo');
185
- }
186
-
187
- const workingDir = resolveWorkingDirectory({ cwd, projectPath });
188
-
189
- const geminiProcess = spawnFunction('npx', args, {
190
- cwd: workingDir,
191
- stdio: ['pipe', 'pipe', 'pipe'],
192
- env: {
193
- ...process.env,
194
- GEMINI_NONINTERACTIVE: '1'
195
- }
196
- });
197
-
198
- const processKey = capturedSessionId || Date.now().toString();
199
- let processRegistryKey = processKey;
200
- activeGeminiProcesses.set(processRegistryKey, geminiProcess);
201
-
202
- const finalizeSessionId = () => capturedSessionId || sessionId || null;
203
- const emitFatalError = (errorMessage) => {
204
- if (fatalErrorSent || !errorMessage) {
205
- return;
206
- }
207
-
208
- fatalErrorSent = true;
209
- ws.send({
210
- type: 'claude-error',
211
- error: errorMessage,
212
- provider: 'gemini',
213
- sessionId: finalizeSessionId()
214
- });
215
- };
216
-
217
- const handleJsonEvent = (event) => {
218
- const incomingSessionId = event?.session_id || event?.sessionId || event?.data?.session_id || event?.data?.sessionId;
219
- if (incomingSessionId && !capturedSessionId) {
220
- capturedSessionId = incomingSessionId;
221
- if (ws.setSessionId && typeof ws.setSessionId === 'function') {
222
- ws.setSessionId(capturedSessionId);
223
- }
224
-
225
- if (processRegistryKey !== capturedSessionId) {
226
- activeGeminiProcesses.delete(processRegistryKey);
227
- activeGeminiProcesses.set(capturedSessionId, geminiProcess);
228
- processRegistryKey = capturedSessionId;
229
- }
230
-
231
- if (!sessionId && !sentSessionCreated) {
232
- sentSessionCreated = true;
233
- ws.send({
234
- type: 'session-created',
235
- sessionId: capturedSessionId,
236
- provider: 'gemini'
237
- });
238
- }
239
- }
240
-
241
- const type = event?.type || event?.event || event?.kind;
242
- if (type === 'result' || type === 'done' || type === 'complete') {
243
- const eventStatus = String(event?.status || event?.result?.status || '').trim().toLowerCase();
244
- const eventError = extractGeminiEventError(event);
245
- if (eventStatus === 'error' || eventError) {
246
- sawErroredTerminalEvent = true;
247
- emitFatalError(eventError || 'Gemini CLI request failed');
248
- return;
249
- }
250
-
251
- sawSuccessfulTerminalEvent = true;
252
- ws.send({
253
- type: 'claude-response',
254
- data: { type: 'content_block_stop' },
255
- provider: 'gemini',
256
- sessionId: finalizeSessionId()
257
- });
258
-
259
- ws.send({
260
- type: 'gemini-result',
261
- data: event,
262
- sessionId: finalizeSessionId()
263
- });
264
- return;
265
- }
266
-
267
- const textChunks = extractAssistantTextChunks(event, command);
268
- emitTextChunks(ws, finalizeSessionId(), textChunks);
269
- };
270
-
271
- let stdoutBuffer = '';
272
- geminiProcess.stdout.on('data', (data) => {
273
- stdoutBuffer += data.toString();
274
- const lines = stdoutBuffer.split('\n');
275
- stdoutBuffer = lines.pop() || '';
276
-
277
- for (const line of lines) {
278
- try {
279
- const event = parseGeminiJsonLine(line);
280
- if (!event) continue;
281
- handleJsonEvent(event);
282
- } catch {}
283
- }
284
- });
285
-
286
- geminiProcess.stderr.on('data', (data) => {
287
- const chunk = data.toString();
288
- stderrBuffer += chunk;
289
-
290
- const normalizedChunk = chunk.trim();
291
- if (normalizedChunk) {
292
- console.warn('Gemini CLI stderr:', normalizedChunk);
293
- }
294
- });
295
-
296
- geminiProcess.on('close', (code) => {
297
- if (stdoutBuffer.trim()) {
298
- try {
299
- const finalEvent = parseGeminiJsonLine(stdoutBuffer);
300
- if (finalEvent) {
301
- handleJsonEvent(finalEvent);
302
- }
303
- } catch {}
304
- }
305
-
306
- activeGeminiProcesses.delete(processRegistryKey);
307
-
308
- if (code === 0 && !sawErroredTerminalEvent) {
309
- if (!sawSuccessfulTerminalEvent) {
310
- ws.send({
311
- type: 'claude-response',
312
- data: { type: 'content_block_stop' },
313
- provider: 'gemini',
314
- sessionId: finalizeSessionId()
315
- });
316
- }
317
-
318
- ws.send({
319
- type: 'claude-complete',
320
- sessionId: finalizeSessionId(),
321
- provider: 'gemini',
322
- exitCode: code,
323
- isNewSession: !sessionId && !!command
324
- });
325
- resolve();
326
- } else {
327
- const errorMessage = formatGeminiProcessError(code, stderrBuffer);
328
- emitFatalError(errorMessage);
329
- reject(new Error(errorMessage));
330
- }
331
- });
332
-
333
- geminiProcess.on('error', (error) => {
334
- activeGeminiProcesses.delete(processRegistryKey);
335
-
336
- emitFatalError(error.message);
337
-
338
- reject(error);
339
- });
340
-
341
- geminiProcess.stdin.end();
1
+ import {
2
+ abortAgentSession,
3
+ executeAgentPrompt,
4
+ getActiveAgentSessions,
5
+ isAgentSessionActive
6
+ } from './acp-runtime/index.js';
7
+ import { GEMINI_MODELS } from '../shared/modelConstants.js';
8
+
9
+ export async function queryGemini(command, options = {}, writer) {
10
+ return executeAgentPrompt({
11
+ agentKey: 'gemini',
12
+ command,
13
+ options: {
14
+ ...options,
15
+ model: options.model || GEMINI_MODELS.DEFAULT
16
+ },
17
+ writer
342
18
  });
343
19
  }
344
20
 
345
- function abortGeminiSession(sessionId) {
346
- const process = activeGeminiProcesses.get(sessionId);
347
- if (process) {
348
- process.kill('SIGTERM');
349
- activeGeminiProcesses.delete(sessionId);
350
- return true;
351
- }
352
- return false;
21
+ export async function abortGeminiSession(sessionId) {
22
+ return abortAgentSession('gemini', sessionId);
353
23
  }
354
24
 
355
- function isGeminiSessionActive(sessionId) {
356
- return activeGeminiProcesses.has(sessionId);
25
+ export function isGeminiSessionActive(sessionId) {
26
+ return isAgentSessionActive('gemini', sessionId);
357
27
  }
358
28
 
359
- function getActiveGeminiSessions() {
360
- return Array.from(activeGeminiProcesses.keys());
29
+ export function getActiveGeminiSessions() {
30
+ return getActiveAgentSessions('gemini');
361
31
  }
362
-
363
- export {
364
- queryGemini,
365
- abortGeminiSession,
366
- isGeminiSessionActive,
367
- getActiveGeminiSessions
368
- };