happy-imou-cloud 2.0.7 → 2.0.9
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 +1 -1
- package/dist/{api-Dwkm7s_E.cjs → api-CUTdFiFP.cjs} +3 -4
- package/dist/{api-dwwHBzLc.mjs → api-CnvyGas2.mjs} +3 -4
- package/dist/{command-Cfq3Uc0S.mjs → command-BGA3qCKR.mjs} +3 -3
- package/dist/{command-DiAVIsxX.cjs → command-DLAJZsKX.cjs} +3 -3
- package/dist/{index-HyqLXzw-.mjs → index-BpZL4RcT.mjs} +208 -32
- package/dist/{index-CfqxEoyl.cjs → index-D4OdFq68.cjs} +210 -34
- package/dist/index.cjs +3 -3
- package/dist/index.mjs +3 -3
- package/dist/lib.cjs +1 -1
- package/dist/lib.mjs +1 -1
- package/dist/{BaseReasoningProcessor-ClrT-x-H.mjs → names-C9iJODqA.mjs} +98 -3
- package/dist/{BaseReasoningProcessor-DphULXS-.cjs → names-YEhZwVT0.cjs} +100 -2
- package/dist/{persistence-Dg-rxY2a.mjs → persistence-BPV3AmJL.mjs} +100 -4
- package/dist/{persistence-hbhwAYIV.cjs → persistence-CxvL0cwp.cjs} +110 -1
- package/dist/{registerKillSessionHandler-BAvk4GYO.mjs → registerKillSessionHandler-C2O8b5wH.mjs} +2 -2
- package/dist/{registerKillSessionHandler-D1ouN10n.cjs → registerKillSessionHandler-rqd7duc9.cjs} +2 -2
- package/dist/{runClaude-OxYbt3ZQ.mjs → runClaude-8inO7C5p.mjs} +4 -4
- package/dist/{runClaude-CZmJ7qEP.cjs → runClaude-KwIVwFp1.cjs} +5 -5
- package/dist/{runCodex-ByVTEbSY.mjs → runCodex-BQ-fN5E6.mjs} +5 -6
- package/dist/{runCodex-CtncAgso.cjs → runCodex-Ba8COxZe.cjs} +24 -25
- package/dist/{runGemini-BRO6A2jm.mjs → runGemini-BE0FizuV.mjs} +5 -6
- package/dist/{runGemini-ChwjLmhI.cjs → runGemini-DtdLLX9o.cjs} +20 -21
- package/package.json +3 -4
- package/scripts/build.mjs +66 -0
- package/scripts/release-smoke.mjs +166 -30
package/bin/happy-cloud.mjs
CHANGED
|
@@ -19,7 +19,7 @@ var path = require('path');
|
|
|
19
19
|
var expoServerSdk = require('expo-server-sdk');
|
|
20
20
|
|
|
21
21
|
var name = "happy-imou-cloud";
|
|
22
|
-
var version = "2.0.
|
|
22
|
+
var version = "2.0.9";
|
|
23
23
|
var description = "hicloud - Imou 企业定制版。关键是 happy!移动端远程 AI 编程工具,支持 Claude Code、Codex 和 Gemini CLI";
|
|
24
24
|
var author = "long.zhu";
|
|
25
25
|
var license = "MIT";
|
|
@@ -62,7 +62,7 @@ var scripts = {
|
|
|
62
62
|
"// ==== TypeScript & Build ====": "",
|
|
63
63
|
"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",
|
|
64
64
|
typecheck: "tsc --noEmit",
|
|
65
|
-
build: "
|
|
65
|
+
build: "node ./scripts/build.mjs",
|
|
66
66
|
"// ==== Testing ====": "",
|
|
67
67
|
test: "yarn build && vitest run",
|
|
68
68
|
"test:integration": "yarn build && vitest run --config vitest.integration.config.ts",
|
|
@@ -118,7 +118,6 @@ var dependencies = {
|
|
|
118
118
|
"expo-server-sdk": "^3.15.0",
|
|
119
119
|
fastify: "^5.6.2",
|
|
120
120
|
"fastify-type-provider-zod": "4.0.2",
|
|
121
|
-
"happy-protocol": "0.1.0",
|
|
122
121
|
"http-proxy": "^1.18.1",
|
|
123
122
|
"http-proxy-middleware": "^3.0.5",
|
|
124
123
|
ink: "^6.5.1",
|
|
@@ -433,7 +432,7 @@ async function listDaemonLogFiles(limit = 50) {
|
|
|
433
432
|
return { file, path: fullPath, modified: stats.mtime };
|
|
434
433
|
}).sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
435
434
|
try {
|
|
436
|
-
const { readDaemonState } = await Promise.resolve().then(function () { return require('./persistence-
|
|
435
|
+
const { readDaemonState } = await Promise.resolve().then(function () { return require('./persistence-CxvL0cwp.cjs'); });
|
|
437
436
|
const state = await readDaemonState();
|
|
438
437
|
if (!state) {
|
|
439
438
|
return logs;
|
|
@@ -17,7 +17,7 @@ import { resolve, join as join$1 } from 'path';
|
|
|
17
17
|
import { Expo } from 'expo-server-sdk';
|
|
18
18
|
|
|
19
19
|
var name = "happy-imou-cloud";
|
|
20
|
-
var version = "2.0.
|
|
20
|
+
var version = "2.0.9";
|
|
21
21
|
var description = "hicloud - Imou 企业定制版。关键是 happy!移动端远程 AI 编程工具,支持 Claude Code、Codex 和 Gemini CLI";
|
|
22
22
|
var author = "long.zhu";
|
|
23
23
|
var license = "MIT";
|
|
@@ -60,7 +60,7 @@ var scripts = {
|
|
|
60
60
|
"// ==== TypeScript & Build ====": "",
|
|
61
61
|
"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",
|
|
62
62
|
typecheck: "tsc --noEmit",
|
|
63
|
-
build: "
|
|
63
|
+
build: "node ./scripts/build.mjs",
|
|
64
64
|
"// ==== Testing ====": "",
|
|
65
65
|
test: "yarn build && vitest run",
|
|
66
66
|
"test:integration": "yarn build && vitest run --config vitest.integration.config.ts",
|
|
@@ -116,7 +116,6 @@ var dependencies = {
|
|
|
116
116
|
"expo-server-sdk": "^3.15.0",
|
|
117
117
|
fastify: "^5.6.2",
|
|
118
118
|
"fastify-type-provider-zod": "4.0.2",
|
|
119
|
-
"happy-protocol": "0.1.0",
|
|
120
119
|
"http-proxy": "^1.18.1",
|
|
121
120
|
"http-proxy-middleware": "^3.0.5",
|
|
122
121
|
ink: "^6.5.1",
|
|
@@ -431,7 +430,7 @@ async function listDaemonLogFiles(limit = 50) {
|
|
|
431
430
|
return { file, path: fullPath, modified: stats.mtime };
|
|
432
431
|
}).sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
433
432
|
try {
|
|
434
|
-
const { readDaemonState } = await import('./persistence-
|
|
433
|
+
const { readDaemonState } = await import('./persistence-BPV3AmJL.mjs');
|
|
435
434
|
const state = await readDaemonState();
|
|
436
435
|
if (!state) {
|
|
437
436
|
return logs;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as createDefaultRuntimeShell } from './index-
|
|
1
|
+
import { c as createDefaultRuntimeShell } from './index-BpZL4RcT.mjs';
|
|
2
2
|
import 'chalk';
|
|
3
|
-
import './api-
|
|
3
|
+
import './api-CnvyGas2.mjs';
|
|
4
4
|
import 'axios';
|
|
5
5
|
import 'fs';
|
|
6
6
|
import 'node:fs';
|
|
@@ -17,7 +17,7 @@ import 'fs/promises';
|
|
|
17
17
|
import 'crypto';
|
|
18
18
|
import 'path';
|
|
19
19
|
import 'expo-server-sdk';
|
|
20
|
-
import './persistence-
|
|
20
|
+
import './persistence-BPV3AmJL.mjs';
|
|
21
21
|
import 'node:fs/promises';
|
|
22
22
|
import 'os';
|
|
23
23
|
import 'tmp';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var index = require('./index-
|
|
3
|
+
var index = require('./index-D4OdFq68.cjs');
|
|
4
4
|
require('chalk');
|
|
5
|
-
require('./api-
|
|
5
|
+
require('./api-CUTdFiFP.cjs');
|
|
6
6
|
require('axios');
|
|
7
7
|
require('fs');
|
|
8
8
|
require('node:fs');
|
|
@@ -19,7 +19,7 @@ require('fs/promises');
|
|
|
19
19
|
require('crypto');
|
|
20
20
|
require('path');
|
|
21
21
|
require('expo-server-sdk');
|
|
22
|
-
require('./persistence-
|
|
22
|
+
require('./persistence-CxvL0cwp.cjs');
|
|
23
23
|
require('node:fs/promises');
|
|
24
24
|
require('os');
|
|
25
25
|
require('tmp');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import chalk from 'chalk';
|
|
2
|
-
import { l as logger, e as encodeBase64, c as configuration, h as buildAuthenticatedHeaders, S as SigningBootstrapRequiredError, j as SIGNING_BOOTSTRAP_REQUIRED_MESSAGE, k as encodeBase64Url, f as delay, m as buildClientHeaders, n as decodeBase64, H as HAPPY_CLOUD_DAEMON_PORT, p as packageJson, A as ApiClient, o as getLatestDaemonLog } from './api-
|
|
3
|
-
import { writeCredentialsLegacy, writeCredentialsDataKey, readCredentials, readSettings, updateSettings, readDaemonState, clearDaemonState, acquireDaemonLock, writeDaemonState, releaseDaemonLock, validateProfileForAgent, getProfileEnvironmentVariables, clearCredentials, clearMachineId } from './persistence-
|
|
2
|
+
import { l as logger, e as encodeBase64, c as configuration, h as buildAuthenticatedHeaders, S as SigningBootstrapRequiredError, j as SIGNING_BOOTSTRAP_REQUIRED_MESSAGE, k as encodeBase64Url, f as delay, m as buildClientHeaders, n as decodeBase64, H as HAPPY_CLOUD_DAEMON_PORT, p as packageJson, A as ApiClient, o as getLatestDaemonLog } from './api-CnvyGas2.mjs';
|
|
3
|
+
import { writeCredentialsLegacy, writeCredentialsDataKey, readCredentials, readSettings, updateSettings, readDaemonState, clearDaemonState, acquireDaemonLock, writeDaemonState, releaseDaemonLock, validateProfileForAgent, getProfileEnvironmentVariables, clearCredentials, clearMachineId } from './persistence-BPV3AmJL.mjs';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import fs from 'fs/promises';
|
|
6
6
|
import os, { homedir } from 'os';
|
|
@@ -1108,6 +1108,21 @@ function getEnvironmentInfo() {
|
|
|
1108
1108
|
terminal: process.env.TERM
|
|
1109
1109
|
};
|
|
1110
1110
|
}
|
|
1111
|
+
function resolveDaemonSpawnDiagnostics(projectRoot, fileExists = existsSync) {
|
|
1112
|
+
const wrapperCandidates = [
|
|
1113
|
+
join(projectRoot, "bin", "happy-cloud.mjs"),
|
|
1114
|
+
join(projectRoot, "bin", "happy.mjs")
|
|
1115
|
+
];
|
|
1116
|
+
const cliEntrypoint = join(projectRoot, "dist", "index.mjs");
|
|
1117
|
+
const wrapperPath = wrapperCandidates.find((candidate) => fileExists(candidate)) ?? wrapperCandidates[0];
|
|
1118
|
+
return {
|
|
1119
|
+
projectRoot,
|
|
1120
|
+
wrapperPath,
|
|
1121
|
+
cliEntrypoint,
|
|
1122
|
+
wrapperExists: fileExists(wrapperPath),
|
|
1123
|
+
cliEntrypointExists: fileExists(cliEntrypoint)
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1111
1126
|
function getLogFiles(logDir) {
|
|
1112
1127
|
if (!existsSync(logDir)) {
|
|
1113
1128
|
return [];
|
|
@@ -1134,14 +1149,12 @@ async function runDoctorCommand(filter) {
|
|
|
1134
1149
|
console.log(`Node.js Version: ${chalk.green(process.version)}`);
|
|
1135
1150
|
console.log("");
|
|
1136
1151
|
console.log(chalk.bold("\u{1F527} Daemon Spawn Diagnostics"));
|
|
1137
|
-
const
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
console.log(`
|
|
1141
|
-
console.log(`Wrapper
|
|
1142
|
-
console.log(`CLI
|
|
1143
|
-
console.log(`Wrapper Exists: ${existsSync(wrapperPath) ? chalk.green("\u2713 Yes") : chalk.red("\u274C No")}`);
|
|
1144
|
-
console.log(`CLI Exists: ${existsSync(cliEntrypoint) ? chalk.green("\u2713 Yes") : chalk.red("\u274C No")}`);
|
|
1152
|
+
const diagnostics = resolveDaemonSpawnDiagnostics(projectPath());
|
|
1153
|
+
console.log(`Project Root: ${chalk.blue(diagnostics.projectRoot)}`);
|
|
1154
|
+
console.log(`Wrapper Script: ${chalk.blue(diagnostics.wrapperPath)}`);
|
|
1155
|
+
console.log(`CLI Entrypoint: ${chalk.blue(diagnostics.cliEntrypoint)}`);
|
|
1156
|
+
console.log(`Wrapper Exists: ${diagnostics.wrapperExists ? chalk.green("\u2713 Yes") : chalk.red("\u274C No")}`);
|
|
1157
|
+
console.log(`CLI Exists: ${diagnostics.cliEntrypointExists ? chalk.green("\u2713 Yes") : chalk.red("\u274C No")}`);
|
|
1145
1158
|
console.log("");
|
|
1146
1159
|
console.log(chalk.bold("\u2699\uFE0F Configuration"));
|
|
1147
1160
|
console.log(`Happy Home: ${chalk.blue(configuration.happyCloudHomeDir)}`);
|
|
@@ -4191,6 +4204,9 @@ class DefaultTransport {
|
|
|
4191
4204
|
getInitTimeout() {
|
|
4192
4205
|
return DEFAULT_TIMEOUTS.init;
|
|
4193
4206
|
}
|
|
4207
|
+
getInitDelayMs() {
|
|
4208
|
+
return 0;
|
|
4209
|
+
}
|
|
4194
4210
|
/**
|
|
4195
4211
|
* Default: pass through all lines that are valid JSON objects/arrays
|
|
4196
4212
|
*/
|
|
@@ -4251,11 +4267,16 @@ class DefaultTransport {
|
|
|
4251
4267
|
determineToolName(toolName, _toolCallId, _input, _context) {
|
|
4252
4268
|
return toolName;
|
|
4253
4269
|
}
|
|
4270
|
+
getPostPromptNoUpdatesTimeoutMs() {
|
|
4271
|
+
return 3e4;
|
|
4272
|
+
}
|
|
4254
4273
|
}
|
|
4255
4274
|
|
|
4256
4275
|
const GEMINI_TIMEOUTS = {
|
|
4257
4276
|
/** Gemini CLI can be slow on first start (downloading models, etc.) */
|
|
4258
4277
|
init: 12e4,
|
|
4278
|
+
/** Gemini ACP can swallow an initialize request sent too early after spawn */
|
|
4279
|
+
initDelay: 2500,
|
|
4259
4280
|
/** Standard tool call timeout */
|
|
4260
4281
|
toolCall: 12e4,
|
|
4261
4282
|
/** Investigation tools (codebase_investigator) can run for a long time */
|
|
@@ -4290,6 +4311,9 @@ class GeminiTransport {
|
|
|
4290
4311
|
getInitTimeout() {
|
|
4291
4312
|
return GEMINI_TIMEOUTS.init;
|
|
4292
4313
|
}
|
|
4314
|
+
getInitDelayMs() {
|
|
4315
|
+
return GEMINI_TIMEOUTS.initDelay;
|
|
4316
|
+
}
|
|
4293
4317
|
/**
|
|
4294
4318
|
* Filter Gemini CLI debug output from stdout.
|
|
4295
4319
|
*
|
|
@@ -5026,6 +5050,10 @@ ${line}` : line;
|
|
|
5026
5050
|
controller.error(error);
|
|
5027
5051
|
} finally {
|
|
5028
5052
|
reader.releaseLock();
|
|
5053
|
+
try {
|
|
5054
|
+
params.onDone?.();
|
|
5055
|
+
} catch {
|
|
5056
|
+
}
|
|
5029
5057
|
if (!controllerErrored) {
|
|
5030
5058
|
controller.close();
|
|
5031
5059
|
}
|
|
@@ -5088,7 +5116,7 @@ async function killProcessTree(proc, options) {
|
|
|
5088
5116
|
if (!pid) {
|
|
5089
5117
|
return;
|
|
5090
5118
|
}
|
|
5091
|
-
const graceMs = Math.max(1, options?.graceMs);
|
|
5119
|
+
const graceMs = Math.max(1, options?.graceMs ?? 1e3);
|
|
5092
5120
|
const descendants = await resolveDescendantPids(pid).catch(() => []);
|
|
5093
5121
|
const allPids = [...descendants, pid];
|
|
5094
5122
|
for (const targetPid of allPids) {
|
|
@@ -5113,6 +5141,29 @@ const RETRY_CONFIG = {
|
|
|
5113
5141
|
/** Maximum delay between retries in ms */
|
|
5114
5142
|
maxDelayMs: 5e3
|
|
5115
5143
|
};
|
|
5144
|
+
const DEFAULT_POST_PROMPT_NO_UPDATES_TIMEOUT_MS = 3e4;
|
|
5145
|
+
function readPositiveIntegerEnv(name) {
|
|
5146
|
+
const raw = typeof process.env[name] === "string" ? process.env[name].trim() : "";
|
|
5147
|
+
if (!raw) {
|
|
5148
|
+
return null;
|
|
5149
|
+
}
|
|
5150
|
+
const value = Number(raw);
|
|
5151
|
+
if (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
|
|
5152
|
+
return null;
|
|
5153
|
+
}
|
|
5154
|
+
return value;
|
|
5155
|
+
}
|
|
5156
|
+
function resolvePostPromptNoUpdatesTimeoutMs(transport) {
|
|
5157
|
+
const transportValue = transport.getPostPromptNoUpdatesTimeoutMs?.();
|
|
5158
|
+
if (typeof transportValue === "number" && Number.isFinite(transportValue) && transportValue > 0) {
|
|
5159
|
+
return Math.trunc(transportValue);
|
|
5160
|
+
}
|
|
5161
|
+
const envValue = readPositiveIntegerEnv("HAPPY_ACP_POST_PROMPT_NO_UPDATES_TIMEOUT_MS") ?? readPositiveIntegerEnv("HAPPIER_ACP_POST_PROMPT_NO_UPDATES_TIMEOUT_MS");
|
|
5162
|
+
if (envValue != null) {
|
|
5163
|
+
return envValue;
|
|
5164
|
+
}
|
|
5165
|
+
return DEFAULT_POST_PROMPT_NO_UPDATES_TIMEOUT_MS;
|
|
5166
|
+
}
|
|
5116
5167
|
function getSessionUpdates(params) {
|
|
5117
5168
|
const notification = params;
|
|
5118
5169
|
const updates = Array.isArray(notification.updates) ? notification.updates.filter((update) => Boolean(update && typeof update === "object")) : [];
|
|
@@ -5288,6 +5339,17 @@ function normalizeAcpError(error) {
|
|
|
5288
5339
|
}
|
|
5289
5340
|
return normalized;
|
|
5290
5341
|
}
|
|
5342
|
+
function getStatusErrorDetail(message) {
|
|
5343
|
+
if (message.type !== "status" || message.status !== "error") {
|
|
5344
|
+
return null;
|
|
5345
|
+
}
|
|
5346
|
+
const detail = typeof message.detail === "string" ? message.detail.trim() : "";
|
|
5347
|
+
return detail || "Unknown ACP transport error";
|
|
5348
|
+
}
|
|
5349
|
+
function looksLikeDroppedStdoutError(text) {
|
|
5350
|
+
const lower = text.toLowerCase();
|
|
5351
|
+
return lower.startsWith("error") || lower.includes("error:") || lower.includes("exception") || lower.includes("traceback") || lower.includes("invalid request") || lower.includes("invalid_request") || lower.includes("unauthorized") || lower.includes("forbidden") || lower.includes("permission denied") || /\b(4\d\d|5\d\d)\b/.test(lower) && (lower.includes("http") || lower.includes("status") || lower.includes("request"));
|
|
5352
|
+
}
|
|
5291
5353
|
function createAcpAbortError(message) {
|
|
5292
5354
|
const error = new Error(message);
|
|
5293
5355
|
error.name = "AbortError";
|
|
@@ -5410,6 +5472,12 @@ class AcpBackend {
|
|
|
5410
5472
|
responseCompletionOutcome = null;
|
|
5411
5473
|
/** Whether the current prompt is still waiting for completion */
|
|
5412
5474
|
waitingForResponse = false;
|
|
5475
|
+
/** First fatal prompt-level error observed for the current turn */
|
|
5476
|
+
responseCompletionError = null;
|
|
5477
|
+
/** Fallback completion when prompt returns but the agent emits no session updates */
|
|
5478
|
+
postPromptCompletionIdleTimeout = null;
|
|
5479
|
+
/** Whether at least one session/update arrived after the current prompt */
|
|
5480
|
+
sawSessionUpdateSincePrompt = false;
|
|
5413
5481
|
/** Transport handler for agent-specific behavior */
|
|
5414
5482
|
transport;
|
|
5415
5483
|
/** Keep a short rolling stderr buffer so startup failures can surface the real cause. */
|
|
@@ -5433,6 +5501,12 @@ class AcpBackend {
|
|
|
5433
5501
|
this.idleTimeout = null;
|
|
5434
5502
|
}
|
|
5435
5503
|
}
|
|
5504
|
+
clearPostPromptCompletionIdleTimeout() {
|
|
5505
|
+
if (this.postPromptCompletionIdleTimeout) {
|
|
5506
|
+
clearTimeout(this.postPromptCompletionIdleTimeout);
|
|
5507
|
+
this.postPromptCompletionIdleTimeout = null;
|
|
5508
|
+
}
|
|
5509
|
+
}
|
|
5436
5510
|
clearToolCallTracking() {
|
|
5437
5511
|
this.activeToolCalls.clear();
|
|
5438
5512
|
for (const timeout of this.toolCallTimeouts.values()) {
|
|
@@ -5446,9 +5520,26 @@ class AcpBackend {
|
|
|
5446
5520
|
}
|
|
5447
5521
|
resetResponseTrackingForNewPrompt() {
|
|
5448
5522
|
this.responseCompletionOutcome = null;
|
|
5523
|
+
this.responseCompletionError = null;
|
|
5524
|
+
this.sawSessionUpdateSincePrompt = false;
|
|
5449
5525
|
this.clearIdleTimeoutState();
|
|
5526
|
+
this.clearPostPromptCompletionIdleTimeout();
|
|
5450
5527
|
this.clearToolCallTracking();
|
|
5451
5528
|
}
|
|
5529
|
+
failPendingResponseWait(error) {
|
|
5530
|
+
if (this.responseCompletionError) {
|
|
5531
|
+
return;
|
|
5532
|
+
}
|
|
5533
|
+
this.responseCompletionError = error;
|
|
5534
|
+
this.responseCompletionOutcome = null;
|
|
5535
|
+
this.waitingForResponse = false;
|
|
5536
|
+
this.clearPostPromptCompletionIdleTimeout();
|
|
5537
|
+
if (this.idleRejecter) {
|
|
5538
|
+
this.idleRejecter(error);
|
|
5539
|
+
}
|
|
5540
|
+
this.idleResolver = null;
|
|
5541
|
+
this.idleRejecter = null;
|
|
5542
|
+
}
|
|
5452
5543
|
settleResponseWaiter(outcome) {
|
|
5453
5544
|
const hasActiveWaiter = Boolean(this.idleResolver || this.idleRejecter);
|
|
5454
5545
|
if (!this.waitingForResponse && !hasActiveWaiter) {
|
|
@@ -5484,6 +5575,38 @@ class AcpBackend {
|
|
|
5484
5575
|
}
|
|
5485
5576
|
}
|
|
5486
5577
|
}
|
|
5578
|
+
handleDroppedStdoutLine(entry) {
|
|
5579
|
+
if (entry.reason !== "transport_filter_null" || !this.waitingForResponse || this.responseCompletionError) {
|
|
5580
|
+
return;
|
|
5581
|
+
}
|
|
5582
|
+
const raw = entry.line.trim();
|
|
5583
|
+
if (!raw) {
|
|
5584
|
+
return;
|
|
5585
|
+
}
|
|
5586
|
+
const context = {
|
|
5587
|
+
activeToolCalls: this.activeToolCalls,
|
|
5588
|
+
hasActiveInvestigation: this.transport.isInvestigationTool ? Array.from(this.activeToolCalls).some((id) => this.transport.isInvestigationTool(id)) : false
|
|
5589
|
+
};
|
|
5590
|
+
const transportResult = this.transport.handleStderr?.(entry.line, context);
|
|
5591
|
+
const transportMessage = transportResult?.message ?? null;
|
|
5592
|
+
if (transportMessage) {
|
|
5593
|
+
this.emit(transportMessage);
|
|
5594
|
+
const errorDetail = getStatusErrorDetail(transportMessage);
|
|
5595
|
+
if (errorDetail) {
|
|
5596
|
+
this.failPendingResponseWait(new Error(errorDetail));
|
|
5597
|
+
}
|
|
5598
|
+
return;
|
|
5599
|
+
}
|
|
5600
|
+
if (!looksLikeDroppedStdoutError(raw)) {
|
|
5601
|
+
return;
|
|
5602
|
+
}
|
|
5603
|
+
this.emit({
|
|
5604
|
+
type: "status",
|
|
5605
|
+
status: "error",
|
|
5606
|
+
detail: raw
|
|
5607
|
+
});
|
|
5608
|
+
this.failPendingResponseWait(new Error(raw));
|
|
5609
|
+
}
|
|
5487
5610
|
async startSession(initialPrompt) {
|
|
5488
5611
|
if (this.disposed) {
|
|
5489
5612
|
throw new Error("Backend has been disposed");
|
|
@@ -5523,20 +5646,27 @@ class AcpBackend {
|
|
|
5523
5646
|
const result = this.transport.handleStderr(text, context);
|
|
5524
5647
|
if (result.message) {
|
|
5525
5648
|
this.emit(result.message);
|
|
5649
|
+
const errorDetail = getStatusErrorDetail(result.message);
|
|
5650
|
+
if (errorDetail && this.waitingForResponse) {
|
|
5651
|
+
this.failPendingResponseWait(new Error(errorDetail));
|
|
5652
|
+
}
|
|
5526
5653
|
}
|
|
5527
5654
|
}
|
|
5528
5655
|
});
|
|
5529
5656
|
this.process.on("error", (err) => {
|
|
5530
5657
|
logger.debug(`[AcpBackend] Process error:`, err);
|
|
5531
|
-
this.
|
|
5658
|
+
if (this.waitingForResponse) {
|
|
5659
|
+
this.failPendingResponseWait(err);
|
|
5660
|
+
}
|
|
5532
5661
|
this.emit({ type: "status", status: "error", detail: err.message });
|
|
5533
5662
|
});
|
|
5534
5663
|
this.process.on("exit", (code, signal) => {
|
|
5535
5664
|
if (!this.disposed && code !== 0 && code !== null) {
|
|
5536
|
-
this.
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5665
|
+
if (this.waitingForResponse) {
|
|
5666
|
+
this.failPendingResponseWait(
|
|
5667
|
+
new Error(`ACP process exited with code ${code}${signal ? ` (${signal})` : ""}`)
|
|
5668
|
+
);
|
|
5669
|
+
}
|
|
5540
5670
|
logger.debug(`[AcpBackend] Process exited with code ${code}, signal ${signal}`);
|
|
5541
5671
|
this.emit({ type: "status", status: "stopped", detail: `Exit code: ${code}` });
|
|
5542
5672
|
}
|
|
@@ -5553,14 +5683,17 @@ class AcpBackend {
|
|
|
5553
5683
|
transport: this.transport,
|
|
5554
5684
|
onDroppedLine: (entry) => {
|
|
5555
5685
|
droppedStdoutLines.push(entry);
|
|
5686
|
+
this.handleDroppedStdoutLine(entry);
|
|
5687
|
+
},
|
|
5688
|
+
onDone: () => {
|
|
5689
|
+
if (droppedStdoutLines.length > 0) {
|
|
5690
|
+
logger.debug(
|
|
5691
|
+
`[AcpBackend] Filtered out ${droppedStdoutLines.length} stdout lines from ${this.transport.agentName}`,
|
|
5692
|
+
droppedStdoutLines.slice(0, 5)
|
|
5693
|
+
);
|
|
5694
|
+
}
|
|
5556
5695
|
}
|
|
5557
5696
|
});
|
|
5558
|
-
if (droppedStdoutLines.length > 0) {
|
|
5559
|
-
logger.debug(
|
|
5560
|
-
`[AcpBackend] Filtered out ${droppedStdoutLines.length} stdout lines from ${this.transport.agentName}`,
|
|
5561
|
-
droppedStdoutLines.slice(0, 5)
|
|
5562
|
-
);
|
|
5563
|
-
}
|
|
5564
5697
|
const stream = ndJsonStream(writable, filteredReadable);
|
|
5565
5698
|
const client = {
|
|
5566
5699
|
sessionUpdate: async (params) => {
|
|
@@ -5722,6 +5855,11 @@ class AcpBackend {
|
|
|
5722
5855
|
}
|
|
5723
5856
|
};
|
|
5724
5857
|
const initTimeout = this.transport.getInitTimeout();
|
|
5858
|
+
const initDelayMs = this.transport.getInitDelayMs?.() ?? 0;
|
|
5859
|
+
if (initDelayMs > 0) {
|
|
5860
|
+
logger.debug(`[AcpBackend] Waiting ${initDelayMs}ms before initialize (${this.transport.agentName})...`);
|
|
5861
|
+
await delay(initDelayMs);
|
|
5862
|
+
}
|
|
5725
5863
|
logger.debug(`[AcpBackend] Initializing connection (timeout: ${initTimeout}ms)...`);
|
|
5726
5864
|
await withRetry(
|
|
5727
5865
|
async () => {
|
|
@@ -5827,6 +5965,16 @@ class AcpBackend {
|
|
|
5827
5965
|
return { sessionId };
|
|
5828
5966
|
} catch (error) {
|
|
5829
5967
|
const enrichedError = enrichAcpError(error, this.getRecentStderrExcerpt());
|
|
5968
|
+
if (this.process) {
|
|
5969
|
+
try {
|
|
5970
|
+
await killProcessTree(this.process, { graceMs: 250 });
|
|
5971
|
+
} catch {
|
|
5972
|
+
} finally {
|
|
5973
|
+
this.process = null;
|
|
5974
|
+
}
|
|
5975
|
+
}
|
|
5976
|
+
this.connection = null;
|
|
5977
|
+
this.acpSessionId = null;
|
|
5830
5978
|
logger.debug("[AcpBackend] Error starting session:", enrichedError);
|
|
5831
5979
|
this.emit({
|
|
5832
5980
|
type: "status",
|
|
@@ -5887,6 +6035,8 @@ class AcpBackend {
|
|
|
5887
6035
|
logger.debug("[AcpBackend] Received session update without update field:", params);
|
|
5888
6036
|
return;
|
|
5889
6037
|
}
|
|
6038
|
+
this.sawSessionUpdateSincePrompt = true;
|
|
6039
|
+
this.clearPostPromptCompletionIdleTimeout();
|
|
5890
6040
|
for (const update of updates) {
|
|
5891
6041
|
const sessionUpdateType = update.sessionUpdate;
|
|
5892
6042
|
if (sessionUpdateType !== "agent_message_chunk") {
|
|
@@ -5966,6 +6116,29 @@ class AcpBackend {
|
|
|
5966
6116
|
logger.debug(`[AcpBackend] Prompt request:`, JSON.stringify(promptRequest, null, 2));
|
|
5967
6117
|
await this.connection.prompt(promptRequest);
|
|
5968
6118
|
logger.debug("[AcpBackend] Prompt request sent to ACP connection");
|
|
6119
|
+
if (this.waitingForResponse && this.activeToolCalls.size === 0 && this.sawSessionUpdateSincePrompt === false) {
|
|
6120
|
+
const noUpdatesTimeoutMs = resolvePostPromptNoUpdatesTimeoutMs(this.transport);
|
|
6121
|
+
this.postPromptCompletionIdleTimeout = setTimeout(() => {
|
|
6122
|
+
this.postPromptCompletionIdleTimeout = null;
|
|
6123
|
+
if (this.responseCompletionError || !this.waitingForResponse) {
|
|
6124
|
+
return;
|
|
6125
|
+
}
|
|
6126
|
+
if (this.sawSessionUpdateSincePrompt || this.activeToolCalls.size > 0) {
|
|
6127
|
+
return;
|
|
6128
|
+
}
|
|
6129
|
+
const exitCode = this.process?.exitCode;
|
|
6130
|
+
if (typeof exitCode === "number" && Number.isFinite(exitCode) && exitCode !== 0) {
|
|
6131
|
+
this.failPendingResponseWait(new Error(`Exit code: ${exitCode}`));
|
|
6132
|
+
return;
|
|
6133
|
+
}
|
|
6134
|
+
const signalCode = this.process?.signalCode;
|
|
6135
|
+
if (typeof signalCode === "string" && signalCode.trim().length > 0) {
|
|
6136
|
+
this.failPendingResponseWait(new Error(`Signal: ${signalCode}`));
|
|
6137
|
+
return;
|
|
6138
|
+
}
|
|
6139
|
+
this.emitIdleStatus();
|
|
6140
|
+
}, Math.max(100, noUpdatesTimeoutMs));
|
|
6141
|
+
}
|
|
5969
6142
|
} catch (error) {
|
|
5970
6143
|
logger.debug("[AcpBackend] Error sending prompt:", error);
|
|
5971
6144
|
let errorDetail;
|
|
@@ -5981,10 +6154,7 @@ class AcpBackend {
|
|
|
5981
6154
|
status: "error",
|
|
5982
6155
|
detail: errorDetail
|
|
5983
6156
|
});
|
|
5984
|
-
this.
|
|
5985
|
-
kind: "rejected",
|
|
5986
|
-
error: error instanceof Error ? error : normalizeAcpError(error)
|
|
5987
|
-
});
|
|
6157
|
+
this.failPendingResponseWait(error instanceof Error ? error : normalizeAcpError(error));
|
|
5988
6158
|
throw error;
|
|
5989
6159
|
}
|
|
5990
6160
|
}
|
|
@@ -5993,6 +6163,9 @@ class AcpBackend {
|
|
|
5993
6163
|
* Call this after sendPrompt to wait for Gemini to finish responding
|
|
5994
6164
|
*/
|
|
5995
6165
|
async waitForResponseComplete(timeoutMs = 12e4) {
|
|
6166
|
+
if (this.responseCompletionError) {
|
|
6167
|
+
throw this.responseCompletionError;
|
|
6168
|
+
}
|
|
5996
6169
|
const pendingOutcome = this.responseCompletionOutcome;
|
|
5997
6170
|
if (pendingOutcome) {
|
|
5998
6171
|
this.responseCompletionOutcome = null;
|
|
@@ -6031,12 +6204,14 @@ class AcpBackend {
|
|
|
6031
6204
|
* Helper to emit idle status and resolve any waiting promises
|
|
6032
6205
|
*/
|
|
6033
6206
|
emitIdleStatus() {
|
|
6207
|
+
this.clearPostPromptCompletionIdleTimeout();
|
|
6034
6208
|
this.emit({ type: "status", status: "idle" });
|
|
6035
6209
|
this.settleResponseWaiter({ kind: "resolved" });
|
|
6036
6210
|
}
|
|
6037
6211
|
async cancel(sessionId) {
|
|
6038
6212
|
const cancelError = createAcpAbortError("Cancelled by user");
|
|
6039
6213
|
this.clearIdleTimeoutState();
|
|
6214
|
+
this.clearPostPromptCompletionIdleTimeout();
|
|
6040
6215
|
this.clearToolCallTracking();
|
|
6041
6216
|
this.settleResponseWaiter({ kind: "rejected", error: cancelError });
|
|
6042
6217
|
this.emit({ type: "status", status: "stopped", detail: "Cancelled by user" });
|
|
@@ -6097,6 +6272,7 @@ class AcpBackend {
|
|
|
6097
6272
|
}
|
|
6098
6273
|
}
|
|
6099
6274
|
this.clearIdleTimeoutState();
|
|
6275
|
+
this.clearPostPromptCompletionIdleTimeout();
|
|
6100
6276
|
this.listeners = [];
|
|
6101
6277
|
this.connection = null;
|
|
6102
6278
|
this.acpSessionId = null;
|
|
@@ -6745,12 +6921,12 @@ async function ensureUnifiedDaemonStarted() {
|
|
|
6745
6921
|
async function executeUnifiedProvider(opts) {
|
|
6746
6922
|
const credentials = await ensureUnifiedRuntimePrerequisites(opts.credentials);
|
|
6747
6923
|
if (opts.provider === "claude") {
|
|
6748
|
-
const { runClaude } = await import('./runClaude-
|
|
6924
|
+
const { runClaude } = await import('./runClaude-8inO7C5p.mjs');
|
|
6749
6925
|
await runClaude(credentials, opts.claudeOptions ?? {});
|
|
6750
6926
|
return;
|
|
6751
6927
|
}
|
|
6752
6928
|
if (opts.provider === "codex") {
|
|
6753
|
-
const { runCodex } = await import('./runCodex-
|
|
6929
|
+
const { runCodex } = await import('./runCodex-BQ-fN5E6.mjs');
|
|
6754
6930
|
await runCodex({
|
|
6755
6931
|
credentials,
|
|
6756
6932
|
startedBy: opts.startedBy,
|
|
@@ -6760,7 +6936,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
6760
6936
|
return;
|
|
6761
6937
|
}
|
|
6762
6938
|
if (opts.provider === "gemini") {
|
|
6763
|
-
const { runGemini } = await import('./runGemini-
|
|
6939
|
+
const { runGemini } = await import('./runGemini-BE0FizuV.mjs');
|
|
6764
6940
|
await runGemini({
|
|
6765
6941
|
credentials,
|
|
6766
6942
|
startedBy: opts.startedBy
|
|
@@ -6802,7 +6978,7 @@ function shouldRunMainClaudeFlow(opts) {
|
|
|
6802
6978
|
return;
|
|
6803
6979
|
} else if (subcommand === "runtime") {
|
|
6804
6980
|
if (args[1] === "providers") {
|
|
6805
|
-
const { renderRuntimeProviders } = await import('./command-
|
|
6981
|
+
const { renderRuntimeProviders } = await import('./command-BGA3qCKR.mjs');
|
|
6806
6982
|
console.log(renderRuntimeProviders());
|
|
6807
6983
|
return;
|
|
6808
6984
|
}
|
|
@@ -6980,8 +7156,8 @@ function shouldRunMainClaudeFlow(opts) {
|
|
|
6980
7156
|
const projectId = args[3];
|
|
6981
7157
|
try {
|
|
6982
7158
|
const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return config; });
|
|
6983
|
-
const { readCredentials: readCredentials2 } = await import('./persistence-
|
|
6984
|
-
const { ApiClient: ApiClient2 } = await import('./api-
|
|
7159
|
+
const { readCredentials: readCredentials2 } = await import('./persistence-BPV3AmJL.mjs');
|
|
7160
|
+
const { ApiClient: ApiClient2 } = await import('./api-CnvyGas2.mjs').then(function (n) { return n.q; });
|
|
6985
7161
|
let userEmail = void 0;
|
|
6986
7162
|
try {
|
|
6987
7163
|
const credentials = await readCredentials2();
|