jinzd-ai-cli 0.4.304 → 0.4.305
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-OX27K4GG.js} +2 -2
- package/dist/{chunk-7L7T46PI.js → chunk-3ZABGT3S.js} +1 -1
- package/dist/{chunk-RWM2GFRC.js → chunk-45UVJXFQ.js} +6 -2
- package/dist/{chunk-6AGTDWO3.js → chunk-4DH5LZR5.js} +1 -1
- package/dist/{chunk-23SJVOQD.js → chunk-72AR23R2.js} +26 -5
- package/dist/{chunk-OECPGCHP.js → chunk-AXKERUXH.js} +3 -3
- package/dist/{chunk-VOIOUJXI.js → chunk-C2Z77RTU.js} +13 -5
- package/dist/{chunk-F3EJ4MIP.js → chunk-GOIDKTBE.js} +12 -11
- package/dist/{chunk-BSBGPDCX.js → chunk-I6RATYRF.js} +1 -1
- package/dist/{chunk-2PLYXL4P.js → chunk-IPUTC5EX.js} +12 -10
- package/dist/{chunk-7XU4BATV.js → chunk-KBDKKHXB.js} +15 -6
- package/dist/{chunk-4DEMGCK4.js → chunk-LF67HWWR.js} +37 -13
- package/dist/{chunk-RHCRE6XE.js → chunk-NBYSFFCO.js} +1 -1
- package/dist/{chunk-XIMM47EG.js → chunk-NFLE34QG.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-2GRGLTAK.js → chunk-PA4C452B.js} +1 -1
- package/dist/{chunk-I2WJ44D3.js → chunk-Q4DAKI7I.js} +1 -1
- package/dist/{chunk-TBTVV3KK.js → chunk-SZ4226XY.js} +1 -1
- package/dist/{chunk-3FI4TE5S.js → chunk-UFK5CTBU.js} +3 -3
- package/dist/{chunk-PPIISCFN.js → chunk-UFW4S3RU.js} +37 -13
- package/dist/{chunk-CKH4KQ4E.js → chunk-WQ3OMEC4.js} +6 -2
- package/dist/{chunk-UUSRWSSX.js → chunk-XJOK5353.js} +15 -6
- package/dist/{chunk-DNGUSTL6.js → chunk-YPOWDFOV.js} +1 -1
- package/dist/{ci-QYTLBM6Z.js → ci-IIMLOXFH.js} +4 -4
- package/dist/{ci-format-E5X27NC2.js → ci-format-FMBJC3EY.js} +2 -2
- package/dist/{constants-JDPKWZJP.js → constants-55QQ3RYE.js} +1 -1
- package/dist/{constants-F5WA5VPO.js → constants-7NVAPQVR.js} +1 -1
- package/dist/{doctor-cli-7NSEVW63.js → doctor-cli-ZHZ2XOF3.js} +4 -4
- package/dist/electron-server.js +232 -106
- package/dist/{hub-43PWTGGO.js → hub-MF53NIM2.js} +4 -4
- package/dist/{hub-server-RF6LURXY.js → hub-server-CBKCP5VD.js} +1 -1
- package/dist/index.js +24 -23
- package/dist/{indexer-RBEKMFT4.js → indexer-FPKY7LCT.js} +2 -2
- package/dist/{indexer-TNDWGFRI.js → indexer-P3FPRTPJ.js} +2 -2
- package/dist/{persistent-memory-A7RPDLR7.js → persistent-memory-LCOKUDTM.js} +2 -2
- package/dist/{persistent-memory-SHG3CJCP.js → persistent-memory-NACQW2VB.js} +2 -2
- package/dist/{pr-LW755EL7.js → pr-NHWE5FW7.js} +4 -4
- package/dist/{run-tests-MJ3SCW27.js → run-tests-EUPJLDXS.js} +2 -2
- package/dist/{run-tests-HV37CM5U.js → run-tests-ZG5X25BJ.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-EZ5ULLDP.js → server-PG7QCH53.js} +193 -82
- package/dist/{server-WI3Q4EDH.js → server-PNYRE3AL.js} +9 -9
- 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-WAJTCE6D.js} +9 -9
- package/dist/{usage-MAIV6XUZ.js → usage-DOES3DOS.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-YPOWDFOV.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-SZ4226XY.js";
|
|
70
70
|
import {
|
|
71
71
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
72
72
|
APP_NAME,
|
|
@@ -100,12 +100,30 @@ import {
|
|
|
100
100
|
SUBAGENT_MAX_ROUNDS_LIMIT,
|
|
101
101
|
VERSION,
|
|
102
102
|
buildUserIdentityPrompt
|
|
103
|
-
} from "./chunk-
|
|
103
|
+
} from "./chunk-PA4C452B.js";
|
|
104
104
|
|
|
105
105
|
// src/web/server.ts
|
|
106
106
|
import express from "express";
|
|
107
107
|
import { createServer } from "http";
|
|
108
108
|
import { WebSocketServer } from "ws";
|
|
109
|
+
|
|
110
|
+
// src/web/chat-limits.ts
|
|
111
|
+
var MAX_CHAT_BYTES = 16 * 1024 * 1024;
|
|
112
|
+
var MAX_IMAGE_BYTES = 10 * 1024 * 1024;
|
|
113
|
+
var MAX_IMAGE_BASE64_CHARS = Math.ceil(MAX_IMAGE_BYTES / 3) * 4;
|
|
114
|
+
var IMAGE_MIMES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"]);
|
|
115
|
+
function imageValidationError(images) {
|
|
116
|
+
if (images === void 0) return;
|
|
117
|
+
if (!Array.isArray(images)) return "Images must be an array.";
|
|
118
|
+
for (const image of images) {
|
|
119
|
+
if (!image || typeof image.data !== "string" || !IMAGE_MIMES.has(image.mime)) {
|
|
120
|
+
return "Unsupported image format. Use PNG, JPEG, GIF, WebP or SVG.";
|
|
121
|
+
}
|
|
122
|
+
if (image.data.length > MAX_IMAGE_BASE64_CHARS) return "Image too large (maximum 10 MiB before base64 encoding).";
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/web/server.ts
|
|
109
127
|
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
128
|
import { existsSync as existsSync32, readFileSync as readFileSync25, readdirSync as readdirSync15, statSync as statSync16, realpathSync as realpathSync3, createReadStream } from "fs";
|
|
111
129
|
import { networkInterfaces } from "os";
|
|
@@ -7816,26 +7834,11 @@ function checkNetworkPolicy(toolName, args, policy) {
|
|
|
7816
7834
|
// src/tools/truncate.ts
|
|
7817
7835
|
var DEFAULT_MAX_TOOL_OUTPUT_CHARS = 12e3;
|
|
7818
7836
|
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) {
|
|
7837
|
+
function getMaxOutputChars(contextWindow, cap = activeCap) {
|
|
7830
7838
|
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),
|
|
7839
|
+
return Math.max(DEFAULT_MAX_TOOL_OUTPUT_CHARS, Math.min(Math.floor(contextWindow / 4), cap > 0 ? cap : DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP));
|
|
7832
7840
|
}
|
|
7833
7841
|
var activeMaxChars = DEFAULT_MAX_TOOL_OUTPUT_CHARS;
|
|
7834
|
-
var lastContextWindow = 0;
|
|
7835
|
-
function setContextWindow(contextWindow) {
|
|
7836
|
-
lastContextWindow = contextWindow;
|
|
7837
|
-
activeMaxChars = getMaxOutputChars(contextWindow);
|
|
7838
|
-
}
|
|
7839
7842
|
function getActiveMaxChars() {
|
|
7840
7843
|
return activeMaxChars;
|
|
7841
7844
|
}
|
|
@@ -8965,7 +8968,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
8965
8968
|
const mode = appendMode ? "appended" : "written";
|
|
8966
8969
|
void (async () => {
|
|
8967
8970
|
try {
|
|
8968
|
-
const { updateFile } = await import("./indexer-
|
|
8971
|
+
const { updateFile } = await import("./indexer-FPKY7LCT.js");
|
|
8969
8972
|
await updateFile(process.cwd(), filePath);
|
|
8970
8973
|
} catch {
|
|
8971
8974
|
}
|
|
@@ -14096,6 +14099,27 @@ function suggestSkillName(names, input) {
|
|
|
14096
14099
|
// src/web/tool-executor-web.ts
|
|
14097
14100
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
14098
14101
|
import { tmpdir as tmpdir2 } from "os";
|
|
14102
|
+
|
|
14103
|
+
// src/web/socket-send.ts
|
|
14104
|
+
var MAX_PENDING_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
14105
|
+
var stalled = /* @__PURE__ */ new WeakSet();
|
|
14106
|
+
function sendSocketMessage(ws, message) {
|
|
14107
|
+
if (ws.readyState !== ws.OPEN || stalled.has(ws)) return false;
|
|
14108
|
+
const data = JSON.stringify(message);
|
|
14109
|
+
if ((ws.bufferedAmount || 0) + Buffer.byteLength(data) > MAX_PENDING_OUTPUT_BYTES) {
|
|
14110
|
+
stalled.add(ws);
|
|
14111
|
+
ws.close(1013, "Output backlog exceeded; reconnect to reload session");
|
|
14112
|
+
const timer = setTimeout(() => {
|
|
14113
|
+
if (ws.readyState !== ws.CLOSED) ws.terminate();
|
|
14114
|
+
}, 1e3);
|
|
14115
|
+
timer.unref();
|
|
14116
|
+
return false;
|
|
14117
|
+
}
|
|
14118
|
+
ws.send(data);
|
|
14119
|
+
return true;
|
|
14120
|
+
}
|
|
14121
|
+
|
|
14122
|
+
// src/web/tool-executor-web.ts
|
|
14099
14123
|
import { existsSync as existsSync22, readFileSync as readFileSync17 } from "fs";
|
|
14100
14124
|
var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
14101
14125
|
constructor(registry, ws) {
|
|
@@ -14125,6 +14149,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
14125
14149
|
// 5 minutes
|
|
14126
14150
|
/** Publicly readable by SessionHandler to check if confirm is active */
|
|
14127
14151
|
confirming = false;
|
|
14152
|
+
maxOutputChars = DEFAULT_MAX_TOOL_OUTPUT_CHARS;
|
|
14128
14153
|
/** Session-level auto-approve toggle (/yolo command) */
|
|
14129
14154
|
sessionAutoApprove = false;
|
|
14130
14155
|
/** Session-level Auto Mode: rule-based low-risk auto approval. */
|
|
@@ -14207,9 +14232,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
14207
14232
|
this.confirming = false;
|
|
14208
14233
|
}
|
|
14209
14234
|
send(msg) {
|
|
14210
|
-
|
|
14211
|
-
this.ws.send(JSON.stringify(msg));
|
|
14212
|
-
}
|
|
14235
|
+
sendSocketMessage(this.ws, msg);
|
|
14213
14236
|
}
|
|
14214
14237
|
sendToolCallStart(call) {
|
|
14215
14238
|
const dangerLevel = getDangerLevel(call.name, call.arguments);
|
|
@@ -14402,7 +14425,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
14402
14425
|
if (!toolCallStarted) this.sendToolCallStart(call);
|
|
14403
14426
|
try {
|
|
14404
14427
|
const rawContent = await runTool(tool, call.arguments, call.name);
|
|
14405
|
-
const content = truncateOutput(rawContent, call.name);
|
|
14428
|
+
const content = truncateOutput(rawContent, call.name, this.maxOutputChars);
|
|
14406
14429
|
this.sendToolCallResult(call, rawContent, false);
|
|
14407
14430
|
runHook(this.hookConfig?.postToolExecution, { tool: call.name, status: "ok" });
|
|
14408
14431
|
runLifecycleHooks(this.hookConfig, "PostToolUse", { tool: call.name, status: "ok" }, { configDir: this.hookConfigDir });
|
|
@@ -14467,7 +14490,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
14467
14490
|
}
|
|
14468
14491
|
try {
|
|
14469
14492
|
const rawContent = await runTool(tool, call.arguments, call.name);
|
|
14470
|
-
const content = truncateOutput(rawContent, call.name);
|
|
14493
|
+
const content = truncateOutput(rawContent, call.name, this.maxOutputChars);
|
|
14471
14494
|
this.sendToolCallResult(call, rawContent, false);
|
|
14472
14495
|
results[i] = { callId: call.id, content, isError: false };
|
|
14473
14496
|
} catch (err) {
|
|
@@ -15416,6 +15439,7 @@ var HubAgent = class {
|
|
|
15416
15439
|
model: this.modelId,
|
|
15417
15440
|
systemPrompt,
|
|
15418
15441
|
stream: false,
|
|
15442
|
+
signal: opts?.signal,
|
|
15419
15443
|
temperature: 0.7,
|
|
15420
15444
|
maxTokens: 4096,
|
|
15421
15445
|
...this.thinking ?? {}
|
|
@@ -15450,15 +15474,21 @@ var HubAgent = class {
|
|
|
15450
15474
|
model: this.modelId,
|
|
15451
15475
|
systemPrompt,
|
|
15452
15476
|
stream: true,
|
|
15477
|
+
signal: opts?.signal,
|
|
15453
15478
|
temperature: 0.7,
|
|
15454
15479
|
maxTokens: 4096,
|
|
15455
15480
|
...this.thinking ?? {}
|
|
15456
15481
|
});
|
|
15457
|
-
|
|
15458
|
-
|
|
15459
|
-
|
|
15460
|
-
|
|
15482
|
+
try {
|
|
15483
|
+
for await (const chunk of stream) {
|
|
15484
|
+
if (chunk.delta) {
|
|
15485
|
+
raw += chunk.delta;
|
|
15486
|
+
onToken?.(chunk.delta);
|
|
15487
|
+
}
|
|
15488
|
+
if (opts?.signal?.aborted) break;
|
|
15461
15489
|
}
|
|
15490
|
+
} catch (error) {
|
|
15491
|
+
if (!opts?.signal?.aborted) throw error;
|
|
15462
15492
|
}
|
|
15463
15493
|
const { content, passed, converged } = parseTurn(raw);
|
|
15464
15494
|
return {
|
|
@@ -15473,7 +15503,7 @@ var HubAgent = class {
|
|
|
15473
15503
|
/**
|
|
15474
15504
|
* Generate a summary of the entire discussion from this agent's perspective.
|
|
15475
15505
|
*/
|
|
15476
|
-
async summarize(topic, history) {
|
|
15506
|
+
async summarize(topic, history, signal) {
|
|
15477
15507
|
const provider = this.providers.get(this.providerId);
|
|
15478
15508
|
if (!provider) {
|
|
15479
15509
|
throw new Error(`Provider "${this.providerId}" not available`);
|
|
@@ -15490,6 +15520,7 @@ var HubAgent = class {
|
|
|
15490
15520
|
model: this.modelId,
|
|
15491
15521
|
stream: false,
|
|
15492
15522
|
temperature: 0.3,
|
|
15523
|
+
signal,
|
|
15493
15524
|
maxTokens: 4096,
|
|
15494
15525
|
...this.thinking ?? {}
|
|
15495
15526
|
});
|
|
@@ -15583,6 +15614,8 @@ var DiscussionOrchestrator = class {
|
|
|
15583
15614
|
agents = [];
|
|
15584
15615
|
state;
|
|
15585
15616
|
aborted = false;
|
|
15617
|
+
controller = new AbortController();
|
|
15618
|
+
cancelReview;
|
|
15586
15619
|
humanSteer;
|
|
15587
15620
|
voteConverge;
|
|
15588
15621
|
/** Callback for rendering events */
|
|
@@ -15622,6 +15655,8 @@ var DiscussionOrchestrator = class {
|
|
|
15622
15655
|
/** Signal the orchestrator to stop after current turn */
|
|
15623
15656
|
abort() {
|
|
15624
15657
|
this.aborted = true;
|
|
15658
|
+
this.controller.abort();
|
|
15659
|
+
this.cancelReview?.();
|
|
15625
15660
|
}
|
|
15626
15661
|
/**
|
|
15627
15662
|
* Run the full discussion on a given topic.
|
|
@@ -15632,6 +15667,7 @@ var DiscussionOrchestrator = class {
|
|
|
15632
15667
|
this.state.round = 0;
|
|
15633
15668
|
this.state.finished = false;
|
|
15634
15669
|
this.aborted = false;
|
|
15670
|
+
this.controller = new AbortController();
|
|
15635
15671
|
try {
|
|
15636
15672
|
for (let round = 1; round <= this.state.maxRounds; round++) {
|
|
15637
15673
|
if (this.aborted) {
|
|
@@ -15652,7 +15688,7 @@ var DiscussionOrchestrator = class {
|
|
|
15652
15688
|
round,
|
|
15653
15689
|
this.state.maxRounds,
|
|
15654
15690
|
(token) => this.emit({ type: "agent_token", roleId: agent.role.id, token }),
|
|
15655
|
-
{ voteConverge: this.voteConverge }
|
|
15691
|
+
{ voteConverge: this.voteConverge, signal: this.controller.signal }
|
|
15656
15692
|
);
|
|
15657
15693
|
this.state.messages.push(message);
|
|
15658
15694
|
if (message.converged) convergedCount++;
|
|
@@ -15664,6 +15700,7 @@ var DiscussionOrchestrator = class {
|
|
|
15664
15700
|
this.emit({ type: "agent_spoke", roleId: agent.role.id, message });
|
|
15665
15701
|
}
|
|
15666
15702
|
} catch (err) {
|
|
15703
|
+
if (this.aborted) break;
|
|
15667
15704
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
15668
15705
|
this.emit({ type: "error", roleId: agent.role.id, message: errMsg });
|
|
15669
15706
|
this.state.messages.push({
|
|
@@ -15675,6 +15712,10 @@ var DiscussionOrchestrator = class {
|
|
|
15675
15712
|
}
|
|
15676
15713
|
}
|
|
15677
15714
|
this.emit({ type: "round_end", round });
|
|
15715
|
+
if (this.aborted) {
|
|
15716
|
+
this.emit({ type: "discussion_end", reason: "user_interrupt" });
|
|
15717
|
+
break;
|
|
15718
|
+
}
|
|
15678
15719
|
if (allPassed && round > 1) {
|
|
15679
15720
|
this.emit({ type: "discussion_end", reason: "consensus" });
|
|
15680
15721
|
break;
|
|
@@ -15691,7 +15732,18 @@ var DiscussionOrchestrator = class {
|
|
|
15691
15732
|
break;
|
|
15692
15733
|
}
|
|
15693
15734
|
if (this.humanSteer && this.onRoundReview && !this.aborted) {
|
|
15694
|
-
const
|
|
15735
|
+
const stopped = new Promise((resolve13) => {
|
|
15736
|
+
this.cancelReview = () => resolve13({ action: "stop" });
|
|
15737
|
+
});
|
|
15738
|
+
let steer;
|
|
15739
|
+
try {
|
|
15740
|
+
steer = await Promise.race([
|
|
15741
|
+
this.onRoundReview({ round, maxRounds: this.state.maxRounds, state: this.state }),
|
|
15742
|
+
stopped
|
|
15743
|
+
]);
|
|
15744
|
+
} finally {
|
|
15745
|
+
this.cancelReview = void 0;
|
|
15746
|
+
}
|
|
15695
15747
|
if (steer.action === "stop") {
|
|
15696
15748
|
this.emit({ type: "discussion_end", reason: "human_stop" });
|
|
15697
15749
|
break;
|
|
@@ -15712,7 +15764,7 @@ var DiscussionOrchestrator = class {
|
|
|
15712
15764
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
15713
15765
|
this.emit({ type: "error", message: errMsg });
|
|
15714
15766
|
}
|
|
15715
|
-
await this.generateSummary();
|
|
15767
|
+
if (!this.aborted) await this.generateSummary();
|
|
15716
15768
|
this.state.finished = true;
|
|
15717
15769
|
return this.state;
|
|
15718
15770
|
}
|
|
@@ -15730,7 +15782,7 @@ var DiscussionOrchestrator = class {
|
|
|
15730
15782
|
return;
|
|
15731
15783
|
}
|
|
15732
15784
|
try {
|
|
15733
|
-
this.state.summary = await summarizer.summarize(this.state.topic, this.state.messages);
|
|
15785
|
+
this.state.summary = await summarizer.summarize(this.state.topic, this.state.messages, this.controller.signal);
|
|
15734
15786
|
this.emit({ type: "summary", content: this.state.summary });
|
|
15735
15787
|
} catch (err) {
|
|
15736
15788
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -16206,11 +16258,11 @@ function routeDecisionRows(message, currentModel, decision) {
|
|
|
16206
16258
|
}
|
|
16207
16259
|
|
|
16208
16260
|
// src/web/commands/provider-commands.ts
|
|
16209
|
-
function syncSessionProvider(ctx) {
|
|
16261
|
+
async function syncSessionProvider(ctx) {
|
|
16210
16262
|
const session = ctx.sessions.current;
|
|
16211
16263
|
if (!session) return;
|
|
16212
16264
|
session.updateProvider(ctx.currentProvider, ctx.currentModel);
|
|
16213
|
-
ctx.saveIfNeeded();
|
|
16265
|
+
await ctx.saveIfNeeded();
|
|
16214
16266
|
}
|
|
16215
16267
|
async function handleProvider(args, ctx) {
|
|
16216
16268
|
const id = args[0];
|
|
@@ -16253,7 +16305,7 @@ async function handleProvider(args, ctx) {
|
|
|
16253
16305
|
ctx.currentProvider = outcome.providerId;
|
|
16254
16306
|
ctx.currentModel = outcome.model;
|
|
16255
16307
|
ctx.updateContextWindow();
|
|
16256
|
-
syncSessionProvider(ctx);
|
|
16308
|
+
await syncSessionProvider(ctx);
|
|
16257
16309
|
ctx.send({ type: "info", message: outcome.message });
|
|
16258
16310
|
ctx.sendStatus();
|
|
16259
16311
|
}
|
|
@@ -16307,7 +16359,7 @@ async function handleModel(args, ctx) {
|
|
|
16307
16359
|
}
|
|
16308
16360
|
ctx.currentModel = outcome.model;
|
|
16309
16361
|
ctx.updateContextWindow();
|
|
16310
|
-
syncSessionProvider(ctx);
|
|
16362
|
+
await syncSessionProvider(ctx);
|
|
16311
16363
|
ctx.send({
|
|
16312
16364
|
type: "info",
|
|
16313
16365
|
message: outcome.warning ? `${outcome.message} \u2014 ${outcome.warning}` : outcome.message
|
|
@@ -17192,7 +17244,7 @@ async function handleClear(_args, ctx) {
|
|
|
17192
17244
|
const prior = ctx.sessions.current;
|
|
17193
17245
|
const priorId = prior?.id ?? null;
|
|
17194
17246
|
const priorCount = prior?.messages.length ?? 0;
|
|
17195
|
-
ctx.saveIfNeeded();
|
|
17247
|
+
await ctx.saveIfNeeded();
|
|
17196
17248
|
const created = ctx.sessions.createSession(ctx.currentProvider, ctx.currentModel);
|
|
17197
17249
|
ctx.unsavedSessions.set(created.id, created);
|
|
17198
17250
|
ctx.resetWebSessionUsage();
|
|
@@ -17214,7 +17266,7 @@ Cache: write=${cacheCreate} read=${cacheRead}` : "";
|
|
|
17214
17266
|
Cost: ${formatCost(cost)}` : "";
|
|
17215
17267
|
let memoryLine = "";
|
|
17216
17268
|
try {
|
|
17217
|
-
const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-
|
|
17269
|
+
const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-LCOKUDTM.js");
|
|
17218
17270
|
const pending = countPendingMemories2(ctx.config.getConfigDir());
|
|
17219
17271
|
if (pending > 0) memoryLine = `
|
|
17220
17272
|
Memory: \u26A0 ${pending} pending approval \u2014 see the Memory panel or /memory`;
|
|
@@ -17230,8 +17282,13 @@ Tokens: in=${ctx.sessionTokenUsage.inputTokens} out=${ctx.sessionTokenUsage.outp
|
|
|
17230
17282
|
}
|
|
17231
17283
|
async function handleSession(args, ctx) {
|
|
17232
17284
|
const sub = args[0];
|
|
17285
|
+
if (sub === "save") {
|
|
17286
|
+
await ctx.saveIfNeeded();
|
|
17287
|
+
if (!ctx.sessions.current?.dirty) ctx.send({ type: "info", message: "Session saved." });
|
|
17288
|
+
return;
|
|
17289
|
+
}
|
|
17233
17290
|
if (sub === "new") {
|
|
17234
|
-
ctx.saveIfNeeded();
|
|
17291
|
+
await ctx.saveIfNeeded();
|
|
17235
17292
|
const created = ctx.sessions.createSession(ctx.currentProvider, ctx.currentModel);
|
|
17236
17293
|
ctx.unsavedSessions.set(created.id, created);
|
|
17237
17294
|
ctx.resetWebSessionUsage();
|
|
@@ -17242,7 +17299,7 @@ async function handleSession(args, ctx) {
|
|
|
17242
17299
|
}
|
|
17243
17300
|
if (sub === "load" && args[1]) {
|
|
17244
17301
|
const targetId = args[1];
|
|
17245
|
-
ctx.saveIfNeeded();
|
|
17302
|
+
await ctx.saveIfNeeded();
|
|
17246
17303
|
const cachedExact = ctx.unsavedSessions.get(targetId);
|
|
17247
17304
|
const cached = cachedExact ?? [...ctx.unsavedSessions.values()].find((s) => s.id.startsWith(targetId));
|
|
17248
17305
|
if (cached) {
|
|
@@ -18197,7 +18254,7 @@ async function handleTest(args, ctx) {
|
|
|
18197
18254
|
const isCommand = argStr.includes(" ") || /^(mvn|gradle|npm|pytest|cargo|go)\b/.test(argStr);
|
|
18198
18255
|
testArgs = isCommand ? { command: argStr } : { filter: argStr };
|
|
18199
18256
|
}
|
|
18200
|
-
const runTests = ctx.runTests ?? (await import("./run-tests-
|
|
18257
|
+
const runTests = ctx.runTests ?? (await import("./run-tests-EUPJLDXS.js")).executeTests;
|
|
18201
18258
|
const report = await runTests(testArgs);
|
|
18202
18259
|
ctx.send({ type: "info", message: report });
|
|
18203
18260
|
} catch (err) {
|
|
@@ -18489,9 +18546,9 @@ async function handleContext(_args, ctx) {
|
|
|
18489
18546
|
async function handleIndex(args, ctx) {
|
|
18490
18547
|
const sub = (args[0] ?? "status").toLowerCase();
|
|
18491
18548
|
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-
|
|
18549
|
+
const { loadIndex: loadIndex2, clearIndex } = await import("./store-SIKHCUYJ.js");
|
|
18550
|
+
const { indexProject: indexProject2 } = await import("./indexer-FPKY7LCT.js");
|
|
18551
|
+
const { loadVectorStore, clearVectorStore } = await import("./vector-store-JAXWDZYW.js");
|
|
18495
18552
|
if (sub === "status") {
|
|
18496
18553
|
const idx = loadIndex2(root);
|
|
18497
18554
|
const vec = loadVectorStore(root);
|
|
@@ -18514,6 +18571,7 @@ async function handleIndex(args, ctx) {
|
|
|
18514
18571
|
} else if (sub === "rebuild") {
|
|
18515
18572
|
ctx.send({ type: "info", message: `Indexing ${root}\u2026` });
|
|
18516
18573
|
const { stats } = await indexProject2(root, { force: true });
|
|
18574
|
+
if (stats.incomplete) ctx.send({ type: "info", message: "Partial index: scan limit or unreadable files reached. Unscanned entries were retained." });
|
|
18517
18575
|
ctx.send({
|
|
18518
18576
|
type: "info",
|
|
18519
18577
|
message: `\u2713 Indexed ${stats.filesParsed} files (${stats.symbols} symbols) in ${stats.durationMs}ms`
|
|
@@ -18540,7 +18598,7 @@ async function handleIndex(args, ctx) {
|
|
|
18540
18598
|
message: `Building semantic index for ${idx.symbolCount} symbols\u2026 (first run downloads ~117 MB model)`
|
|
18541
18599
|
});
|
|
18542
18600
|
try {
|
|
18543
|
-
const { rebuildSemanticIndex } = await import("./semantic-
|
|
18601
|
+
const { rebuildSemanticIndex } = await import("./semantic-QOBAXEQ4.js");
|
|
18544
18602
|
const stats = await rebuildSemanticIndex(root);
|
|
18545
18603
|
const first = stats.modelFirstLoadMs ? ` (model load+first batch ${stats.modelFirstLoadMs}ms)` : "";
|
|
18546
18604
|
ctx.send({
|
|
@@ -18587,7 +18645,7 @@ async function handleMemory(args, ctx) {
|
|
|
18587
18645
|
}
|
|
18588
18646
|
if (intent.kind === "status") {
|
|
18589
18647
|
const { getChatIndexStatus: getChatIndexStatus2 } = await import("./chat-index-DGUOUUQW.js");
|
|
18590
|
-
const { listMemoryEntries: listMemoryEntries2, isMemoryExpired: isMemoryExpired2, formatMemoryForPrompt: formatMemoryForPrompt2 } = await import("./persistent-memory-
|
|
18648
|
+
const { listMemoryEntries: listMemoryEntries2, isMemoryExpired: isMemoryExpired2, formatMemoryForPrompt: formatMemoryForPrompt2 } = await import("./persistent-memory-LCOKUDTM.js");
|
|
18591
18649
|
const memories = listMemoryEntries2(configDir, { includeExpired: true, includeRejected: true });
|
|
18592
18650
|
const injected = formatMemoryForPrompt2(configDir, process.cwd(), ctx.config.get("memory")?.maxChars);
|
|
18593
18651
|
const view = buildMemoryStatusView({
|
|
@@ -18610,7 +18668,7 @@ async function handleMemory(args, ctx) {
|
|
|
18610
18668
|
return;
|
|
18611
18669
|
}
|
|
18612
18670
|
if (intent.kind === "recall") {
|
|
18613
|
-
const { searchPersistentMemories: searchPersistentMemories2, touchMemoryReferences: touchMemoryReferences2 } = await import("./persistent-memory-
|
|
18671
|
+
const { searchPersistentMemories: searchPersistentMemories2, touchMemoryReferences: touchMemoryReferences2 } = await import("./persistent-memory-LCOKUDTM.js");
|
|
18614
18672
|
const { searchChatMemory: searchChatMemory2 } = await import("./chat-index-DGUOUUQW.js");
|
|
18615
18673
|
const persistentHits = searchPersistentMemories2(configDir, intent.query, process.cwd(), 3);
|
|
18616
18674
|
if (persistentHits.length > 0) {
|
|
@@ -18731,7 +18789,7 @@ async function handleMemory(args, ctx) {
|
|
|
18731
18789
|
ctx.handleMemoryManage(intent.action, args[1], intent.action === "expire" ? args[2] : void 0);
|
|
18732
18790
|
} else if (intent.kind === "export") {
|
|
18733
18791
|
if (args[1] === "md") {
|
|
18734
|
-
const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-
|
|
18792
|
+
const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-LCOKUDTM.js");
|
|
18735
18793
|
ctx.send({
|
|
18736
18794
|
type: "export_data",
|
|
18737
18795
|
format: "md",
|
|
@@ -19736,7 +19794,7 @@ No tools match "${filter}".
|
|
|
19736
19794
|
const { join: join29 } = await import("path");
|
|
19737
19795
|
const { existsSync: existsSync33 } = await import("fs");
|
|
19738
19796
|
const { getGitRoot: getGitRoot2 } = await import("./git-context-VNCA2GU7.js");
|
|
19739
|
-
const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-
|
|
19797
|
+
const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-55QQ3RYE.js");
|
|
19740
19798
|
const { approveProject, hashMcpFile } = await import("./project-trust-MUG325AW.js");
|
|
19741
19799
|
const cwd = process.cwd();
|
|
19742
19800
|
const projectRoot = getGitRoot2(cwd) ?? cwd;
|
|
@@ -20655,7 +20713,7 @@ function createReviewCommands() {
|
|
|
20655
20713
|
usage: "/test [command|filter]",
|
|
20656
20714
|
async execute(args, ctx) {
|
|
20657
20715
|
try {
|
|
20658
|
-
const { executeTests } = await import("./run-tests-
|
|
20716
|
+
const { executeTests } = await import("./run-tests-EUPJLDXS.js");
|
|
20659
20717
|
const argStr = args.join(" ").trim();
|
|
20660
20718
|
let testArgs = {};
|
|
20661
20719
|
if (argStr) {
|
|
@@ -20906,9 +20964,9 @@ function createMemoryCommands() {
|
|
|
20906
20964
|
async execute(args, ctx) {
|
|
20907
20965
|
const sub = (args[0] ?? "status").toLowerCase();
|
|
20908
20966
|
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-
|
|
20967
|
+
const { loadIndex: loadIndex2, clearIndex } = await import("./store-SIKHCUYJ.js");
|
|
20968
|
+
const { indexProject: indexProject2 } = await import("./indexer-FPKY7LCT.js");
|
|
20969
|
+
const { loadVectorStore, clearVectorStore } = await import("./vector-store-JAXWDZYW.js");
|
|
20912
20970
|
if (sub === "status") {
|
|
20913
20971
|
const idx = loadIndex2(root);
|
|
20914
20972
|
const vec = loadVectorStore(root);
|
|
@@ -20939,6 +20997,7 @@ function createMemoryCommands() {
|
|
|
20939
20997
|
}
|
|
20940
20998
|
});
|
|
20941
20999
|
process.stdout.write("\r\x1B[K");
|
|
21000
|
+
if (stats.incomplete) console.log(theme.dim(" Partial index: scan limit or unreadable files reached; unscanned entries retained."));
|
|
20942
21001
|
console.log(theme.success(
|
|
20943
21002
|
` \u2713 Indexed ${stats.filesParsed} files (${stats.symbols} symbols) in ${stats.durationMs}ms`
|
|
20944
21003
|
));
|
|
@@ -20967,7 +21026,7 @@ function createMemoryCommands() {
|
|
|
20967
21026
|
}
|
|
20968
21027
|
console.log(theme.dim(` Building semantic index for ${idx.symbolCount} symbols\u2026`));
|
|
20969
21028
|
console.log(theme.dim(" (First run downloads ~117 MB embedding model to ~/.aicli/models/)"));
|
|
20970
|
-
const { rebuildSemanticIndex } = await import("./semantic-
|
|
21029
|
+
const { rebuildSemanticIndex } = await import("./semantic-QOBAXEQ4.js");
|
|
20971
21030
|
try {
|
|
20972
21031
|
const stats = await rebuildSemanticIndex(root, {
|
|
20973
21032
|
onProgress: (done, total) => {
|
|
@@ -22566,7 +22625,7 @@ var webCommandHandlers = {
|
|
|
22566
22625
|
search: handleSearch,
|
|
22567
22626
|
undo: handleUndo
|
|
22568
22627
|
};
|
|
22569
|
-
var SessionHandler = class {
|
|
22628
|
+
var SessionHandler = class _SessionHandler {
|
|
22570
22629
|
ws;
|
|
22571
22630
|
config;
|
|
22572
22631
|
costTracker;
|
|
@@ -22617,6 +22676,7 @@ var SessionHandler = class {
|
|
|
22617
22676
|
abortController = null;
|
|
22618
22677
|
userInterjection = null;
|
|
22619
22678
|
processing = false;
|
|
22679
|
+
disconnected = false;
|
|
22620
22680
|
/** P4: active hub discussion orchestrator (web room), if any. */
|
|
22621
22681
|
hubOrchestrator = null;
|
|
22622
22682
|
/** P4b: pending between-rounds human-steer prompts (requestId → resolver). */
|
|
@@ -22643,9 +22703,13 @@ var SessionHandler = class {
|
|
|
22643
22703
|
* 会话一旦保存到磁盘(有消息后),会从此 Map 中移除。
|
|
22644
22704
|
*/
|
|
22645
22705
|
unsavedSessions = /* @__PURE__ */ new Map();
|
|
22706
|
+
// Failed saves survive a socket reconnect, scoped to the owning config directory.
|
|
22707
|
+
static recovery = /* @__PURE__ */ new Map();
|
|
22708
|
+
static acceptedChats = /* @__PURE__ */ new Map();
|
|
22646
22709
|
constructor(ws, shared) {
|
|
22647
22710
|
this.ws = ws;
|
|
22648
22711
|
this.config = shared.config;
|
|
22712
|
+
this.unsavedSessions = new Map(_SessionHandler.recovery.get(this.config.getConfigDir()));
|
|
22649
22713
|
this.costTracker = shared.costTracker;
|
|
22650
22714
|
this.providers = shared.providers;
|
|
22651
22715
|
this.sessions = shared.sessions;
|
|
@@ -22713,9 +22777,7 @@ var SessionHandler = class {
|
|
|
22713
22777
|
* 接口之间的任何漂移(少一个成员、签名改了)都会当场编译失败,而不是等到运行时。
|
|
22714
22778
|
*/
|
|
22715
22779
|
send(msg) {
|
|
22716
|
-
|
|
22717
|
-
this.ws.send(JSON.stringify(msg));
|
|
22718
|
-
}
|
|
22780
|
+
sendSocketMessage(this.ws, msg);
|
|
22719
22781
|
}
|
|
22720
22782
|
sendStatus() {
|
|
22721
22783
|
const providerList = this.providers.listAvailable().map((p) => ({
|
|
@@ -22769,6 +22831,7 @@ var SessionHandler = class {
|
|
|
22769
22831
|
});
|
|
22770
22832
|
}
|
|
22771
22833
|
async handleMessage(raw) {
|
|
22834
|
+
if (this.disconnected) return;
|
|
22772
22835
|
let msg;
|
|
22773
22836
|
try {
|
|
22774
22837
|
msg = JSON.parse(raw);
|
|
@@ -22777,8 +22840,33 @@ var SessionHandler = class {
|
|
|
22777
22840
|
return;
|
|
22778
22841
|
}
|
|
22779
22842
|
switch (msg.type) {
|
|
22780
|
-
case "chat":
|
|
22843
|
+
case "chat": {
|
|
22844
|
+
if (msg.messageId) {
|
|
22845
|
+
if (typeof msg.messageId !== "string" || msg.messageId.length > 200) throw new Error("Invalid message ID");
|
|
22846
|
+
const scope = this.config.getConfigDir();
|
|
22847
|
+
const accepted = _SessionHandler.acceptedChats.get(scope) ?? /* @__PURE__ */ new Map();
|
|
22848
|
+
const prior = accepted.get(msg.messageId);
|
|
22849
|
+
if (prior) {
|
|
22850
|
+
this.send({ type: "chat_ack", messageId: msg.messageId, accepted: true, duplicate: true, sessionId: prior });
|
|
22851
|
+
return;
|
|
22852
|
+
}
|
|
22853
|
+
if (this.processing || typeof msg.content !== "string") {
|
|
22854
|
+
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." });
|
|
22855
|
+
return;
|
|
22856
|
+
}
|
|
22857
|
+
const imageError = imageValidationError(msg.images);
|
|
22858
|
+
if (imageError) {
|
|
22859
|
+
this.send({ type: "chat_ack", messageId: msg.messageId, accepted: false, error: imageError });
|
|
22860
|
+
return;
|
|
22861
|
+
}
|
|
22862
|
+
this.ensureSession();
|
|
22863
|
+
accepted.set(msg.messageId, this.sessions.current.id);
|
|
22864
|
+
while (accepted.size > 1e3) accepted.delete(accepted.keys().next().value);
|
|
22865
|
+
_SessionHandler.acceptedChats.set(scope, accepted);
|
|
22866
|
+
this.send({ type: "chat_ack", messageId: msg.messageId, accepted: true, sessionId: this.sessions.current.id });
|
|
22867
|
+
}
|
|
22781
22868
|
return this.handleChat(msg.content, msg.images);
|
|
22869
|
+
}
|
|
22782
22870
|
case "command":
|
|
22783
22871
|
return this.handleCommand(msg.name, msg.args);
|
|
22784
22872
|
case "confirm_response":
|
|
@@ -22820,7 +22908,7 @@ var SessionHandler = class {
|
|
|
22820
22908
|
case "hub_start":
|
|
22821
22909
|
return this.handleHubStart(msg);
|
|
22822
22910
|
case "hub_abort":
|
|
22823
|
-
this.
|
|
22911
|
+
this.cancelHub();
|
|
22824
22912
|
return;
|
|
22825
22913
|
case "hub_steer": {
|
|
22826
22914
|
const resolve13 = this.pendingHubReview.get(msg.requestId);
|
|
@@ -22835,7 +22923,10 @@ var SessionHandler = class {
|
|
|
22835
22923
|
return;
|
|
22836
22924
|
}
|
|
22837
22925
|
}
|
|
22838
|
-
onDisconnect() {
|
|
22926
|
+
onDisconnect(closeSocket = true) {
|
|
22927
|
+
if (this.disconnected) return;
|
|
22928
|
+
this.disconnected = true;
|
|
22929
|
+
if (closeSocket && this.ws.readyState === this.ws.OPEN) this.ws.close(1001, "Connection closed; reconnect to restore session");
|
|
22839
22930
|
runLifecycleHooks(mergePluginHooks(this.config.get("hooks") ?? void 0, this.config.getConfigDir()), "Stop", { sessionId: this.sessions.current?.id }, { configDir: this.config.getConfigDir() });
|
|
22840
22931
|
this.toolExecutor.cancelAll();
|
|
22841
22932
|
if (this.abortController) this.abortController.abort();
|
|
@@ -22843,18 +22934,24 @@ var SessionHandler = class {
|
|
|
22843
22934
|
this.pendingAskUser.clear();
|
|
22844
22935
|
for (const resolve13 of this.pendingAutoPause.values()) resolve13({ action: "stop" });
|
|
22845
22936
|
this.pendingAutoPause.clear();
|
|
22846
|
-
this.
|
|
22937
|
+
this.cancelHub();
|
|
22938
|
+
void this.saveIfNeeded();
|
|
22847
22939
|
if (this._bashSessionKey) clearBashSession(this._bashSessionKey);
|
|
22848
22940
|
}
|
|
22941
|
+
cancelHub() {
|
|
22942
|
+
this.hubOrchestrator?.abort();
|
|
22943
|
+
for (const resolve13 of this.pendingHubReview.values()) resolve13({ action: "stop" });
|
|
22944
|
+
this.pendingHubReview.clear();
|
|
22945
|
+
}
|
|
22849
22946
|
/** 根据当前模型 context window 更新工具输出截断上限 */
|
|
22850
22947
|
updateContextWindow() {
|
|
22851
22948
|
try {
|
|
22852
22949
|
const cfgOutputCap = this.config.get("maxToolOutputChars");
|
|
22853
|
-
if (typeof cfgOutputCap === "number" && cfgOutputCap > 0) setMaxOutputCap(cfgOutputCap);
|
|
22854
22950
|
const provider = this.providers.get(this.currentProvider);
|
|
22855
22951
|
const modelInfo = provider?.info.models.find((m) => m.id === this.currentModel);
|
|
22856
|
-
|
|
22952
|
+
this.toolExecutor.maxOutputChars = getMaxOutputChars(modelInfo?.contextWindow, cfgOutputCap ?? 0);
|
|
22857
22953
|
} catch {
|
|
22954
|
+
this.toolExecutor.maxOutputChars = DEFAULT_MAX_TOOL_OUTPUT_CHARS;
|
|
22858
22955
|
}
|
|
22859
22956
|
}
|
|
22860
22957
|
/** 获取当前模型的 context window 大小(0 表示未知)*/
|
|
@@ -22906,12 +23003,24 @@ var SessionHandler = class {
|
|
|
22906
23003
|
return total;
|
|
22907
23004
|
}
|
|
22908
23005
|
/** Save session only if it exists and has messages (never persist empty "Untitled" sessions). */
|
|
22909
|
-
saveIfNeeded() {
|
|
22910
|
-
|
|
22911
|
-
|
|
22912
|
-
|
|
22913
|
-
|
|
22914
|
-
|
|
23006
|
+
async saveIfNeeded() {
|
|
23007
|
+
const session = this.sessions.current;
|
|
23008
|
+
if (!session?.messages.length) return;
|
|
23009
|
+
const scope = this.config.getConfigDir();
|
|
23010
|
+
try {
|
|
23011
|
+
if (session.dirty) autoTrimSessionIfNeeded(session);
|
|
23012
|
+
await this.sessions.save();
|
|
23013
|
+
if (session.dirty) return;
|
|
23014
|
+
this.unsavedSessions.delete(session.id);
|
|
23015
|
+
_SessionHandler.recovery.get(scope)?.delete(session.id);
|
|
23016
|
+
} catch (err) {
|
|
23017
|
+
this.unsavedSessions.set(session.id, session);
|
|
23018
|
+
const recovered = _SessionHandler.recovery.get(scope) ?? /* @__PURE__ */ new Map();
|
|
23019
|
+
recovered.set(session.id, session);
|
|
23020
|
+
_SessionHandler.recovery.set(scope, recovered);
|
|
23021
|
+
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.`;
|
|
23022
|
+
this.send({ type: "info", message });
|
|
23023
|
+
process.stderr.write(message + "\n");
|
|
22915
23024
|
}
|
|
22916
23025
|
}
|
|
22917
23026
|
/** Lazily create a session if none exists yet (deferred from constructor). */
|
|
@@ -23036,20 +23145,15 @@ var SessionHandler = class {
|
|
|
23036
23145
|
const kinds = [...new Set(secretHits.map((h) => h.kind))].join(", ");
|
|
23037
23146
|
this.send({ type: "info", message: "Possible secret in prompt (" + kinds + ")." });
|
|
23038
23147
|
}
|
|
23039
|
-
const
|
|
23040
|
-
|
|
23148
|
+
const imageError = imageValidationError(images);
|
|
23149
|
+
if (imageError) {
|
|
23150
|
+
this.send({ type: "error", message: imageError });
|
|
23151
|
+
return;
|
|
23152
|
+
}
|
|
23041
23153
|
let msgContent;
|
|
23042
23154
|
if (images && images.length > 0) {
|
|
23043
23155
|
const parts = [];
|
|
23044
23156
|
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
23157
|
parts.push({
|
|
23054
23158
|
type: "image_url",
|
|
23055
23159
|
image_url: { url: `data:${img.mime};base64,${img.data}` }
|
|
@@ -23103,7 +23207,7 @@ var SessionHandler = class {
|
|
|
23103
23207
|
this.send({ type: "error", message });
|
|
23104
23208
|
} finally {
|
|
23105
23209
|
this.processing = false;
|
|
23106
|
-
this.saveIfNeeded();
|
|
23210
|
+
await this.saveIfNeeded();
|
|
23107
23211
|
this.sendStatus();
|
|
23108
23212
|
this.sendSessionList();
|
|
23109
23213
|
}
|
|
@@ -23620,6 +23724,10 @@ ${summaryContent}`,
|
|
|
23620
23724
|
}
|
|
23621
23725
|
// ── Commands ─────────────────────────────────────────────────────
|
|
23622
23726
|
async handleCommand(name, args) {
|
|
23727
|
+
if (this.processing && ["session", "clear", "provider", "model", "rewind", "branch", "compact"].includes(name)) {
|
|
23728
|
+
this.send({ type: "info", message: "Stop the current request before changing sessions or conversation settings." });
|
|
23729
|
+
return;
|
|
23730
|
+
}
|
|
23623
23731
|
const cliOnlyReason = CLI_ONLY_WEB_COMMANDS.get(name);
|
|
23624
23732
|
if (cliOnlyReason) {
|
|
23625
23733
|
this.send({
|
|
@@ -24566,7 +24674,7 @@ async function startWebServer(options = {}) {
|
|
|
24566
24674
|
next();
|
|
24567
24675
|
});
|
|
24568
24676
|
const server = createServer(app);
|
|
24569
|
-
const WS_MAX_PAYLOAD =
|
|
24677
|
+
const WS_MAX_PAYLOAD = MAX_CHAT_BYTES;
|
|
24570
24678
|
const WS_MSG_RATE_PER_SEC = 30;
|
|
24571
24679
|
const WS_MSG_BURST = 60;
|
|
24572
24680
|
const WS_PREAUTH_TIMEOUT_MS = 3e4;
|
|
@@ -24639,6 +24747,7 @@ async function startWebServer(options = {}) {
|
|
|
24639
24747
|
app.get("/api/status", (_req, res) => {
|
|
24640
24748
|
res.json({
|
|
24641
24749
|
version: VERSION,
|
|
24750
|
+
chatLimits: { maxChatBytes: MAX_CHAT_BYTES, maxImageBytes: MAX_IMAGE_BYTES },
|
|
24642
24751
|
providers: availableProviders.map((p) => ({
|
|
24643
24752
|
id: p.info.id,
|
|
24644
24753
|
displayName: p.info.displayName,
|
|
@@ -24847,11 +24956,6 @@ async function startWebServer(options = {}) {
|
|
|
24847
24956
|
const tabId = params.get("tabId") || `tab-${Date.now()}`;
|
|
24848
24957
|
const cookies = parseCookie(req.headers.cookie ?? "");
|
|
24849
24958
|
const token = cookies["aicli_token"] || "";
|
|
24850
|
-
const existing = handlers.get(tabId);
|
|
24851
|
-
if (existing) {
|
|
24852
|
-
existing.onDisconnect();
|
|
24853
|
-
handlers.delete(tabId);
|
|
24854
|
-
}
|
|
24855
24959
|
let tokens = WS_MSG_BURST;
|
|
24856
24960
|
let lastRefill = Date.now();
|
|
24857
24961
|
const takeToken = () => {
|
|
@@ -24867,6 +24971,32 @@ async function startWebServer(options = {}) {
|
|
|
24867
24971
|
};
|
|
24868
24972
|
let authenticatedUser = null;
|
|
24869
24973
|
let handler = null;
|
|
24974
|
+
let handlerKey = "";
|
|
24975
|
+
const attachHandler = (resources) => {
|
|
24976
|
+
if (handler) {
|
|
24977
|
+
handler.onDisconnect(false);
|
|
24978
|
+
if (handlers.get(handlerKey) === handler) handlers.delete(handlerKey);
|
|
24979
|
+
}
|
|
24980
|
+
handlerKey = JSON.stringify([authenticatedUser, tabId]);
|
|
24981
|
+
const existing = handlers.get(handlerKey);
|
|
24982
|
+
existing?.onDisconnect();
|
|
24983
|
+
const attached = new SessionHandler(ws, { ...resources, sessions: new SessionManager(resources.config) });
|
|
24984
|
+
handlers.set(handlerKey, attached);
|
|
24985
|
+
return attached;
|
|
24986
|
+
};
|
|
24987
|
+
let alive = true;
|
|
24988
|
+
ws.on("pong", () => {
|
|
24989
|
+
alive = true;
|
|
24990
|
+
});
|
|
24991
|
+
const heartbeat = setInterval(() => {
|
|
24992
|
+
if (!alive) {
|
|
24993
|
+
ws.terminate();
|
|
24994
|
+
return;
|
|
24995
|
+
}
|
|
24996
|
+
alive = false;
|
|
24997
|
+
if (ws.readyState === ws.OPEN) ws.ping();
|
|
24998
|
+
}, 3e4);
|
|
24999
|
+
heartbeat.unref();
|
|
24870
25000
|
let preAuthTimer = setTimeout(() => {
|
|
24871
25001
|
if (!handler && ws.readyState === ws.OPEN) {
|
|
24872
25002
|
try {
|
|
@@ -24887,14 +25017,12 @@ async function startWebServer(options = {}) {
|
|
|
24887
25017
|
const authRequired = authManager.isEnabled();
|
|
24888
25018
|
if (!authRequired) {
|
|
24889
25019
|
console.log(` \u2713 Tab connected: ${tabId.slice(0, 12)} (no auth) (total: ${handlers.size + 1})`);
|
|
24890
|
-
handler =
|
|
24891
|
-
handlers.set(tabId, handler);
|
|
25020
|
+
handler = attachHandler(shared);
|
|
24892
25021
|
clearPreAuthTimer();
|
|
24893
25022
|
} else if (authenticatedUser) {
|
|
24894
25023
|
console.log(` \u2713 Tab connected: ${tabId.slice(0, 12)} (user: ${authenticatedUser}) (total: ${handlers.size + 1})`);
|
|
24895
25024
|
const userShared = getUserShared(authenticatedUser);
|
|
24896
|
-
handler =
|
|
24897
|
-
handlers.set(tabId, handler);
|
|
25025
|
+
handler = attachHandler(userShared);
|
|
24898
25026
|
clearPreAuthTimer();
|
|
24899
25027
|
} else {
|
|
24900
25028
|
console.log(` \u23F3 Tab waiting auth: ${tabId.slice(0, 12)}`);
|
|
@@ -24939,8 +25067,7 @@ async function startWebServer(options = {}) {
|
|
|
24939
25067
|
authManager.login(username, password);
|
|
24940
25068
|
authenticatedUser = username;
|
|
24941
25069
|
const userShared = getUserShared(username);
|
|
24942
|
-
handler =
|
|
24943
|
-
handlers.set(tabId, handler);
|
|
25070
|
+
handler = attachHandler(userShared);
|
|
24944
25071
|
clearPreAuthTimer();
|
|
24945
25072
|
console.log(` \u2713 User registered & connected: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
24946
25073
|
ws.send(JSON.stringify({ type: "auth_result", success: true, username }));
|
|
@@ -24955,8 +25082,7 @@ async function startWebServer(options = {}) {
|
|
|
24955
25082
|
}
|
|
24956
25083
|
authenticatedUser = verifiedUser;
|
|
24957
25084
|
const userShared = getUserShared(verifiedUser);
|
|
24958
|
-
handler =
|
|
24959
|
-
handlers.set(tabId, handler);
|
|
25085
|
+
handler = attachHandler(userShared);
|
|
24960
25086
|
clearPreAuthTimer();
|
|
24961
25087
|
console.log(` \u2713 Token auth: ${verifiedUser} (tab: ${tabId.slice(0, 12)})`);
|
|
24962
25088
|
ws.send(JSON.stringify({ type: "auth_result", success: true, username: verifiedUser }));
|
|
@@ -24970,8 +25096,7 @@ async function startWebServer(options = {}) {
|
|
|
24970
25096
|
}
|
|
24971
25097
|
authenticatedUser = username;
|
|
24972
25098
|
const userShared = getUserShared(username);
|
|
24973
|
-
handler =
|
|
24974
|
-
handlers.set(tabId, handler);
|
|
25099
|
+
handler = attachHandler(userShared);
|
|
24975
25100
|
clearPreAuthTimer();
|
|
24976
25101
|
console.log(` \u2713 User logged in: ${username} (tab: ${tabId.slice(0, 12)})`);
|
|
24977
25102
|
ws.send(JSON.stringify({ type: "auth_result", success: true, username }));
|
|
@@ -24999,10 +25124,11 @@ async function startWebServer(options = {}) {
|
|
|
24999
25124
|
}
|
|
25000
25125
|
});
|
|
25001
25126
|
ws.on("close", () => {
|
|
25127
|
+
clearInterval(heartbeat);
|
|
25002
25128
|
clearPreAuthTimer();
|
|
25003
|
-
const wasTracked = handlers.
|
|
25129
|
+
const wasTracked = handlers.get(handlerKey) === handler;
|
|
25004
25130
|
if (handler) handler.onDisconnect();
|
|
25005
|
-
if (wasTracked) handlers.delete(
|
|
25131
|
+
if (wasTracked) handlers.delete(handlerKey);
|
|
25006
25132
|
console.log(` \u2717 Tab disconnected: ${tabId.slice(0, 12)} (total: ${handlers.size})`);
|
|
25007
25133
|
});
|
|
25008
25134
|
ws.on("error", (err) => {
|