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
|
@@ -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,
|
|
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,
|
|
6
|
-
import { g as getInitialGeminiModel, r as readGeminiLocalConfig, G as GEMINI_MODEL_ENV, s as saveGeminiModelToConfig, a as createGeminiBackend, b as stopCaffeinate } from './index-
|
|
7
|
-
import { B as BaseReasoningProcessor, b as bootstrapManagedProviderSession } from './BaseReasoningProcessor-
|
|
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-
|
|
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
|
-
|
|
610
|
-
|
|
611
|
-
|
|
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
|
-
|
|
761
|
-
|
|
762
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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,
|
|
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
|
|
1075
|
-
errorMsg = `Model "${
|
|
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.
|
|
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, []);
|