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.
Files changed (44) hide show
  1. package/bin/happy-cloud.mjs +21 -21
  2. package/compat/acp-sdk-schema/index.js +27 -27
  3. package/compat/acp-sdk-schema/types.gen.js +2 -2
  4. package/compat/acp-sdk-schema/zod.gen.js +1553 -1553
  5. package/compat/ink-build/components/Cursor.d.ts +83 -83
  6. package/compat/ink-build/components/Cursor.js +52 -52
  7. package/compat/ink-build/components/CursorContext.d.ts +11 -11
  8. package/compat/ink-build/components/CursorContext.js +7 -7
  9. package/compat/ink-build/components/ErrorBoundary.d.ts +18 -18
  10. package/compat/ink-build/components/ErrorBoundary.js +22 -22
  11. package/compat/ink-build/hooks/use-cursor.d.ts +12 -12
  12. package/compat/ink-build/hooks/use-cursor.js +28 -28
  13. package/dist/{BaseReasoningProcessor-Dn9FxfxU.mjs → BaseReasoningProcessor-BaOWkVcu.mjs} +3 -3
  14. package/dist/{BaseReasoningProcessor-CBMK-8Gi.cjs → BaseReasoningProcessor-CzvqwxuY.cjs} +3 -3
  15. package/dist/ProviderSelectionHandler-Q8pl7e-d.mjs +261 -0
  16. package/dist/ProviderSelectionHandler-wwbfeK_s.cjs +265 -0
  17. package/dist/{api-DBy5lPZw.mjs → api-Cxifhw5r.mjs} +3 -3
  18. package/dist/{api-DId_j3C2.cjs → api-DZimmN4C.cjs} +2 -2
  19. package/dist/{command-CeaBwYCW.mjs → command-B6LM3Nml.mjs} +3 -3
  20. package/dist/{command-DwfUpmId.cjs → command-RcCJI1jl.cjs} +3 -3
  21. package/dist/{index-CuuYSKiv.cjs → index-Cuvs0lFS.cjs} +168 -75
  22. package/dist/{index-66vjECEd.mjs → index-Des7I5WX.mjs} +165 -72
  23. package/dist/index.cjs +3 -3
  24. package/dist/index.mjs +3 -3
  25. package/dist/lib.cjs +1 -1
  26. package/dist/lib.d.cts +36 -36
  27. package/dist/lib.d.mts +36 -36
  28. package/dist/lib.mjs +1 -1
  29. package/dist/{persistence-BOWh1NER.mjs → persistence-6d4U4Sh8.mjs} +1 -1
  30. package/dist/{persistence-Dzr6sFwD.cjs → persistence-C8-MtdQK.cjs} +1 -1
  31. package/dist/{registerKillSessionHandler-D4_wpN18.mjs → registerKillSessionHandler-BFBkz_XT.mjs} +417 -5
  32. package/dist/{registerKillSessionHandler-Dg_iRBPm.cjs → registerKillSessionHandler-BapPCRmp.cjs} +419 -4
  33. package/dist/{runClaude-B74dHAnQ.mjs → runClaude-CPV5Uap2.mjs} +34 -5
  34. package/dist/{runClaude-oIFzkfuU.cjs → runClaude-DVnqKa1q.cjs} +37 -8
  35. package/dist/{runCodex-D_9CuL6M.cjs → runCodex-Bzsp8gFO.cjs} +29 -21
  36. package/dist/{runCodex-mLHjsgVj.mjs → runCodex-CwtLSTMJ.mjs} +26 -18
  37. package/dist/{runGemini-CcWGezMt.cjs → runGemini-6Dwyk_Km.cjs} +267 -82
  38. package/dist/{runGemini-BMiho2ab.mjs → runGemini-Bmoxehlh.mjs} +267 -82
  39. package/package.json +9 -9
  40. package/scripts/build.mjs +68 -68
  41. package/scripts/ensureAcpSdkCompat.mjs +170 -172
  42. package/scripts/release-smoke.mjs +38 -35
  43. package/dist/ProviderSelectionHandler-BuXk-8ji.cjs +0 -680
  44. 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-DId_j3C2.cjs');
7
- var registerKillSessionHandler = require('./registerKillSessionHandler-Dg_iRBPm.cjs');
8
- var index = require('./index-CuuYSKiv.cjs');
9
- var BaseReasoningProcessor = require('./BaseReasoningProcessor-CBMK-8Gi.cjs');
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-Dzr6sFwD.cjs');
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
- session.sendAgentMessage("gemini", {
612
- type: "turn_aborted",
613
- id: node_crypto.randomUUID()
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
- session.sendAgentMessage("gemini", {
763
- type: "turn_aborted",
764
- id: node_crypto.randomUUID()
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
- messageBuffer.addMessage("\u2550".repeat(40), "status");
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
- reasoningProcessor.abort();
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 (injectHistoryContext && conversationHistory.hasHistory()) {
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, 12e4);
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 currentModel2 = displayedModel || "gemini-2.5-pro";
1077
- errorMsg = `Model "${currentModel2}" not found. Available models: gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite`;
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);