happy-imou-cloud 2.0.13 → 2.0.16
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/dist/BaseReasoningProcessor-BqMAZlIw.cjs +323 -0
- package/dist/BaseReasoningProcessor-C04_LHjN.mjs +320 -0
- package/dist/ProviderSelectionHandler-CO9NkAt6.cjs +265 -0
- package/dist/ProviderSelectionHandler-DDWyn9Lo.mjs +261 -0
- package/dist/{api-DH5-IqeM.cjs → api-Db1SQcP_.cjs} +2 -2
- package/dist/{api-D1meoL-9.mjs → api-sRF6xXi-.mjs} +2 -2
- package/dist/{command-CMvWClny.mjs → command-WcgGTRnG.mjs} +4 -3
- package/dist/{command-Ch8Dgidj.cjs → command-eRjSBm2C.cjs} +4 -3
- package/dist/{index-CryJfCh5.cjs → index-B6ID1zDR.cjs} +1106 -50
- package/dist/{index-Cxrx9m5D.mjs → index-DpWeKfvS.mjs} +1102 -50
- package/dist/index.cjs +5 -4
- package/dist/index.mjs +5 -4
- package/dist/lib.cjs +1 -1
- package/dist/lib.mjs +1 -1
- package/dist/{persistence-9Iu0wGNM.mjs → persistence-B89V4xY5.mjs} +1 -1
- package/dist/{persistence-Bl3FYvwd.cjs → persistence-Btu2VPXI.cjs} +1 -1
- package/dist/{registerKillSessionHandler-BElGmD1E.mjs → registerKillSessionHandler-CwmYlUfS.mjs} +541 -5
- package/dist/{registerKillSessionHandler-BjkY-oUn.cjs → registerKillSessionHandler-eZ2TsHqx.cjs} +549 -4
- package/dist/{runClaude-CDZxAF3l.cjs → runClaude-C9-ylbQh.cjs} +599 -747
- package/dist/{runClaude-D7dF4RDM.mjs → runClaude-kRPXCaBj.mjs} +591 -738
- package/dist/{runCodex-DnGz1XES.mjs → runCodex-B1xN0wAU.mjs} +9 -215
- package/dist/{runCodex-Cik8VzFs.cjs → runCodex-CRNBxY5f.cjs} +20 -226
- package/dist/{runGemini-BM2BQ4I7.cjs → runGemini-BZ5hqJyl.cjs} +16 -15
- package/dist/{runGemini-B8tXMHeL.mjs → runGemini-Xn2VwS88.mjs} +8 -7
- package/package.json +1 -1
- package/scripts/release-smoke.mjs +6 -5
- package/dist/ConversationHistory-V3VLmjJf.cjs +0 -868
- package/dist/ConversationHistory-_ciJNIgH.mjs +0 -856
- package/dist/createKeepAliveController-C5cQlDRr.mjs +0 -51
- package/dist/createKeepAliveController-DO8H6d5E.cjs +0 -54
package/dist/index.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
require('chalk');
|
|
4
|
-
require('./api-
|
|
5
|
-
require('./persistence-
|
|
4
|
+
require('./api-Db1SQcP_.cjs');
|
|
5
|
+
require('./persistence-Btu2VPXI.cjs');
|
|
6
6
|
require('zod');
|
|
7
|
-
require('./index-
|
|
7
|
+
require('./index-B6ID1zDR.cjs');
|
|
8
8
|
require('node:child_process');
|
|
9
9
|
require('node:fs');
|
|
10
10
|
require('cross-spawn');
|
|
@@ -17,6 +17,7 @@ require('os');
|
|
|
17
17
|
require('child_process');
|
|
18
18
|
require('node:path');
|
|
19
19
|
require('node:os');
|
|
20
|
+
require('node:readline');
|
|
20
21
|
require('node:fs/promises');
|
|
21
22
|
require('fs/promises');
|
|
22
23
|
require('crypto');
|
|
@@ -34,7 +35,7 @@ require('ink');
|
|
|
34
35
|
require('url');
|
|
35
36
|
require('fastify');
|
|
36
37
|
require('fastify-type-provider-zod');
|
|
37
|
-
require('node:readline');
|
|
38
38
|
require('http');
|
|
39
39
|
require('util');
|
|
40
|
+
require('node:url');
|
|
40
41
|
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import 'chalk';
|
|
2
|
-
import './api-
|
|
3
|
-
import './persistence-
|
|
2
|
+
import './api-sRF6xXi-.mjs';
|
|
3
|
+
import './persistence-B89V4xY5.mjs';
|
|
4
4
|
import 'zod';
|
|
5
|
-
import './index-
|
|
5
|
+
import './index-DpWeKfvS.mjs';
|
|
6
6
|
import 'node:child_process';
|
|
7
7
|
import 'node:fs';
|
|
8
8
|
import 'cross-spawn';
|
|
@@ -15,6 +15,7 @@ import 'os';
|
|
|
15
15
|
import 'child_process';
|
|
16
16
|
import 'node:path';
|
|
17
17
|
import 'node:os';
|
|
18
|
+
import 'node:readline';
|
|
18
19
|
import 'node:fs/promises';
|
|
19
20
|
import 'fs/promises';
|
|
20
21
|
import 'crypto';
|
|
@@ -32,6 +33,6 @@ import 'ink';
|
|
|
32
33
|
import 'url';
|
|
33
34
|
import 'fastify';
|
|
34
35
|
import 'fastify-type-provider-zod';
|
|
35
|
-
import 'node:readline';
|
|
36
36
|
import 'http';
|
|
37
37
|
import 'util';
|
|
38
|
+
import 'node:url';
|
package/dist/lib.cjs
CHANGED
package/dist/lib.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as ApiClient, a as ApiSessionClient, c as configuration, l as logger } from './api-
|
|
1
|
+
export { A as ApiClient, a as ApiSessionClient, c as configuration, l as logger } from './api-sRF6xXi-.mjs';
|
|
2
2
|
export { R as RawJSONLinesSchema } from './types-CiliQpqS.mjs';
|
|
3
3
|
import 'axios';
|
|
4
4
|
import 'chalk';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { unlink, readFile, mkdir, open, stat, writeFile, rename } from 'node:fs/promises';
|
|
2
2
|
import { existsSync, unlinkSync, readdirSync, constants, writeFileSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { join, dirname } from 'node:path';
|
|
4
|
-
import { c as configuration, l as logger, e as encodeBase64 } from './api-
|
|
4
|
+
import { c as configuration, l as logger, e as encodeBase64 } from './api-sRF6xXi-.mjs';
|
|
5
5
|
import * as z from 'zod';
|
|
6
6
|
import 'axios';
|
|
7
7
|
import 'chalk';
|
package/dist/{registerKillSessionHandler-BElGmD1E.mjs → registerKillSessionHandler-CwmYlUfS.mjs}
RENAMED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { i as initialMachineMetadata,
|
|
2
|
-
import { readSettings } from './persistence-
|
|
1
|
+
import { i as initialMachineMetadata, e as projectPath, R as RuntimeShell, h as resolveCanonicalToolNameV2, f as formatDisplayMessage } from './index-DpWeKfvS.mjs';
|
|
2
|
+
import { readSettings } from './persistence-B89V4xY5.mjs';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import { resolve } from 'node:path';
|
|
5
|
-
import { c as configuration, p as packageJson, l as logger } from './api-
|
|
5
|
+
import { c as configuration, p as packageJson, l as logger } from './api-sRF6xXi-.mjs';
|
|
6
|
+
import { randomUUID } from 'node:crypto';
|
|
6
7
|
import { createHash } from 'crypto';
|
|
7
8
|
import 'axios';
|
|
8
9
|
import 'node:events';
|
|
9
10
|
import 'socket.io-client';
|
|
10
|
-
import 'node:crypto';
|
|
11
11
|
import 'tweetnacl';
|
|
12
12
|
import 'fs/promises';
|
|
13
13
|
import 'path';
|
|
@@ -59,6 +59,199 @@ function createSessionMetadata(opts) {
|
|
|
59
59
|
return { state, metadata };
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
async function launchRuntimeHandleWithFactoryResult(opts) {
|
|
63
|
+
const shell = opts.shell ?? new RuntimeShell();
|
|
64
|
+
let factoryResult;
|
|
65
|
+
const session = await shell.launch({
|
|
66
|
+
provider: opts.provider,
|
|
67
|
+
cwd: opts.cwd,
|
|
68
|
+
env: opts.env,
|
|
69
|
+
createBackend: (factoryOpts) => {
|
|
70
|
+
factoryResult = opts.createBackendResult(factoryOpts);
|
|
71
|
+
return factoryResult.backend;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
if (factoryResult === void 0) {
|
|
75
|
+
throw new Error(`Runtime provider "${opts.provider}" did not create a backend result`);
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
session,
|
|
79
|
+
factoryResult
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function inferToolResultError(result) {
|
|
84
|
+
if (!result || typeof result !== "object") {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const record = result;
|
|
88
|
+
if (record.isError === true || record.is_error === true) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
if (typeof record.error === "string" && record.error.trim().length > 0) {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
if (record.success === false) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
const status = typeof record.status === "string" ? record.status.toLowerCase() : "";
|
|
98
|
+
return status === "failed" || status === "cancelled" || status === "error" || status === "denied" || status === "aborted";
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function buildToolHappierMetaV2(input) {
|
|
102
|
+
return {
|
|
103
|
+
...input,
|
|
104
|
+
v: 2
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function attachToolHappierMetaV2(value, meta) {
|
|
108
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
...value,
|
|
113
|
+
_happier: buildToolHappierMetaV2(meta)
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function getDefaultExecToolName(provider) {
|
|
118
|
+
switch (provider) {
|
|
119
|
+
case "claude":
|
|
120
|
+
return "ClaudeBash";
|
|
121
|
+
case "codex":
|
|
122
|
+
return "CodexBash";
|
|
123
|
+
case "gemini":
|
|
124
|
+
return "GeminiBash";
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function getDefaultPatchToolName(provider) {
|
|
128
|
+
switch (provider) {
|
|
129
|
+
case "claude":
|
|
130
|
+
return "ClaudePatch";
|
|
131
|
+
case "codex":
|
|
132
|
+
return "CodexPatch";
|
|
133
|
+
case "gemini":
|
|
134
|
+
return "GeminiPatch";
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function attachToolMeta(provider, rawToolName, value) {
|
|
138
|
+
const canonicalToolName = resolveCanonicalToolNameV2(rawToolName);
|
|
139
|
+
return attachToolHappierMetaV2(value, {
|
|
140
|
+
v: 2,
|
|
141
|
+
protocol: "acp",
|
|
142
|
+
provider,
|
|
143
|
+
rawToolName,
|
|
144
|
+
canonicalToolName
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
function forwardAgentMessageToProviderSession(msg, options) {
|
|
148
|
+
const createId = options.createId ?? randomUUID;
|
|
149
|
+
const toolResultType = options.toolResultType ?? "tool-result";
|
|
150
|
+
switch (msg.type) {
|
|
151
|
+
case "tool-call": {
|
|
152
|
+
options.send({
|
|
153
|
+
type: "tool-call",
|
|
154
|
+
name: resolveCanonicalToolNameV2(msg.toolName),
|
|
155
|
+
callId: msg.callId,
|
|
156
|
+
input: attachToolMeta(options.provider, msg.toolName, msg.args),
|
|
157
|
+
id: createId()
|
|
158
|
+
});
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
case "tool-result": {
|
|
162
|
+
options.send({
|
|
163
|
+
type: toolResultType,
|
|
164
|
+
callId: msg.callId,
|
|
165
|
+
output: attachToolMeta(options.provider, msg.toolName, msg.result),
|
|
166
|
+
id: createId(),
|
|
167
|
+
isError: inferToolResultError(msg.result)
|
|
168
|
+
});
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
case "fs-edit": {
|
|
172
|
+
options.send({
|
|
173
|
+
type: "file-edit",
|
|
174
|
+
description: msg.description,
|
|
175
|
+
diff: msg.diff,
|
|
176
|
+
filePath: msg.path || "unknown",
|
|
177
|
+
id: createId()
|
|
178
|
+
});
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
case "terminal-output": {
|
|
182
|
+
options.send({
|
|
183
|
+
type: "terminal-output",
|
|
184
|
+
data: msg.data,
|
|
185
|
+
callId: msg.callId ?? createId()
|
|
186
|
+
});
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
case "permission-request": {
|
|
190
|
+
const payload = msg.payload && typeof msg.payload === "object" ? msg.payload : {};
|
|
191
|
+
options.send({
|
|
192
|
+
type: "permission-request",
|
|
193
|
+
permissionId: msg.id,
|
|
194
|
+
toolName: typeof payload.toolName === "string" ? payload.toolName : msg.reason || "unknown",
|
|
195
|
+
description: msg.reason || (typeof payload.toolName === "string" ? payload.toolName : ""),
|
|
196
|
+
options: payload
|
|
197
|
+
});
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
case "exec-approval-request": {
|
|
201
|
+
const rawToolName = options.execToolName ?? getDefaultExecToolName(options.provider);
|
|
202
|
+
const { call_id, type: _type, ...inputs } = msg;
|
|
203
|
+
options.send({
|
|
204
|
+
type: "tool-call",
|
|
205
|
+
name: resolveCanonicalToolNameV2(rawToolName),
|
|
206
|
+
callId: call_id,
|
|
207
|
+
input: attachToolMeta(options.provider, rawToolName, inputs),
|
|
208
|
+
id: createId()
|
|
209
|
+
});
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
case "patch-apply-begin": {
|
|
213
|
+
const rawToolName = options.patchToolName ?? getDefaultPatchToolName(options.provider);
|
|
214
|
+
options.send({
|
|
215
|
+
type: "tool-call",
|
|
216
|
+
name: resolveCanonicalToolNameV2(rawToolName),
|
|
217
|
+
callId: msg.call_id,
|
|
218
|
+
input: attachToolMeta(options.provider, rawToolName, {
|
|
219
|
+
auto_approved: msg.auto_approved,
|
|
220
|
+
changes: msg.changes
|
|
221
|
+
}),
|
|
222
|
+
id: createId()
|
|
223
|
+
});
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
case "patch-apply-end": {
|
|
227
|
+
const rawToolName = options.patchToolName ?? getDefaultPatchToolName(options.provider);
|
|
228
|
+
options.send({
|
|
229
|
+
type: toolResultType,
|
|
230
|
+
callId: msg.call_id,
|
|
231
|
+
output: attachToolMeta(options.provider, rawToolName, {
|
|
232
|
+
stdout: msg.stdout,
|
|
233
|
+
stderr: msg.stderr,
|
|
234
|
+
success: msg.success
|
|
235
|
+
}),
|
|
236
|
+
id: createId(),
|
|
237
|
+
isError: !msg.success
|
|
238
|
+
});
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
case "token-count": {
|
|
242
|
+
const { type: _type, ...payload } = msg;
|
|
243
|
+
options.send({
|
|
244
|
+
type: "token_count",
|
|
245
|
+
...payload,
|
|
246
|
+
id: createId()
|
|
247
|
+
});
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
default:
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
62
255
|
async function closeProviderSession(session, opts = {}) {
|
|
63
256
|
let firstError;
|
|
64
257
|
const captureError = (error) => {
|
|
@@ -99,6 +292,27 @@ async function closeProviderSession(session, opts = {}) {
|
|
|
99
292
|
}
|
|
100
293
|
}
|
|
101
294
|
|
|
295
|
+
function createAbortError() {
|
|
296
|
+
const error = new Error("Operation aborted");
|
|
297
|
+
error.name = "AbortError";
|
|
298
|
+
return error;
|
|
299
|
+
}
|
|
300
|
+
async function waitForResponseCompleteWithAbort(backend, signal, timeoutMs = 12e4) {
|
|
301
|
+
if (!backend.waitForResponseComplete) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (signal.aborted) {
|
|
305
|
+
throw createAbortError();
|
|
306
|
+
}
|
|
307
|
+
await new Promise((resolve, reject) => {
|
|
308
|
+
const onAbort = () => reject(createAbortError());
|
|
309
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
310
|
+
backend.waitForResponseComplete(timeoutMs).then(resolve).catch(reject).finally(() => {
|
|
311
|
+
signal.removeEventListener("abort", onAbort);
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
102
316
|
function supportsAgentStateUpdateEvents(sessionClient) {
|
|
103
317
|
return typeof sessionClient.once === "function" && typeof sessionClient.off === "function";
|
|
104
318
|
}
|
|
@@ -294,6 +508,328 @@ class MessageBuffer {
|
|
|
294
508
|
}
|
|
295
509
|
}
|
|
296
510
|
|
|
511
|
+
let ConversationHistory$1 = class ConversationHistory {
|
|
512
|
+
messages = [];
|
|
513
|
+
maxMessages;
|
|
514
|
+
maxCharacters;
|
|
515
|
+
constructor(options = {}) {
|
|
516
|
+
this.maxMessages = options.maxMessages ?? 20;
|
|
517
|
+
this.maxCharacters = options.maxCharacters ?? 5e4;
|
|
518
|
+
}
|
|
519
|
+
isDuplicate(role, content) {
|
|
520
|
+
if (this.messages.length === 0) {
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
for (let index = this.messages.length - 1; index >= 0; index -= 1) {
|
|
524
|
+
const message = this.messages[index];
|
|
525
|
+
if (message.role !== role) {
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
const normalizedIncoming = content.trim().replace(/\s+/g, " ");
|
|
529
|
+
const normalizedExisting = message.content.replace(/\s+/g, " ");
|
|
530
|
+
return normalizedIncoming === normalizedExisting;
|
|
531
|
+
}
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
addUserMessage(content) {
|
|
535
|
+
this.addMessage("user", content);
|
|
536
|
+
}
|
|
537
|
+
addAssistantMessage(content) {
|
|
538
|
+
this.addMessage("assistant", content);
|
|
539
|
+
}
|
|
540
|
+
hasHistory() {
|
|
541
|
+
return this.messages.length > 0;
|
|
542
|
+
}
|
|
543
|
+
size() {
|
|
544
|
+
return this.messages.length;
|
|
545
|
+
}
|
|
546
|
+
clear() {
|
|
547
|
+
this.messages = [];
|
|
548
|
+
logger.debug("[ConversationHistory] History cleared");
|
|
549
|
+
}
|
|
550
|
+
getContextForNewSession(prefixMessage = "Continue from the prior session using the conversation below as context.") {
|
|
551
|
+
if (this.messages.length === 0) {
|
|
552
|
+
return "";
|
|
553
|
+
}
|
|
554
|
+
const formattedMessages = this.messages.map((message) => {
|
|
555
|
+
const role = message.role === "user" ? "User" : "Assistant";
|
|
556
|
+
const content = message.content.length > 2e3 ? `${message.content.slice(0, 2e3)}... [truncated]` : message.content;
|
|
557
|
+
return `${role}: ${content}`;
|
|
558
|
+
}).join("\n\n");
|
|
559
|
+
return [
|
|
560
|
+
"[PREVIOUS CONVERSATION CONTEXT]",
|
|
561
|
+
prefixMessage,
|
|
562
|
+
"",
|
|
563
|
+
formattedMessages,
|
|
564
|
+
"",
|
|
565
|
+
"[END OF PREVIOUS CONTEXT]",
|
|
566
|
+
""
|
|
567
|
+
].join("\n");
|
|
568
|
+
}
|
|
569
|
+
getSummary() {
|
|
570
|
+
const totalChars = this.messages.reduce((sum, message) => sum + message.content.length, 0);
|
|
571
|
+
const userCount = this.messages.filter((message) => message.role === "user").length;
|
|
572
|
+
const assistantCount = this.messages.filter((message) => message.role === "assistant").length;
|
|
573
|
+
return `${this.messages.length} messages (${userCount} user, ${assistantCount} assistant), ${totalChars} chars`;
|
|
574
|
+
}
|
|
575
|
+
addMessage(role, content) {
|
|
576
|
+
const trimmedContent = content.trim();
|
|
577
|
+
if (!trimmedContent) {
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
if (this.isDuplicate(role, trimmedContent)) {
|
|
581
|
+
logger.debug(`[ConversationHistory] Skipping duplicate ${role} message (${trimmedContent.length} chars)`);
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
this.messages.push({
|
|
585
|
+
role,
|
|
586
|
+
content: trimmedContent,
|
|
587
|
+
timestamp: Date.now()
|
|
588
|
+
});
|
|
589
|
+
this.trimHistory();
|
|
590
|
+
logger.debug(`[ConversationHistory] Added ${role} message (${trimmedContent.length} chars), total: ${this.messages.length}`);
|
|
591
|
+
}
|
|
592
|
+
trimHistory() {
|
|
593
|
+
while (this.messages.length > this.maxMessages) {
|
|
594
|
+
this.messages.shift();
|
|
595
|
+
}
|
|
596
|
+
let totalChars = this.messages.reduce((sum, message) => sum + message.content.length, 0);
|
|
597
|
+
while (totalChars > this.maxCharacters && this.messages.length > 1) {
|
|
598
|
+
const removed = this.messages.shift();
|
|
599
|
+
if (removed) {
|
|
600
|
+
totalChars -= removed.content.length;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
const INTERACTION_SUPERSEDED_ERROR = "Interaction superseded by new user message";
|
|
607
|
+
const INTERACTION_TIMED_OUT_ERROR = "Interaction timed out waiting for user response";
|
|
608
|
+
const DEFAULT_INTERACTION_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
609
|
+
function getPendingInteractionTimeoutMs() {
|
|
610
|
+
const raw = Number(process.env.HAPPY_INTERACTION_TIMEOUT_MS);
|
|
611
|
+
if (Number.isFinite(raw) && raw > 0) {
|
|
612
|
+
return raw;
|
|
613
|
+
}
|
|
614
|
+
return DEFAULT_INTERACTION_TIMEOUT_MS;
|
|
615
|
+
}
|
|
616
|
+
class BasePermissionHandler {
|
|
617
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
618
|
+
session;
|
|
619
|
+
isResetting = false;
|
|
620
|
+
constructor(session) {
|
|
621
|
+
this.session = session;
|
|
622
|
+
this.setupRpcHandler();
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Update the session reference (used after offline reconnection swaps sessions).
|
|
626
|
+
* This is critical for avoiding stale session references after onSessionSwap.
|
|
627
|
+
*/
|
|
628
|
+
updateSession(newSession) {
|
|
629
|
+
logger.debug(`${this.getLogPrefix()} Session reference updated`);
|
|
630
|
+
this.session = newSession;
|
|
631
|
+
this.setupRpcHandler();
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Setup RPC handler for permission responses.
|
|
635
|
+
*/
|
|
636
|
+
setupRpcHandler() {
|
|
637
|
+
this.session.rpcHandlerManager.registerHandler(
|
|
638
|
+
"permission",
|
|
639
|
+
async (response) => {
|
|
640
|
+
const pending = this.pendingRequests.get(response.id);
|
|
641
|
+
if (!pending) {
|
|
642
|
+
logger.debug(`${this.getLogPrefix()} Permission request not found or already resolved`);
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
this.pendingRequests.delete(response.id);
|
|
646
|
+
this.clearPendingRequestTimeout(pending);
|
|
647
|
+
const result = response.approved ? { decision: response.decision === "approved_for_session" ? "approved_for_session" : "approved" } : { decision: response.decision === "denied" ? "denied" : "abort" };
|
|
648
|
+
pending.resolve(result);
|
|
649
|
+
this.session.updateAgentState((currentState) => {
|
|
650
|
+
const request = currentState.requests?.[response.id];
|
|
651
|
+
if (!request) return currentState;
|
|
652
|
+
const { [response.id]: _, ...remainingRequests } = currentState.requests || {};
|
|
653
|
+
let res = {
|
|
654
|
+
...currentState,
|
|
655
|
+
requests: remainingRequests,
|
|
656
|
+
completedRequests: {
|
|
657
|
+
...currentState.completedRequests,
|
|
658
|
+
[response.id]: {
|
|
659
|
+
...request,
|
|
660
|
+
completedAt: Date.now(),
|
|
661
|
+
status: response.approved ? "approved" : "denied",
|
|
662
|
+
decision: result.decision
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
return res;
|
|
667
|
+
});
|
|
668
|
+
logger.debug(`${this.getLogPrefix()} Permission ${response.approved ? "approved" : "denied"} for ${pending.toolName}`);
|
|
669
|
+
}
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Add a pending request to the agent state.
|
|
674
|
+
*/
|
|
675
|
+
addPendingRequestToState(toolCallId, toolName, input) {
|
|
676
|
+
this.session.updateAgentState((currentState) => ({
|
|
677
|
+
...currentState,
|
|
678
|
+
requests: {
|
|
679
|
+
...currentState.requests,
|
|
680
|
+
[toolCallId]: {
|
|
681
|
+
tool: toolName,
|
|
682
|
+
arguments: input,
|
|
683
|
+
createdAt: Date.now()
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}));
|
|
687
|
+
}
|
|
688
|
+
registerPendingRequest(toolCallId, toolName, input, logSuffix = "") {
|
|
689
|
+
return new Promise((resolve, reject) => {
|
|
690
|
+
const pending = {
|
|
691
|
+
resolve,
|
|
692
|
+
reject,
|
|
693
|
+
toolName,
|
|
694
|
+
input
|
|
695
|
+
};
|
|
696
|
+
pending.timeoutHandle = setTimeout(() => {
|
|
697
|
+
this.handlePendingRequestTimeout(toolCallId, pending);
|
|
698
|
+
}, getPendingInteractionTimeoutMs());
|
|
699
|
+
this.pendingRequests.set(toolCallId, pending);
|
|
700
|
+
this.addPendingRequestToState(toolCallId, toolName, input);
|
|
701
|
+
logger.debug(`${this.getLogPrefix()} Permission request sent for tool: ${toolName} (${toolCallId})${logSuffix}`);
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
hasPendingRequests() {
|
|
705
|
+
return this.pendingRequests.size > 0;
|
|
706
|
+
}
|
|
707
|
+
supersedePendingRequests(reason = INTERACTION_SUPERSEDED_ERROR) {
|
|
708
|
+
const pendingSnapshot = Array.from(this.pendingRequests.entries());
|
|
709
|
+
if (pendingSnapshot.length === 0) {
|
|
710
|
+
return 0;
|
|
711
|
+
}
|
|
712
|
+
this.pendingRequests.clear();
|
|
713
|
+
const completedAt = Date.now();
|
|
714
|
+
for (const [, pending] of pendingSnapshot) {
|
|
715
|
+
this.clearPendingRequestTimeout(pending);
|
|
716
|
+
pending.resolve({ decision: "abort" });
|
|
717
|
+
}
|
|
718
|
+
this.session.updateAgentState((currentState) => {
|
|
719
|
+
const requests = { ...currentState.requests || {} };
|
|
720
|
+
const completedRequests = { ...currentState.completedRequests || {} };
|
|
721
|
+
for (const [id, request] of Object.entries(requests)) {
|
|
722
|
+
if (request.requestKind === "selection") {
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
completedRequests[id] = {
|
|
726
|
+
...request,
|
|
727
|
+
completedAt,
|
|
728
|
+
status: "denied",
|
|
729
|
+
reason,
|
|
730
|
+
decision: "abort",
|
|
731
|
+
requestKind: request.requestKind || "permission"
|
|
732
|
+
};
|
|
733
|
+
delete requests[id];
|
|
734
|
+
}
|
|
735
|
+
return {
|
|
736
|
+
...currentState,
|
|
737
|
+
requests,
|
|
738
|
+
completedRequests
|
|
739
|
+
};
|
|
740
|
+
});
|
|
741
|
+
logger.debug(`${this.getLogPrefix()} Superseded ${pendingSnapshot.length} pending permission request(s)`);
|
|
742
|
+
return pendingSnapshot.length;
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Reset state for new sessions.
|
|
746
|
+
* This method is idempotent - safe to call multiple times.
|
|
747
|
+
*/
|
|
748
|
+
reset() {
|
|
749
|
+
if (this.isResetting) {
|
|
750
|
+
logger.debug(`${this.getLogPrefix()} Reset already in progress, skipping`);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
this.isResetting = true;
|
|
754
|
+
try {
|
|
755
|
+
const pendingSnapshot = Array.from(this.pendingRequests.entries());
|
|
756
|
+
this.pendingRequests.clear();
|
|
757
|
+
for (const [id, pending] of pendingSnapshot) {
|
|
758
|
+
try {
|
|
759
|
+
this.clearPendingRequestTimeout(pending);
|
|
760
|
+
pending.reject(new Error("Session reset"));
|
|
761
|
+
} catch (err) {
|
|
762
|
+
logger.debug(`${this.getLogPrefix()} Error rejecting pending request ${id}:`, err);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
this.session.updateAgentState((currentState) => {
|
|
766
|
+
const pendingRequests = currentState.requests || {};
|
|
767
|
+
const completedRequests = { ...currentState.completedRequests };
|
|
768
|
+
for (const [id, request] of Object.entries(pendingRequests)) {
|
|
769
|
+
completedRequests[id] = {
|
|
770
|
+
...request,
|
|
771
|
+
completedAt: Date.now(),
|
|
772
|
+
status: "canceled",
|
|
773
|
+
reason: "Session reset"
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
return {
|
|
777
|
+
...currentState,
|
|
778
|
+
requests: {},
|
|
779
|
+
completedRequests
|
|
780
|
+
};
|
|
781
|
+
});
|
|
782
|
+
logger.debug(`${this.getLogPrefix()} Permission handler reset`);
|
|
783
|
+
} finally {
|
|
784
|
+
this.isResetting = false;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
clearPendingRequestTimeout(pending) {
|
|
788
|
+
if (pending?.timeoutHandle) {
|
|
789
|
+
clearTimeout(pending.timeoutHandle);
|
|
790
|
+
pending.timeoutHandle = void 0;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
handlePendingRequestTimeout(toolCallId, pending) {
|
|
794
|
+
const active = this.pendingRequests.get(toolCallId);
|
|
795
|
+
if (!active || active !== pending) {
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
this.pendingRequests.delete(toolCallId);
|
|
799
|
+
this.clearPendingRequestTimeout(active);
|
|
800
|
+
active.resolve({ decision: "abort" });
|
|
801
|
+
this.session.updateAgentState((currentState) => {
|
|
802
|
+
const request = currentState.requests?.[toolCallId] || {
|
|
803
|
+
tool: active.toolName,
|
|
804
|
+
arguments: active.input,
|
|
805
|
+
createdAt: Date.now(),
|
|
806
|
+
requestKind: "permission"
|
|
807
|
+
};
|
|
808
|
+
const { [toolCallId]: _, ...remainingRequests } = currentState.requests || {};
|
|
809
|
+
return {
|
|
810
|
+
...currentState,
|
|
811
|
+
requests: remainingRequests,
|
|
812
|
+
completedRequests: {
|
|
813
|
+
...currentState.completedRequests,
|
|
814
|
+
[toolCallId]: {
|
|
815
|
+
...request,
|
|
816
|
+
completedAt: Date.now(),
|
|
817
|
+
status: "canceled",
|
|
818
|
+
reason: INTERACTION_TIMED_OUT_ERROR,
|
|
819
|
+
decision: "abort",
|
|
820
|
+
requestKind: request.requestKind || "permission"
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
});
|
|
825
|
+
this.session.sendSessionEvent({
|
|
826
|
+
type: "message",
|
|
827
|
+
message: "Pending interaction timed out waiting for a response. Send a new message to continue."
|
|
828
|
+
});
|
|
829
|
+
logger.debug(`${this.getLogPrefix()} Permission request timed out for ${active.toolName} (${toolCallId})`);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
297
833
|
class MessageQueue2 {
|
|
298
834
|
queue = [];
|
|
299
835
|
// Made public for testing
|
|
@@ -640,4 +1176,4 @@ function registerKillSessionHandler(rpcHandlerManager, killThisHappy) {
|
|
|
640
1176
|
});
|
|
641
1177
|
}
|
|
642
1178
|
|
|
643
|
-
export { MissingMachineIdError as M,
|
|
1179
|
+
export { BasePermissionHandler as B, ConversationHistory$1 as C, INTERACTION_SUPERSEDED_ERROR as I, MissingMachineIdError as M, INTERACTION_TIMED_OUT_ERROR as a, MessageQueue2 as b, createSessionMetadata as c, MessageBuffer as d, ensureManagedProviderMachine as e, closeProviderSession as f, getPendingInteractionTimeoutMs as g, hashObject as h, inferToolResultError as i, forwardAgentMessageToProviderSession as j, launchRuntimeHandleWithFactoryResult as l, registerKillSessionHandler as r, syncControlledByUserState as s, waitForResponseCompleteWithAbort as w };
|