jinzd-ai-cli 0.4.11 → 0.4.13
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/{chunk-XIG6AWFW.js → chunk-52BIUGQ2.js} +25 -5
- package/dist/{chunk-QQFHC5ZL.js → chunk-DP3M26PP.js} +1 -1
- package/dist/{chunk-6VJEC2KE.js → chunk-TLOCRYJC.js} +1 -1
- package/dist/{chunk-ZNEOCSLS.js → chunk-TXCLZ72H.js} +23 -4
- package/dist/{hub-BDIESJ2N.js → hub-LURPCJ4Z.js} +1 -1
- package/dist/index.js +10 -6
- package/dist/{run-tests-F7MPQZSN.js → run-tests-PVVT37LK.js} +1 -1
- package/dist/{run-tests-YH63BYL2.js → run-tests-UPW6W4QE.js} +1 -1
- package/dist/{server-A37MB2OC.js → server-KPNMFMDU.js} +39 -5
- package/dist/{task-orchestrator-75SMDOOF.js → task-orchestrator-XXOS7WHR.js} +59 -18
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
EnvLoader,
|
|
4
4
|
schemaToJsonSchema
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-TXCLZ72H.js";
|
|
6
6
|
import {
|
|
7
7
|
APP_NAME,
|
|
8
8
|
CONFIG_DIR_NAME,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
MCP_TOOL_PREFIX,
|
|
16
16
|
PLUGINS_DIR_NAME,
|
|
17
17
|
VERSION
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-DP3M26PP.js";
|
|
19
19
|
|
|
20
20
|
// src/config/config-manager.ts
|
|
21
21
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
@@ -277,7 +277,8 @@ ${err}`
|
|
|
277
277
|
if (rawValue === "true") value = true;
|
|
278
278
|
else if (rawValue === "false") value = false;
|
|
279
279
|
else if (rawValue !== "" && !isNaN(Number(rawValue))) value = Number(rawValue);
|
|
280
|
-
|
|
280
|
+
const draft = JSON.parse(JSON.stringify(this.config));
|
|
281
|
+
let current = draft;
|
|
281
282
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
282
283
|
const key = keys[i];
|
|
283
284
|
if (current[key] == null || typeof current[key] !== "object") {
|
|
@@ -286,6 +287,12 @@ ${err}`
|
|
|
286
287
|
current = current[key];
|
|
287
288
|
}
|
|
288
289
|
current[keys[keys.length - 1]] = value;
|
|
290
|
+
const result = ConfigSchema.safeParse(draft);
|
|
291
|
+
if (!result.success) {
|
|
292
|
+
const firstErr = result.error.errors[0];
|
|
293
|
+
throw new ConfigError(`Invalid config value for "${path}": ${firstErr?.message ?? "validation failed"}`);
|
|
294
|
+
}
|
|
295
|
+
this.config = result.data;
|
|
289
296
|
this.save();
|
|
290
297
|
}
|
|
291
298
|
/** 获取完整配置对象的格式化 JSON 字符串(用于 /config show 等展示) */
|
|
@@ -1331,6 +1338,9 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
1331
1338
|
}
|
|
1332
1339
|
yield { type: "done" };
|
|
1333
1340
|
} catch (err) {
|
|
1341
|
+
if (err instanceof Error && (err.name === "AbortError" || err.name === "TimeoutError")) {
|
|
1342
|
+
throw err;
|
|
1343
|
+
}
|
|
1334
1344
|
throw this.wrapError(err);
|
|
1335
1345
|
}
|
|
1336
1346
|
}
|
|
@@ -2073,7 +2083,7 @@ var ProviderRegistry = class {
|
|
|
2073
2083
|
};
|
|
2074
2084
|
|
|
2075
2085
|
// src/session/session-manager.ts
|
|
2076
|
-
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, unlinkSync } from "fs";
|
|
2086
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, unlinkSync, renameSync } from "fs";
|
|
2077
2087
|
import { join as join2 } from "path";
|
|
2078
2088
|
import { v4 as uuidv4 } from "uuid";
|
|
2079
2089
|
|
|
@@ -2298,7 +2308,9 @@ var SessionManager = class {
|
|
|
2298
2308
|
if (!this._current) return;
|
|
2299
2309
|
mkdirSync2(this.historyDir, { recursive: true });
|
|
2300
2310
|
const filePath = join2(this.historyDir, `${this._current.id}.json`);
|
|
2301
|
-
|
|
2311
|
+
const tmpPath = filePath + ".tmp";
|
|
2312
|
+
writeFileSync2(tmpPath, JSON.stringify(this._current.toJSON(), null, 2), "utf-8");
|
|
2313
|
+
renameSync(tmpPath, filePath);
|
|
2302
2314
|
}
|
|
2303
2315
|
loadSession(id) {
|
|
2304
2316
|
const filePath = join2(this.historyDir, `${id}.json`);
|
|
@@ -2530,6 +2542,7 @@ var McpClient = class {
|
|
|
2530
2542
|
config;
|
|
2531
2543
|
process = null;
|
|
2532
2544
|
nextId = 1;
|
|
2545
|
+
// M8: wraps at MAX_SAFE_INTEGER via getNextId()
|
|
2533
2546
|
connected = false;
|
|
2534
2547
|
serverInfo = null;
|
|
2535
2548
|
/** stderr 收集(最多保留最后 2KB,用于错误报告) */
|
|
@@ -2662,6 +2675,7 @@ var McpClient = class {
|
|
|
2662
2675
|
return reject(new Error(`MCP server [${this.serverId}] stdin not writable`));
|
|
2663
2676
|
}
|
|
2664
2677
|
const id = this.nextId++;
|
|
2678
|
+
if (this.nextId > Number.MAX_SAFE_INTEGER - 1) this.nextId = 1;
|
|
2665
2679
|
const request = {
|
|
2666
2680
|
jsonrpc: "2.0",
|
|
2667
2681
|
id,
|
|
@@ -2712,6 +2726,12 @@ var McpClient = class {
|
|
|
2712
2726
|
*/
|
|
2713
2727
|
handleStdoutData(chunk) {
|
|
2714
2728
|
this.stdoutBuffer += chunk;
|
|
2729
|
+
if (this.stdoutBuffer.length > 1048576) {
|
|
2730
|
+
process.stderr.write(`[mcp] stdout buffer exceeded 1MB \u2014 clearing
|
|
2731
|
+
`);
|
|
2732
|
+
this.stdoutBuffer = "";
|
|
2733
|
+
return;
|
|
2734
|
+
}
|
|
2715
2735
|
const lines = this.stdoutBuffer.split("\n");
|
|
2716
2736
|
this.stdoutBuffer = lines.pop() ?? "";
|
|
2717
2737
|
for (const line of lines) {
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
7
7
|
SUBAGENT_MAX_ROUNDS_LIMIT,
|
|
8
8
|
runTestsTool
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-DP3M26PP.js";
|
|
10
10
|
|
|
11
11
|
// src/tools/builtin/bash.ts
|
|
12
12
|
import { execSync } from "child_process";
|
|
@@ -534,6 +534,13 @@ Suggestion: read existing text versions (.md / .txt) in the project, or install
|
|
|
534
534
|
if (BINARY_EXTENSIONS.has(ext)) {
|
|
535
535
|
return `[Binary file: ${filePath} (${ext})]
|
|
536
536
|
This is a binary file and cannot be read as text. If there is a text version (.md / .txt) in the project, please read that instead.`;
|
|
537
|
+
}
|
|
538
|
+
try {
|
|
539
|
+
const fstat = statSync2(normalizedPath);
|
|
540
|
+
if (fstat.size > MAX_FILE_BYTES) {
|
|
541
|
+
return `[File too large: ${filePath} | ${(fstat.size / 1024 / 1024).toFixed(1)} MB exceeds ${MAX_FILE_BYTES / 1024 / 1024} MB limit]`;
|
|
542
|
+
}
|
|
543
|
+
} catch {
|
|
537
544
|
}
|
|
538
545
|
const buf = readFileSync2(normalizedPath);
|
|
539
546
|
if (encoding === "base64") {
|
|
@@ -1003,6 +1010,10 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
|
|
|
1003
1010
|
const maxResults = Math.max(1, Number(args["max_results"] ?? 50));
|
|
1004
1011
|
if (!pattern) throw new Error("pattern is required");
|
|
1005
1012
|
if (!existsSync6(rootPath)) throw new Error(`Path not found: ${rootPath}`);
|
|
1013
|
+
const MAX_PATTERN_LENGTH = 1e3;
|
|
1014
|
+
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
1015
|
+
throw new Error(`Pattern too long (${pattern.length} chars, max ${MAX_PATTERN_LENGTH}). Use a shorter pattern.`);
|
|
1016
|
+
}
|
|
1006
1017
|
let regex;
|
|
1007
1018
|
try {
|
|
1008
1019
|
regex = new RegExp(pattern, ignoreCase ? "gi" : "g");
|
|
@@ -1386,11 +1397,11 @@ ${stderr}`);
|
|
|
1386
1397
|
// src/tools/builtin/web-fetch.ts
|
|
1387
1398
|
import { promises as dnsPromises } from "dns";
|
|
1388
1399
|
function htmlToText(html) {
|
|
1400
|
+
let text = html.replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<noscript[\s\S]*?<\/noscript>/gi, "").replace(/<svg[\s\S]*?<\/svg>/gi, "");
|
|
1389
1401
|
const HTML_REGEX_LIMIT = 2e5;
|
|
1390
|
-
if (
|
|
1391
|
-
|
|
1402
|
+
if (text.length > HTML_REGEX_LIMIT) {
|
|
1403
|
+
text = text.slice(0, HTML_REGEX_LIMIT);
|
|
1392
1404
|
}
|
|
1393
|
-
let text = html.replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<noscript[\s\S]*?<\/noscript>/gi, "").replace(/<svg[\s\S]*?<\/svg>/gi, "");
|
|
1394
1405
|
text = text.replace(/<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi, (_m, lvl, content) => {
|
|
1395
1406
|
const prefix = "#".repeat(Number(lvl));
|
|
1396
1407
|
return `
|
|
@@ -1848,6 +1859,14 @@ var EnvLoader = class {
|
|
|
1848
1859
|
if (val) return val;
|
|
1849
1860
|
}
|
|
1850
1861
|
const dynamicEnvVar = `AICLI_API_KEY_${providerId.toUpperCase().replace(/-/g, "_")}`;
|
|
1862
|
+
if (fixedEnvVar && fixedEnvVar !== dynamicEnvVar) {
|
|
1863
|
+
const fixedVal = process.env[fixedEnvVar];
|
|
1864
|
+
const dynVal = process.env[dynamicEnvVar];
|
|
1865
|
+
if (fixedVal && dynVal && fixedVal !== dynVal) {
|
|
1866
|
+
process.stderr.write(`[warn] env var collision: ${fixedEnvVar} and ${dynamicEnvVar} have different values for provider "${providerId}". Using ${fixedEnvVar}.
|
|
1867
|
+
`);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1851
1870
|
return process.env[dynamicEnvVar] || void 0;
|
|
1852
1871
|
}
|
|
1853
1872
|
static getDefaultProvider() {
|
|
@@ -381,7 +381,7 @@ ${content}`);
|
|
|
381
381
|
}
|
|
382
382
|
}
|
|
383
383
|
async function runTaskMode(config, providers, configManager, topic) {
|
|
384
|
-
const { TaskOrchestrator } = await import("./task-orchestrator-
|
|
384
|
+
const { TaskOrchestrator } = await import("./task-orchestrator-XXOS7WHR.js");
|
|
385
385
|
const orchestrator = new TaskOrchestrator(config, providers, configManager);
|
|
386
386
|
let interrupted = false;
|
|
387
387
|
const onSigint = () => {
|
package/dist/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
saveDevState,
|
|
24
24
|
sessionHasMeaningfulContent,
|
|
25
25
|
setupProxy
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-52BIUGQ2.js";
|
|
27
27
|
import {
|
|
28
28
|
ToolRegistry,
|
|
29
29
|
askUserContext,
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
theme,
|
|
39
39
|
truncateOutput,
|
|
40
40
|
undoStack
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-TXCLZ72H.js";
|
|
42
42
|
import {
|
|
43
43
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
44
44
|
AUTHOR,
|
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
REPO_URL,
|
|
59
59
|
SKILLS_DIR_NAME,
|
|
60
60
|
VERSION
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-DP3M26PP.js";
|
|
62
62
|
|
|
63
63
|
// src/index.ts
|
|
64
64
|
import { program } from "commander";
|
|
@@ -1914,7 +1914,7 @@ ${hint}` : "")
|
|
|
1914
1914
|
description: "Run project tests and show structured report",
|
|
1915
1915
|
usage: "/test [command|filter]",
|
|
1916
1916
|
async execute(args, _ctx) {
|
|
1917
|
-
const { executeTests } = await import("./run-tests-
|
|
1917
|
+
const { executeTests } = await import("./run-tests-PVVT37LK.js");
|
|
1918
1918
|
const argStr = args.join(" ").trim();
|
|
1919
1919
|
let testArgs = {};
|
|
1920
1920
|
if (argStr) {
|
|
@@ -4254,6 +4254,10 @@ Session '${this.resumeSessionId}' not found.
|
|
|
4254
4254
|
}, PASTE_DEBOUNCE_MS);
|
|
4255
4255
|
});
|
|
4256
4256
|
this.rl.on("close", () => {
|
|
4257
|
+
if (pasteTimer) {
|
|
4258
|
+
clearTimeout(pasteTimer);
|
|
4259
|
+
pasteTimer = null;
|
|
4260
|
+
}
|
|
4257
4261
|
if (!processing) {
|
|
4258
4262
|
resolve3();
|
|
4259
4263
|
}
|
|
@@ -5524,7 +5528,7 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
5524
5528
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
5525
5529
|
process.exit(1);
|
|
5526
5530
|
}
|
|
5527
|
-
const { startWebServer } = await import("./server-
|
|
5531
|
+
const { startWebServer } = await import("./server-KPNMFMDU.js");
|
|
5528
5532
|
await startWebServer({ port, host: options.host });
|
|
5529
5533
|
});
|
|
5530
5534
|
program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
|
|
@@ -5757,7 +5761,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
|
|
|
5757
5761
|
}),
|
|
5758
5762
|
config.get("customProviders")
|
|
5759
5763
|
);
|
|
5760
|
-
const { startHub } = await import("./hub-
|
|
5764
|
+
const { startHub } = await import("./hub-LURPCJ4Z.js");
|
|
5761
5765
|
await startHub(
|
|
5762
5766
|
{
|
|
5763
5767
|
topic: topic ?? "",
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
renderDiff,
|
|
19
19
|
runHook,
|
|
20
20
|
setupProxy
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-52BIUGQ2.js";
|
|
22
22
|
import {
|
|
23
23
|
AuthManager
|
|
24
24
|
} from "./chunk-BYNY5JPB.js";
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
spawnAgentContext,
|
|
33
33
|
truncateOutput,
|
|
34
34
|
undoStack
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-TXCLZ72H.js";
|
|
36
36
|
import {
|
|
37
37
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
38
38
|
CONTEXT_FILE_CANDIDATES,
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
PLAN_MODE_SYSTEM_ADDON,
|
|
45
45
|
SKILLS_DIR_NAME,
|
|
46
46
|
VERSION
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-DP3M26PP.js";
|
|
48
48
|
|
|
49
49
|
// src/web/server.ts
|
|
50
50
|
import express from "express";
|
|
@@ -57,7 +57,7 @@ import { networkInterfaces } from "os";
|
|
|
57
57
|
// src/web/tool-executor-web.ts
|
|
58
58
|
import { randomUUID } from "crypto";
|
|
59
59
|
import { existsSync, readFileSync } from "fs";
|
|
60
|
-
var ToolExecutorWeb = class {
|
|
60
|
+
var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
61
61
|
constructor(registry, ws) {
|
|
62
62
|
this.registry = registry;
|
|
63
63
|
this.ws = ws;
|
|
@@ -71,6 +71,10 @@ var ToolExecutorWeb = class {
|
|
|
71
71
|
pendingConfirms = /* @__PURE__ */ new Map();
|
|
72
72
|
/** Pending batch confirm promises */
|
|
73
73
|
pendingBatchConfirms = /* @__PURE__ */ new Map();
|
|
74
|
+
/** M7 fix: timers for confirm cleanup if client crashes */
|
|
75
|
+
pendingTimers = /* @__PURE__ */ new Map();
|
|
76
|
+
static CONFIRM_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
77
|
+
// 5 minutes
|
|
74
78
|
/** Publicly readable by SessionHandler to check if confirm is active */
|
|
75
79
|
confirming = false;
|
|
76
80
|
/** Session-level auto-approve toggle (/yolo command) */
|
|
@@ -86,10 +90,19 @@ var ToolExecutorWeb = class {
|
|
|
86
90
|
if (opts.permissionRules) this.permissionRules = opts.permissionRules;
|
|
87
91
|
if (opts.defaultPermission) this.defaultPermission = opts.defaultPermission;
|
|
88
92
|
}
|
|
93
|
+
/** Clear M7 timeout timer for a requestId */
|
|
94
|
+
clearPendingTimer(requestId) {
|
|
95
|
+
const timer = this.pendingTimers.get(requestId);
|
|
96
|
+
if (timer) {
|
|
97
|
+
clearTimeout(timer);
|
|
98
|
+
this.pendingTimers.delete(requestId);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
89
101
|
/** Resolve a pending confirm from client response */
|
|
90
102
|
resolveConfirm(requestId, approved) {
|
|
91
103
|
const resolve3 = this.pendingConfirms.get(requestId);
|
|
92
104
|
if (resolve3) {
|
|
105
|
+
this.clearPendingTimer(requestId);
|
|
93
106
|
this.pendingConfirms.delete(requestId);
|
|
94
107
|
this.confirming = false;
|
|
95
108
|
resolve3(approved);
|
|
@@ -99,6 +112,7 @@ var ToolExecutorWeb = class {
|
|
|
99
112
|
resolveBatchConfirm(requestId, decision) {
|
|
100
113
|
const resolve3 = this.pendingBatchConfirms.get(requestId);
|
|
101
114
|
if (resolve3) {
|
|
115
|
+
this.clearPendingTimer(requestId);
|
|
102
116
|
this.pendingBatchConfirms.delete(requestId);
|
|
103
117
|
this.confirming = false;
|
|
104
118
|
if (decision === "all" || decision === "none") {
|
|
@@ -190,6 +204,11 @@ var ToolExecutorWeb = class {
|
|
|
190
204
|
this.send(msg);
|
|
191
205
|
return new Promise((resolve3) => {
|
|
192
206
|
this.pendingConfirms.set(requestId, resolve3);
|
|
207
|
+
this.pendingTimers.set(requestId, setTimeout(() => {
|
|
208
|
+
if (this.pendingConfirms.has(requestId)) {
|
|
209
|
+
this.resolveConfirm(requestId, false);
|
|
210
|
+
}
|
|
211
|
+
}, _ToolExecutorWeb.CONFIRM_TIMEOUT_MS));
|
|
193
212
|
});
|
|
194
213
|
}
|
|
195
214
|
/** WebSocket-based batch confirm */
|
|
@@ -210,6 +229,11 @@ var ToolExecutorWeb = class {
|
|
|
210
229
|
this.send(msg);
|
|
211
230
|
return new Promise((resolve3) => {
|
|
212
231
|
this.pendingBatchConfirms.set(requestId, resolve3);
|
|
232
|
+
this.pendingTimers.set(requestId, setTimeout(() => {
|
|
233
|
+
if (this.pendingBatchConfirms.has(requestId)) {
|
|
234
|
+
this.resolveBatchConfirm(requestId, "none");
|
|
235
|
+
}
|
|
236
|
+
}, _ToolExecutorWeb.CONFIRM_TIMEOUT_MS));
|
|
213
237
|
});
|
|
214
238
|
}
|
|
215
239
|
async execute(call) {
|
|
@@ -585,10 +609,20 @@ var SessionHandler = class _SessionHandler {
|
|
|
585
609
|
try {
|
|
586
610
|
this.ensureSession();
|
|
587
611
|
const session = this.sessions.current;
|
|
612
|
+
const ALLOWED_IMAGE_MIMES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"]);
|
|
613
|
+
const MAX_IMAGE_SIZE = 10 * 1024 * 1024;
|
|
588
614
|
let msgContent;
|
|
589
615
|
if (images && images.length > 0) {
|
|
590
616
|
const parts = [];
|
|
591
617
|
for (const img of images) {
|
|
618
|
+
if (!ALLOWED_IMAGE_MIMES.has(img.mime)) {
|
|
619
|
+
this.send({ type: "error", message: `Rejected image: unsupported MIME type "${img.mime}"` });
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
if (img.data && img.data.length > MAX_IMAGE_SIZE) {
|
|
623
|
+
this.send({ type: "error", message: `Rejected image: too large (${(img.data.length / 1024 / 1024).toFixed(1)} MB, max 10 MB)` });
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
592
626
|
parts.push({
|
|
593
627
|
type: "image_url",
|
|
594
628
|
image_url: { url: `data:${img.mime};base64,${img.data}` }
|
|
@@ -1438,7 +1472,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
1438
1472
|
case "test": {
|
|
1439
1473
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
1440
1474
|
try {
|
|
1441
|
-
const { executeTests } = await import("./run-tests-
|
|
1475
|
+
const { executeTests } = await import("./run-tests-PVVT37LK.js");
|
|
1442
1476
|
const argStr = args.join(" ").trim();
|
|
1443
1477
|
let testArgs = {};
|
|
1444
1478
|
if (argStr) {
|
|
@@ -4,18 +4,35 @@ import {
|
|
|
4
4
|
getDangerLevel,
|
|
5
5
|
googleSearchContext,
|
|
6
6
|
truncateOutput
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-TXCLZ72H.js";
|
|
8
8
|
import {
|
|
9
9
|
SUBAGENT_ALLOWED_TOOLS
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-DP3M26PP.js";
|
|
11
11
|
|
|
12
12
|
// src/hub/task-orchestrator.ts
|
|
13
13
|
import { createInterface } from "readline";
|
|
14
|
-
import { existsSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
14
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync, renameSync } from "fs";
|
|
15
15
|
import { join } from "path";
|
|
16
16
|
import chalk from "chalk";
|
|
17
17
|
|
|
18
18
|
// src/hub/task-executor.ts
|
|
19
|
+
var TASK_EXCLUDED_TOOLS = /* @__PURE__ */ new Set(["write_todos", "save_memory", "ask_user", "spawn_agent"]);
|
|
20
|
+
var _cachedRegistry = null;
|
|
21
|
+
var _cachedToolDefs = null;
|
|
22
|
+
function getTaskToolRegistry() {
|
|
23
|
+
if (_cachedRegistry && _cachedToolDefs) {
|
|
24
|
+
return { registry: _cachedRegistry, toolDefs: _cachedToolDefs };
|
|
25
|
+
}
|
|
26
|
+
const registry = new ToolRegistry();
|
|
27
|
+
for (const tool of registry.listAll()) {
|
|
28
|
+
if (!SUBAGENT_ALLOWED_TOOLS.has(tool.definition.name) || TASK_EXCLUDED_TOOLS.has(tool.definition.name)) {
|
|
29
|
+
registry.unregister(tool.definition.name);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
_cachedRegistry = registry;
|
|
33
|
+
_cachedToolDefs = registry.getDefinitions();
|
|
34
|
+
return { registry, toolDefs: _cachedToolDefs };
|
|
35
|
+
}
|
|
19
36
|
async function executeTask(options) {
|
|
20
37
|
const {
|
|
21
38
|
task,
|
|
@@ -29,14 +46,7 @@ async function executeTask(options) {
|
|
|
29
46
|
onToolResult,
|
|
30
47
|
onRound
|
|
31
48
|
} = options;
|
|
32
|
-
const
|
|
33
|
-
const registry = new ToolRegistry();
|
|
34
|
-
for (const tool of registry.listAll()) {
|
|
35
|
-
if (!SUBAGENT_ALLOWED_TOOLS.has(tool.definition.name) || TASK_EXCLUDED_TOOLS.has(tool.definition.name)) {
|
|
36
|
-
registry.unregister(tool.definition.name);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
const toolDefs = registry.getDefinitions();
|
|
49
|
+
const { registry, toolDefs } = getTaskToolRegistry();
|
|
40
50
|
const contextSection = context ? `
|
|
41
51
|
|
|
42
52
|
## Reference Documents
|
|
@@ -256,7 +266,9 @@ var TaskOrchestrator = class {
|
|
|
256
266
|
}
|
|
257
267
|
saveState(plan) {
|
|
258
268
|
try {
|
|
259
|
-
|
|
269
|
+
const tmpPath = this.stateFilePath + ".tmp";
|
|
270
|
+
writeFileSync(tmpPath, JSON.stringify(plan, null, 2), "utf-8");
|
|
271
|
+
renameSync(tmpPath, this.stateFilePath);
|
|
260
272
|
} catch {
|
|
261
273
|
}
|
|
262
274
|
}
|
|
@@ -280,9 +292,14 @@ var TaskOrchestrator = class {
|
|
|
280
292
|
async askResume() {
|
|
281
293
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
282
294
|
try {
|
|
283
|
-
const answer = await
|
|
284
|
-
|
|
285
|
-
|
|
295
|
+
const answer = await Promise.race([
|
|
296
|
+
new Promise((resolve) => {
|
|
297
|
+
rl.question(chalk.green(" Resume from saved state? (y/n) > "), resolve);
|
|
298
|
+
}),
|
|
299
|
+
new Promise(
|
|
300
|
+
(_, reject) => setTimeout(() => reject(new Error("timeout")), 3e4)
|
|
301
|
+
)
|
|
302
|
+
]).catch(() => "y");
|
|
286
303
|
return answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes";
|
|
287
304
|
} finally {
|
|
288
305
|
rl.close();
|
|
@@ -342,13 +359,29 @@ Example:
|
|
|
342
359
|
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
343
360
|
throw new Error("Empty task list");
|
|
344
361
|
}
|
|
362
|
+
const validRoleIds = new Set(roles.map((r) => r.id));
|
|
363
|
+
for (let i = 0; i < parsed.length; i++) {
|
|
364
|
+
const t = parsed[i];
|
|
365
|
+
if (typeof t !== "object" || t === null) {
|
|
366
|
+
throw new Error(`Task #${i + 1}: not an object`);
|
|
367
|
+
}
|
|
368
|
+
if (typeof t.description !== "string" || !t.description.trim()) {
|
|
369
|
+
throw new Error(`Task #${i + 1}: missing or empty "description"`);
|
|
370
|
+
}
|
|
371
|
+
if (typeof t.assignee !== "string" || !t.assignee.trim()) {
|
|
372
|
+
throw new Error(`Task #${i + 1}: missing or empty "assignee"`);
|
|
373
|
+
}
|
|
374
|
+
if (t.dependencies != null && !Array.isArray(t.dependencies)) {
|
|
375
|
+
throw new Error(`Task #${i + 1}: "dependencies" must be an array`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
345
378
|
const roleMap = new Map(roles.map((r) => [r.id, r.name]));
|
|
346
379
|
const tasks = parsed.map((t, i) => ({
|
|
347
|
-
id: t.id
|
|
380
|
+
id: typeof t.id === "number" ? t.id : i + 1,
|
|
348
381
|
description: t.description,
|
|
349
382
|
assignee: t.assignee,
|
|
350
383
|
assigneeName: roleMap.get(t.assignee) ?? t.assignee,
|
|
351
|
-
dependencies: t.dependencies
|
|
384
|
+
dependencies: Array.isArray(t.dependencies) ? t.dependencies : [],
|
|
352
385
|
status: "pending"
|
|
353
386
|
}));
|
|
354
387
|
return { goal, tasks };
|
|
@@ -413,7 +446,15 @@ Example:
|
|
|
413
446
|
if (!nextTask) {
|
|
414
447
|
const pending = plan.tasks.filter((t) => t.status === "pending");
|
|
415
448
|
if (pending.length === 0) break;
|
|
416
|
-
|
|
449
|
+
const failedIds = new Set(plan.tasks.filter((t) => t.status === "failed").map((t) => t.id));
|
|
450
|
+
const hasCycle = pending.every(
|
|
451
|
+
(t) => t.dependencies.some((d) => !completedIds.has(d) && !failedIds.has(d) && pending.some((p) => p.id === d))
|
|
452
|
+
);
|
|
453
|
+
if (hasCycle) {
|
|
454
|
+
console.log(chalk.red(` \u2717 Circular dependency detected among ${pending.length} task(s):`));
|
|
455
|
+
} else {
|
|
456
|
+
console.log(chalk.red(` \u2717 Cannot proceed: ${pending.length} task(s) have unmet dependencies.`));
|
|
457
|
+
}
|
|
417
458
|
for (const t of pending) {
|
|
418
459
|
const unmet = t.dependencies.filter((d) => !completedIds.has(d));
|
|
419
460
|
console.log(chalk.dim(` Task #${t.id}: waiting on [${unmet.join(", ")}]`));
|