happy-imou-cloud 2.1.1 → 2.1.2
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/bin/happy-cloud.mjs +21 -21
- package/compat/acp-sdk-schema/index.js +27 -27
- package/compat/acp-sdk-schema/types.gen.js +2 -2
- package/compat/acp-sdk-schema/zod.gen.js +1553 -1553
- package/compat/ink-build/components/Cursor.d.ts +83 -83
- package/compat/ink-build/components/Cursor.js +52 -52
- package/compat/ink-build/components/CursorContext.d.ts +11 -11
- package/compat/ink-build/components/CursorContext.js +7 -7
- package/compat/ink-build/components/ErrorBoundary.d.ts +18 -18
- package/compat/ink-build/components/ErrorBoundary.js +22 -22
- package/compat/ink-build/hooks/use-cursor.d.ts +12 -12
- package/compat/ink-build/hooks/use-cursor.js +28 -28
- package/dist/{BaseReasoningProcessor-Dn9FxfxU.mjs → BaseReasoningProcessor-BaOWkVcu.mjs} +3 -3
- package/dist/{BaseReasoningProcessor-CBMK-8Gi.cjs → BaseReasoningProcessor-CzvqwxuY.cjs} +3 -3
- package/dist/ProviderSelectionHandler-Q8pl7e-d.mjs +261 -0
- package/dist/ProviderSelectionHandler-wwbfeK_s.cjs +265 -0
- package/dist/{api-DBy5lPZw.mjs → api-Cxifhw5r.mjs} +3 -3
- package/dist/{api-DId_j3C2.cjs → api-DZimmN4C.cjs} +2 -2
- package/dist/{command-CeaBwYCW.mjs → command-B6LM3Nml.mjs} +3 -3
- package/dist/{command-DwfUpmId.cjs → command-RcCJI1jl.cjs} +3 -3
- package/dist/{index-CuuYSKiv.cjs → index-Cuvs0lFS.cjs} +168 -75
- package/dist/{index-66vjECEd.mjs → index-Des7I5WX.mjs} +165 -72
- package/dist/index.cjs +3 -3
- package/dist/index.mjs +3 -3
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.cts +36 -36
- package/dist/lib.d.mts +36 -36
- package/dist/lib.mjs +1 -1
- package/dist/{persistence-BOWh1NER.mjs → persistence-6d4U4Sh8.mjs} +1 -1
- package/dist/{persistence-Dzr6sFwD.cjs → persistence-C8-MtdQK.cjs} +1 -1
- package/dist/{registerKillSessionHandler-D4_wpN18.mjs → registerKillSessionHandler-BFBkz_XT.mjs} +417 -5
- package/dist/{registerKillSessionHandler-Dg_iRBPm.cjs → registerKillSessionHandler-BapPCRmp.cjs} +419 -4
- package/dist/{runClaude-B74dHAnQ.mjs → runClaude-CPV5Uap2.mjs} +34 -5
- package/dist/{runClaude-oIFzkfuU.cjs → runClaude-DVnqKa1q.cjs} +37 -8
- package/dist/{runCodex-D_9CuL6M.cjs → runCodex-Bzsp8gFO.cjs} +29 -21
- package/dist/{runCodex-mLHjsgVj.mjs → runCodex-CwtLSTMJ.mjs} +26 -18
- package/dist/{runGemini-CcWGezMt.cjs → runGemini-6Dwyk_Km.cjs} +267 -82
- package/dist/{runGemini-BMiho2ab.mjs → runGemini-Bmoxehlh.mjs} +267 -82
- package/package.json +9 -9
- package/scripts/build.mjs +68 -68
- package/scripts/ensureAcpSdkCompat.mjs +170 -172
- package/scripts/release-smoke.mjs +38 -35
- package/dist/ProviderSelectionHandler-BuXk-8ji.cjs +0 -680
- package/dist/ProviderSelectionHandler-CMaQThYO.mjs +0 -673
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
var ink = require('ink');
|
|
4
4
|
var React = require('react');
|
|
5
5
|
var node_crypto = require('node:crypto');
|
|
6
|
-
var api = require('./api-
|
|
7
|
-
var registerKillSessionHandler = require('./registerKillSessionHandler-
|
|
8
|
-
var index = require('./index-
|
|
9
|
-
var BaseReasoningProcessor = require('./BaseReasoningProcessor-
|
|
6
|
+
var api = require('./api-DZimmN4C.cjs');
|
|
7
|
+
var registerKillSessionHandler = require('./registerKillSessionHandler-BapPCRmp.cjs');
|
|
8
|
+
var index = require('./index-Cuvs0lFS.cjs');
|
|
9
|
+
var BaseReasoningProcessor = require('./BaseReasoningProcessor-CzvqwxuY.cjs');
|
|
10
10
|
require('cross-spawn');
|
|
11
11
|
require('@agentclientprotocol/sdk');
|
|
12
12
|
require('ps-list');
|
|
@@ -17,7 +17,7 @@ require('node:child_process');
|
|
|
17
17
|
require('node:readline');
|
|
18
18
|
require('tweetnacl');
|
|
19
19
|
require('axios');
|
|
20
|
-
require('./persistence-
|
|
20
|
+
require('./persistence-C8-MtdQK.cjs');
|
|
21
21
|
require('open');
|
|
22
22
|
require('chalk');
|
|
23
23
|
require('fs');
|
|
@@ -432,6 +432,116 @@ class ConversationHistory extends registerKillSessionHandler.ConversationHistory
|
|
|
432
432
|
}
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
+
const GEMINI_PERMISSION_MODES = [
|
|
436
|
+
"default",
|
|
437
|
+
"read-only",
|
|
438
|
+
"safe-yolo",
|
|
439
|
+
"yolo"
|
|
440
|
+
];
|
|
441
|
+
function isGeminiPermissionMode(value) {
|
|
442
|
+
return typeof value === "string" && GEMINI_PERMISSION_MODES.includes(value);
|
|
443
|
+
}
|
|
444
|
+
function hasMetaOverride(message, key) {
|
|
445
|
+
return Object.prototype.hasOwnProperty.call(message.meta ?? {}, key);
|
|
446
|
+
}
|
|
447
|
+
function resolveGeminiQueuedMessage(message, currentState) {
|
|
448
|
+
const previousState = { ...currentState };
|
|
449
|
+
const currentPermissionMode = isGeminiPermissionMode(currentState.permissionMode) ? currentState.permissionMode : void 0;
|
|
450
|
+
let permissionMode = currentPermissionMode;
|
|
451
|
+
let invalidPermissionMode = null;
|
|
452
|
+
if (hasMetaOverride(message, "permissionMode")) {
|
|
453
|
+
if (isGeminiPermissionMode(message.meta?.permissionMode)) {
|
|
454
|
+
permissionMode = message.meta.permissionMode;
|
|
455
|
+
} else if (message.meta?.permissionMode) {
|
|
456
|
+
invalidPermissionMode = String(message.meta.permissionMode);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
const hasModelOverride = hasMetaOverride(message, "model");
|
|
460
|
+
const model = hasModelOverride ? message.meta?.model || void 0 : currentState.model;
|
|
461
|
+
const originalUserMessage = message.content.text;
|
|
462
|
+
let queuedText = originalUserMessage;
|
|
463
|
+
const isFirstMessage = currentState.isFirstMessage ?? true;
|
|
464
|
+
let nextIsFirstMessage = isFirstMessage;
|
|
465
|
+
if (isFirstMessage && message.meta?.appendSystemPrompt) {
|
|
466
|
+
queuedText = `${message.meta.appendSystemPrompt}
|
|
467
|
+
|
|
468
|
+
${originalUserMessage}`;
|
|
469
|
+
nextIsFirstMessage = false;
|
|
470
|
+
}
|
|
471
|
+
const nextState = {
|
|
472
|
+
permissionMode: permissionMode ?? "default",
|
|
473
|
+
model,
|
|
474
|
+
isFirstMessage: nextIsFirstMessage
|
|
475
|
+
};
|
|
476
|
+
return {
|
|
477
|
+
previousState,
|
|
478
|
+
nextState,
|
|
479
|
+
invalidPermissionMode,
|
|
480
|
+
hasModelOverride,
|
|
481
|
+
queuedMessage: {
|
|
482
|
+
text: queuedText,
|
|
483
|
+
mode: {
|
|
484
|
+
permissionMode: nextState.permissionMode ?? "default",
|
|
485
|
+
model,
|
|
486
|
+
originalUserMessage
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
function bindGeminiUserMessageQueue(opts) {
|
|
492
|
+
let currentState = { ...opts.initialState };
|
|
493
|
+
opts.session.onUserMessage((message) => {
|
|
494
|
+
const happyOrgResult = opts.happyOrg ? registerKillSessionHandler.resolveHappyOrgQueuedTurn({
|
|
495
|
+
metadata: opts.happyOrg.getMetadata(),
|
|
496
|
+
message
|
|
497
|
+
}) : {
|
|
498
|
+
nextMetadata: null,
|
|
499
|
+
queuedTurn: null,
|
|
500
|
+
blocked: false,
|
|
501
|
+
statusMessage: void 0
|
|
502
|
+
};
|
|
503
|
+
if (opts.happyOrg && happyOrgResult.nextMetadata) {
|
|
504
|
+
opts.happyOrg.updateMetadata(() => happyOrgResult.nextMetadata);
|
|
505
|
+
}
|
|
506
|
+
if (happyOrgResult.blocked) {
|
|
507
|
+
if (happyOrgResult.statusMessage) {
|
|
508
|
+
opts.happyOrg?.emitStatusMessage(happyOrgResult.statusMessage);
|
|
509
|
+
}
|
|
510
|
+
api.logger.debugLargeJson("[gemini] User message blocked by Happy Org runtime:", message);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
const resolution = resolveGeminiQueuedMessage(message, currentState);
|
|
514
|
+
currentState = resolution.nextState;
|
|
515
|
+
resolution.queuedMessage.mode.happyOrg = happyOrgResult.queuedTurn;
|
|
516
|
+
opts.onResolvedMessage?.(resolution);
|
|
517
|
+
opts.messageQueue.push(resolution.queuedMessage.text, resolution.queuedMessage.mode);
|
|
518
|
+
api.logger.debugLargeJson("[gemini] User message queued:", message);
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function normalizeGeminiBackendError(error) {
|
|
523
|
+
if (typeof error === "string") {
|
|
524
|
+
return error.trim() || "Gemini runtime exited unexpectedly";
|
|
525
|
+
}
|
|
526
|
+
if (error instanceof Error) {
|
|
527
|
+
return error.message.trim() || "Gemini runtime exited unexpectedly";
|
|
528
|
+
}
|
|
529
|
+
if (typeof error === "object" && error !== null) {
|
|
530
|
+
const record = error;
|
|
531
|
+
const fields = [record.message, record.details, record.detail, record.stderr].map((value) => typeof value === "string" ? value.trim() : "").filter(Boolean);
|
|
532
|
+
if (fields.length > 0) {
|
|
533
|
+
return fields.join("\n");
|
|
534
|
+
}
|
|
535
|
+
try {
|
|
536
|
+
const serialized = JSON.stringify(record);
|
|
537
|
+
if (serialized && serialized !== "{}") {
|
|
538
|
+
return serialized;
|
|
539
|
+
}
|
|
540
|
+
} catch {
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return "Gemini runtime exited unexpectedly";
|
|
544
|
+
}
|
|
435
545
|
async function runGemini(opts) {
|
|
436
546
|
const sessionTag = node_crypto.randomUUID();
|
|
437
547
|
api.connectionState.setBackend("Gemini");
|
|
@@ -513,67 +623,21 @@ async function runGemini(opts) {
|
|
|
513
623
|
await registerKillSessionHandler.syncControlledByUserState(session, false);
|
|
514
624
|
const messageQueue = new registerKillSessionHandler.MessageQueue2((mode) => registerKillSessionHandler.hashObject({
|
|
515
625
|
permissionMode: mode.permissionMode,
|
|
516
|
-
model: mode.model
|
|
626
|
+
model: mode.model,
|
|
627
|
+
happyOrg: mode.happyOrg ? {
|
|
628
|
+
taskId: mode.happyOrg.context.taskId,
|
|
629
|
+
organizationId: mode.happyOrg.context.organizationId,
|
|
630
|
+
memberAgentId: mode.happyOrg.context.memberAgentId,
|
|
631
|
+
supervisorAgentId: mode.happyOrg.context.supervisorAgentId,
|
|
632
|
+
reopenContext: mode.happyOrg.reopenContext ?? null
|
|
633
|
+
} : null
|
|
517
634
|
}));
|
|
518
635
|
const conversationHistory = new ConversationHistory({ maxMessages: 20, maxCharacters: 5e4 });
|
|
519
|
-
let currentPermissionMode = void 0;
|
|
520
|
-
let currentModel = void 0;
|
|
521
|
-
session.onUserMessage((message) => {
|
|
522
|
-
let messagePermissionMode = currentPermissionMode;
|
|
523
|
-
if (message.meta?.permissionMode) {
|
|
524
|
-
const validModes = ["default", "read-only", "safe-yolo", "yolo"];
|
|
525
|
-
if (validModes.includes(message.meta.permissionMode)) {
|
|
526
|
-
messagePermissionMode = message.meta.permissionMode;
|
|
527
|
-
currentPermissionMode = messagePermissionMode;
|
|
528
|
-
updatePermissionMode(messagePermissionMode);
|
|
529
|
-
api.logger.debug(`[Gemini] Permission mode updated from user message to: ${currentPermissionMode}`);
|
|
530
|
-
} else {
|
|
531
|
-
api.logger.debug(`[Gemini] Invalid permission mode received: ${message.meta.permissionMode}`);
|
|
532
|
-
}
|
|
533
|
-
} else {
|
|
534
|
-
api.logger.debug(`[Gemini] User message received with no permission mode override, using current: ${currentPermissionMode ?? "default (effective)"}`);
|
|
535
|
-
}
|
|
536
|
-
if (currentPermissionMode === void 0) {
|
|
537
|
-
currentPermissionMode = "default";
|
|
538
|
-
updatePermissionMode("default");
|
|
539
|
-
}
|
|
540
|
-
let messageModel = currentModel;
|
|
541
|
-
if (message.meta?.hasOwnProperty("model")) {
|
|
542
|
-
if (message.meta.model === null) {
|
|
543
|
-
messageModel = void 0;
|
|
544
|
-
currentModel = void 0;
|
|
545
|
-
} else if (message.meta.model) {
|
|
546
|
-
const previousModel = currentModel;
|
|
547
|
-
messageModel = message.meta.model;
|
|
548
|
-
currentModel = messageModel;
|
|
549
|
-
if (previousModel !== messageModel) {
|
|
550
|
-
updateDisplayedModel(messageModel, true);
|
|
551
|
-
messageBuffer.addMessage(`Model changed to: ${messageModel}`, "system");
|
|
552
|
-
api.logger.debug(`[Gemini] Model changed from ${previousModel} to ${messageModel}`);
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
const originalUserMessage = message.content.text;
|
|
557
|
-
let fullPrompt = originalUserMessage;
|
|
558
|
-
if (isFirstMessage && message.meta?.appendSystemPrompt) {
|
|
559
|
-
fullPrompt = message.meta.appendSystemPrompt + "\n\n" + originalUserMessage;
|
|
560
|
-
isFirstMessage = false;
|
|
561
|
-
}
|
|
562
|
-
const mode = {
|
|
563
|
-
permissionMode: messagePermissionMode || "default",
|
|
564
|
-
model: messageModel,
|
|
565
|
-
originalUserMessage
|
|
566
|
-
// Store original message separately
|
|
567
|
-
};
|
|
568
|
-
messageQueue.push(fullPrompt, mode);
|
|
569
|
-
conversationHistory.addUserMessage(originalUserMessage);
|
|
570
|
-
});
|
|
571
636
|
let thinking = false;
|
|
572
637
|
session.keepAlive(thinking, "remote");
|
|
573
638
|
const keepAliveInterval = setInterval(() => {
|
|
574
639
|
session.keepAlive(thinking, "remote");
|
|
575
640
|
}, 2e3);
|
|
576
|
-
let isFirstMessage = true;
|
|
577
641
|
const sendReady = () => {
|
|
578
642
|
session.sendSessionEvent({ type: "ready" });
|
|
579
643
|
try {
|
|
@@ -606,12 +670,22 @@ async function runGemini(opts) {
|
|
|
606
670
|
let shouldExit = false;
|
|
607
671
|
let runtimeHandle = null;
|
|
608
672
|
let unsubscribeRuntimeMessages = null;
|
|
673
|
+
let handleQueuedUserMessage = null;
|
|
674
|
+
let emitGeminiStatusMessage = null;
|
|
675
|
+
let turnAbortedSent = false;
|
|
676
|
+
let turnInFlight = false;
|
|
677
|
+
let shouldInjectHistoryOnNextSession = false;
|
|
678
|
+
let currentModeHash = null;
|
|
679
|
+
let unexpectedRuntimeStopRecovery = null;
|
|
609
680
|
async function handleAbort() {
|
|
610
681
|
api.logger.debug("[Gemini] Abort requested - stopping current task");
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
682
|
+
if (!turnAbortedSent) {
|
|
683
|
+
session.sendAgentMessage("gemini", {
|
|
684
|
+
type: "turn_aborted",
|
|
685
|
+
id: node_crypto.randomUUID()
|
|
686
|
+
});
|
|
687
|
+
turnAbortedSent = true;
|
|
688
|
+
}
|
|
615
689
|
reasoningProcessor.abort();
|
|
616
690
|
diffProcessor.reset();
|
|
617
691
|
try {
|
|
@@ -672,6 +746,10 @@ async function runGemini(opts) {
|
|
|
672
746
|
api.logger.debug(`[gemini] Model unchanged, skipping update message`);
|
|
673
747
|
}
|
|
674
748
|
};
|
|
749
|
+
emitGeminiStatusMessage = (message) => {
|
|
750
|
+
messageBuffer.addMessage(message, "status");
|
|
751
|
+
session.sendSessionEvent({ type: "message", message });
|
|
752
|
+
};
|
|
675
753
|
if (hasTTY) {
|
|
676
754
|
console.clear();
|
|
677
755
|
const DisplayComponent = () => {
|
|
@@ -712,10 +790,56 @@ async function runGemini(opts) {
|
|
|
712
790
|
const updatePermissionMode = (mode) => {
|
|
713
791
|
permissionHandler.setPermissionMode(mode);
|
|
714
792
|
};
|
|
793
|
+
handleQueuedUserMessage = ({ previousState, nextState, invalidPermissionMode, hasModelOverride }) => {
|
|
794
|
+
if (invalidPermissionMode) {
|
|
795
|
+
api.logger.debug(`[Gemini] Invalid permission mode received: ${invalidPermissionMode}`);
|
|
796
|
+
} else if (nextState.permissionMode) {
|
|
797
|
+
api.logger.debug(`[Gemini] Permission mode resolved to: ${nextState.permissionMode}`);
|
|
798
|
+
}
|
|
799
|
+
updatePermissionMode(nextState.permissionMode ?? "default");
|
|
800
|
+
if (hasModelOverride && nextState.model && previousState.model !== nextState.model) {
|
|
801
|
+
updateDisplayedModel(nextState.model, true);
|
|
802
|
+
messageBuffer.addMessage(`Model changed to: ${nextState.model}`, "system");
|
|
803
|
+
api.logger.debug(`[Gemini] Model changed from ${previousState.model} to ${nextState.model}`);
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
bindGeminiUserMessageQueue({
|
|
807
|
+
session,
|
|
808
|
+
messageQueue,
|
|
809
|
+
initialState: {},
|
|
810
|
+
happyOrg: {
|
|
811
|
+
getMetadata: () => session.getMetadataSnapshot?.() ?? metadata,
|
|
812
|
+
updateMetadata: (handler) => session.updateMetadata(handler),
|
|
813
|
+
emitStatusMessage: (message) => {
|
|
814
|
+
emitGeminiStatusMessage?.(message);
|
|
815
|
+
}
|
|
816
|
+
},
|
|
817
|
+
onResolvedMessage: (resolution) => {
|
|
818
|
+
handleQueuedUserMessage?.(resolution);
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
const emitTurnReport = (report) => {
|
|
822
|
+
session.sendAgentMessage("gemini", {
|
|
823
|
+
type: "turn-report",
|
|
824
|
+
id: node_crypto.randomUUID(),
|
|
825
|
+
report
|
|
826
|
+
});
|
|
827
|
+
};
|
|
715
828
|
let accumulatedResponse = "";
|
|
716
829
|
let isResponseInProgress = false;
|
|
717
830
|
let hadToolCallInTurn = false;
|
|
718
831
|
let taskStartedSent = false;
|
|
832
|
+
const resetTurnState = () => {
|
|
833
|
+
reasoningProcessor.abort();
|
|
834
|
+
diffProcessor.reset();
|
|
835
|
+
accumulatedResponse = "";
|
|
836
|
+
isResponseInProgress = false;
|
|
837
|
+
hadToolCallInTurn = false;
|
|
838
|
+
taskStartedSent = false;
|
|
839
|
+
turnAbortedSent = false;
|
|
840
|
+
thinking = false;
|
|
841
|
+
session.keepAlive(thinking, "remote");
|
|
842
|
+
};
|
|
719
843
|
const disposeRuntimeHandle = async () => {
|
|
720
844
|
if (!runtimeHandle) {
|
|
721
845
|
return;
|
|
@@ -730,6 +854,37 @@ async function runGemini(opts) {
|
|
|
730
854
|
api.logger.debug("[Gemini] Error disposing runtime session:", error);
|
|
731
855
|
}
|
|
732
856
|
};
|
|
857
|
+
const queueHistoryInjectionForRestart = (reason) => {
|
|
858
|
+
messageBuffer.addMessage("\u2550".repeat(40), "status");
|
|
859
|
+
if (conversationHistory.hasHistory()) {
|
|
860
|
+
shouldInjectHistoryOnNextSession = true;
|
|
861
|
+
const message = `${reason} Preserving ${conversationHistory.size()} earlier messages of context.`;
|
|
862
|
+
emitGeminiStatusMessage?.(message);
|
|
863
|
+
api.logger.debug(`[Gemini] Will inject conversation history after restart: ${conversationHistory.getSummary()}`);
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
emitGeminiStatusMessage?.(reason);
|
|
867
|
+
};
|
|
868
|
+
const recoverUnexpectedRuntimeStop = async (detail) => {
|
|
869
|
+
if (shouldExit || messageQueue.isClosed() || unexpectedRuntimeStopRecovery) {
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
const errorMessage = normalizeGeminiBackendError(detail);
|
|
873
|
+
unexpectedRuntimeStopRecovery = (async () => {
|
|
874
|
+
queueHistoryInjectionForRestart(
|
|
875
|
+
`Gemini runtime stopped unexpectedly (${errorMessage}). Starting a new Gemini session on the next message...`
|
|
876
|
+
);
|
|
877
|
+
await disposeRuntimeHandle();
|
|
878
|
+
currentModeHash = null;
|
|
879
|
+
permissionHandler.reset();
|
|
880
|
+
resetTurnState();
|
|
881
|
+
})();
|
|
882
|
+
try {
|
|
883
|
+
await unexpectedRuntimeStopRecovery;
|
|
884
|
+
} finally {
|
|
885
|
+
unexpectedRuntimeStopRecovery = null;
|
|
886
|
+
}
|
|
887
|
+
};
|
|
733
888
|
function setupGeminiMessageHandler(activeRuntimeHandle) {
|
|
734
889
|
const forwardAgentMessage = (agentMessage) => {
|
|
735
890
|
registerKillSessionHandler.forwardAgentMessageToProviderSession(agentMessage, {
|
|
@@ -759,10 +914,13 @@ async function runGemini(opts) {
|
|
|
759
914
|
api.logger.debug(`[gemini] Status changed: ${msg.status}${statusDetail ? ` - ${statusDetail}` : ""}`);
|
|
760
915
|
if (msg.status === "error") {
|
|
761
916
|
api.logger.debug(`[gemini] \u26A0\uFE0F Error status received: ${statusDetail || "Unknown error"}`);
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
917
|
+
if (!turnAbortedSent) {
|
|
918
|
+
session.sendAgentMessage("gemini", {
|
|
919
|
+
type: "turn_aborted",
|
|
920
|
+
id: node_crypto.randomUUID()
|
|
921
|
+
});
|
|
922
|
+
turnAbortedSent = true;
|
|
923
|
+
}
|
|
766
924
|
}
|
|
767
925
|
if (msg.status === "running") {
|
|
768
926
|
thinking = true;
|
|
@@ -777,6 +935,9 @@ async function runGemini(opts) {
|
|
|
777
935
|
messageBuffer.addMessage("Thinking...", "system");
|
|
778
936
|
} else if (msg.status === "idle" || msg.status === "stopped") {
|
|
779
937
|
reasoningProcessor.complete();
|
|
938
|
+
if (msg.status === "stopped" && !turnInFlight) {
|
|
939
|
+
void recoverUnexpectedRuntimeStop(msg.detail);
|
|
940
|
+
}
|
|
780
941
|
} else if (msg.status === "error") {
|
|
781
942
|
thinking = false;
|
|
782
943
|
session.keepAlive(thinking, "remote");
|
|
@@ -936,7 +1097,6 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
936
1097
|
};
|
|
937
1098
|
}
|
|
938
1099
|
try {
|
|
939
|
-
let currentModeHash = null;
|
|
940
1100
|
let pending = null;
|
|
941
1101
|
while (!shouldExit) {
|
|
942
1102
|
let message = pending;
|
|
@@ -959,19 +1119,11 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
959
1119
|
if (!message) {
|
|
960
1120
|
break;
|
|
961
1121
|
}
|
|
962
|
-
let injectHistoryContext = false;
|
|
963
1122
|
if (runtimeHandle && currentModeHash && message.hash !== currentModeHash) {
|
|
964
1123
|
api.logger.debug("[Gemini] Mode changed \u2013 restarting Gemini session");
|
|
965
|
-
|
|
966
|
-
if (conversationHistory.hasHistory()) {
|
|
967
|
-
messageBuffer.addMessage(`Switching model (preserving ${conversationHistory.size()} messages of context)...`, "status");
|
|
968
|
-
injectHistoryContext = true;
|
|
969
|
-
api.logger.debug(`[Gemini] Will inject conversation history: ${conversationHistory.getSummary()}`);
|
|
970
|
-
} else {
|
|
971
|
-
messageBuffer.addMessage("Starting new Gemini session (mode changed)...", "status");
|
|
972
|
-
}
|
|
1124
|
+
queueHistoryInjectionForRestart("Starting new Gemini session (execution settings changed)...");
|
|
973
1125
|
permissionHandler.reset();
|
|
974
|
-
|
|
1126
|
+
resetTurnState();
|
|
975
1127
|
await disposeRuntimeHandle();
|
|
976
1128
|
const launchedRuntime = await launchGeminiRuntimeHandle(message.mode);
|
|
977
1129
|
const actualModel = launchedRuntime.model;
|
|
@@ -987,7 +1139,9 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
987
1139
|
const userMessageToShow = message.mode?.originalUserMessage || message.message;
|
|
988
1140
|
messageBuffer.addMessage(userMessageToShow, "user");
|
|
989
1141
|
isProcessingMessage = true;
|
|
1142
|
+
let turnStatus = "task_complete";
|
|
990
1143
|
try {
|
|
1144
|
+
turnInFlight = true;
|
|
991
1145
|
let activeHandle = runtimeHandle;
|
|
992
1146
|
if (!activeHandle) {
|
|
993
1147
|
const launchedRuntime = await launchGeminiRuntimeHandle(message.mode);
|
|
@@ -1007,12 +1161,17 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
1007
1161
|
isResponseInProgress = false;
|
|
1008
1162
|
hadToolCallInTurn = false;
|
|
1009
1163
|
taskStartedSent = false;
|
|
1164
|
+
turnAbortedSent = false;
|
|
1010
1165
|
let promptToSend = message.message;
|
|
1011
|
-
if (
|
|
1166
|
+
if (shouldInjectHistoryOnNextSession && conversationHistory.hasHistory()) {
|
|
1012
1167
|
const historyContext = conversationHistory.getContextForNewSession();
|
|
1013
1168
|
promptToSend = historyContext + promptToSend;
|
|
1014
1169
|
api.logger.debug(`[gemini] Injected conversation history context (${historyContext.length} chars)`);
|
|
1015
1170
|
}
|
|
1171
|
+
if (message.mode.happyOrg) {
|
|
1172
|
+
promptToSend = registerKillSessionHandler.buildHappyOrgTurnPrompt(promptToSend, message.mode.happyOrg);
|
|
1173
|
+
}
|
|
1174
|
+
conversationHistory.addUserMessage(userMessageToShow);
|
|
1016
1175
|
api.logger.debug(`[gemini] Sending prompt to Gemini (length: ${promptToSend.length}): ${promptToSend.substring(0, 100)}...`);
|
|
1017
1176
|
api.logger.debug(`[gemini] Full prompt: ${promptToSend}`);
|
|
1018
1177
|
const MAX_RETRIES = 3;
|
|
@@ -1022,8 +1181,9 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
1022
1181
|
try {
|
|
1023
1182
|
await activeHandle.sendPrompt(promptToSend);
|
|
1024
1183
|
api.logger.debug("[gemini] Prompt sent successfully");
|
|
1025
|
-
await registerKillSessionHandler.waitForResponseCompleteWithAbort(activeHandle.backend, abortController.signal,
|
|
1184
|
+
await registerKillSessionHandler.waitForResponseCompleteWithAbort(activeHandle.backend, abortController.signal, 10 * 6e4);
|
|
1026
1185
|
api.logger.debug("[gemini] Response complete");
|
|
1186
|
+
shouldInjectHistoryOnNextSession = false;
|
|
1027
1187
|
break;
|
|
1028
1188
|
} catch (promptError) {
|
|
1029
1189
|
lastError = promptError;
|
|
@@ -1061,6 +1221,14 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
1061
1221
|
} catch (error) {
|
|
1062
1222
|
api.logger.debug("[gemini] Error in gemini session:", error);
|
|
1063
1223
|
const isAbortError = error instanceof Error && error.name === "AbortError";
|
|
1224
|
+
turnStatus = "turn_aborted";
|
|
1225
|
+
if (!turnAbortedSent) {
|
|
1226
|
+
session.sendAgentMessage("gemini", {
|
|
1227
|
+
type: "turn_aborted",
|
|
1228
|
+
id: node_crypto.randomUUID()
|
|
1229
|
+
});
|
|
1230
|
+
turnAbortedSent = true;
|
|
1231
|
+
}
|
|
1064
1232
|
if (isAbortError) {
|
|
1065
1233
|
messageBuffer.addMessage("Aborted by user", "status");
|
|
1066
1234
|
session.sendSessionEvent({ type: "message", message: "Aborted by user" });
|
|
@@ -1073,8 +1241,8 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
1073
1241
|
const errorMessage = errObj.message || errObj.error?.message || "";
|
|
1074
1242
|
const errorString = String(error);
|
|
1075
1243
|
if (errorCode === 404 || errorDetails.includes("notFound") || errorDetails.includes("404") || errorMessage.includes("not found") || errorMessage.includes("404")) {
|
|
1076
|
-
const
|
|
1077
|
-
errorMsg = `Model "${
|
|
1244
|
+
const currentModel = displayedModel || "gemini-2.5-pro";
|
|
1245
|
+
errorMsg = `Model "${currentModel}" not found. Available models: gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite`;
|
|
1078
1246
|
} else if (errorCode === -32603 || errorDetails.includes("empty response") || errorDetails.includes("Model stream ended")) {
|
|
1079
1247
|
errorMsg = "Gemini API returned empty response after retries. This is a temporary issue - please try again.";
|
|
1080
1248
|
} else if (errorCode === 429 || errorDetails.includes("429") || errorMessage.includes("429") || errorString.includes("429") || errorDetails.includes("rateLimitExceeded") || errorDetails.includes("RESOURCE_EXHAUSTED") || errorMessage.includes("Rate limit exceeded") || errorMessage.includes("Resource exhausted") || errorString.includes("rateLimitExceeded") || errorString.includes("RESOURCE_EXHAUSTED")) {
|
|
@@ -1107,9 +1275,26 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
|
|
|
1107
1275
|
});
|
|
1108
1276
|
}
|
|
1109
1277
|
} finally {
|
|
1278
|
+
turnInFlight = false;
|
|
1110
1279
|
permissionHandler.reset();
|
|
1111
1280
|
reasoningProcessor.abort();
|
|
1112
1281
|
diffProcessor.reset();
|
|
1282
|
+
const finalizedTurn = registerKillSessionHandler.finalizeHappyOrgTurn({
|
|
1283
|
+
metadata: session.getMetadataSnapshot?.() ?? null,
|
|
1284
|
+
queuedTurn: message.mode.happyOrg,
|
|
1285
|
+
responseText: accumulatedResponse,
|
|
1286
|
+
turnStatus
|
|
1287
|
+
});
|
|
1288
|
+
if (finalizedTurn.nextMetadata && typeof session.updateMetadata === "function") {
|
|
1289
|
+
session.updateMetadata(() => finalizedTurn.nextMetadata);
|
|
1290
|
+
}
|
|
1291
|
+
if (finalizedTurn.report) {
|
|
1292
|
+
emitTurnReport(finalizedTurn.report);
|
|
1293
|
+
}
|
|
1294
|
+
if (finalizedTurn.terminateMessage) {
|
|
1295
|
+
emitGeminiStatusMessage?.(finalizedTurn.terminateMessage);
|
|
1296
|
+
}
|
|
1297
|
+
accumulatedResponse = finalizedTurn.cleanedText;
|
|
1113
1298
|
if (accumulatedResponse.trim()) {
|
|
1114
1299
|
const { text: messageText, options } = parseOptionsFromText(accumulatedResponse);
|
|
1115
1300
|
conversationHistory.addAssistantMessage(messageText);
|