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
@@ -1,10 +1,10 @@
1
1
  import { useStdout, useInput, Box, Text, render } from 'ink';
2
2
  import React, { useState, useRef, useEffect, useCallback } from 'react';
3
3
  import { randomUUID } from 'node:crypto';
4
- import { l as logger, f as connectionState, A as ApiClient } from './api-DBy5lPZw.mjs';
5
- import { B as BasePermissionHandler, C as ConversationHistory$1, e as ensureManagedProviderMachine, M as MissingMachineIdError, s as syncControlledByUserState, b as MessageQueue2, h as hashObject, d as MessageBuffer, r as registerKillSessionHandler, w as waitForResponseCompleteWithAbort, f as closeProviderSession, l as launchRuntimeHandleWithFactoryResult, i as inferToolResultError, j as forwardAgentMessageToProviderSession } from './registerKillSessionHandler-D4_wpN18.mjs';
6
- import { g as getInitialGeminiModel, r as readGeminiLocalConfig, G as GEMINI_MODEL_ENV, s as saveGeminiModelToConfig, a as createGeminiBackend, b as stopCaffeinate } from './index-66vjECEd.mjs';
7
- import { B as BaseReasoningProcessor, b as bootstrapManagedProviderSession } from './BaseReasoningProcessor-Dn9FxfxU.mjs';
4
+ import { l as logger, b as connectionState, A as ApiClient } from './api-Cxifhw5r.mjs';
5
+ import { B as BasePermissionHandler, C as ConversationHistory$1, r as resolveHappyOrgQueuedTurn, e as ensureManagedProviderMachine, M as MissingMachineIdError, s as syncControlledByUserState, b as MessageQueue2, h as hashObject, d as registerKillSessionHandler, f as MessageBuffer, i as buildHappyOrgTurnPrompt, w as waitForResponseCompleteWithAbort, j as finalizeHappyOrgTurn, k as closeProviderSession, l as launchRuntimeHandleWithFactoryResult, m as inferToolResultError, n as forwardAgentMessageToProviderSession } from './registerKillSessionHandler-BFBkz_XT.mjs';
6
+ import { g as getInitialGeminiModel, r as readGeminiLocalConfig, G as GEMINI_MODEL_ENV, s as saveGeminiModelToConfig, a as createGeminiBackend, b as stopCaffeinate } from './index-Des7I5WX.mjs';
7
+ import { B as BaseReasoningProcessor, b as bootstrapManagedProviderSession } from './BaseReasoningProcessor-BaOWkVcu.mjs';
8
8
  import 'cross-spawn';
9
9
  import '@agentclientprotocol/sdk';
10
10
  import 'ps-list';
@@ -15,7 +15,7 @@ import 'node:child_process';
15
15
  import 'node:readline';
16
16
  import 'tweetnacl';
17
17
  import 'axios';
18
- import './persistence-BOWh1NER.mjs';
18
+ import './persistence-6d4U4Sh8.mjs';
19
19
  import 'open';
20
20
  import 'chalk';
21
21
  import 'fs';
@@ -430,6 +430,116 @@ class ConversationHistory extends ConversationHistory$1 {
430
430
  }
431
431
  }
432
432
 
433
+ const GEMINI_PERMISSION_MODES = [
434
+ "default",
435
+ "read-only",
436
+ "safe-yolo",
437
+ "yolo"
438
+ ];
439
+ function isGeminiPermissionMode(value) {
440
+ return typeof value === "string" && GEMINI_PERMISSION_MODES.includes(value);
441
+ }
442
+ function hasMetaOverride(message, key) {
443
+ return Object.prototype.hasOwnProperty.call(message.meta ?? {}, key);
444
+ }
445
+ function resolveGeminiQueuedMessage(message, currentState) {
446
+ const previousState = { ...currentState };
447
+ const currentPermissionMode = isGeminiPermissionMode(currentState.permissionMode) ? currentState.permissionMode : void 0;
448
+ let permissionMode = currentPermissionMode;
449
+ let invalidPermissionMode = null;
450
+ if (hasMetaOverride(message, "permissionMode")) {
451
+ if (isGeminiPermissionMode(message.meta?.permissionMode)) {
452
+ permissionMode = message.meta.permissionMode;
453
+ } else if (message.meta?.permissionMode) {
454
+ invalidPermissionMode = String(message.meta.permissionMode);
455
+ }
456
+ }
457
+ const hasModelOverride = hasMetaOverride(message, "model");
458
+ const model = hasModelOverride ? message.meta?.model || void 0 : currentState.model;
459
+ const originalUserMessage = message.content.text;
460
+ let queuedText = originalUserMessage;
461
+ const isFirstMessage = currentState.isFirstMessage ?? true;
462
+ let nextIsFirstMessage = isFirstMessage;
463
+ if (isFirstMessage && message.meta?.appendSystemPrompt) {
464
+ queuedText = `${message.meta.appendSystemPrompt}
465
+
466
+ ${originalUserMessage}`;
467
+ nextIsFirstMessage = false;
468
+ }
469
+ const nextState = {
470
+ permissionMode: permissionMode ?? "default",
471
+ model,
472
+ isFirstMessage: nextIsFirstMessage
473
+ };
474
+ return {
475
+ previousState,
476
+ nextState,
477
+ invalidPermissionMode,
478
+ hasModelOverride,
479
+ queuedMessage: {
480
+ text: queuedText,
481
+ mode: {
482
+ permissionMode: nextState.permissionMode ?? "default",
483
+ model,
484
+ originalUserMessage
485
+ }
486
+ }
487
+ };
488
+ }
489
+ function bindGeminiUserMessageQueue(opts) {
490
+ let currentState = { ...opts.initialState };
491
+ opts.session.onUserMessage((message) => {
492
+ const happyOrgResult = opts.happyOrg ? resolveHappyOrgQueuedTurn({
493
+ metadata: opts.happyOrg.getMetadata(),
494
+ message
495
+ }) : {
496
+ nextMetadata: null,
497
+ queuedTurn: null,
498
+ blocked: false,
499
+ statusMessage: void 0
500
+ };
501
+ if (opts.happyOrg && happyOrgResult.nextMetadata) {
502
+ opts.happyOrg.updateMetadata(() => happyOrgResult.nextMetadata);
503
+ }
504
+ if (happyOrgResult.blocked) {
505
+ if (happyOrgResult.statusMessage) {
506
+ opts.happyOrg?.emitStatusMessage(happyOrgResult.statusMessage);
507
+ }
508
+ logger.debugLargeJson("[gemini] User message blocked by Happy Org runtime:", message);
509
+ return;
510
+ }
511
+ const resolution = resolveGeminiQueuedMessage(message, currentState);
512
+ currentState = resolution.nextState;
513
+ resolution.queuedMessage.mode.happyOrg = happyOrgResult.queuedTurn;
514
+ opts.onResolvedMessage?.(resolution);
515
+ opts.messageQueue.push(resolution.queuedMessage.text, resolution.queuedMessage.mode);
516
+ logger.debugLargeJson("[gemini] User message queued:", message);
517
+ });
518
+ }
519
+
520
+ function normalizeGeminiBackendError(error) {
521
+ if (typeof error === "string") {
522
+ return error.trim() || "Gemini runtime exited unexpectedly";
523
+ }
524
+ if (error instanceof Error) {
525
+ return error.message.trim() || "Gemini runtime exited unexpectedly";
526
+ }
527
+ if (typeof error === "object" && error !== null) {
528
+ const record = error;
529
+ const fields = [record.message, record.details, record.detail, record.stderr].map((value) => typeof value === "string" ? value.trim() : "").filter(Boolean);
530
+ if (fields.length > 0) {
531
+ return fields.join("\n");
532
+ }
533
+ try {
534
+ const serialized = JSON.stringify(record);
535
+ if (serialized && serialized !== "{}") {
536
+ return serialized;
537
+ }
538
+ } catch {
539
+ }
540
+ }
541
+ return "Gemini runtime exited unexpectedly";
542
+ }
433
543
  async function runGemini(opts) {
434
544
  const sessionTag = randomUUID();
435
545
  connectionState.setBackend("Gemini");
@@ -511,67 +621,21 @@ async function runGemini(opts) {
511
621
  await syncControlledByUserState(session, false);
512
622
  const messageQueue = new MessageQueue2((mode) => hashObject({
513
623
  permissionMode: mode.permissionMode,
514
- model: mode.model
624
+ model: mode.model,
625
+ happyOrg: mode.happyOrg ? {
626
+ taskId: mode.happyOrg.context.taskId,
627
+ organizationId: mode.happyOrg.context.organizationId,
628
+ memberAgentId: mode.happyOrg.context.memberAgentId,
629
+ supervisorAgentId: mode.happyOrg.context.supervisorAgentId,
630
+ reopenContext: mode.happyOrg.reopenContext ?? null
631
+ } : null
515
632
  }));
516
633
  const conversationHistory = new ConversationHistory({ maxMessages: 20, maxCharacters: 5e4 });
517
- let currentPermissionMode = void 0;
518
- let currentModel = void 0;
519
- session.onUserMessage((message) => {
520
- let messagePermissionMode = currentPermissionMode;
521
- if (message.meta?.permissionMode) {
522
- const validModes = ["default", "read-only", "safe-yolo", "yolo"];
523
- if (validModes.includes(message.meta.permissionMode)) {
524
- messagePermissionMode = message.meta.permissionMode;
525
- currentPermissionMode = messagePermissionMode;
526
- updatePermissionMode(messagePermissionMode);
527
- logger.debug(`[Gemini] Permission mode updated from user message to: ${currentPermissionMode}`);
528
- } else {
529
- logger.debug(`[Gemini] Invalid permission mode received: ${message.meta.permissionMode}`);
530
- }
531
- } else {
532
- logger.debug(`[Gemini] User message received with no permission mode override, using current: ${currentPermissionMode ?? "default (effective)"}`);
533
- }
534
- if (currentPermissionMode === void 0) {
535
- currentPermissionMode = "default";
536
- updatePermissionMode("default");
537
- }
538
- let messageModel = currentModel;
539
- if (message.meta?.hasOwnProperty("model")) {
540
- if (message.meta.model === null) {
541
- messageModel = void 0;
542
- currentModel = void 0;
543
- } else if (message.meta.model) {
544
- const previousModel = currentModel;
545
- messageModel = message.meta.model;
546
- currentModel = messageModel;
547
- if (previousModel !== messageModel) {
548
- updateDisplayedModel(messageModel, true);
549
- messageBuffer.addMessage(`Model changed to: ${messageModel}`, "system");
550
- logger.debug(`[Gemini] Model changed from ${previousModel} to ${messageModel}`);
551
- }
552
- }
553
- }
554
- const originalUserMessage = message.content.text;
555
- let fullPrompt = originalUserMessage;
556
- if (isFirstMessage && message.meta?.appendSystemPrompt) {
557
- fullPrompt = message.meta.appendSystemPrompt + "\n\n" + originalUserMessage;
558
- isFirstMessage = false;
559
- }
560
- const mode = {
561
- permissionMode: messagePermissionMode || "default",
562
- model: messageModel,
563
- originalUserMessage
564
- // Store original message separately
565
- };
566
- messageQueue.push(fullPrompt, mode);
567
- conversationHistory.addUserMessage(originalUserMessage);
568
- });
569
634
  let thinking = false;
570
635
  session.keepAlive(thinking, "remote");
571
636
  const keepAliveInterval = setInterval(() => {
572
637
  session.keepAlive(thinking, "remote");
573
638
  }, 2e3);
574
- let isFirstMessage = true;
575
639
  const sendReady = () => {
576
640
  session.sendSessionEvent({ type: "ready" });
577
641
  try {
@@ -604,12 +668,22 @@ async function runGemini(opts) {
604
668
  let shouldExit = false;
605
669
  let runtimeHandle = null;
606
670
  let unsubscribeRuntimeMessages = null;
671
+ let handleQueuedUserMessage = null;
672
+ let emitGeminiStatusMessage = null;
673
+ let turnAbortedSent = false;
674
+ let turnInFlight = false;
675
+ let shouldInjectHistoryOnNextSession = false;
676
+ let currentModeHash = null;
677
+ let unexpectedRuntimeStopRecovery = null;
607
678
  async function handleAbort() {
608
679
  logger.debug("[Gemini] Abort requested - stopping current task");
609
- session.sendAgentMessage("gemini", {
610
- type: "turn_aborted",
611
- id: randomUUID()
612
- });
680
+ if (!turnAbortedSent) {
681
+ session.sendAgentMessage("gemini", {
682
+ type: "turn_aborted",
683
+ id: randomUUID()
684
+ });
685
+ turnAbortedSent = true;
686
+ }
613
687
  reasoningProcessor.abort();
614
688
  diffProcessor.reset();
615
689
  try {
@@ -670,6 +744,10 @@ async function runGemini(opts) {
670
744
  logger.debug(`[gemini] Model unchanged, skipping update message`);
671
745
  }
672
746
  };
747
+ emitGeminiStatusMessage = (message) => {
748
+ messageBuffer.addMessage(message, "status");
749
+ session.sendSessionEvent({ type: "message", message });
750
+ };
673
751
  if (hasTTY) {
674
752
  console.clear();
675
753
  const DisplayComponent = () => {
@@ -710,10 +788,56 @@ async function runGemini(opts) {
710
788
  const updatePermissionMode = (mode) => {
711
789
  permissionHandler.setPermissionMode(mode);
712
790
  };
791
+ handleQueuedUserMessage = ({ previousState, nextState, invalidPermissionMode, hasModelOverride }) => {
792
+ if (invalidPermissionMode) {
793
+ logger.debug(`[Gemini] Invalid permission mode received: ${invalidPermissionMode}`);
794
+ } else if (nextState.permissionMode) {
795
+ logger.debug(`[Gemini] Permission mode resolved to: ${nextState.permissionMode}`);
796
+ }
797
+ updatePermissionMode(nextState.permissionMode ?? "default");
798
+ if (hasModelOverride && nextState.model && previousState.model !== nextState.model) {
799
+ updateDisplayedModel(nextState.model, true);
800
+ messageBuffer.addMessage(`Model changed to: ${nextState.model}`, "system");
801
+ logger.debug(`[Gemini] Model changed from ${previousState.model} to ${nextState.model}`);
802
+ }
803
+ };
804
+ bindGeminiUserMessageQueue({
805
+ session,
806
+ messageQueue,
807
+ initialState: {},
808
+ happyOrg: {
809
+ getMetadata: () => session.getMetadataSnapshot?.() ?? metadata,
810
+ updateMetadata: (handler) => session.updateMetadata(handler),
811
+ emitStatusMessage: (message) => {
812
+ emitGeminiStatusMessage?.(message);
813
+ }
814
+ },
815
+ onResolvedMessage: (resolution) => {
816
+ handleQueuedUserMessage?.(resolution);
817
+ }
818
+ });
819
+ const emitTurnReport = (report) => {
820
+ session.sendAgentMessage("gemini", {
821
+ type: "turn-report",
822
+ id: randomUUID(),
823
+ report
824
+ });
825
+ };
713
826
  let accumulatedResponse = "";
714
827
  let isResponseInProgress = false;
715
828
  let hadToolCallInTurn = false;
716
829
  let taskStartedSent = false;
830
+ const resetTurnState = () => {
831
+ reasoningProcessor.abort();
832
+ diffProcessor.reset();
833
+ accumulatedResponse = "";
834
+ isResponseInProgress = false;
835
+ hadToolCallInTurn = false;
836
+ taskStartedSent = false;
837
+ turnAbortedSent = false;
838
+ thinking = false;
839
+ session.keepAlive(thinking, "remote");
840
+ };
717
841
  const disposeRuntimeHandle = async () => {
718
842
  if (!runtimeHandle) {
719
843
  return;
@@ -728,6 +852,37 @@ async function runGemini(opts) {
728
852
  logger.debug("[Gemini] Error disposing runtime session:", error);
729
853
  }
730
854
  };
855
+ const queueHistoryInjectionForRestart = (reason) => {
856
+ messageBuffer.addMessage("\u2550".repeat(40), "status");
857
+ if (conversationHistory.hasHistory()) {
858
+ shouldInjectHistoryOnNextSession = true;
859
+ const message = `${reason} Preserving ${conversationHistory.size()} earlier messages of context.`;
860
+ emitGeminiStatusMessage?.(message);
861
+ logger.debug(`[Gemini] Will inject conversation history after restart: ${conversationHistory.getSummary()}`);
862
+ return;
863
+ }
864
+ emitGeminiStatusMessage?.(reason);
865
+ };
866
+ const recoverUnexpectedRuntimeStop = async (detail) => {
867
+ if (shouldExit || messageQueue.isClosed() || unexpectedRuntimeStopRecovery) {
868
+ return;
869
+ }
870
+ const errorMessage = normalizeGeminiBackendError(detail);
871
+ unexpectedRuntimeStopRecovery = (async () => {
872
+ queueHistoryInjectionForRestart(
873
+ `Gemini runtime stopped unexpectedly (${errorMessage}). Starting a new Gemini session on the next message...`
874
+ );
875
+ await disposeRuntimeHandle();
876
+ currentModeHash = null;
877
+ permissionHandler.reset();
878
+ resetTurnState();
879
+ })();
880
+ try {
881
+ await unexpectedRuntimeStopRecovery;
882
+ } finally {
883
+ unexpectedRuntimeStopRecovery = null;
884
+ }
885
+ };
731
886
  function setupGeminiMessageHandler(activeRuntimeHandle) {
732
887
  const forwardAgentMessage = (agentMessage) => {
733
888
  forwardAgentMessageToProviderSession(agentMessage, {
@@ -757,10 +912,13 @@ async function runGemini(opts) {
757
912
  logger.debug(`[gemini] Status changed: ${msg.status}${statusDetail ? ` - ${statusDetail}` : ""}`);
758
913
  if (msg.status === "error") {
759
914
  logger.debug(`[gemini] \u26A0\uFE0F Error status received: ${statusDetail || "Unknown error"}`);
760
- session.sendAgentMessage("gemini", {
761
- type: "turn_aborted",
762
- id: randomUUID()
763
- });
915
+ if (!turnAbortedSent) {
916
+ session.sendAgentMessage("gemini", {
917
+ type: "turn_aborted",
918
+ id: randomUUID()
919
+ });
920
+ turnAbortedSent = true;
921
+ }
764
922
  }
765
923
  if (msg.status === "running") {
766
924
  thinking = true;
@@ -775,6 +933,9 @@ async function runGemini(opts) {
775
933
  messageBuffer.addMessage("Thinking...", "system");
776
934
  } else if (msg.status === "idle" || msg.status === "stopped") {
777
935
  reasoningProcessor.complete();
936
+ if (msg.status === "stopped" && !turnInFlight) {
937
+ void recoverUnexpectedRuntimeStop(msg.detail);
938
+ }
778
939
  } else if (msg.status === "error") {
779
940
  thinking = false;
780
941
  session.keepAlive(thinking, "remote");
@@ -934,7 +1095,6 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
934
1095
  };
935
1096
  }
936
1097
  try {
937
- let currentModeHash = null;
938
1098
  let pending = null;
939
1099
  while (!shouldExit) {
940
1100
  let message = pending;
@@ -957,19 +1117,11 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
957
1117
  if (!message) {
958
1118
  break;
959
1119
  }
960
- let injectHistoryContext = false;
961
1120
  if (runtimeHandle && currentModeHash && message.hash !== currentModeHash) {
962
1121
  logger.debug("[Gemini] Mode changed \u2013 restarting Gemini session");
963
- messageBuffer.addMessage("\u2550".repeat(40), "status");
964
- if (conversationHistory.hasHistory()) {
965
- messageBuffer.addMessage(`Switching model (preserving ${conversationHistory.size()} messages of context)...`, "status");
966
- injectHistoryContext = true;
967
- logger.debug(`[Gemini] Will inject conversation history: ${conversationHistory.getSummary()}`);
968
- } else {
969
- messageBuffer.addMessage("Starting new Gemini session (mode changed)...", "status");
970
- }
1122
+ queueHistoryInjectionForRestart("Starting new Gemini session (execution settings changed)...");
971
1123
  permissionHandler.reset();
972
- reasoningProcessor.abort();
1124
+ resetTurnState();
973
1125
  await disposeRuntimeHandle();
974
1126
  const launchedRuntime = await launchGeminiRuntimeHandle(message.mode);
975
1127
  const actualModel = launchedRuntime.model;
@@ -985,7 +1137,9 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
985
1137
  const userMessageToShow = message.mode?.originalUserMessage || message.message;
986
1138
  messageBuffer.addMessage(userMessageToShow, "user");
987
1139
  isProcessingMessage = true;
1140
+ let turnStatus = "task_complete";
988
1141
  try {
1142
+ turnInFlight = true;
989
1143
  let activeHandle = runtimeHandle;
990
1144
  if (!activeHandle) {
991
1145
  const launchedRuntime = await launchGeminiRuntimeHandle(message.mode);
@@ -1005,12 +1159,17 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
1005
1159
  isResponseInProgress = false;
1006
1160
  hadToolCallInTurn = false;
1007
1161
  taskStartedSent = false;
1162
+ turnAbortedSent = false;
1008
1163
  let promptToSend = message.message;
1009
- if (injectHistoryContext && conversationHistory.hasHistory()) {
1164
+ if (shouldInjectHistoryOnNextSession && conversationHistory.hasHistory()) {
1010
1165
  const historyContext = conversationHistory.getContextForNewSession();
1011
1166
  promptToSend = historyContext + promptToSend;
1012
1167
  logger.debug(`[gemini] Injected conversation history context (${historyContext.length} chars)`);
1013
1168
  }
1169
+ if (message.mode.happyOrg) {
1170
+ promptToSend = buildHappyOrgTurnPrompt(promptToSend, message.mode.happyOrg);
1171
+ }
1172
+ conversationHistory.addUserMessage(userMessageToShow);
1014
1173
  logger.debug(`[gemini] Sending prompt to Gemini (length: ${promptToSend.length}): ${promptToSend.substring(0, 100)}...`);
1015
1174
  logger.debug(`[gemini] Full prompt: ${promptToSend}`);
1016
1175
  const MAX_RETRIES = 3;
@@ -1020,8 +1179,9 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
1020
1179
  try {
1021
1180
  await activeHandle.sendPrompt(promptToSend);
1022
1181
  logger.debug("[gemini] Prompt sent successfully");
1023
- await waitForResponseCompleteWithAbort(activeHandle.backend, abortController.signal, 12e4);
1182
+ await waitForResponseCompleteWithAbort(activeHandle.backend, abortController.signal, 10 * 6e4);
1024
1183
  logger.debug("[gemini] Response complete");
1184
+ shouldInjectHistoryOnNextSession = false;
1025
1185
  break;
1026
1186
  } catch (promptError) {
1027
1187
  lastError = promptError;
@@ -1059,6 +1219,14 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
1059
1219
  } catch (error) {
1060
1220
  logger.debug("[gemini] Error in gemini session:", error);
1061
1221
  const isAbortError = error instanceof Error && error.name === "AbortError";
1222
+ turnStatus = "turn_aborted";
1223
+ if (!turnAbortedSent) {
1224
+ session.sendAgentMessage("gemini", {
1225
+ type: "turn_aborted",
1226
+ id: randomUUID()
1227
+ });
1228
+ turnAbortedSent = true;
1229
+ }
1062
1230
  if (isAbortError) {
1063
1231
  messageBuffer.addMessage("Aborted by user", "status");
1064
1232
  session.sendSessionEvent({ type: "message", message: "Aborted by user" });
@@ -1071,8 +1239,8 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
1071
1239
  const errorMessage = errObj.message || errObj.error?.message || "";
1072
1240
  const errorString = String(error);
1073
1241
  if (errorCode === 404 || errorDetails.includes("notFound") || errorDetails.includes("404") || errorMessage.includes("not found") || errorMessage.includes("404")) {
1074
- const currentModel2 = displayedModel || "gemini-2.5-pro";
1075
- errorMsg = `Model "${currentModel2}" not found. Available models: gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite`;
1242
+ const currentModel = displayedModel || "gemini-2.5-pro";
1243
+ errorMsg = `Model "${currentModel}" not found. Available models: gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite`;
1076
1244
  } else if (errorCode === -32603 || errorDetails.includes("empty response") || errorDetails.includes("Model stream ended")) {
1077
1245
  errorMsg = "Gemini API returned empty response after retries. This is a temporary issue - please try again.";
1078
1246
  } 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")) {
@@ -1105,9 +1273,26 @@ Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca`;
1105
1273
  });
1106
1274
  }
1107
1275
  } finally {
1276
+ turnInFlight = false;
1108
1277
  permissionHandler.reset();
1109
1278
  reasoningProcessor.abort();
1110
1279
  diffProcessor.reset();
1280
+ const finalizedTurn = finalizeHappyOrgTurn({
1281
+ metadata: session.getMetadataSnapshot?.() ?? null,
1282
+ queuedTurn: message.mode.happyOrg,
1283
+ responseText: accumulatedResponse,
1284
+ turnStatus
1285
+ });
1286
+ if (finalizedTurn.nextMetadata && typeof session.updateMetadata === "function") {
1287
+ session.updateMetadata(() => finalizedTurn.nextMetadata);
1288
+ }
1289
+ if (finalizedTurn.report) {
1290
+ emitTurnReport(finalizedTurn.report);
1291
+ }
1292
+ if (finalizedTurn.terminateMessage) {
1293
+ emitGeminiStatusMessage?.(finalizedTurn.terminateMessage);
1294
+ }
1295
+ accumulatedResponse = finalizedTurn.cleanedText;
1111
1296
  if (accumulatedResponse.trim()) {
1112
1297
  const { text: messageText, options } = parseOptionsFromText(accumulatedResponse);
1113
1298
  conversationHistory.addAssistantMessage(messageText);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "happy-imou-cloud",
3
- "version": "2.1.1",
3
+ "version": "2.1.2",
4
4
  "description": "hicloud - Imou 企业定制版。关键是 happy!移动端远程 AI 编程工具,支持 Claude Code、Codex 和 Gemini CLI",
5
5
  "author": "long.zhu",
6
6
  "license": "MIT",
@@ -33,13 +33,13 @@
33
33
  }
34
34
  }
35
35
  },
36
- "files": [
37
- "dist",
38
- "compat",
39
- "bin",
40
- "scripts",
41
- "package.json"
42
- ],
36
+ "files": [
37
+ "dist",
38
+ "compat",
39
+ "bin",
40
+ "scripts",
41
+ "package.json"
42
+ ],
43
43
  "scripts": {
44
44
  "// ==== TypeScript & Build ====": "",
45
45
  "why do we need to build before running tests / dev?": "We need the binary to be built so we run daemon commands which directly run the binary - we don't want them to go out of sync or have custom spawn logic depending how we started happy",
@@ -85,7 +85,7 @@
85
85
  "unlink:dev": "node scripts/link-dev.cjs unlink"
86
86
  },
87
87
  "dependencies": {
88
- "@agentclientprotocol/sdk": "0.14.1",
88
+ "@agentclientprotocol/sdk": "0.14.1",
89
89
  "@stablelib/base64": "^2.0.1",
90
90
  "@stablelib/hex": "^2.0.1",
91
91
  "@types/cross-spawn": "^6.0.6",
package/scripts/build.mjs CHANGED
@@ -1,68 +1,68 @@
1
- #!/usr/bin/env node
2
-
3
- import { spawnSync } from 'node:child_process';
4
- import { existsSync, readFileSync, rmSync } from 'node:fs';
5
- import { dirname, resolve } from 'node:path';
6
- import { fileURLToPath } from 'node:url';
7
- import { ensureAcpSdkCompat } from './ensureAcpSdkCompat.mjs';
8
-
9
- const here = dirname(fileURLToPath(import.meta.url));
10
- const packageRoot = resolve(here, '..');
11
- const workspaceRoot = resolve(packageRoot, '..', '..');
12
- const packageJson = JSON.parse(readFileSync(resolve(packageRoot, 'package.json'), 'utf8'));
13
-
14
- function resolveTool(binName, packageSpecs) {
15
- const suffix = process.platform === 'win32' ? '.cmd' : '';
16
- const packageLocalBin = resolve(packageRoot, 'node_modules', '.bin', `${binName}${suffix}`);
17
- const workspaceLocalBin = resolve(workspaceRoot, 'node_modules', '.bin', `${binName}${suffix}`);
18
-
19
- if (existsSync(packageLocalBin)) {
20
- return {
21
- command: packageLocalBin,
22
- prefixArgs: [],
23
- };
24
- }
25
-
26
- if (existsSync(workspaceLocalBin)) {
27
- return {
28
- command: workspaceLocalBin,
29
- prefixArgs: [],
30
- };
31
- }
32
-
33
- return {
34
- command: 'npx',
35
- prefixArgs: ['-y', ...packageSpecs.flatMap((packageSpec) => ['-p', packageSpec]), binName],
36
- };
37
- }
38
-
39
- function runStep(name, command, args) {
40
- console.log(`\n[build] ${name}`);
41
- console.log(`> ${command} ${args.join(' ')}`);
42
-
43
- const result = spawnSync(command, args, {
44
- cwd: packageRoot,
45
- stdio: 'inherit',
46
- shell: process.platform === 'win32',
47
- env: process.env,
48
- });
49
-
50
- if (result.status !== 0) {
51
- throw new Error(`[build] Step failed: ${name}`);
52
- }
53
- }
54
-
55
- function runTool(name, tool, args) {
56
- runStep(name, tool.command, [...tool.prefixArgs, ...args]);
57
- }
58
-
59
- const tsc = resolveTool('tsc', [`typescript@${packageJson.devDependencies.typescript}`]);
60
- const pkgroll = resolveTool('pkgroll', [
61
- `pkgroll@${packageJson.devDependencies.pkgroll}`,
62
- `typescript@${packageJson.devDependencies.typescript}`,
63
- ]);
64
-
65
- ensureAcpSdkCompat();
66
- rmSync(resolve(packageRoot, 'dist'), { recursive: true, force: true });
67
- runTool('typecheck', tsc, ['--noEmit']);
68
- runTool('bundle', pkgroll, []);
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from 'node:child_process';
4
+ import { existsSync, readFileSync, rmSync } from 'node:fs';
5
+ import { dirname, resolve } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { ensureAcpSdkCompat } from './ensureAcpSdkCompat.mjs';
8
+
9
+ const here = dirname(fileURLToPath(import.meta.url));
10
+ const packageRoot = resolve(here, '..');
11
+ const workspaceRoot = resolve(packageRoot, '..', '..');
12
+ const packageJson = JSON.parse(readFileSync(resolve(packageRoot, 'package.json'), 'utf8'));
13
+
14
+ function resolveTool(binName, packageSpecs) {
15
+ const suffix = process.platform === 'win32' ? '.cmd' : '';
16
+ const packageLocalBin = resolve(packageRoot, 'node_modules', '.bin', `${binName}${suffix}`);
17
+ const workspaceLocalBin = resolve(workspaceRoot, 'node_modules', '.bin', `${binName}${suffix}`);
18
+
19
+ if (existsSync(packageLocalBin)) {
20
+ return {
21
+ command: packageLocalBin,
22
+ prefixArgs: [],
23
+ };
24
+ }
25
+
26
+ if (existsSync(workspaceLocalBin)) {
27
+ return {
28
+ command: workspaceLocalBin,
29
+ prefixArgs: [],
30
+ };
31
+ }
32
+
33
+ return {
34
+ command: 'npx',
35
+ prefixArgs: ['-y', ...packageSpecs.flatMap((packageSpec) => ['-p', packageSpec]), binName],
36
+ };
37
+ }
38
+
39
+ function runStep(name, command, args) {
40
+ console.log(`\n[build] ${name}`);
41
+ console.log(`> ${command} ${args.join(' ')}`);
42
+
43
+ const result = spawnSync(command, args, {
44
+ cwd: packageRoot,
45
+ stdio: 'inherit',
46
+ shell: process.platform === 'win32',
47
+ env: process.env,
48
+ });
49
+
50
+ if (result.status !== 0) {
51
+ throw new Error(`[build] Step failed: ${name}`);
52
+ }
53
+ }
54
+
55
+ function runTool(name, tool, args) {
56
+ runStep(name, tool.command, [...tool.prefixArgs, ...args]);
57
+ }
58
+
59
+ const tsc = resolveTool('tsc', [`typescript@${packageJson.devDependencies.typescript}`]);
60
+ const pkgroll = resolveTool('pkgroll', [
61
+ `pkgroll@${packageJson.devDependencies.pkgroll}`,
62
+ `typescript@${packageJson.devDependencies.typescript}`,
63
+ ]);
64
+
65
+ ensureAcpSdkCompat();
66
+ rmSync(resolve(packageRoot, 'dist'), { recursive: true, force: true });
67
+ runTool('typecheck', tsc, ['--noEmit']);
68
+ runTool('bundle', pkgroll, []);