@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.
- package/dist/assets/App-BWSqiXAT.js +220 -0
- package/dist/assets/App-DrlLKa8f.css +1 -0
- package/dist/assets/ReviewApp-nz3mbArg.js +1 -0
- package/dist/assets/{_basePickBy-DkiHsp3X.js → _basePickBy-C19AekOu.js} +1 -1
- package/dist/assets/{_baseUniq-7ElXb2sX.js → _baseUniq-JsnevLw_.js} +1 -1
- package/dist/assets/{arc-CEsS3MdK.js → arc-BLpcuBlf.js} +1 -1
- package/dist/assets/architectureDiagram-2XIMDMQ5-CarjBOOv.js +36 -0
- package/dist/assets/{blockDiagram-WCTKOSBZ-Cza6M6Ht.js → blockDiagram-WCTKOSBZ-DQBLwsUS.js} +3 -3
- package/dist/assets/c4Diagram-IC4MRINW-CGobwBIj.js +10 -0
- package/dist/assets/channel-DkFNxV_H.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB--HkodwbY.js → chunk-4BX2VUAB-De63kbgc.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-CyBuez4e.js → chunk-55IACEB6-DtTDDdM9.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-CuzG4iAl.js → chunk-FMBD7UC4-DHuwd8tw.js} +1 -1
- package/dist/assets/{chunk-JSJVCQXG-BNi8S861.js → chunk-JSJVCQXG-BgytFtmO.js} +1 -1
- package/dist/assets/{chunk-KX2RTZJC-D817O-GT.js → chunk-KX2RTZJC-nZdp86aN.js} +1 -1
- package/dist/assets/chunk-NQ4KR5QH-CMH6EDP2.js +220 -0
- package/dist/assets/{chunk-QZHKN3VN-VMEn-zxh.js → chunk-QZHKN3VN-DvUQ3mnO.js} +1 -1
- package/dist/assets/chunk-WL4C6EOR-Dn7db_6t.js +189 -0
- package/dist/assets/classDiagram-VBA2DB6C-DtwCEe8S.js +1 -0
- package/dist/assets/classDiagram-v2-RAHNMMFH-DtwCEe8S.js +1 -0
- package/dist/assets/clone-C0lCEIEO.js +1 -0
- package/dist/assets/cose-bilkent-S5V4N54A-DD_nzqsz.js +1 -0
- package/dist/assets/cytoscape.esm-5J0xJHOV.js +321 -0
- package/dist/assets/{dagre-KLK3FWXG-Bqp7DjEa.js → dagre-KLK3FWXG-CHYIvW47.js} +1 -1
- package/dist/assets/diagram-E7M64L7V-TVdvHtGc.js +24 -0
- package/dist/assets/{diagram-IFDJBPK2--fHfW6V2.js → diagram-IFDJBPK2-Dzsiln_C.js} +1 -1
- package/dist/assets/{diagram-P4PSJMXO-D1kQI5RB.js → diagram-P4PSJMXO-DKnGbUpE.js} +1 -1
- package/dist/assets/erDiagram-INFDFZHY-5Kw0bByo.js +70 -0
- package/dist/assets/{flowDiagram-PKNHOUZH-DWeNr4yg.js → flowDiagram-PKNHOUZH-BAZ2-jKp.js} +4 -4
- package/dist/assets/ganttDiagram-A5KZAMGK-CsADFkcq.js +292 -0
- package/dist/assets/{gitGraphDiagram-K3NZZRJ6-B5a8UWjN.js → gitGraphDiagram-K3NZZRJ6-BflpyjGy.js} +1 -1
- package/dist/assets/{graph-Cw1rYoD9.js → graph-suelaXFh.js} +1 -1
- package/dist/assets/highlighted-body-OFNGDK62-CZrBMazC.js +1 -0
- package/dist/assets/index-B01NxbUv.css +1 -0
- package/dist/assets/index-DW5pGgQ_.js +2 -0
- package/dist/assets/{infoDiagram-LFFYTUFH-D2u70rhN.js → infoDiagram-LFFYTUFH-pfD1FA3p.js} +1 -1
- package/dist/assets/ishikawaDiagram-PHBUUO56-ndm9snwO.js +70 -0
- package/dist/assets/journeyDiagram-4ABVD52K-HgF2t7z5.js +139 -0
- package/dist/assets/{kanban-definition-K7BYSVSG-DbVt0v29.js → kanban-definition-K7BYSVSG-FWinmur1.js} +5 -5
- package/dist/assets/{layout-W_tRx4UV.js → layout-vcz43XvZ.js} +1 -1
- package/dist/assets/{linear-CcMb2ay-.js → linear-le4gc0vx.js} +1 -1
- package/dist/assets/mermaid-GHXKKRXX-CK8m3lad.js +870 -0
- package/dist/assets/mindmap-definition-YRQLILUH-CNq9SKj4.js +68 -0
- package/dist/assets/{pieDiagram-SKSYHLDU-CDyJaACv.js → pieDiagram-SKSYHLDU-C7PKDh3b.js} +2 -2
- package/dist/assets/quadrantDiagram-337W2JSQ-B7FnztNO.js +7 -0
- package/dist/assets/requirementDiagram-Z7DCOOCP-Bl_BM2Th.js +73 -0
- package/dist/assets/{sankeyDiagram-WA2Y5GQK-Di1ShaMF.js → sankeyDiagram-WA2Y5GQK-4gulcOP4.js} +3 -3
- package/dist/assets/sequenceDiagram-2WXFIKYE-VEuJDwyJ.js +145 -0
- package/dist/assets/{stateDiagram-RAJIS63D-CVZYMqyW.js → stateDiagram-RAJIS63D-CB4Vl7qM.js} +1 -1
- package/dist/assets/stateDiagram-v2-FVOUBMTO-C85ucl39.js +1 -0
- package/dist/assets/timeline-definition-YZTLITO2-BPGKhi7f.js +61 -0
- package/dist/assets/{treemap-KZPCXAKY-CGG4gx3C.js → treemap-KZPCXAKY-DZSEE6Hz.js} +58 -58
- package/dist/assets/vendor-codemirror-CyOKkaQZ.js +31 -0
- package/dist/assets/vendor-react-CP4yFTs7.js +8 -0
- package/dist/assets/vendor-xterm-DfcmCpbH.js +66 -0
- package/dist/assets/{vennDiagram-LZ73GAT5-Dds37L2k.js → vennDiagram-LZ73GAT5-8E_G06fI.js} +4 -4
- package/dist/assets/xychartDiagram-JWTSCODW-CbBk50-O.js +7 -0
- package/dist/index.html +4 -4
- package/package.json +2 -1
- package/server/_legacy-providers/README.md +30 -0
- package/server/_legacy-providers/claude-sdk.js +956 -0
- package/server/_legacy-providers/gemini-cli.js +368 -0
- package/server/_legacy-providers/openai-codex.js +705 -0
- package/server/_legacy-providers/opencode-cli.js +674 -0
- package/server/acp-runtime/client.js +1805 -0
- package/server/acp-runtime/client.test.js +688 -0
- package/server/acp-runtime/index.js +419 -0
- package/server/acp-runtime/registry.js +45 -0
- package/server/acp-runtime/session-store.js +254 -0
- package/server/acp-runtime/session-store.test.js +89 -0
- package/server/channels/runtime/AgentRuntimeAdapter.js +21 -70
- package/server/claude-sdk.js +24 -944
- package/server/cli.js +4 -2
- package/server/external-agent/service.js +52 -63
- package/server/gemini-cli.js +23 -360
- package/server/index.js +47 -44
- package/server/openai-codex.js +24 -698
- package/server/opencode-cli.js +70 -640
- package/server/routes/agent.js +2 -0
- package/server/routes/git.js +3 -20
- package/server/routes/session-core.js +44 -10
- package/server/session-core/abortSession.js +2 -18
- package/server/session-core/eventStore.js +5 -1
- package/server/session-core/providerAdapters.js +98 -10
- package/server/session-core/runtimeState.js +16 -17
- package/server/session-core/runtimeWriter.js +19 -12
- package/shared/conversationEvents.js +347 -10
- package/shared/conversationEvents.test.js +403 -0
- package/dist/assets/App-CYTE30Cf.js +0 -484
- package/dist/assets/App-qxJ8_QYu.css +0 -32
- package/dist/assets/ReviewApp-BEicSBzW.js +0 -1
- package/dist/assets/architectureDiagram-2XIMDMQ5-BubZ7T3U.js +0 -36
- package/dist/assets/c4Diagram-IC4MRINW-jhjtOQ12.js +0 -10
- package/dist/assets/channel-RmqTALN0.js +0 -1
- package/dist/assets/chunk-NQ4KR5QH-DyujyOvx.js +0 -220
- package/dist/assets/chunk-WL4C6EOR-CQHHFLvx.js +0 -189
- package/dist/assets/classDiagram-VBA2DB6C-wvVV1ggz.js +0 -1
- package/dist/assets/classDiagram-v2-RAHNMMFH-wvVV1ggz.js +0 -1
- package/dist/assets/clone-oT5aWXpf.js +0 -1
- package/dist/assets/cose-bilkent-S5V4N54A-qykDd54p.js +0 -1
- package/dist/assets/cytoscape.esm-2ZfV8NB5.js +0 -331
- package/dist/assets/diagram-E7M64L7V-BKtx468K.js +0 -24
- package/dist/assets/erDiagram-INFDFZHY-DT9YzdNw.js +0 -70
- package/dist/assets/ganttDiagram-A5KZAMGK--IgwcUhI.js +0 -292
- package/dist/assets/highlighted-body-TPN3WLV5-BCxJHuqY.js +0 -1
- package/dist/assets/index-CBuAXA5S.js +0 -2
- package/dist/assets/index-CyLWKyxy.css +0 -1
- package/dist/assets/ishikawaDiagram-PHBUUO56-Cl8yrezU.js +0 -70
- package/dist/assets/journeyDiagram-4ABVD52K-ddP0AMU9.js +0 -139
- package/dist/assets/mermaid-O7DHMXV3-BBJqt8pT.js +0 -988
- package/dist/assets/mindmap-definition-YRQLILUH-BGhZa7Na.js +0 -68
- package/dist/assets/quadrantDiagram-337W2JSQ-BSYuqf0Q.js +0 -7
- package/dist/assets/requirementDiagram-Z7DCOOCP-Cfi9YX9H.js +0 -73
- package/dist/assets/sequenceDiagram-2WXFIKYE-CYTTG38e.js +0 -145
- package/dist/assets/stateDiagram-v2-FVOUBMTO-Bbl0b4-i.js +0 -1
- package/dist/assets/timeline-definition-YZTLITO2-B1sdb5mK.js +0 -61
- package/dist/assets/vendor-codemirror-Dz7_EqNA.js +0 -39
- package/dist/assets/vendor-react-Cpt6D04s.js +0 -59
- package/dist/assets/vendor-xterm-DfaPXD3y.js +0 -66
- 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 ||
|
|
810
|
+
${packageJson.homepage || defaultProjectUrl}
|
|
809
811
|
|
|
810
812
|
Report Issues:
|
|
811
|
-
${packageJson.bugs?.url ||
|
|
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 {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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.
|
|
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 === '
|
|
307
|
-
this.
|
|
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
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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.
|
|
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
|
-
|
|
422
|
-
|
|
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
|
-
},
|
|
430
|
-
|
|
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(),
|
package/server/gemini-cli.js
CHANGED
|
@@ -1,368 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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
|
|
25
|
+
export function isGeminiSessionActive(sessionId) {
|
|
26
|
+
return isAgentSessionActive('gemini', sessionId);
|
|
357
27
|
}
|
|
358
28
|
|
|
359
|
-
function getActiveGeminiSessions() {
|
|
360
|
-
return
|
|
29
|
+
export function getActiveGeminiSessions() {
|
|
30
|
+
return getActiveAgentSessions('gemini');
|
|
361
31
|
}
|
|
362
|
-
|
|
363
|
-
export {
|
|
364
|
-
queryGemini,
|
|
365
|
-
abortGeminiSession,
|
|
366
|
-
isGeminiSessionActive,
|
|
367
|
-
getActiveGeminiSessions
|
|
368
|
-
};
|