jinzd-ai-cli 0.4.304 → 0.4.306
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/{batch-RY2F7BX5.js → batch-JLGWQ7HU.js} +2 -2
- package/dist/{chunk-3FI4TE5S.js → chunk-3RDJEYF7.js} +3 -3
- package/dist/{chunk-RWM2GFRC.js → chunk-45UVJXFQ.js} +6 -2
- package/dist/{chunk-23SJVOQD.js → chunk-72AR23R2.js} +26 -5
- package/dist/{chunk-I2WJ44D3.js → chunk-7SSCSSC6.js} +1 -1
- package/dist/{chunk-F3EJ4MIP.js → chunk-AYLK4EGA.js} +12 -11
- package/dist/{chunk-VOIOUJXI.js → chunk-C2Z77RTU.js} +13 -5
- package/dist/{chunk-2GRGLTAK.js → chunk-JDT6UG6R.js} +3 -1
- package/dist/{chunk-OECPGCHP.js → chunk-JFNBJHR6.js} +3 -3
- package/dist/{chunk-6AGTDWO3.js → chunk-K5SDDTN7.js} +1 -1
- package/dist/{chunk-7XU4BATV.js → chunk-KBDKKHXB.js} +15 -6
- package/dist/{chunk-7L7T46PI.js → chunk-LAP7TPPK.js} +1 -1
- package/dist/{chunk-4DEMGCK4.js → chunk-LF67HWWR.js} +37 -13
- package/dist/{chunk-TBTVV3KK.js → chunk-NLB7JZSO.js} +1 -1
- package/dist/{chunk-BXP6YZ2P.js → chunk-NRSYBKWA.js} +5 -4
- package/dist/{chunk-IEQAE3QG.js → chunk-OAXLMF3U.js} +5 -4
- package/dist/{chunk-XIMM47EG.js → chunk-OC2XHG5L.js} +1 -1
- package/dist/{chunk-RHCRE6XE.js → chunk-T6WQEQI4.js} +3 -1
- package/dist/{chunk-PPIISCFN.js → chunk-UFW4S3RU.js} +37 -13
- package/dist/{chunk-2PLYXL4P.js → chunk-VLDB2SVN.js} +33 -21
- package/dist/{chunk-BSBGPDCX.js → chunk-W6PLV3EY.js} +1 -1
- package/dist/{chunk-CKH4KQ4E.js → chunk-WQ3OMEC4.js} +6 -2
- package/dist/{chunk-DNGUSTL6.js → chunk-WZWRA7R2.js} +1 -1
- package/dist/{chunk-UUSRWSSX.js → chunk-XJOK5353.js} +15 -6
- package/dist/{ci-QYTLBM6Z.js → ci-6PHEL3C7.js} +4 -4
- package/dist/{ci-format-E5X27NC2.js → ci-format-AZYUP6BL.js} +2 -2
- package/dist/{constants-JDPKWZJP.js → constants-AXBD2EEB.js} +3 -1
- package/dist/{constants-F5WA5VPO.js → constants-CPNWIUGZ.js} +3 -1
- package/dist/{doctor-cli-7NSEVW63.js → doctor-cli-DDHHDMMJ.js} +4 -4
- package/dist/electron-server.js +253 -117
- package/dist/{hub-43PWTGGO.js → hub-DDYAWLF2.js} +4 -4
- package/dist/{hub-server-RF6LURXY.js → hub-server-CBKCP5VD.js} +1 -1
- package/dist/index.js +25 -24
- package/dist/{indexer-RBEKMFT4.js → indexer-FPKY7LCT.js} +2 -2
- package/dist/{indexer-TNDWGFRI.js → indexer-P3FPRTPJ.js} +2 -2
- package/dist/{persistent-memory-SHG3CJCP.js → persistent-memory-6XYYOMSC.js} +2 -2
- package/dist/{persistent-memory-A7RPDLR7.js → persistent-memory-XZISXRWT.js} +2 -2
- package/dist/{pr-LW755EL7.js → pr-UY4ZCZNI.js} +4 -4
- package/dist/{run-tests-HV37CM5U.js → run-tests-BAMLVLO6.js} +2 -2
- package/dist/{run-tests-MJ3SCW27.js → run-tests-UEE3OMLS.js} +2 -2
- package/dist/{semantic-HLAE2O4F.js → semantic-7GM5M4MU.js} +3 -3
- package/dist/{semantic-VPNKXKKK.js → semantic-QOBAXEQ4.js} +3 -3
- package/dist/{server-WI3Q4EDH.js → server-6IC33CJK.js} +9 -9
- package/dist/{server-EZ5ULLDP.js → server-DBHLVQVP.js} +193 -82
- package/dist/{store-MWNHVGJT.js → store-CMSSWZF7.js} +3 -1
- package/dist/{store-VO37H6LS.js → store-SIKHCUYJ.js} +3 -1
- package/dist/{task-orchestrator-PWW5I2KE.js → task-orchestrator-RDDETDW2.js} +9 -9
- package/dist/{usage-MAIV6XUZ.js → usage-D473ZVIP.js} +2 -2
- package/dist/{vector-store-JBAE6PS4.js → vector-store-JAXWDZYW.js} +1 -1
- package/dist/{vector-store-BBDXB5IQ.js → vector-store-PWIOQFTP.js} +1 -1
- package/dist/web/client/app.js +135 -60
- package/dist/web/client/app.js.br +0 -0
- package/dist/web/client/app.js.gz +0 -0
- package/dist/web/client/chat-input.js +38 -0
- package/dist/web/client/chat-input.js.br +0 -0
- package/dist/web/client/chat-input.js.gz +0 -0
- package/dist/web/client/index.html +1 -1
- package/dist/web/client/index.html.br +0 -0
- package/dist/web/client/index.html.gz +0 -0
- package/dist/web/client/stream-render.js +33 -0
- package/dist/web/client/style.css +2 -1
- package/dist/web/client/style.css.br +0 -0
- package/dist/web/client/style.css.gz +0 -0
- package/package.json +1 -1
package/dist/electron-server.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
hasSemanticIndex,
|
|
3
3
|
semanticSearch
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-KBDKKHXB.js";
|
|
5
|
+
import "./chunk-OAXLMF3U.js";
|
|
6
6
|
import {
|
|
7
7
|
buildChatIndex,
|
|
8
8
|
clearChatIndex,
|
|
@@ -22,10 +22,10 @@ import {
|
|
|
22
22
|
} from "./chunk-SKET65WZ.js";
|
|
23
23
|
import {
|
|
24
24
|
indexProject
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-UFW4S3RU.js";
|
|
26
26
|
import {
|
|
27
27
|
loadIndex
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-45UVJXFQ.js";
|
|
29
29
|
import {
|
|
30
30
|
addMemoryEntry,
|
|
31
31
|
backupMemoryStore,
|
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
touchMemoryReferences,
|
|
51
51
|
updateMemoryApproval,
|
|
52
52
|
updateMemoryEntry
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-WZWRA7R2.js";
|
|
54
54
|
import {
|
|
55
55
|
DEFAULT_PATTERNS,
|
|
56
56
|
redactJson,
|
|
@@ -66,7 +66,7 @@ import {
|
|
|
66
66
|
} from "./chunk-CGLYJRFA.js";
|
|
67
67
|
import {
|
|
68
68
|
runTestsTool
|
|
69
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-NLB7JZSO.js";
|
|
70
70
|
import {
|
|
71
71
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
72
72
|
APP_NAME,
|
|
@@ -84,6 +84,7 @@ import {
|
|
|
84
84
|
DESCRIPTION,
|
|
85
85
|
DEV_STATE_FILE_NAME,
|
|
86
86
|
HISTORY_DIR_NAME,
|
|
87
|
+
MAX_GLOB_MATCHES,
|
|
87
88
|
MCP_CALL_TIMEOUT,
|
|
88
89
|
MCP_CONNECT_TIMEOUT,
|
|
89
90
|
MCP_PROJECT_CONFIG_NAME,
|
|
@@ -100,12 +101,30 @@ import {
|
|
|
100
101
|
SUBAGENT_MAX_ROUNDS_LIMIT,
|
|
101
102
|
VERSION,
|
|
102
103
|
buildUserIdentityPrompt
|
|
103
|
-
} from "./chunk-
|
|
104
|
+
} from "./chunk-JDT6UG6R.js";
|
|
104
105
|
|
|
105
106
|
// src/web/server.ts
|
|
106
107
|
import express from "express";
|
|
107
108
|
import { createServer } from "http";
|
|
108
109
|
import { WebSocketServer } from "ws";
|
|
110
|
+
|
|
111
|
+
// src/web/chat-limits.ts
|
|
112
|
+
var MAX_CHAT_BYTES = 16 * 1024 * 1024;
|
|
113
|
+
var MAX_IMAGE_BYTES = 10 * 1024 * 1024;
|
|
114
|
+
var MAX_IMAGE_BASE64_CHARS = Math.ceil(MAX_IMAGE_BYTES / 3) * 4;
|
|
115
|
+
var IMAGE_MIMES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"]);
|
|
116
|
+
function imageValidationError(images) {
|
|
117
|
+
if (images === void 0) return;
|
|
118
|
+
if (!Array.isArray(images)) return "Images must be an array.";
|
|
119
|
+
for (const image of images) {
|
|
120
|
+
if (!image || typeof image.data !== "string" || !IMAGE_MIMES.has(image.mime)) {
|
|
121
|
+
return "Unsupported image format. Use PNG, JPEG, GIF, WebP or SVG.";
|
|
122
|
+
}
|
|
123
|
+
if (image.data.length > MAX_IMAGE_BASE64_CHARS) return "Image too large (maximum 10 MiB before base64 encoding).";
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/web/server.ts
|
|
109
128
|
import { join as join28, dirname as dirname11, resolve as resolve12, relative as relative6, sep as sep5, extname as extname3, resolve as resolvePath } from "path";
|
|
110
129
|
import { existsSync as existsSync32, readFileSync as readFileSync25, readdirSync as readdirSync15, statSync as statSync16, realpathSync as realpathSync3, createReadStream } from "fs";
|
|
111
130
|
import { networkInterfaces } from "os";
|
|
@@ -7013,7 +7032,7 @@ ${err.message}`;
|
|
|
7013
7032
|
if (IMAGE_EXTENSIONS.has(ext)) {
|
|
7014
7033
|
return `[Image file: ${filePath} (${ext})]
|
|
7015
7034
|
read_file cannot return image content \u2014 tool results are text-only, so no tool can put an image into your context.
|
|
7016
|
-
To have an image looked at, ask the USER to reference it from their input line with @${filePath} (they can use a glob for a batch, e.g. @dir/*.png, up to
|
|
7035
|
+
To have an image looked at, ask the USER to reference it from their input line with @${filePath} (they can use a glob for a batch, e.g. @dir/*.png, up to ${MAX_GLOB_MATCHES} at a time). A vision-capable model must be active, or the reference is rejected.
|
|
7017
7036
|
Do NOT claim you inspected this image \u2014 you have not seen it.`;
|
|
7018
7037
|
}
|
|
7019
7038
|
return `[Binary file: ${filePath} (${ext})]
|
|
@@ -7816,26 +7835,11 @@ function checkNetworkPolicy(toolName, args, policy) {
|
|
|
7816
7835
|
// src/tools/truncate.ts
|
|
7817
7836
|
var DEFAULT_MAX_TOOL_OUTPUT_CHARS = 12e3;
|
|
7818
7837
|
var activeCap = DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP;
|
|
7819
|
-
function
|
|
7820
|
-
if (!cap || cap <= 0) {
|
|
7821
|
-
activeCap = DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP;
|
|
7822
|
-
} else {
|
|
7823
|
-
activeCap = Math.max(DEFAULT_MAX_TOOL_OUTPUT_CHARS, cap);
|
|
7824
|
-
}
|
|
7825
|
-
if (lastContextWindow > 0) {
|
|
7826
|
-
activeMaxChars = getMaxOutputChars(lastContextWindow);
|
|
7827
|
-
}
|
|
7828
|
-
}
|
|
7829
|
-
function getMaxOutputChars(contextWindow) {
|
|
7838
|
+
function getMaxOutputChars(contextWindow, cap = activeCap) {
|
|
7830
7839
|
if (!contextWindow || contextWindow <= 0) return DEFAULT_MAX_TOOL_OUTPUT_CHARS;
|
|
7831
|
-
return Math.max(DEFAULT_MAX_TOOL_OUTPUT_CHARS, Math.min(Math.floor(contextWindow / 4),
|
|
7840
|
+
return Math.max(DEFAULT_MAX_TOOL_OUTPUT_CHARS, Math.min(Math.floor(contextWindow / 4), cap > 0 ? cap : DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP));
|
|
7832
7841
|
}
|
|
7833
7842
|
var activeMaxChars = DEFAULT_MAX_TOOL_OUTPUT_CHARS;
|
|
7834
|
-
var lastContextWindow = 0;
|
|
7835
|
-
function setContextWindow(contextWindow) {
|
|
7836
|
-
lastContextWindow = contextWindow;
|
|
7837
|
-
activeMaxChars = getMaxOutputChars(contextWindow);
|
|
7838
|
-
}
|
|
7839
7843
|
function getActiveMaxChars() {
|
|
7840
7844
|
return activeMaxChars;
|
|
7841
7845
|
}
|
|
@@ -8204,7 +8208,7 @@ var RuleBasedActionClassifier = class {
|
|
|
8204
8208
|
var defaultActionClassifier = new RuleBasedActionClassifier();
|
|
8205
8209
|
|
|
8206
8210
|
// src/tools/executor.ts
|
|
8207
|
-
var PS_ERROR_RECORD_HEAD = /^(?:[\w.\-]+\s:\s)?(?:At line:\d+ char:\s*\d+|所在位置\s
|
|
8211
|
+
var PS_ERROR_RECORD_HEAD = /^(?:[\w.\-]+\s:\s)?(?:At (?:line:\d+|\S+:\d+) char:\s*\d+|所在位置\s+(?:行|\S+):\d+\s+字符:\s*\d+)\s*$/;
|
|
8208
8212
|
var ERROR_SIGNATURE = new RegExp(
|
|
8209
8213
|
[
|
|
8210
8214
|
// Python / .NET / Java: "OSError: …", "pkg.SomeException: …"
|
|
@@ -8220,11 +8224,22 @@ var ERROR_SIGNATURE = new RegExp(
|
|
|
8220
8224
|
"i"
|
|
8221
8225
|
);
|
|
8222
8226
|
var PY_TRACEBACK_FRAME = /^\s*File ".*", line \d+/;
|
|
8227
|
+
function isPreviewNoise(line) {
|
|
8228
|
+
const t = line.trim();
|
|
8229
|
+
return /^(?:\[[^\]]+\]\s*)?Exit code \d+:$/.test(t) || // `formatFailureHeader()` 在拿不到退出码时打的那句 —— 与 "Exit code N:" 同类噪声。
|
|
8230
|
+
// 不过滤的话它会占掉第一个展示位:2026-09-05 实战里 `export_slides.ps1 | Select-Object
|
|
8231
|
+
// -First 3` 提前掐断管道被 PowerShell 报成失败,预览成了
|
|
8232
|
+
// 「Command failed before exit code could be determined: · …\slide-02.png」——
|
|
8233
|
+
// 两段都没用,而真正有信息的 `EXPORTED 25 slide(s)` 就在第二行、从没上屏。
|
|
8234
|
+
/^(?:\[[^\]]+\]\s*)?Command failed before exit code could be determined:$/.test(t) || /^Traceback \(most recent call last\):$/.test(t) || line.startsWith("[Command failed") || line.startsWith("\u{1F4A1} ") || t === "";
|
|
8235
|
+
}
|
|
8223
8236
|
function buildErrorPreview(content) {
|
|
8224
8237
|
const raw = content.split("\n").map((l) => l.replace(/\r+$/, ""));
|
|
8225
8238
|
const undecorated = [];
|
|
8239
|
+
const decorationNeighbours = [];
|
|
8226
8240
|
for (let i = 0; i < raw.length; i++) {
|
|
8227
8241
|
if (PS_ERROR_RECORD_HEAD.test(raw[i])) {
|
|
8242
|
+
if (undecorated.length > 0) decorationNeighbours.push(undecorated[undecorated.length - 1]);
|
|
8228
8243
|
i++;
|
|
8229
8244
|
while (i < raw.length && /^\s*\+ /.test(raw[i])) i++;
|
|
8230
8245
|
i--;
|
|
@@ -8232,14 +8247,7 @@ function buildErrorPreview(content) {
|
|
|
8232
8247
|
}
|
|
8233
8248
|
undecorated.push(raw[i]);
|
|
8234
8249
|
}
|
|
8235
|
-
const meaningful = undecorated.filter(
|
|
8236
|
-
(l) => !/^(?:\[[^\]]+\]\s*)?Exit code \d+:$/.test(l.trim()) && // `formatFailureHeader()` 在拿不到退出码时打的那句 —— 与 "Exit code N:" 同类噪声。
|
|
8237
|
-
// 不过滤的话它会占掉第一个展示位:2026-09-05 实战里 `export_slides.ps1 | Select-Object
|
|
8238
|
-
// -First 3` 提前掐断管道被 PowerShell 报成失败,预览成了
|
|
8239
|
-
// 「Command failed before exit code could be determined: · …\slide-02.png」——
|
|
8240
|
-
// 两段都没用,而真正有信息的 `EXPORTED 25 slide(s)` 就在第二行、从没上屏。
|
|
8241
|
-
!/^(?:\[[^\]]+\]\s*)?Command failed before exit code could be determined:$/.test(l.trim()) && !/^Traceback \(most recent call last\):$/.test(l.trim()) && !l.startsWith("[Command failed") && !l.startsWith("\u{1F4A1} ") && l.trim() !== ""
|
|
8242
|
-
);
|
|
8250
|
+
const meaningful = undecorated.filter((l) => !isPreviewNoise(l));
|
|
8243
8251
|
let primary = -1;
|
|
8244
8252
|
for (let i = meaningful.length - 1; i >= 0; i--) {
|
|
8245
8253
|
if (ERROR_SIGNATURE.test(meaningful[i])) {
|
|
@@ -8258,7 +8266,12 @@ function buildErrorPreview(content) {
|
|
|
8258
8266
|
}
|
|
8259
8267
|
previewLines = frame !== null ? [frame, meaningful[primary]] : [meaningful[primary]];
|
|
8260
8268
|
} else {
|
|
8261
|
-
|
|
8269
|
+
const neighbour = decorationNeighbours.find((l) => !isPreviewNoise(l));
|
|
8270
|
+
if (neighbour !== void 0) {
|
|
8271
|
+
previewLines = [neighbour];
|
|
8272
|
+
} else {
|
|
8273
|
+
previewLines = meaningful.length > 2 ? [meaningful[0], meaningful[meaningful.length - 1]] : meaningful.slice(0, 2);
|
|
8274
|
+
}
|
|
8262
8275
|
}
|
|
8263
8276
|
const preview = previewLines.map((l) => l.length > 160 ? l.slice(0, 160) + "\u2026" : l).join(" \xB7 ") || content.slice(0, 120);
|
|
8264
8277
|
return preview.slice(0, 240);
|
|
@@ -8965,7 +8978,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
8965
8978
|
const mode = appendMode ? "appended" : "written";
|
|
8966
8979
|
void (async () => {
|
|
8967
8980
|
try {
|
|
8968
|
-
const { updateFile } = await import("./indexer-
|
|
8981
|
+
const { updateFile } = await import("./indexer-FPKY7LCT.js");
|
|
8969
8982
|
await updateFile(process.cwd(), filePath);
|
|
8970
8983
|
} catch {
|
|
8971
8984
|
}
|
|
@@ -14096,6 +14109,27 @@ function suggestSkillName(names, input) {
|
|
|
14096
14109
|
// src/web/tool-executor-web.ts
|
|
14097
14110
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
14098
14111
|
import { tmpdir as tmpdir2 } from "os";
|
|
14112
|
+
|
|
14113
|
+
// src/web/socket-send.ts
|
|
14114
|
+
var MAX_PENDING_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
14115
|
+
var stalled = /* @__PURE__ */ new WeakSet();
|
|
14116
|
+
function sendSocketMessage(ws, message) {
|
|
14117
|
+
if (ws.readyState !== ws.OPEN || stalled.has(ws)) return false;
|
|
14118
|
+
const data = JSON.stringify(message);
|
|
14119
|
+
if ((ws.bufferedAmount || 0) + Buffer.byteLength(data) > MAX_PENDING_OUTPUT_BYTES) {
|
|
14120
|
+
stalled.add(ws);
|
|
14121
|
+
ws.close(1013, "Output backlog exceeded; reconnect to reload session");
|
|
14122
|
+
const timer = setTimeout(() => {
|
|
14123
|
+
if (ws.readyState !== ws.CLOSED) ws.terminate();
|
|
14124
|
+
}, 1e3);
|
|
14125
|
+
timer.unref();
|
|
14126
|
+
return false;
|
|
14127
|
+
}
|
|
14128
|
+
ws.send(data);
|
|
14129
|
+
return true;
|
|
14130
|
+
}
|
|
14131
|
+
|
|
14132
|
+
// src/web/tool-executor-web.ts
|
|
14099
14133
|
import { existsSync as existsSync22, readFileSync as readFileSync17 } from "fs";
|
|
14100
14134
|
var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
14101
14135
|
constructor(registry, ws) {
|
|
@@ -14125,6 +14159,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
14125
14159
|
// 5 minutes
|
|
14126
14160
|
/** Publicly readable by SessionHandler to check if confirm is active */
|
|
14127
14161
|
confirming = false;
|
|
14162
|
+
maxOutputChars = DEFAULT_MAX_TOOL_OUTPUT_CHARS;
|
|
14128
14163
|
/** Session-level auto-approve toggle (/yolo command) */
|
|
14129
14164
|
sessionAutoApprove = false;
|
|
14130
14165
|
/** Session-level Auto Mode: rule-based low-risk auto approval. */
|
|
@@ -14207,9 +14242,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
14207
14242
|
this.confirming = false;
|
|
14208
14243
|
}
|
|
14209
14244
|
send(msg) {
|
|
14210
|
-
|
|
14211
|
-
this.ws.send(JSON.stringify(msg));
|
|
14212
|
-
}
|
|
14245
|
+
sendSocketMessage(this.ws, msg);
|
|
14213
14246
|
}
|
|
14214
14247
|
sendToolCallStart(call) {
|
|
14215
14248
|
const dangerLevel = getDangerLevel(call.name, call.arguments);
|
|
@@ -14402,7 +14435,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
14402
14435
|
if (!toolCallStarted) this.sendToolCallStart(call);
|
|
14403
14436
|
try {
|
|
14404
14437
|
const rawContent = await runTool(tool, call.arguments, call.name);
|
|
14405
|
-
const content = truncateOutput(rawContent, call.name);
|
|
14438
|
+
const content = truncateOutput(rawContent, call.name, this.maxOutputChars);
|
|
14406
14439
|
this.sendToolCallResult(call, rawContent, false);
|
|
14407
14440
|
runHook(this.hookConfig?.postToolExecution, { tool: call.name, status: "ok" });
|
|
14408
14441
|
runLifecycleHooks(this.hookConfig, "PostToolUse", { tool: call.name, status: "ok" }, { configDir: this.hookConfigDir });
|
|
@@ -14467,7 +14500,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
14467
14500
|
}
|
|
14468
14501
|
try {
|
|
14469
14502
|
const rawContent = await runTool(tool, call.arguments, call.name);
|
|
14470
|
-
const content = truncateOutput(rawContent, call.name);
|
|
14503
|
+
const content = truncateOutput(rawContent, call.name, this.maxOutputChars);
|
|
14471
14504
|
this.sendToolCallResult(call, rawContent, false);
|
|
14472
14505
|
results[i] = { callId: call.id, content, isError: false };
|
|
14473
14506
|
} catch (err) {
|
|
@@ -15416,6 +15449,7 @@ var HubAgent = class {
|
|
|
15416
15449
|
model: this.modelId,
|
|
15417
15450
|
systemPrompt,
|
|
15418
15451
|
stream: false,
|
|
15452
|
+
signal: opts?.signal,
|
|
15419
15453
|
temperature: 0.7,
|
|
15420
15454
|
maxTokens: 4096,
|
|
15421
15455
|
...this.thinking ?? {}
|
|
@@ -15450,15 +15484,21 @@ var HubAgent = class {
|
|
|
15450
15484
|
model: this.modelId,
|
|
15451
15485
|
systemPrompt,
|
|
15452
15486
|
stream: true,
|
|
15487
|
+
signal: opts?.signal,
|
|
15453
15488
|
temperature: 0.7,
|
|
15454
15489
|
maxTokens: 4096,
|
|
15455
15490
|
...this.thinking ?? {}
|
|
15456
15491
|
});
|
|
15457
|
-
|
|
15458
|
-
|
|
15459
|
-
|
|
15460
|
-
|
|
15492
|
+
try {
|
|
15493
|
+
for await (const chunk of stream) {
|
|
15494
|
+
if (chunk.delta) {
|
|
15495
|
+
raw += chunk.delta;
|
|
15496
|
+
onToken?.(chunk.delta);
|
|
15497
|
+
}
|
|
15498
|
+
if (opts?.signal?.aborted) break;
|
|
15461
15499
|
}
|
|
15500
|
+
} catch (error) {
|
|
15501
|
+
if (!opts?.signal?.aborted) throw error;
|
|
15462
15502
|
}
|
|
15463
15503
|
const { content, passed, converged } = parseTurn(raw);
|
|
15464
15504
|
return {
|
|
@@ -15473,7 +15513,7 @@ var HubAgent = class {
|
|
|
15473
15513
|
/**
|
|
15474
15514
|
* Generate a summary of the entire discussion from this agent's perspective.
|
|
15475
15515
|
*/
|
|
15476
|
-
async summarize(topic, history) {
|
|
15516
|
+
async summarize(topic, history, signal) {
|
|
15477
15517
|
const provider = this.providers.get(this.providerId);
|
|
15478
15518
|
if (!provider) {
|
|
15479
15519
|
throw new Error(`Provider "${this.providerId}" not available`);
|
|
@@ -15490,6 +15530,7 @@ var HubAgent = class {
|
|
|
15490
15530
|
model: this.modelId,
|
|
15491
15531
|
stream: false,
|
|
15492
15532
|
temperature: 0.3,
|
|
15533
|
+
signal,
|
|
15493
15534
|
maxTokens: 4096,
|
|
15494
15535
|
...this.thinking ?? {}
|
|
15495
15536
|
});
|
|
@@ -15583,6 +15624,8 @@ var DiscussionOrchestrator = class {
|
|
|
15583
15624
|
agents = [];
|
|
15584
15625
|
state;
|
|
15585
15626
|
aborted = false;
|
|
15627
|
+
controller = new AbortController();
|
|
15628
|
+
cancelReview;
|
|
15586
15629
|
humanSteer;
|
|
15587
15630
|
voteConverge;
|
|
15588
15631
|
/** Callback for rendering events */
|
|
@@ -15622,6 +15665,8 @@ var DiscussionOrchestrator = class {
|
|
|
15622
15665
|
/** Signal the orchestrator to stop after current turn */
|
|
15623
15666
|
abort() {
|
|
15624
15667
|
this.aborted = true;
|
|
15668
|
+
this.controller.abort();
|
|
15669
|
+
this.cancelReview?.();
|
|
15625
15670
|
}
|
|
15626
15671
|
/**
|
|
15627
15672
|
* Run the full discussion on a given topic.
|
|
@@ -15632,6 +15677,7 @@ var DiscussionOrchestrator = class {
|
|
|
15632
15677
|
this.state.round = 0;
|
|
15633
15678
|
this.state.finished = false;
|
|
15634
15679
|
this.aborted = false;
|
|
15680
|
+
this.controller = new AbortController();
|
|
15635
15681
|
try {
|
|
15636
15682
|
for (let round = 1; round <= this.state.maxRounds; round++) {
|
|
15637
15683
|
if (this.aborted) {
|
|
@@ -15652,7 +15698,7 @@ var DiscussionOrchestrator = class {
|
|
|
15652
15698
|
round,
|
|
15653
15699
|
this.state.maxRounds,
|
|
15654
15700
|
(token) => this.emit({ type: "agent_token", roleId: agent.role.id, token }),
|
|
15655
|
-
{ voteConverge: this.voteConverge }
|
|
15701
|
+
{ voteConverge: this.voteConverge, signal: this.controller.signal }
|
|
15656
15702
|
);
|
|
15657
15703
|
this.state.messages.push(message);
|
|
15658
15704
|
if (message.converged) convergedCount++;
|
|
@@ -15664,6 +15710,7 @@ var DiscussionOrchestrator = class {
|
|
|
15664
15710
|
this.emit({ type: "agent_spoke", roleId: agent.role.id, message });
|
|
15665
15711
|
}
|
|
15666
15712
|
} catch (err) {
|
|
15713
|
+
if (this.aborted) break;
|
|
15667
15714
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
15668
15715
|
this.emit({ type: "error", roleId: agent.role.id, message: errMsg });
|
|
15669
15716
|
this.state.messages.push({
|
|
@@ -15675,6 +15722,10 @@ var DiscussionOrchestrator = class {
|
|
|
15675
15722
|
}
|
|
15676
15723
|
}
|
|
15677
15724
|
this.emit({ type: "round_end", round });
|
|
15725
|
+
if (this.aborted) {
|
|
15726
|
+
this.emit({ type: "discussion_end", reason: "user_interrupt" });
|
|
15727
|
+
break;
|
|
15728
|
+
}
|
|
15678
15729
|
if (allPassed && round > 1) {
|
|
15679
15730
|
this.emit({ type: "discussion_end", reason: "consensus" });
|
|
15680
15731
|
break;
|
|
@@ -15691,7 +15742,18 @@ var DiscussionOrchestrator = class {
|
|
|
15691
15742
|
break;
|
|
15692
15743
|
}
|
|
15693
15744
|
if (this.humanSteer && this.onRoundReview && !this.aborted) {
|
|
15694
|
-
const
|
|
15745
|
+
const stopped = new Promise((resolve13) => {
|
|
15746
|
+
this.cancelReview = () => resolve13({ action: "stop" });
|
|
15747
|
+
});
|
|
15748
|
+
let steer;
|
|
15749
|
+
try {
|
|
15750
|
+
steer = await Promise.race([
|
|
15751
|
+
this.onRoundReview({ round, maxRounds: this.state.maxRounds, state: this.state }),
|
|
15752
|
+
stopped
|
|
15753
|
+
]);
|
|
15754
|
+
} finally {
|
|
15755
|
+
this.cancelReview = void 0;
|
|
15756
|
+
}
|
|
15695
15757
|
if (steer.action === "stop") {
|
|
15696
15758
|
this.emit({ type: "discussion_end", reason: "human_stop" });
|
|
15697
15759
|
break;
|
|
@@ -15712,7 +15774,7 @@ var DiscussionOrchestrator = class {
|
|
|
15712
15774
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
15713
15775
|
this.emit({ type: "error", message: errMsg });
|
|
15714
15776
|
}
|
|
15715
|
-
await this.generateSummary();
|
|
15777
|
+
if (!this.aborted) await this.generateSummary();
|
|
15716
15778
|
this.state.finished = true;
|
|
15717
15779
|
return this.state;
|
|
15718
15780
|
}
|
|
@@ -15730,7 +15792,7 @@ var DiscussionOrchestrator = class {
|
|
|
15730
15792
|
return;
|
|
15731
15793
|
}
|
|
15732
15794
|
try {
|
|
15733
|
-
this.state.summary = await summarizer.summarize(this.state.topic, this.state.messages);
|
|
15795
|
+
this.state.summary = await summarizer.summarize(this.state.topic, this.state.messages, this.controller.signal);
|
|
15734
15796
|
this.emit({ type: "summary", content: this.state.summary });
|
|
15735
15797
|
} catch (err) {
|
|
15736
15798
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -16206,11 +16268,11 @@ function routeDecisionRows(message, currentModel, decision) {
|
|
|
16206
16268
|
}
|
|
16207
16269
|
|
|
16208
16270
|
// src/web/commands/provider-commands.ts
|
|
16209
|
-
function syncSessionProvider(ctx) {
|
|
16271
|
+
async function syncSessionProvider(ctx) {
|
|
16210
16272
|
const session = ctx.sessions.current;
|
|
16211
16273
|
if (!session) return;
|
|
16212
16274
|
session.updateProvider(ctx.currentProvider, ctx.currentModel);
|
|
16213
|
-
ctx.saveIfNeeded();
|
|
16275
|
+
await ctx.saveIfNeeded();
|
|
16214
16276
|
}
|
|
16215
16277
|
async function handleProvider(args, ctx) {
|
|
16216
16278
|
const id = args[0];
|
|
@@ -16253,7 +16315,7 @@ async function handleProvider(args, ctx) {
|
|
|
16253
16315
|
ctx.currentProvider = outcome.providerId;
|
|
16254
16316
|
ctx.currentModel = outcome.model;
|
|
16255
16317
|
ctx.updateContextWindow();
|
|
16256
|
-
syncSessionProvider(ctx);
|
|
16318
|
+
await syncSessionProvider(ctx);
|
|
16257
16319
|
ctx.send({ type: "info", message: outcome.message });
|
|
16258
16320
|
ctx.sendStatus();
|
|
16259
16321
|
}
|
|
@@ -16307,7 +16369,7 @@ async function handleModel(args, ctx) {
|
|
|
16307
16369
|
}
|
|
16308
16370
|
ctx.currentModel = outcome.model;
|
|
16309
16371
|
ctx.updateContextWindow();
|
|
16310
|
-
syncSessionProvider(ctx);
|
|
16372
|
+
await syncSessionProvider(ctx);
|
|
16311
16373
|
ctx.send({
|
|
16312
16374
|
type: "info",
|
|
16313
16375
|
message: outcome.warning ? `${outcome.message} \u2014 ${outcome.warning}` : outcome.message
|
|
@@ -17192,7 +17254,7 @@ async function handleClear(_args, ctx) {
|
|
|
17192
17254
|
const prior = ctx.sessions.current;
|
|
17193
17255
|
const priorId = prior?.id ?? null;
|
|
17194
17256
|
const priorCount = prior?.messages.length ?? 0;
|
|
17195
|
-
ctx.saveIfNeeded();
|
|
17257
|
+
await ctx.saveIfNeeded();
|
|
17196
17258
|
const created = ctx.sessions.createSession(ctx.currentProvider, ctx.currentModel);
|
|
17197
17259
|
ctx.unsavedSessions.set(created.id, created);
|
|
17198
17260
|
ctx.resetWebSessionUsage();
|
|
@@ -17214,7 +17276,7 @@ Cache: write=${cacheCreate} read=${cacheRead}` : "";
|
|
|
17214
17276
|
Cost: ${formatCost(cost)}` : "";
|
|
17215
17277
|
let memoryLine = "";
|
|
17216
17278
|
try {
|
|
17217
|
-
const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-
|
|
17279
|
+
const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-XZISXRWT.js");
|
|
17218
17280
|
const pending = countPendingMemories2(ctx.config.getConfigDir());
|
|
17219
17281
|
if (pending > 0) memoryLine = `
|
|
17220
17282
|
Memory: \u26A0 ${pending} pending approval \u2014 see the Memory panel or /memory`;
|
|
@@ -17230,8 +17292,13 @@ Tokens: in=${ctx.sessionTokenUsage.inputTokens} out=${ctx.sessionTokenUsage.outp
|
|
|
17230
17292
|
}
|
|
17231
17293
|
async function handleSession(args, ctx) {
|
|
17232
17294
|
const sub = args[0];
|
|
17295
|
+
if (sub === "save") {
|
|
17296
|
+
await ctx.saveIfNeeded();
|
|
17297
|
+
if (!ctx.sessions.current?.dirty) ctx.send({ type: "info", message: "Session saved." });
|
|
17298
|
+
return;
|
|
17299
|
+
}
|
|
17233
17300
|
if (sub === "new") {
|
|
17234
|
-
ctx.saveIfNeeded();
|
|
17301
|
+
await ctx.saveIfNeeded();
|
|
17235
17302
|
const created = ctx.sessions.createSession(ctx.currentProvider, ctx.currentModel);
|
|
17236
17303
|
ctx.unsavedSessions.set(created.id, created);
|
|
17237
17304
|
ctx.resetWebSessionUsage();
|
|
@@ -17242,7 +17309,7 @@ async function handleSession(args, ctx) {
|
|
|
17242
17309
|
}
|
|
17243
17310
|
if (sub === "load" && args[1]) {
|
|
17244
17311
|
const targetId = args[1];
|
|
17245
|
-
ctx.saveIfNeeded();
|
|
17312
|
+
await ctx.saveIfNeeded();
|
|
17246
17313
|
const cachedExact = ctx.unsavedSessions.get(targetId);
|
|
17247
17314
|
const cached = cachedExact ?? [...ctx.unsavedSessions.values()].find((s) => s.id.startsWith(targetId));
|
|
17248
17315
|
if (cached) {
|
|
@@ -18197,7 +18264,7 @@ async function handleTest(args, ctx) {
|
|
|
18197
18264
|
const isCommand = argStr.includes(" ") || /^(mvn|gradle|npm|pytest|cargo|go)\b/.test(argStr);
|
|
18198
18265
|
testArgs = isCommand ? { command: argStr } : { filter: argStr };
|
|
18199
18266
|
}
|
|
18200
|
-
const runTests = ctx.runTests ?? (await import("./run-tests-
|
|
18267
|
+
const runTests = ctx.runTests ?? (await import("./run-tests-UEE3OMLS.js")).executeTests;
|
|
18201
18268
|
const report = await runTests(testArgs);
|
|
18202
18269
|
ctx.send({ type: "info", message: report });
|
|
18203
18270
|
} catch (err) {
|
|
@@ -18489,9 +18556,9 @@ async function handleContext(_args, ctx) {
|
|
|
18489
18556
|
async function handleIndex(args, ctx) {
|
|
18490
18557
|
const sub = (args[0] ?? "status").toLowerCase();
|
|
18491
18558
|
const root = process.cwd();
|
|
18492
|
-
const { loadIndex: loadIndex2, clearIndex } = await import("./store-
|
|
18493
|
-
const { indexProject: indexProject2 } = await import("./indexer-
|
|
18494
|
-
const { loadVectorStore, clearVectorStore } = await import("./vector-store-
|
|
18559
|
+
const { loadIndex: loadIndex2, clearIndex } = await import("./store-SIKHCUYJ.js");
|
|
18560
|
+
const { indexProject: indexProject2 } = await import("./indexer-FPKY7LCT.js");
|
|
18561
|
+
const { loadVectorStore, clearVectorStore } = await import("./vector-store-JAXWDZYW.js");
|
|
18495
18562
|
if (sub === "status") {
|
|
18496
18563
|
const idx = loadIndex2(root);
|
|
18497
18564
|
const vec = loadVectorStore(root);
|
|
@@ -18514,6 +18581,7 @@ async function handleIndex(args, ctx) {
|
|
|
18514
18581
|
} else if (sub === "rebuild") {
|
|
18515
18582
|
ctx.send({ type: "info", message: `Indexing ${root}\u2026` });
|
|
18516
18583
|
const { stats } = await indexProject2(root, { force: true });
|
|
18584
|
+
if (stats.incomplete) ctx.send({ type: "info", message: "Partial index: scan limit or unreadable files reached. Unscanned entries were retained." });
|
|
18517
18585
|
ctx.send({
|
|
18518
18586
|
type: "info",
|
|
18519
18587
|
message: `\u2713 Indexed ${stats.filesParsed} files (${stats.symbols} symbols) in ${stats.durationMs}ms`
|
|
@@ -18540,7 +18608,7 @@ async function handleIndex(args, ctx) {
|
|
|
18540
18608
|
message: `Building semantic index for ${idx.symbolCount} symbols\u2026 (first run downloads ~117 MB model)`
|
|
18541
18609
|
});
|
|
18542
18610
|
try {
|
|
18543
|
-
const { rebuildSemanticIndex } = await import("./semantic-
|
|
18611
|
+
const { rebuildSemanticIndex } = await import("./semantic-QOBAXEQ4.js");
|
|
18544
18612
|
const stats = await rebuildSemanticIndex(root);
|
|
18545
18613
|
const first = stats.modelFirstLoadMs ? ` (model load+first batch ${stats.modelFirstLoadMs}ms)` : "";
|
|
18546
18614
|
ctx.send({
|
|
@@ -18587,7 +18655,7 @@ async function handleMemory(args, ctx) {
|
|
|
18587
18655
|
}
|
|
18588
18656
|
if (intent.kind === "status") {
|
|
18589
18657
|
const { getChatIndexStatus: getChatIndexStatus2 } = await import("./chat-index-DGUOUUQW.js");
|
|
18590
|
-
const { listMemoryEntries: listMemoryEntries2, isMemoryExpired: isMemoryExpired2, formatMemoryForPrompt: formatMemoryForPrompt2 } = await import("./persistent-memory-
|
|
18658
|
+
const { listMemoryEntries: listMemoryEntries2, isMemoryExpired: isMemoryExpired2, formatMemoryForPrompt: formatMemoryForPrompt2 } = await import("./persistent-memory-XZISXRWT.js");
|
|
18591
18659
|
const memories = listMemoryEntries2(configDir, { includeExpired: true, includeRejected: true });
|
|
18592
18660
|
const injected = formatMemoryForPrompt2(configDir, process.cwd(), ctx.config.get("memory")?.maxChars);
|
|
18593
18661
|
const view = buildMemoryStatusView({
|
|
@@ -18610,7 +18678,7 @@ async function handleMemory(args, ctx) {
|
|
|
18610
18678
|
return;
|
|
18611
18679
|
}
|
|
18612
18680
|
if (intent.kind === "recall") {
|
|
18613
|
-
const { searchPersistentMemories: searchPersistentMemories2, touchMemoryReferences: touchMemoryReferences2 } = await import("./persistent-memory-
|
|
18681
|
+
const { searchPersistentMemories: searchPersistentMemories2, touchMemoryReferences: touchMemoryReferences2 } = await import("./persistent-memory-XZISXRWT.js");
|
|
18614
18682
|
const { searchChatMemory: searchChatMemory2 } = await import("./chat-index-DGUOUUQW.js");
|
|
18615
18683
|
const persistentHits = searchPersistentMemories2(configDir, intent.query, process.cwd(), 3);
|
|
18616
18684
|
if (persistentHits.length > 0) {
|
|
@@ -18731,7 +18799,7 @@ async function handleMemory(args, ctx) {
|
|
|
18731
18799
|
ctx.handleMemoryManage(intent.action, args[1], intent.action === "expire" ? args[2] : void 0);
|
|
18732
18800
|
} else if (intent.kind === "export") {
|
|
18733
18801
|
if (args[1] === "md") {
|
|
18734
|
-
const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-
|
|
18802
|
+
const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-XZISXRWT.js");
|
|
18735
18803
|
ctx.send({
|
|
18736
18804
|
type: "export_data",
|
|
18737
18805
|
format: "md",
|
|
@@ -19736,7 +19804,7 @@ No tools match "${filter}".
|
|
|
19736
19804
|
const { join: join29 } = await import("path");
|
|
19737
19805
|
const { existsSync: existsSync33 } = await import("fs");
|
|
19738
19806
|
const { getGitRoot: getGitRoot2 } = await import("./git-context-VNCA2GU7.js");
|
|
19739
|
-
const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-
|
|
19807
|
+
const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-AXBD2EEB.js");
|
|
19740
19808
|
const { approveProject, hashMcpFile } = await import("./project-trust-MUG325AW.js");
|
|
19741
19809
|
const cwd = process.cwd();
|
|
19742
19810
|
const projectRoot = getGitRoot2(cwd) ?? cwd;
|
|
@@ -20655,7 +20723,7 @@ function createReviewCommands() {
|
|
|
20655
20723
|
usage: "/test [command|filter]",
|
|
20656
20724
|
async execute(args, ctx) {
|
|
20657
20725
|
try {
|
|
20658
|
-
const { executeTests } = await import("./run-tests-
|
|
20726
|
+
const { executeTests } = await import("./run-tests-UEE3OMLS.js");
|
|
20659
20727
|
const argStr = args.join(" ").trim();
|
|
20660
20728
|
let testArgs = {};
|
|
20661
20729
|
if (argStr) {
|
|
@@ -20906,9 +20974,9 @@ function createMemoryCommands() {
|
|
|
20906
20974
|
async execute(args, ctx) {
|
|
20907
20975
|
const sub = (args[0] ?? "status").toLowerCase();
|
|
20908
20976
|
const root = process.cwd();
|
|
20909
|
-
const { loadIndex: loadIndex2, clearIndex } = await import("./store-
|
|
20910
|
-
const { indexProject: indexProject2 } = await import("./indexer-
|
|
20911
|
-
const { loadVectorStore, clearVectorStore } = await import("./vector-store-
|
|
20977
|
+
const { loadIndex: loadIndex2, clearIndex } = await import("./store-SIKHCUYJ.js");
|
|
20978
|
+
const { indexProject: indexProject2 } = await import("./indexer-FPKY7LCT.js");
|
|
20979
|
+
const { loadVectorStore, clearVectorStore } = await import("./vector-store-JAXWDZYW.js");
|
|
20912
20980
|
if (sub === "status") {
|
|
20913
20981
|
const idx = loadIndex2(root);
|
|
20914
20982
|
const vec = loadVectorStore(root);
|
|
@@ -20939,6 +21007,7 @@ function createMemoryCommands() {
|
|
|
20939
21007
|
}
|
|
20940
21008
|
});
|
|
20941
21009
|
process.stdout.write("\r\x1B[K");
|
|
21010
|
+
if (stats.incomplete) console.log(theme.dim(" Partial index: scan limit or unreadable files reached; unscanned entries retained."));
|
|
20942
21011
|
console.log(theme.success(
|
|
20943
21012
|
` \u2713 Indexed ${stats.filesParsed} files (${stats.symbols} symbols) in ${stats.durationMs}ms`
|
|
20944
21013
|
));
|
|
@@ -20967,7 +21036,7 @@ function createMemoryCommands() {
|
|
|
20967
21036
|
}
|
|
20968
21037
|
console.log(theme.dim(` Building semantic index for ${idx.symbolCount} symbols\u2026`));
|
|
20969
21038
|
console.log(theme.dim(" (First run downloads ~117 MB embedding model to ~/.aicli/models/)"));
|
|
20970
|
-
const { rebuildSemanticIndex } = await import("./semantic-
|
|
21039
|
+
const { rebuildSemanticIndex } = await import("./semantic-QOBAXEQ4.js");
|
|
20971
21040
|
try {
|
|
20972
21041
|
const stats = await rebuildSemanticIndex(root, {
|
|
20973
21042
|
onProgress: (done, total) => {
|
|
@@ -22566,7 +22635,7 @@ var webCommandHandlers = {
|
|
|
22566
22635
|
search: handleSearch,
|
|
22567
22636
|
undo: handleUndo
|
|
22568
22637
|
};
|
|
22569
|
-
var SessionHandler = class {
|
|
22638
|
+
var SessionHandler = class _SessionHandler {
|
|
22570
22639
|
ws;
|
|
22571
22640
|
config;
|
|
22572
22641
|
costTracker;
|
|
@@ -22617,6 +22686,7 @@ var SessionHandler = class {
|
|
|
22617
22686
|
abortController = null;
|
|
22618
22687
|
userInterjection = null;
|
|
22619
22688
|
processing = false;
|
|
22689
|
+
disconnected = false;
|
|
22620
22690
|
/** P4: active hub discussion orchestrator (web room), if any. */
|
|
22621
22691
|
hubOrchestrator = null;
|
|
22622
22692
|
/** P4b: pending between-rounds human-steer prompts (requestId → resolver). */
|
|
@@ -22643,9 +22713,13 @@ var SessionHandler = class {
|
|
|
22643
22713
|
* 会话一旦保存到磁盘(有消息后),会从此 Map 中移除。
|
|
22644
22714
|
*/
|
|
22645
22715
|
unsavedSessions = /* @__PURE__ */ new Map();
|
|
22716
|
+
// Failed saves survive a socket reconnect, scoped to the owning config directory.
|
|
22717
|
+
static recovery = /* @__PURE__ */ new Map();
|
|
22718
|
+
static acceptedChats = /* @__PURE__ */ new Map();
|
|
22646
22719
|
constructor(ws, shared) {
|
|
22647
22720
|
this.ws = ws;
|
|
22648
22721
|
this.config = shared.config;
|
|
22722
|
+
this.unsavedSessions = new Map(_SessionHandler.recovery.get(this.config.getConfigDir()));
|
|
22649
22723
|
this.costTracker = shared.costTracker;
|
|
22650
22724
|
this.providers = shared.providers;
|
|
22651
22725
|
this.sessions = shared.sessions;
|
|
@@ -22713,9 +22787,7 @@ var SessionHandler = class {
|
|
|
22713
22787
|
* 接口之间的任何漂移(少一个成员、签名改了)都会当场编译失败,而不是等到运行时。
|
|
22714
22788
|
*/
|
|
22715
22789
|
send(msg) {
|
|
22716
|
-
|
|
22717
|
-
this.ws.send(JSON.stringify(msg));
|
|
22718
|
-
}
|
|
22790
|
+
sendSocketMessage(this.ws, msg);
|
|
22719
22791
|
}
|
|
22720
22792
|
sendStatus() {
|
|
22721
22793
|
const providerList = this.providers.listAvailable().map((p) => ({
|
|
@@ -22769,6 +22841,7 @@ var SessionHandler = class {
|
|
|
22769
22841
|
});
|
|
22770
22842
|
}
|
|
22771
22843
|
async handleMessage(raw) {
|
|
22844
|
+
if (this.disconnected) return;
|
|
22772
22845
|
let msg;
|
|
22773
22846
|
try {
|
|
22774
22847
|
msg = JSON.parse(raw);
|
|
@@ -22777,8 +22850,33 @@ var SessionHandler = class {
|
|
|
22777
22850
|
return;
|
|
22778
22851
|
}
|
|
22779
22852
|
switch (msg.type) {
|
|
22780
|
-
case "chat":
|
|
22853
|
+
case "chat": {
|
|
22854
|
+
if (msg.messageId) {
|
|
22855
|
+
if (typeof msg.messageId !== "string" || msg.messageId.length > 200) throw new Error("Invalid message ID");
|
|
22856
|
+
const scope = this.config.getConfigDir();
|
|
22857
|
+
const accepted = _SessionHandler.acceptedChats.get(scope) ?? /* @__PURE__ */ new Map();
|
|
22858
|
+
const prior = accepted.get(msg.messageId);
|
|
22859
|
+
if (prior) {
|
|
22860
|
+
this.send({ type: "chat_ack", messageId: msg.messageId, accepted: true, duplicate: true, sessionId: prior });
|
|
22861
|
+
return;
|
|
22862
|
+
}
|
|
22863
|
+
if (this.processing || typeof msg.content !== "string") {
|
|
22864
|
+
this.send({ type: "chat_ack", messageId: msg.messageId, accepted: false, error: "A request is already running, or the message is invalid. Your draft was kept." });
|
|
22865
|
+
return;
|
|
22866
|
+
}
|
|
22867
|
+
const imageError = imageValidationError(msg.images);
|
|
22868
|
+
if (imageError) {
|
|
22869
|
+
this.send({ type: "chat_ack", messageId: msg.messageId, accepted: false, error: imageError });
|
|
22870
|
+
return;
|
|
22871
|
+
}
|
|
22872
|
+
this.ensureSession();
|
|
22873
|
+
accepted.set(msg.messageId, this.sessions.current.id);
|
|
22874
|
+
while (accepted.size > 1e3) accepted.delete(accepted.keys().next().value);
|
|
22875
|
+
_SessionHandler.acceptedChats.set(scope, accepted);
|
|
22876
|
+
this.send({ type: "chat_ack", messageId: msg.messageId, accepted: true, sessionId: this.sessions.current.id });
|
|
22877
|
+
}
|
|
22781
22878
|
return this.handleChat(msg.content, msg.images);
|
|
22879
|
+
}
|
|
22782
22880
|
case "command":
|
|
22783
22881
|
return this.handleCommand(msg.name, msg.args);
|
|
22784
22882
|
case "confirm_response":
|
|
@@ -22820,7 +22918,7 @@ var SessionHandler = class {
|
|
|
22820
22918
|
case "hub_start":
|
|
22821
22919
|
return this.handleHubStart(msg);
|
|
22822
22920
|
case "hub_abort":
|
|
22823
|
-
this.
|
|
22921
|
+
this.cancelHub();
|
|
22824
22922
|
return;
|
|
22825
22923
|
case "hub_steer": {
|
|
22826
22924
|
const resolve13 = this.pendingHubReview.get(msg.requestId);
|
|
@@ -22835,7 +22933,10 @@ var SessionHandler = class {
|
|
|
22835
22933
|
return;
|
|
22836
22934
|
}
|
|
22837
22935
|
}
|
|
22838
|
-
onDisconnect() {
|
|
22936
|
+
onDisconnect(closeSocket = true) {
|
|
22937
|
+
if (this.disconnected) return;
|
|
22938
|
+
this.disconnected = true;
|
|
22939
|
+
if (closeSocket && this.ws.readyState === this.ws.OPEN) this.ws.close(1001, "Connection closed; reconnect to restore session");
|
|
22839
22940
|
runLifecycleHooks(mergePluginHooks(this.config.get("hooks") ?? void 0, this.config.getConfigDir()), "Stop", { sessionId: this.sessions.current?.id }, { configDir: this.config.getConfigDir() });
|
|
22840
22941
|
this.toolExecutor.cancelAll();
|
|
22841
22942
|
if (this.abortController) this.abortController.abort();
|
|
@@ -22843,18 +22944,24 @@ var SessionHandler = class {
|
|
|
22843
22944
|
this.pendingAskUser.clear();
|
|
22844
22945
|
for (const resolve13 of this.pendingAutoPause.values()) resolve13({ action: "stop" });
|
|
22845
22946
|
this.pendingAutoPause.clear();
|
|
22846
|
-
this.
|
|
22947
|
+
this.cancelHub();
|
|
22948
|
+
void this.saveIfNeeded();
|
|
22847
22949
|
if (this._bashSessionKey) clearBashSession(this._bashSessionKey);
|
|
22848
22950
|
}
|
|
22951
|
+
cancelHub() {
|
|
22952
|
+
this.hubOrchestrator?.abort();
|
|
22953
|
+
for (const resolve13 of this.pendingHubReview.values()) resolve13({ action: "stop" });
|
|
22954
|
+
this.pendingHubReview.clear();
|
|
22955
|
+
}
|
|
22849
22956
|
/** 根据当前模型 context window 更新工具输出截断上限 */
|
|
22850
22957
|
updateContextWindow() {
|
|
22851
22958
|
try {
|
|
22852
22959
|
const cfgOutputCap = this.config.get("maxToolOutputChars");
|
|
22853
|
-
if (typeof cfgOutputCap === "number" && cfgOutputCap > 0) setMaxOutputCap(cfgOutputCap);
|
|
22854
22960
|
const provider = this.providers.get(this.currentProvider);
|
|
22855
22961
|
const modelInfo = provider?.info.models.find((m) => m.id === this.currentModel);
|
|
22856
|
-
|
|
22962
|
+
this.toolExecutor.maxOutputChars = getMaxOutputChars(modelInfo?.contextWindow, cfgOutputCap ?? 0);
|
|
22857
22963
|
} catch {
|
|
22964
|
+
this.toolExecutor.maxOutputChars = DEFAULT_MAX_TOOL_OUTPUT_CHARS;
|
|
22858
22965
|
}
|
|
22859
22966
|
}
|
|
22860
22967
|
/** 获取当前模型的 context window 大小(0 表示未知)*/
|
|
@@ -22906,12 +23013,24 @@ var SessionHandler = class {
|
|
|
22906
23013
|
return total;
|
|
22907
23014
|
}
|
|
22908
23015
|
/** Save session only if it exists and has messages (never persist empty "Untitled" sessions). */
|
|
22909
|
-
saveIfNeeded() {
|
|
22910
|
-
|
|
22911
|
-
|
|
22912
|
-
|
|
22913
|
-
|
|
22914
|
-
|
|
23016
|
+
async saveIfNeeded() {
|
|
23017
|
+
const session = this.sessions.current;
|
|
23018
|
+
if (!session?.messages.length) return;
|
|
23019
|
+
const scope = this.config.getConfigDir();
|
|
23020
|
+
try {
|
|
23021
|
+
if (session.dirty) autoTrimSessionIfNeeded(session);
|
|
23022
|
+
await this.sessions.save();
|
|
23023
|
+
if (session.dirty) return;
|
|
23024
|
+
this.unsavedSessions.delete(session.id);
|
|
23025
|
+
_SessionHandler.recovery.get(scope)?.delete(session.id);
|
|
23026
|
+
} catch (err) {
|
|
23027
|
+
this.unsavedSessions.set(session.id, session);
|
|
23028
|
+
const recovered = _SessionHandler.recovery.get(scope) ?? /* @__PURE__ */ new Map();
|
|
23029
|
+
recovered.set(session.id, session);
|
|
23030
|
+
_SessionHandler.recovery.set(scope, recovered);
|
|
23031
|
+
const message = `Session ${session.id} was NOT saved: ${err instanceof Error ? err.message : String(err)}. Content is retained in memory; use /session save to retry. Do not stop the server before saving.`;
|
|
23032
|
+
this.send({ type: "info", message });
|
|
23033
|
+
process.stderr.write(message + "\n");
|
|
22915
23034
|
}
|
|
22916
23035
|
}
|
|
22917
23036
|
/** Lazily create a session if none exists yet (deferred from constructor). */
|
|
@@ -23036,20 +23155,15 @@ var SessionHandler = class {
|
|
|
23036
23155
|
const kinds = [...new Set(secretHits.map((h) => h.kind))].join(", ");
|
|
23037
23156
|
this.send({ type: "info", message: "Possible secret in prompt (" + kinds + ")." });
|
|
23038
23157
|
}
|
|
23039
|
-
const
|
|
23040
|
-
|
|
23158
|
+
const imageError = imageValidationError(images);
|
|
23159
|
+
if (imageError) {
|
|
23160
|
+
this.send({ type: "error", message: imageError });
|
|
23161
|
+
return;
|
|
23162
|
+
}
|
|
23041
23163
|
let msgContent;
|
|
23042
23164
|
if (images && images.length > 0) {
|
|
23043
23165
|
const parts = [];
|
|
23044
23166
|
for (const img of images) {
|
|
23045
|
-
if (!ALLOWED_IMAGE_MIMES.has(img.mime)) {
|
|
23046
|
-
this.send({ type: "error", message: `Rejected image: unsupported MIME type "${img.mime}"` });
|
|
23047
|
-
return;
|
|
23048
|
-
}
|
|
23049
|
-
if (img.data && img.data.length > MAX_IMAGE_SIZE) {
|
|
23050
|
-
this.send({ type: "error", message: `Rejected image: too large (${(img.data.length / 1024 / 1024).toFixed(1)} MB, max 10 MB)` });
|
|
23051
|
-
return;
|
|
23052
|
-
}
|
|
23053
23167
|
parts.push({
|
|
23054
23168
|
type: "image_url",
|
|
23055
23169
|
image_url: { url: `data:${img.mime};base64,${img.data}` }
|
|
@@ -23103,7 +23217,7 @@ var SessionHandler = class {
|
|
|
23103
23217
|
this.send({ type: "error", message });
|
|
23104
23218
|
} finally {
|
|
23105
23219
|
this.processing = false;
|
|
23106
|
-
this.saveIfNeeded();
|
|
23220
|
+
await this.saveIfNeeded();
|
|
23107
23221
|
this.sendStatus();
|
|
23108
23222
|
this.sendSessionList();
|
|
23109
23223
|
}
|
|
@@ -23620,6 +23734,10 @@ ${summaryContent}`,
|
|
|
23620
23734
|
}
|
|
23621
23735
|
// ── Commands ─────────────────────────────────────────────────────
|
|
23622
23736
|
async handleCommand(name, args) {
|
|
23737
|
+
if (this.processing && ["session", "clear", "provider", "model", "rewind", "branch", "compact"].includes(name)) {
|
|
23738
|
+
this.send({ type: "info", message: "Stop the current request before changing sessions or conversation settings." });
|
|
23739
|
+
return;
|
|
23740
|
+
}
|
|
23623
23741
|
const cliOnlyReason = CLI_ONLY_WEB_COMMANDS.get(name);
|
|
23624
23742
|
if (cliOnlyReason) {
|
|
23625
23743
|
this.send({
|
|
@@ -24566,7 +24684,7 @@ async function startWebServer(options = {}) {
|
|
|
24566
24684
|
next();
|
|
24567
24685
|
});
|
|
24568
24686
|
const server = createServer(app);
|
|
24569
|
-
const WS_MAX_PAYLOAD =
|
|
24687
|
+
const WS_MAX_PAYLOAD = MAX_CHAT_BYTES;
|
|
24570
24688
|
const WS_MSG_RATE_PER_SEC = 30;
|
|
24571
24689
|
const WS_MSG_BURST = 60;
|
|
24572
24690
|
const WS_PREAUTH_TIMEOUT_MS = 3e4;
|
|
@@ -24639,6 +24757,7 @@ async function startWebServer(options = {}) {
|
|
|
24639
24757
|
app.get("/api/status", (_req, res) => {
|
|
24640
24758
|
res.json({
|
|
24641
24759
|
version: VERSION,
|
|
24760
|
+
chatLimits: { maxChatBytes: MAX_CHAT_BYTES, maxImageBytes: MAX_IMAGE_BYTES },
|
|
24642
24761
|
providers: availableProviders.map((p) => ({
|
|
24643
24762
|
id: p.info.id,
|
|
24644
24763
|
displayName: p.info.displayName,
|
|
@@ -24847,11 +24966,6 @@ async function startWebServer(options = {}) {
|
|
|
24847
24966
|
const tabId = params.get("tabId") || `tab-${Date.now()}`;
|
|
24848
24967
|
const cookies = parseCookie(req.headers.cookie ?? "");
|
|
24849
24968
|
const token = cookies["aicli_token"] || "";
|
|
24850
|
-
const existing = handlers.get(tabId);
|
|
24851
|
-
if (existing) {
|
|
24852
|
-
existing.onDisconnect();
|
|
24853
|
-
handlers.delete(tabId);
|
|
24854
|
-
}
|
|
24855
24969
|
let tokens = WS_MSG_BURST;
|
|
24856
24970
|
let lastRefill = Date.now();
|
|
24857
24971
|
const takeToken = () => {
|
|
@@ -24867,6 +24981,32 @@ async function startWebServer(options = {}) {
|
|
|
24867
24981
|
};
|
|
24868
24982
|
let authenticatedUser = null;
|
|
24869
24983
|
let handler = null;
|
|
24984
|
+
let handlerKey = "";
|
|
24985
|
+
const attachHandler = (resources) => {
|
|
24986
|
+
if (handler) {
|
|
24987
|
+
handler.onDisconnect(false);
|
|
24988
|
+
if (handlers.get(handlerKey) === handler) handlers.delete(handlerKey);
|
|
24989
|
+
}
|
|
24990
|
+
handlerKey = JSON.stringify([authenticatedUser, tabId]);
|
|
24991
|
+
const existing = handlers.get(handlerKey);
|
|
24992
|
+
existing?.onDisconnect();
|
|
24993
|
+
const attached = new SessionHandler(ws, { ...resources, sessions: new SessionManager(resources.config) });
|
|
24994
|
+
handlers.set(handlerKey, attached);
|
|
24995
|
+
return attached;
|
|
24996
|
+
};
|
|
24997
|
+
let alive = true;
|
|
24998
|
+
ws.on("pong", () => {
|
|
24999
|
+
alive = true;
|
|
25000
|
+
});
|
|
25001
|
+
const heartbeat = setInterval(() => {
|
|
25002
|
+
if (!alive) {
|
|
25003
|
+
ws.terminate();
|
|
25004
|
+
return;
|
|
25005
|
+
}
|
|
25006
|
+
alive = false;
|
|
25007
|
+
if (ws.readyState === ws.OPEN) ws.ping();
|
|
25008
|
+
}, 3e4);
|
|
25009
|
+
heartbeat.unref();
|
|
24870
25010
|
let preAuthTimer = setTimeout(() => {
|
|
24871
25011
|
if (!handler && ws.readyState === ws.OPEN) {
|
|
24872
25012
|
try {
|
|
@@ -24887,14 +25027,12 @@ async function startWebServer(options = {}) {
|
|
|
24887
25027
|
const authRequired = authManager.isEnabled();
|
|
24888
25028
|
if (!authRequired) {
|
|
24889
25029
|
console.log(` \u2713 Tab connected: ${tabId.slice(0, 12)} (no auth) (total: ${handlers.size + 1})`);
|
|
24890
|
-
handler =
|
|
24891
|
-
handlers.set(tabId, handler);
|
|
25030
|
+
handler = attachHandler(shared);
|
|
24892
25031
|
clearPreAuthTimer();
|
|
24893
25032
|
} else if (authenticatedUser) {
|
|
24894
25033
|
console.log(` \u2713 Tab connected: ${tabId.slice(0, 12)} (user: ${authenticatedUser}) (total: ${handlers.size + 1})`);
|
|
24895
25034
|
const userShared = getUserShared(authenticatedUser);
|
|
24896
|
-
handler =
|
|
24897
|
-
handlers.set(tabId, handler);
|
|
25035
|
+
handler = attachHandler(userShared);
|
|
24898
25036
|
clearPreAuthTimer();
|
|
24899
25037
|
} else {
|
|
24900
25038
|
console.log(` \u23F3 Tab waiting auth: ${tabId.slice(0, 12)}`);
|
|
@@ -24939,8 +25077,7 @@ async function startWebServer(options = {}) {
|
|
|
24939
25077
|
authManager.login(username, password);
|
|
24940
25078
|
authenticatedUser = username;
|
|
24941
25079
|
const userShared = getUserShared(username);
|
|
24942
|
-
handler =
|
|
24943
|
-
handlers.set(tabId, handler);
|
|
25080
|
+
handler = attachHandler(userShared);
|
|
24944
25081
|
clearPreAuthTimer();
|
|
24945
25082
|
console.log(` \u2713 User registered & connected: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
24946
25083
|
ws.send(JSON.stringify({ type: "auth_result", success: true, username }));
|
|
@@ -24955,8 +25092,7 @@ async function startWebServer(options = {}) {
|
|
|
24955
25092
|
}
|
|
24956
25093
|
authenticatedUser = verifiedUser;
|
|
24957
25094
|
const userShared = getUserShared(verifiedUser);
|
|
24958
|
-
handler =
|
|
24959
|
-
handlers.set(tabId, handler);
|
|
25095
|
+
handler = attachHandler(userShared);
|
|
24960
25096
|
clearPreAuthTimer();
|
|
24961
25097
|
console.log(` \u2713 Token auth: ${verifiedUser} (tab: ${tabId.slice(0, 12)})`);
|
|
24962
25098
|
ws.send(JSON.stringify({ type: "auth_result", success: true, username: verifiedUser }));
|
|
@@ -24970,8 +25106,7 @@ async function startWebServer(options = {}) {
|
|
|
24970
25106
|
}
|
|
24971
25107
|
authenticatedUser = username;
|
|
24972
25108
|
const userShared = getUserShared(username);
|
|
24973
|
-
handler =
|
|
24974
|
-
handlers.set(tabId, handler);
|
|
25109
|
+
handler = attachHandler(userShared);
|
|
24975
25110
|
clearPreAuthTimer();
|
|
24976
25111
|
console.log(` \u2713 User logged in: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
24977
25112
|
ws.send(JSON.stringify({ type: "auth_result", success: true, username }));
|
|
@@ -24999,10 +25134,11 @@ async function startWebServer(options = {}) {
|
|
|
24999
25134
|
}
|
|
25000
25135
|
});
|
|
25001
25136
|
ws.on("close", () => {
|
|
25137
|
+
clearInterval(heartbeat);
|
|
25002
25138
|
clearPreAuthTimer();
|
|
25003
|
-
const wasTracked = handlers.
|
|
25139
|
+
const wasTracked = handlers.get(handlerKey) === handler;
|
|
25004
25140
|
if (handler) handler.onDisconnect();
|
|
25005
|
-
if (wasTracked) handlers.delete(
|
|
25141
|
+
if (wasTracked) handlers.delete(handlerKey);
|
|
25006
25142
|
console.log(` \u2717 Tab disconnected: ${tabId.slice(0, 12)} (total: ${handlers.size})`);
|
|
25007
25143
|
});
|
|
25008
25144
|
ws.on("error", (err) => {
|