reasonix 0.43.0 → 0.44.0
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/README.md +49 -11
- package/README.zh-CN.md +35 -7
- package/dashboard/app.css +225 -4
- package/dashboard/dist/app.js +6441 -6080
- package/dashboard/dist/app.js.map +1 -1
- package/data/deepseek-tokenizer.json.gz +0 -0
- package/dist/cli/{acp-DAGPCVFZ.js → acp-TYZ2CTDL.js} +28 -30
- package/dist/cli/acp-TYZ2CTDL.js.map +1 -0
- package/dist/cli/chat-TH7VNNCJ.js +51 -0
- package/dist/cli/chunk-2425HK6U.js +0 -0
- package/dist/cli/chunk-25T6CVUP.js +0 -0
- package/dist/cli/chunk-2UQP6H6T.js +0 -0
- package/dist/cli/{chunk-3Z6IBU3D.js → chunk-2V6EAEUW.js} +95 -31
- package/dist/cli/chunk-2V6EAEUW.js.map +1 -0
- package/dist/cli/{chunk-XCGGEJTI.js → chunk-4CTDEJUF.js} +2 -2
- package/dist/cli/chunk-4QUNBQQ2.js +0 -0
- package/dist/cli/{chunk-74EX7SUH.js → chunk-5QCB62C4.js} +33 -7
- package/dist/cli/{chunk-74EX7SUH.js.map → chunk-5QCB62C4.js.map} +1 -1
- package/dist/cli/chunk-6OWJV3YW.js +390 -0
- package/dist/cli/chunk-6OWJV3YW.js.map +1 -0
- package/dist/cli/chunk-6PBZN4VI.js +0 -0
- package/dist/cli/{chunk-7O5ALB4C.js → chunk-7CIGMZT3.js} +2 -2
- package/dist/cli/{chunk-H6PS7IUE.js → chunk-7UCMM425.js} +7 -3
- package/dist/cli/chunk-7UCMM425.js.map +1 -0
- package/dist/cli/{chunk-TJX6BFZZ.js → chunk-AB2RED3C.js} +3 -3
- package/dist/cli/{chunk-XPDVG52A.js → chunk-AVFXO2EZ.js} +361 -13
- package/dist/cli/chunk-AVFXO2EZ.js.map +1 -0
- package/dist/cli/{chunk-FHOGSSCH.js → chunk-C53JQES5.js} +3 -3
- package/dist/cli/{chunk-RE4RAVFF.js → chunk-CGDR2ELH.js} +92 -30
- package/dist/cli/chunk-CGDR2ELH.js.map +1 -0
- package/dist/cli/{chunk-OSZC7C6F.js → chunk-CWZKQ5FE.js} +7 -4
- package/dist/cli/chunk-CWZKQ5FE.js.map +1 -0
- package/dist/cli/{devtools-YECO25QO.js → chunk-FEZK652I.js} +10 -85
- package/dist/cli/chunk-FEZK652I.js.map +1 -0
- package/dist/cli/{chunk-45U62RI3.js → chunk-HNXDZGC6.js} +104 -2
- package/dist/cli/chunk-HNXDZGC6.js.map +1 -0
- package/dist/cli/chunk-J5XJHLWM.js +0 -0
- package/dist/cli/chunk-JMBMLOBP.js +0 -0
- package/dist/cli/{chunk-5JJRUIPA.js → chunk-JNAQYELD.js} +16 -8
- package/dist/cli/{chunk-5JJRUIPA.js.map → chunk-JNAQYELD.js.map} +1 -1
- package/dist/cli/{chunk-YFGF5NKA.js → chunk-KGBG6M2X.js} +19 -15
- package/dist/cli/chunk-KGBG6M2X.js.map +1 -0
- package/dist/cli/{chunk-3BXRZFWS.js → chunk-KLQTAZIY.js} +12 -4
- package/dist/cli/chunk-KLQTAZIY.js.map +1 -0
- package/dist/cli/{chunk-VK5HG73G.js → chunk-KM465GST.js} +9 -9
- package/dist/cli/{chunk-DOYHN4KB.js → chunk-LIR2HBQH.js} +2 -2
- package/dist/cli/{chunk-YYQAUTTN.js → chunk-MJ6W5UN3.js} +2 -2
- package/dist/cli/{chunk-6PZ3CXBP.js → chunk-MRHHQJAQ.js} +5 -4
- package/dist/cli/chunk-MRHHQJAQ.js.map +1 -0
- package/dist/cli/{chunk-PQXPXJBJ.js → chunk-NVURFF27.js} +16 -5
- package/dist/cli/chunk-NVURFF27.js.map +1 -0
- package/dist/cli/{chunk-2R4QCDOZ.js → chunk-OPFUUYHL.js} +540 -287
- package/dist/cli/chunk-OPFUUYHL.js.map +1 -0
- package/dist/cli/chunk-PLHAZOLZ.js +0 -0
- package/dist/cli/{chunk-HFEAY5DT.js → chunk-R3CTO2HM.js} +2 -2
- package/dist/cli/{chunk-O52OLQL3.js → chunk-RDRC3XDT.js} +136 -38
- package/dist/cli/chunk-RDRC3XDT.js.map +1 -0
- package/dist/cli/chunk-S4XVGLRW.js +0 -0
- package/dist/cli/chunk-SZ5XES2N.js +0 -0
- package/dist/cli/{chunk-2K65GZBT.js → chunk-TEUDEGX2.js} +64 -19
- package/dist/cli/chunk-TEUDEGX2.js.map +1 -0
- package/dist/cli/{chunk-2Z35JOA4.js → chunk-TKVXTQ3T.js} +4 -4
- package/dist/cli/{chunk-2Z35JOA4.js.map → chunk-TKVXTQ3T.js.map} +1 -1
- package/dist/cli/chunk-TUK7OWJA.js +0 -0
- package/dist/cli/{chunk-32TIKD5U.js → chunk-TXJMRPIL.js} +3 -3
- package/dist/cli/{chunk-2KDUS647.js → chunk-V26WPN3J.js} +7 -4
- package/dist/cli/chunk-V26WPN3J.js.map +1 -0
- package/dist/cli/{chunk-F3PXYSNN.js → chunk-WK3UFQY3.js} +2 -2
- package/dist/cli/{chunk-6G3CUUFG.js → chunk-X53B3JIX.js} +3 -3
- package/dist/cli/{chunk-6G3CUUFG.js.map → chunk-X53B3JIX.js.map} +1 -1
- package/dist/cli/chunk-XJXDHAES.js +0 -0
- package/dist/cli/{chunk-6AK4EY3D.js → chunk-XSU4QVFW.js} +1 -81
- package/dist/cli/chunk-XSU4QVFW.js.map +1 -0
- package/dist/cli/chunk-XXC2BYTV.js +0 -0
- package/dist/cli/{chunk-P7EKE5ZQ.js → chunk-Z4S7EYXG.js} +4482 -1310
- package/dist/cli/chunk-Z4S7EYXG.js.map +1 -0
- package/dist/cli/chunk-ZZM6QJ4W.js +0 -0
- package/dist/cli/{chunk-YQ6NTIIE.js → chunk-ZZYBBX5N.js} +13 -5
- package/dist/cli/chunk-ZZYBBX5N.js.map +1 -0
- package/dist/cli/{code-SMKEW6CD.js → code-PSVJ3KEN.js} +48 -36
- package/dist/cli/code-PSVJ3KEN.js.map +1 -0
- package/dist/cli/{commands-FVVB5FZF.js → commands-OCU42XG4.js} +4 -4
- package/dist/cli/{commit-HE4VSPZ7.js → commit-XCQIQCYG.js} +3 -3
- package/dist/cli/{desktop-Q7NDXCON.js → desktop-KWGR4BNE.js} +210 -69
- package/dist/cli/desktop-KWGR4BNE.js.map +1 -0
- package/dist/cli/devtools-HW3WDT3Q.js +91 -0
- package/dist/cli/devtools-HW3WDT3Q.js.map +1 -0
- package/dist/cli/{diff-435UTPC5.js → diff-NHANTNC3.js} +9 -9
- package/dist/cli/{doctor-OT7KH75K.js → doctor-CC5CLOGG.js} +10 -10
- package/dist/cli/events-XEFAD5VX.js +0 -0
- package/dist/cli/index.js +132 -94
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{mcp-WUL2WO75.js → mcp-MPVGBBJF.js} +2 -2
- package/dist/cli/{mcp-browse-RR7R4XET.js → mcp-browse-4XOTC3FJ.js} +3 -3
- package/dist/cli/{mcp-inspect-REGLYBWT.js → mcp-inspect-CEMGKKAH.js} +14 -9
- package/dist/cli/mcp-inspect-CEMGKKAH.js.map +1 -0
- package/dist/cli/{prompt-UW6EFLVR.js → prompt-2D7ID24X.js} +4 -4
- package/dist/cli/prune-sessions-3RWUBYRS.js +0 -0
- package/dist/cli/{replay-YOURXV4C.js → replay-SR44E6RS.js} +10 -10
- package/dist/cli/{run-Q6BUXV66.js → run-MDGL27WL.js} +35 -36
- package/dist/cli/run-MDGL27WL.js.map +1 -0
- package/dist/cli/{server-XGDBRWMB.js → server-27ARQXIZ.js} +67 -24
- package/dist/cli/server-27ARQXIZ.js.map +1 -0
- package/dist/cli/{sessions-FH7QVYSY.js → sessions-CKQXCYGP.js} +18 -18
- package/dist/cli/sessions-CKQXCYGP.js.map +1 -0
- package/dist/cli/{setup-VDS6SVEP.js → setup-TPAGSVXO.js} +6 -6
- package/dist/cli/{stats-MQVI2XQH.js → stats-DPUBZNVX.js} +6 -4
- package/dist/cli/update-6ITLPRDV.js +0 -0
- package/dist/cli/{version-DAHGZY5N.js → version-2X3BHVVK.js} +15 -15
- package/dist/index.d.ts +181 -53
- package/dist/index.js +1322 -533
- package/dist/index.js.map +1 -1
- package/package.json +21 -8
- package/dist/cli/.-3G6VX5S7.js +0 -327
- package/dist/cli/.-6YRPB2C7.js +0 -329
- package/dist/cli/.-EYSVINK3.js +0 -317
- package/dist/cli/acp-DAGPCVFZ.js.map +0 -1
- package/dist/cli/chat-7ES4IBNH.js +0 -50
- package/dist/cli/chunk-2K65GZBT.js.map +0 -1
- package/dist/cli/chunk-2KDUS647.js.map +0 -1
- package/dist/cli/chunk-2R4QCDOZ.js.map +0 -1
- package/dist/cli/chunk-3BXRZFWS.js.map +0 -1
- package/dist/cli/chunk-3Z6IBU3D.js.map +0 -1
- package/dist/cli/chunk-45U62RI3.js.map +0 -1
- package/dist/cli/chunk-6AK4EY3D.js.map +0 -1
- package/dist/cli/chunk-6PZ3CXBP.js.map +0 -1
- package/dist/cli/chunk-H6PS7IUE.js.map +0 -1
- package/dist/cli/chunk-O52OLQL3.js.map +0 -1
- package/dist/cli/chunk-OSZC7C6F.js.map +0 -1
- package/dist/cli/chunk-P7EKE5ZQ.js.map +0 -1
- package/dist/cli/chunk-PQXPXJBJ.js.map +0 -1
- package/dist/cli/chunk-PV55UMTO.js +0 -200
- package/dist/cli/chunk-PV55UMTO.js.map +0 -1
- package/dist/cli/chunk-RE4RAVFF.js.map +0 -1
- package/dist/cli/chunk-XPDVG52A.js.map +0 -1
- package/dist/cli/chunk-YFGF5NKA.js.map +0 -1
- package/dist/cli/chunk-YQ6NTIIE.js.map +0 -1
- package/dist/cli/code-SMKEW6CD.js.map +0 -1
- package/dist/cli/desktop-Q7NDXCON.js.map +0 -1
- package/dist/cli/devtools-YECO25QO.js.map +0 -1
- package/dist/cli/doctor-OT7KH75K.js.map +0 -1
- package/dist/cli/mcp-inspect-REGLYBWT.js.map +0 -1
- package/dist/cli/prompt-UW6EFLVR.js.map +0 -1
- package/dist/cli/run-Q6BUXV66.js.map +0 -1
- package/dist/cli/server-XGDBRWMB.js.map +0 -1
- package/dist/cli/sessions-FH7QVYSY.js.map +0 -1
- package/dist/cli/stats-MQVI2XQH.js.map +0 -1
- /package/dist/cli/{.-3G6VX5S7.js.map → chat-TH7VNNCJ.js.map} +0 -0
- /package/dist/cli/{chunk-XCGGEJTI.js.map → chunk-4CTDEJUF.js.map} +0 -0
- /package/dist/cli/{chunk-7O5ALB4C.js.map → chunk-7CIGMZT3.js.map} +0 -0
- /package/dist/cli/{chunk-TJX6BFZZ.js.map → chunk-AB2RED3C.js.map} +0 -0
- /package/dist/cli/{chunk-FHOGSSCH.js.map → chunk-C53JQES5.js.map} +0 -0
- /package/dist/cli/{chunk-VK5HG73G.js.map → chunk-KM465GST.js.map} +0 -0
- /package/dist/cli/{chunk-DOYHN4KB.js.map → chunk-LIR2HBQH.js.map} +0 -0
- /package/dist/cli/{chunk-YYQAUTTN.js.map → chunk-MJ6W5UN3.js.map} +0 -0
- /package/dist/cli/{chunk-HFEAY5DT.js.map → chunk-R3CTO2HM.js.map} +0 -0
- /package/dist/cli/{chunk-32TIKD5U.js.map → chunk-TXJMRPIL.js.map} +0 -0
- /package/dist/cli/{chunk-F3PXYSNN.js.map → chunk-WK3UFQY3.js.map} +0 -0
- /package/dist/cli/{commands-FVVB5FZF.js.map → commands-OCU42XG4.js.map} +0 -0
- /package/dist/cli/{commit-HE4VSPZ7.js.map → commit-XCQIQCYG.js.map} +0 -0
- /package/dist/cli/{diff-435UTPC5.js.map → diff-NHANTNC3.js.map} +0 -0
- /package/dist/cli/{.-6YRPB2C7.js.map → doctor-CC5CLOGG.js.map} +0 -0
- /package/dist/cli/{mcp-WUL2WO75.js.map → mcp-MPVGBBJF.js.map} +0 -0
- /package/dist/cli/{mcp-browse-RR7R4XET.js.map → mcp-browse-4XOTC3FJ.js.map} +0 -0
- /package/dist/cli/{.-EYSVINK3.js.map → prompt-2D7ID24X.js.map} +0 -0
- /package/dist/cli/{replay-YOURXV4C.js.map → replay-SR44E6RS.js.map} +0 -0
- /package/dist/cli/{setup-VDS6SVEP.js.map → setup-TPAGSVXO.js.map} +0 -0
- /package/dist/cli/{chat-7ES4IBNH.js.map → stats-DPUBZNVX.js.map} +0 -0
- /package/dist/cli/{version-DAHGZY5N.js.map → version-2X3BHVVK.js.map} +0 -0
|
@@ -3,28 +3,32 @@ import { createRequire as __cr } from 'node:module'; if (typeof globalThis.requi
|
|
|
3
3
|
import {
|
|
4
4
|
MemoryStore,
|
|
5
5
|
sanitizeMemoryName
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-JNAQYELD.js";
|
|
7
7
|
import {
|
|
8
8
|
countTokens,
|
|
9
|
+
countTokensBounded,
|
|
9
10
|
estimateConversationTokens,
|
|
10
11
|
estimateRequestTokens
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-6OWJV3YW.js";
|
|
12
13
|
import {
|
|
13
14
|
Usage
|
|
14
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-V26WPN3J.js";
|
|
15
16
|
import {
|
|
16
17
|
applyEdit,
|
|
17
18
|
applyMultiEdit,
|
|
18
19
|
pauseGate
|
|
19
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-RDRC3XDT.js";
|
|
20
21
|
import {
|
|
21
22
|
NEGATIVE_CLAIM_RULE,
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
PROJECT_MEMORY_FILES,
|
|
24
|
+
PROJECT_MEMORY_MAX_CHARS,
|
|
25
|
+
TUI_FORMATTING_RULES,
|
|
26
|
+
memoryEnabled
|
|
27
|
+
} from "./chunk-TEUDEGX2.js";
|
|
24
28
|
import {
|
|
25
29
|
formatHookOutcomeMessage,
|
|
26
30
|
runHooks
|
|
27
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-7CIGMZT3.js";
|
|
28
32
|
import {
|
|
29
33
|
ignoredByLayers,
|
|
30
34
|
loadGitignoreAt,
|
|
@@ -38,23 +42,24 @@ import {
|
|
|
38
42
|
rewriteSession,
|
|
39
43
|
timestampSuffix
|
|
40
44
|
} from "./chunk-6PBZN4VI.js";
|
|
45
|
+
import {
|
|
46
|
+
DEEPSEEK_CONTEXT_TOKENS,
|
|
47
|
+
DEFAULT_CONTEXT_TOKENS,
|
|
48
|
+
SessionStats
|
|
49
|
+
} from "./chunk-ZZYBBX5N.js";
|
|
41
50
|
import {
|
|
42
51
|
t
|
|
43
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-CGDR2ELH.js";
|
|
44
53
|
import {
|
|
45
54
|
DEFAULT_INDEX_EXCLUDES,
|
|
46
55
|
addProjectPathAllowed,
|
|
47
56
|
loadMemoryTypeRegistry,
|
|
57
|
+
loadMetasoApiKey,
|
|
48
58
|
loadProjectPathAllowed,
|
|
49
59
|
require_picomatch,
|
|
50
60
|
webSearchEndpoint,
|
|
51
61
|
webSearchEngine
|
|
52
|
-
} from "./chunk-
|
|
53
|
-
import {
|
|
54
|
-
DEEPSEEK_CONTEXT_TOKENS,
|
|
55
|
-
DEFAULT_CONTEXT_TOKENS,
|
|
56
|
-
SessionStats
|
|
57
|
-
} from "./chunk-YQ6NTIIE.js";
|
|
62
|
+
} from "./chunk-AVFXO2EZ.js";
|
|
58
63
|
import {
|
|
59
64
|
__commonJS,
|
|
60
65
|
__esm,
|
|
@@ -2591,10 +2596,10 @@ var require_helpers = __commonJS({
|
|
|
2591
2596
|
return !arr.includes(node, i + 1);
|
|
2592
2597
|
});
|
|
2593
2598
|
nodes.sort(function(a, b) {
|
|
2594
|
-
var
|
|
2595
|
-
if (
|
|
2599
|
+
var relative6 = compareDocumentPosition(a, b);
|
|
2600
|
+
if (relative6 & DocumentPosition.PRECEDING) {
|
|
2596
2601
|
return -1;
|
|
2597
|
-
} else if (
|
|
2602
|
+
} else if (relative6 & DocumentPosition.FOLLOWING) {
|
|
2598
2603
|
return 1;
|
|
2599
2604
|
}
|
|
2600
2605
|
return 0;
|
|
@@ -6044,12 +6049,12 @@ async function waitForReady(ready, timeoutMs, serverName, signal) {
|
|
|
6044
6049
|
let timer;
|
|
6045
6050
|
let onAbort;
|
|
6046
6051
|
try {
|
|
6047
|
-
await new Promise((
|
|
6052
|
+
await new Promise((resolve5, reject) => {
|
|
6048
6053
|
ready.then(
|
|
6049
6054
|
() => {
|
|
6050
6055
|
if (settled) return;
|
|
6051
6056
|
settled = true;
|
|
6052
|
-
|
|
6057
|
+
resolve5();
|
|
6053
6058
|
},
|
|
6054
6059
|
(err) => {
|
|
6055
6060
|
if (settled) return;
|
|
@@ -6584,6 +6589,40 @@ var VolatileScratch = class {
|
|
|
6584
6589
|
}
|
|
6585
6590
|
};
|
|
6586
6591
|
|
|
6592
|
+
// src/loop/thinking.ts
|
|
6593
|
+
function isThinkingModeModel(model) {
|
|
6594
|
+
if (model.includes("reasoner")) return true;
|
|
6595
|
+
if (model === "deepseek-v4-flash" || model === "deepseek-v4-pro") return true;
|
|
6596
|
+
return false;
|
|
6597
|
+
}
|
|
6598
|
+
function thinkingModeForModel(model) {
|
|
6599
|
+
if (model === "deepseek-chat") return "disabled";
|
|
6600
|
+
if (model.includes("reasoner")) return "enabled";
|
|
6601
|
+
if (model === "deepseek-v4-flash" || model === "deepseek-v4-pro") return "enabled";
|
|
6602
|
+
return void 0;
|
|
6603
|
+
}
|
|
6604
|
+
function stripHallucinatedToolMarkup(s) {
|
|
6605
|
+
let out = s;
|
|
6606
|
+
out = out.replace(/<|DSML|function_calls>[\s\S]*?<\/?|DSML|function_calls>/g, "");
|
|
6607
|
+
out = out.replace(/<\|DSML\|function_calls>[\s\S]*?<\/?\|DSML\|function_calls>/g, "");
|
|
6608
|
+
out = out.replace(/<function_calls>[\s\S]*?<\/function_calls>/g, "");
|
|
6609
|
+
out = out.replace(/<|DSML|[\s\S]*$/g, "");
|
|
6610
|
+
return out.trim();
|
|
6611
|
+
}
|
|
6612
|
+
|
|
6613
|
+
// src/loop/messages.ts
|
|
6614
|
+
function buildAssistantMessage(content, toolCalls, producingModel, reasoningContent) {
|
|
6615
|
+
const msg = { role: "assistant", content };
|
|
6616
|
+
if (toolCalls.length > 0) msg.tool_calls = toolCalls;
|
|
6617
|
+
if (isThinkingModeModel(producingModel) || reasoningContent && reasoningContent.length > 0) {
|
|
6618
|
+
msg.reasoning_content = reasoningContent ?? "";
|
|
6619
|
+
}
|
|
6620
|
+
return msg;
|
|
6621
|
+
}
|
|
6622
|
+
function buildSyntheticAssistantMessage(content, fallbackModel) {
|
|
6623
|
+
return buildAssistantMessage(content, [], fallbackModel, "");
|
|
6624
|
+
}
|
|
6625
|
+
|
|
6587
6626
|
// src/context-manager.ts
|
|
6588
6627
|
var HISTORY_FOLD_THRESHOLD = 0.5;
|
|
6589
6628
|
var HISTORY_FOLD_TAIL_FRACTION = 0.2;
|
|
@@ -6592,6 +6631,8 @@ var HISTORY_FOLD_AGGRESSIVE_TAIL_FRACTION = 0.1;
|
|
|
6592
6631
|
var HISTORY_FOLD_MIN_SAVINGS_FRACTION = 0.3;
|
|
6593
6632
|
var FORCE_SUMMARY_THRESHOLD = 0.8;
|
|
6594
6633
|
var PREFLIGHT_EMERGENCY_THRESHOLD = 0.95;
|
|
6634
|
+
var PREFLIGHT_MECHANICAL_TARGET_FRACTION = 0.7;
|
|
6635
|
+
var HISTORY_FOLD_SUMMARY_TIMEOUT_MS = 15e3;
|
|
6595
6636
|
var HISTORY_FOLD_MARKER = "[CONVERSATION HISTORY SUMMARY \u2014 earlier turns folded for context efficiency]\n\n";
|
|
6596
6637
|
var SKILL_PIN_MEMO_HEADER = "[Active skill memos \u2014 preserved verbatim across the fold:]";
|
|
6597
6638
|
var SKILL_PIN_REGEX = /<skill-pin name="([^"]+)">\n[\s\S]*?\n<\/skill-pin>/g;
|
|
@@ -6646,7 +6687,7 @@ var ContextManager = class {
|
|
|
6646
6687
|
/** Local-side preflight before sending a request — catches oversized payloads early. */
|
|
6647
6688
|
decidePreflight(messages, toolSpecs, model) {
|
|
6648
6689
|
const ctxMax = DEEPSEEK_CONTEXT_TOKENS[model] ?? DEFAULT_CONTEXT_TOKENS;
|
|
6649
|
-
const estimate = estimateRequestTokens(messages, toolSpecs ?? null);
|
|
6690
|
+
const estimate = estimateRequestTokens(messages, toolSpecs ?? null, true);
|
|
6650
6691
|
return {
|
|
6651
6692
|
needsAction: estimate / ctxMax > PREFLIGHT_EMERGENCY_THRESHOLD,
|
|
6652
6693
|
estimateTokens: estimate,
|
|
@@ -6665,7 +6706,7 @@ var ContextManager = class {
|
|
|
6665
6706
|
summaryChars: 0
|
|
6666
6707
|
};
|
|
6667
6708
|
if (all.length === 0) return noop;
|
|
6668
|
-
const tokenCounts = all.map((m) =>
|
|
6709
|
+
const tokenCounts = all.map((m) => countTokensBounded(m.content ?? ""));
|
|
6669
6710
|
const totalTokens = tokenCounts.reduce((a, b) => a + b, 0);
|
|
6670
6711
|
let cumTokens = 0;
|
|
6671
6712
|
let boundary = all.length;
|
|
@@ -6681,16 +6722,18 @@ var ContextManager = class {
|
|
|
6681
6722
|
if (headTokens < totalTokens * HISTORY_FOLD_MIN_SAVINGS_FRACTION) return noop;
|
|
6682
6723
|
const { stubbedHead, pinnedBodies } = extractPinnedSkills(head);
|
|
6683
6724
|
const summary = await this.summarizeForFold(stubbedHead);
|
|
6684
|
-
if (!summary) return noop;
|
|
6725
|
+
if (!summary.content) return noop;
|
|
6685
6726
|
const memoTail = pinnedBodies.length > 0 ? `
|
|
6686
6727
|
|
|
6687
6728
|
${SKILL_PIN_MEMO_HEADER}
|
|
6688
6729
|
|
|
6689
6730
|
${pinnedBodies.join("\n\n")}` : "";
|
|
6690
|
-
const summaryMsg =
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6731
|
+
const summaryMsg = buildAssistantMessage(
|
|
6732
|
+
HISTORY_FOLD_MARKER + summary.content + memoTail,
|
|
6733
|
+
[],
|
|
6734
|
+
model,
|
|
6735
|
+
summary.reasoningContent
|
|
6736
|
+
);
|
|
6694
6737
|
const replacement = [summaryMsg, ...tail];
|
|
6695
6738
|
this.deps.log.compactInPlace(replacement);
|
|
6696
6739
|
this.persistRewrite(replacement);
|
|
@@ -6698,7 +6741,51 @@ ${pinnedBodies.join("\n\n")}` : "";
|
|
|
6698
6741
|
folded: true,
|
|
6699
6742
|
beforeMessages: all.length,
|
|
6700
6743
|
afterMessages: replacement.length,
|
|
6701
|
-
summaryChars: summary.length
|
|
6744
|
+
summaryChars: summary.content.length
|
|
6745
|
+
};
|
|
6746
|
+
}
|
|
6747
|
+
/** Pure local emergency compaction for preflight: drop oldest log entries and keep a valid tail. */
|
|
6748
|
+
mechanicalTruncate(model, opts) {
|
|
6749
|
+
const ctxMax = DEEPSEEK_CONTEXT_TOKENS[model] ?? DEFAULT_CONTEXT_TOKENS;
|
|
6750
|
+
const targetTokens = opts?.targetTokens ?? Math.floor(ctxMax * PREFLIGHT_MECHANICAL_TARGET_FRACTION);
|
|
6751
|
+
const all = this.deps.log.toMessages();
|
|
6752
|
+
const noop = {
|
|
6753
|
+
folded: false,
|
|
6754
|
+
beforeMessages: all.length,
|
|
6755
|
+
afterMessages: all.length,
|
|
6756
|
+
summaryChars: 0
|
|
6757
|
+
};
|
|
6758
|
+
if (all.length === 0) return noop;
|
|
6759
|
+
const tokenCounts = all.map((m) => estimateConversationTokens([m], true));
|
|
6760
|
+
let latestUserBoundary = -1;
|
|
6761
|
+
for (let i = all.length - 1; i >= 0; i--) {
|
|
6762
|
+
if (all[i].role === "user") {
|
|
6763
|
+
latestUserBoundary = i;
|
|
6764
|
+
break;
|
|
6765
|
+
}
|
|
6766
|
+
}
|
|
6767
|
+
let cumTokens = 0;
|
|
6768
|
+
let boundary = all.length;
|
|
6769
|
+
let foundSafeBoundary = false;
|
|
6770
|
+
for (let i = all.length - 1; i >= 0; i--) {
|
|
6771
|
+
const next = cumTokens + tokenCounts[i];
|
|
6772
|
+
if (next > targetTokens) break;
|
|
6773
|
+
cumTokens = next;
|
|
6774
|
+
if (all[i].role === "user") {
|
|
6775
|
+
boundary = i;
|
|
6776
|
+
foundSafeBoundary = true;
|
|
6777
|
+
}
|
|
6778
|
+
}
|
|
6779
|
+
if (boundary <= 0) return noop;
|
|
6780
|
+
const replacement = foundSafeBoundary ? all.slice(boundary) : opts?.allowEmpty ? [] : latestUserBoundary >= 0 ? all.slice(latestUserBoundary) : all;
|
|
6781
|
+
if (replacement.length === all.length) return noop;
|
|
6782
|
+
this.deps.log.compactInPlace(replacement);
|
|
6783
|
+
this.persistRewrite(replacement);
|
|
6784
|
+
return {
|
|
6785
|
+
folded: true,
|
|
6786
|
+
beforeMessages: all.length,
|
|
6787
|
+
afterMessages: replacement.length,
|
|
6788
|
+
summaryChars: 0
|
|
6702
6789
|
};
|
|
6703
6790
|
}
|
|
6704
6791
|
/** Drop a trailing in-flight assistant-with-tool_calls before a forced summary. Tail-only mutation; prefix cache safe. */
|
|
@@ -6724,18 +6811,51 @@ ${pinnedBodies.join("\n\n")}` : "";
|
|
|
6724
6811
|
content: "Summarize the conversation above as plain prose. This summary replaces the original turns to free context \u2014 make it self-contained."
|
|
6725
6812
|
}
|
|
6726
6813
|
];
|
|
6814
|
+
const turnSignal = this.deps.getAbortSignal();
|
|
6815
|
+
const foldCtrl = new AbortController();
|
|
6816
|
+
let cleanupAbort = () => {
|
|
6817
|
+
};
|
|
6818
|
+
let timeout;
|
|
6727
6819
|
try {
|
|
6728
|
-
const
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6820
|
+
const abortPromise = new Promise((_, reject) => {
|
|
6821
|
+
const abort = () => {
|
|
6822
|
+
foldCtrl.abort();
|
|
6823
|
+
reject(new Error("fold-aborted"));
|
|
6824
|
+
};
|
|
6825
|
+
if (turnSignal.aborted) {
|
|
6826
|
+
abort();
|
|
6827
|
+
} else {
|
|
6828
|
+
turnSignal.addEventListener("abort", abort, { once: true });
|
|
6829
|
+
cleanupAbort = () => turnSignal.removeEventListener("abort", abort);
|
|
6830
|
+
}
|
|
6734
6831
|
});
|
|
6832
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
6833
|
+
timeout = setTimeout(() => {
|
|
6834
|
+
foldCtrl.abort();
|
|
6835
|
+
reject(new Error("fold-timeout"));
|
|
6836
|
+
}, HISTORY_FOLD_SUMMARY_TIMEOUT_MS);
|
|
6837
|
+
});
|
|
6838
|
+
const resp = await Promise.race([
|
|
6839
|
+
this.deps.client.chat({
|
|
6840
|
+
model: summaryModel,
|
|
6841
|
+
messages,
|
|
6842
|
+
signal: foldCtrl.signal,
|
|
6843
|
+
thinking: thinkingModeForModel(summaryModel),
|
|
6844
|
+
reasoningEffort: "high"
|
|
6845
|
+
}),
|
|
6846
|
+
abortPromise,
|
|
6847
|
+
timeoutPromise
|
|
6848
|
+
]);
|
|
6735
6849
|
this.deps.stats.record(this.deps.getCurrentTurn(), summaryModel, resp.usage ?? new Usage());
|
|
6736
|
-
return
|
|
6850
|
+
return {
|
|
6851
|
+
content: stripHallucinatedToolMarkup((resp.content ?? "").trim()),
|
|
6852
|
+
reasoningContent: resp.reasoningContent ?? ""
|
|
6853
|
+
};
|
|
6737
6854
|
} catch {
|
|
6738
|
-
return "";
|
|
6855
|
+
return { content: "", reasoningContent: "" };
|
|
6856
|
+
} finally {
|
|
6857
|
+
if (timeout) clearTimeout(timeout);
|
|
6858
|
+
cleanupAbort();
|
|
6739
6859
|
}
|
|
6740
6860
|
}
|
|
6741
6861
|
persistRewrite(messages) {
|
|
@@ -6827,17 +6947,15 @@ function formatDeepSeek5xx(status, probe) {
|
|
|
6827
6947
|
const action = probe?.reachable === false ? t("errors.deepseek5xxActionNetwork") : t("errors.deepseek5xxActionRetry");
|
|
6828
6948
|
return `${head}${probeNote}${action}`;
|
|
6829
6949
|
}
|
|
6830
|
-
function reasonPrefixFor(reason
|
|
6950
|
+
function reasonPrefixFor(reason) {
|
|
6831
6951
|
if (reason === "aborted") return t("errors.reasonAborted");
|
|
6832
6952
|
if (reason === "context-guard") return t("errors.reasonContextGuard");
|
|
6833
|
-
|
|
6834
|
-
return t("errors.reasonBudget", { iterCap });
|
|
6953
|
+
return t("errors.reasonStuck");
|
|
6835
6954
|
}
|
|
6836
|
-
function errorLabelFor(reason
|
|
6955
|
+
function errorLabelFor(reason) {
|
|
6837
6956
|
if (reason === "aborted") return t("errors.labelAborted");
|
|
6838
6957
|
if (reason === "context-guard") return t("errors.labelContextGuard");
|
|
6839
|
-
|
|
6840
|
-
return t("errors.labelBudget", { iterCap });
|
|
6958
|
+
return t("errors.labelStuck");
|
|
6841
6959
|
}
|
|
6842
6960
|
function extractDeepSeekErrorMessage(body) {
|
|
6843
6961
|
const trimmed = body.trim();
|
|
@@ -6879,50 +6997,14 @@ function looksLikePartialEscalationMarker(buf) {
|
|
|
6879
6997
|
return true;
|
|
6880
6998
|
}
|
|
6881
6999
|
|
|
6882
|
-
// src/loop/thinking.ts
|
|
6883
|
-
function isThinkingModeModel(model) {
|
|
6884
|
-
if (model.includes("reasoner")) return true;
|
|
6885
|
-
if (model === "deepseek-v4-flash" || model === "deepseek-v4-pro") return true;
|
|
6886
|
-
return false;
|
|
6887
|
-
}
|
|
6888
|
-
function thinkingModeForModel(model) {
|
|
6889
|
-
if (model === "deepseek-chat") return "disabled";
|
|
6890
|
-
if (model.includes("reasoner")) return "enabled";
|
|
6891
|
-
if (model === "deepseek-v4-flash" || model === "deepseek-v4-pro") return "enabled";
|
|
6892
|
-
return void 0;
|
|
6893
|
-
}
|
|
6894
|
-
function stripHallucinatedToolMarkup(s) {
|
|
6895
|
-
let out = s;
|
|
6896
|
-
out = out.replace(/<|DSML|function_calls>[\s\S]*?<\/?|DSML|function_calls>/g, "");
|
|
6897
|
-
out = out.replace(/<\|DSML\|function_calls>[\s\S]*?<\/?\|DSML\|function_calls>/g, "");
|
|
6898
|
-
out = out.replace(/<function_calls>[\s\S]*?<\/function_calls>/g, "");
|
|
6899
|
-
out = out.replace(/<|DSML|[\s\S]*$/g, "");
|
|
6900
|
-
return out.trim();
|
|
6901
|
-
}
|
|
6902
|
-
|
|
6903
|
-
// src/loop/messages.ts
|
|
6904
|
-
function buildAssistantMessage(content, toolCalls, producingModel, reasoningContent) {
|
|
6905
|
-
const msg = { role: "assistant", content };
|
|
6906
|
-
if (toolCalls.length > 0) msg.tool_calls = toolCalls;
|
|
6907
|
-
if (isThinkingModeModel(producingModel) || reasoningContent && reasoningContent.length > 0) {
|
|
6908
|
-
msg.reasoning_content = reasoningContent ?? "";
|
|
6909
|
-
}
|
|
6910
|
-
return msg;
|
|
6911
|
-
}
|
|
6912
|
-
function buildSyntheticAssistantMessage(content, fallbackModel) {
|
|
6913
|
-
return buildAssistantMessage(content, [], fallbackModel, "");
|
|
6914
|
-
}
|
|
6915
|
-
|
|
6916
7000
|
// src/loop/force-summary.ts
|
|
6917
|
-
|
|
6918
|
-
var PAUSE_SUMMARY_EFFORT = "high";
|
|
6919
|
-
async function* forceSummaryAfterIterLimit(ctx, opts = { reason: "budget" }) {
|
|
7001
|
+
async function* forceSummaryAfterIterLimit(ctx, opts) {
|
|
6920
7002
|
try {
|
|
6921
7003
|
yield { turn: ctx.turn, role: "status", content: t("summary.status") };
|
|
6922
7004
|
const messages = ctx.buildMessages();
|
|
6923
7005
|
messages.push({
|
|
6924
7006
|
role: "user",
|
|
6925
|
-
content: "
|
|
7007
|
+
content: "The turn is being force-summarized (context guard or stuck-state). Summarize in plain prose what you learned from the tool results above. Do NOT emit any tool calls, function-call markup, DSML invocations, or SEARCH/REPLACE edit blocks \u2014 they will be silently discarded. Just plain text."
|
|
6926
7008
|
});
|
|
6927
7009
|
const summaryModel = "deepseek-v4-flash";
|
|
6928
7010
|
const summaryEffort = "high";
|
|
@@ -6936,7 +7018,7 @@ async function* forceSummaryAfterIterLimit(ctx, opts = { reason: "budget" }) {
|
|
|
6936
7018
|
const rawContent = resp.content?.trim() ?? "";
|
|
6937
7019
|
const cleaned = stripHallucinatedToolMarkup(rawContent);
|
|
6938
7020
|
const summary = cleaned || t("summary.hallucinatedFallback");
|
|
6939
|
-
const reasonPrefix = reasonPrefixFor(opts.reason
|
|
7021
|
+
const reasonPrefix = reasonPrefixFor(opts.reason);
|
|
6940
7022
|
const annotated = `${reasonPrefix}
|
|
6941
7023
|
|
|
6942
7024
|
${summary}`;
|
|
@@ -6951,7 +7033,7 @@ ${summary}`;
|
|
|
6951
7033
|
};
|
|
6952
7034
|
yield { turn: ctx.turn, role: "done", content: summary };
|
|
6953
7035
|
} catch (err) {
|
|
6954
|
-
const label = errorLabelFor(opts.reason
|
|
7036
|
+
const label = errorLabelFor(opts.reason);
|
|
6955
7037
|
yield {
|
|
6956
7038
|
turn: ctx.turn,
|
|
6957
7039
|
role: "error",
|
|
@@ -6961,28 +7043,6 @@ ${summary}`;
|
|
|
6961
7043
|
yield { turn: ctx.turn, role: "done", content: "" };
|
|
6962
7044
|
}
|
|
6963
7045
|
}
|
|
6964
|
-
async function summarizePartialProgress(ctx) {
|
|
6965
|
-
try {
|
|
6966
|
-
const messages = ctx.buildMessages();
|
|
6967
|
-
messages.push({
|
|
6968
|
-
role: "user",
|
|
6969
|
-
content: "You're being paused at a checkpoint, not stopped. In 3-6 sentences of plain prose, tell the parent agent: (1) what you accomplished so far, (2) what's still left, (3) any blockers or open questions. Be concrete \u2014 mention specific files / functions / tool results \u2014 so the parent can decide whether to resume you or take over. Do NOT emit any tool calls, function-call markup, DSML invocations, or SEARCH/REPLACE edit blocks \u2014 they will be silently discarded. Just plain text."
|
|
6970
|
-
});
|
|
6971
|
-
const resp = await ctx.client.chat({
|
|
6972
|
-
model: PAUSE_SUMMARY_MODEL,
|
|
6973
|
-
messages,
|
|
6974
|
-
signal: ctx.signal,
|
|
6975
|
-
thinking: thinkingModeForModel(PAUSE_SUMMARY_MODEL),
|
|
6976
|
-
reasoningEffort: PAUSE_SUMMARY_EFFORT
|
|
6977
|
-
});
|
|
6978
|
-
const cleaned = stripHallucinatedToolMarkup(resp.content?.trim() ?? "");
|
|
6979
|
-
if (!cleaned) return null;
|
|
6980
|
-
const stats = ctx.recordStats(PAUSE_SUMMARY_MODEL, resp.usage ?? new Usage());
|
|
6981
|
-
return { summary: cleaned, stats };
|
|
6982
|
-
} catch {
|
|
6983
|
-
return null;
|
|
6984
|
-
}
|
|
6985
|
-
}
|
|
6986
7046
|
|
|
6987
7047
|
// src/loop/shrink.ts
|
|
6988
7048
|
function looksLikeCompleteJson(s) {
|
|
@@ -7015,7 +7075,7 @@ function shrinkOversizedToolResultsByTokens(messages, maxTokens) {
|
|
|
7015
7075
|
if (msg.role !== "tool") return msg;
|
|
7016
7076
|
const content = typeof msg.content === "string" ? msg.content : "";
|
|
7017
7077
|
if (content.length <= maxTokens) return msg;
|
|
7018
|
-
const beforeTokens =
|
|
7078
|
+
const beforeTokens = countTokensBounded(content);
|
|
7019
7079
|
if (beforeTokens <= maxTokens) return msg;
|
|
7020
7080
|
const truncated = truncateForModelByTokens(content, maxTokens);
|
|
7021
7081
|
const afterTokens = countTokens(truncated);
|
|
@@ -7341,11 +7401,16 @@ var StormBreaker = class {
|
|
|
7341
7401
|
function repairTruncatedJson(input) {
|
|
7342
7402
|
const notes = [];
|
|
7343
7403
|
if (!input || !input.trim()) {
|
|
7344
|
-
return {
|
|
7404
|
+
return {
|
|
7405
|
+
repaired: "{}",
|
|
7406
|
+
changed: input !== "{}",
|
|
7407
|
+
notes: ["empty input \u2192 {}"],
|
|
7408
|
+
fallback: false
|
|
7409
|
+
};
|
|
7345
7410
|
}
|
|
7346
7411
|
try {
|
|
7347
7412
|
JSON.parse(input);
|
|
7348
|
-
return { repaired: input, changed: false, notes: [] };
|
|
7413
|
+
return { repaired: input, changed: false, notes: [], fallback: false };
|
|
7349
7414
|
} catch {
|
|
7350
7415
|
}
|
|
7351
7416
|
const stack = [];
|
|
@@ -7400,10 +7465,12 @@ function repairTruncatedJson(input) {
|
|
|
7400
7465
|
}
|
|
7401
7466
|
try {
|
|
7402
7467
|
JSON.parse(s);
|
|
7403
|
-
return { repaired: s, changed:
|
|
7468
|
+
return { repaired: s, changed: s !== input, notes, fallback: false };
|
|
7404
7469
|
} catch (err) {
|
|
7470
|
+
const preview = input.length <= 500 ? input : `${input.slice(0, 500)} \u2026[+${input.length - 500} chars]`;
|
|
7405
7471
|
notes.push(`fallback to {}: ${err.message}`);
|
|
7406
|
-
|
|
7472
|
+
notes.push(`unrecoverable truncation \u2014 original args preview: ${preview}`);
|
|
7473
|
+
return { repaired: "{}", changed: true, notes, fallback: true };
|
|
7407
7474
|
}
|
|
7408
7475
|
}
|
|
7409
7476
|
|
|
@@ -7450,9 +7517,16 @@ var ToolCallRepair = class {
|
|
|
7450
7517
|
const args = call.function?.arguments ?? "";
|
|
7451
7518
|
const r = repairTruncatedJson(args);
|
|
7452
7519
|
if (r.changed) {
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7520
|
+
if (r.fallback) {
|
|
7521
|
+
report.truncationsFixed++;
|
|
7522
|
+
report.notes.push(
|
|
7523
|
+
...r.notes.map((n) => `[${call.function?.name}] \u26A0\uFE0F TRUNCATION UNRECOVERABLE: ${n}`)
|
|
7524
|
+
);
|
|
7525
|
+
} else {
|
|
7526
|
+
call.function.arguments = r.repaired;
|
|
7527
|
+
report.truncationsFixed++;
|
|
7528
|
+
report.notes.push(...r.notes.map((n) => `[${call.function.name}] ${n}`));
|
|
7529
|
+
}
|
|
7456
7530
|
}
|
|
7457
7531
|
}
|
|
7458
7532
|
const filtered = [];
|
|
@@ -7474,12 +7548,10 @@ function signature(call) {
|
|
|
7474
7548
|
|
|
7475
7549
|
// src/loop.ts
|
|
7476
7550
|
var ESCALATION_MODEL = "deepseek-v4-pro";
|
|
7477
|
-
var PARENT_BUDGET_WARN_THRESHOLD = 5;
|
|
7478
7551
|
var CacheFirstLoop = class {
|
|
7479
7552
|
client;
|
|
7480
7553
|
prefix;
|
|
7481
7554
|
tools;
|
|
7482
|
-
maxToolIters;
|
|
7483
7555
|
log = new AppendOnlyLog();
|
|
7484
7556
|
scratch = new VolatileScratch();
|
|
7485
7557
|
stats = new SessionStats();
|
|
@@ -7494,7 +7566,6 @@ var CacheFirstLoop = class {
|
|
|
7494
7566
|
/** One-shot 80% warning latch — cleared by setBudget so a bump re-arms at the new boundary. */
|
|
7495
7567
|
_budgetWarned = false;
|
|
7496
7568
|
sessionName;
|
|
7497
|
-
onIterBudgetExhausted;
|
|
7498
7569
|
hooks;
|
|
7499
7570
|
hookCwd;
|
|
7500
7571
|
/** PauseGate bridge — defaults to singleton, injectable for tests. */
|
|
@@ -7508,12 +7579,25 @@ var CacheFirstLoop = class {
|
|
|
7508
7579
|
_turnAbort = new AbortController();
|
|
7509
7580
|
/** Authoritative running-id set — UI cards consult this instead of trusting end-event delivery. Insert at dispatch entry, delete in finally. */
|
|
7510
7581
|
_inflight = new InflightSet();
|
|
7582
|
+
/** Typeahead steer message set by the UI; step() consumes it at the next iter boundary. */
|
|
7583
|
+
_steer = null;
|
|
7584
|
+
/** Set true when a steer was consumed this turn; cleared on next step() entry. */
|
|
7585
|
+
_steerConsumed = false;
|
|
7586
|
+
/** UI calls this to inject a mid-turn steer message without aborting the current turn.
|
|
7587
|
+
* New text resets steerConsumed — a fresh steer hasn't been consumed yet. */
|
|
7588
|
+
steer(text) {
|
|
7589
|
+
this._steer = text;
|
|
7590
|
+
if (text !== null) this._steerConsumed = false;
|
|
7591
|
+
}
|
|
7592
|
+
/** True when a steer was consumed this turn (UI gate to avoid double-submit). */
|
|
7593
|
+
get steerConsumed() {
|
|
7594
|
+
return this._steerConsumed;
|
|
7595
|
+
}
|
|
7511
7596
|
_proArmedForNextTurn = false;
|
|
7512
7597
|
_escalateThisTurn = false;
|
|
7513
7598
|
_turnFailures;
|
|
7514
7599
|
_turnSelfCorrected = false;
|
|
7515
7600
|
_foldedThisTurn = false;
|
|
7516
|
-
_toolDispatchesThisStep = 0;
|
|
7517
7601
|
context;
|
|
7518
7602
|
/** Subscribe API so UI hooks can derive `running` from finally-guaranteed insertions. */
|
|
7519
7603
|
get inflight() {
|
|
@@ -7533,8 +7617,6 @@ var CacheFirstLoop = class {
|
|
|
7533
7617
|
this._turnFailures = new TurnFailureTracker(
|
|
7534
7618
|
resolveFailureThreshold(opts.failureThreshold, FAILURE_ESCALATION_THRESHOLD)
|
|
7535
7619
|
);
|
|
7536
|
-
this.maxToolIters = opts.maxToolIters ?? 64;
|
|
7537
|
-
this.onIterBudgetExhausted = opts.onIterBudgetExhausted ?? "summarize";
|
|
7538
7620
|
this.hooks = opts.hooks ?? [];
|
|
7539
7621
|
this.hookCwd = opts.hookCwd ?? process.cwd();
|
|
7540
7622
|
this.confirmationGate = opts.confirmationGate ?? pauseGate;
|
|
@@ -7555,23 +7637,6 @@ var CacheFirstLoop = class {
|
|
|
7555
7637
|
stormThreshold: parsePositiveIntEnv(process.env.REASONIX_STORM_THRESHOLD),
|
|
7556
7638
|
stormWindow: parsePositiveIntEnv(process.env.REASONIX_STORM_WINDOW)
|
|
7557
7639
|
});
|
|
7558
|
-
if (!this.tools.hasResultAugmenter) {
|
|
7559
|
-
this.tools.setResultAugmenter((_name, _args, result) => {
|
|
7560
|
-
this._toolDispatchesThisStep++;
|
|
7561
|
-
const remaining = this.maxToolIters - this._toolDispatchesThisStep;
|
|
7562
|
-
if (remaining <= 0) {
|
|
7563
|
-
return `${result}
|
|
7564
|
-
|
|
7565
|
-
[budget: 0 of ${this.maxToolIters} tool calls left this turn \u2014 finalize NOW; the next iter forces a summary]`;
|
|
7566
|
-
}
|
|
7567
|
-
if (remaining <= PARENT_BUDGET_WARN_THRESHOLD) {
|
|
7568
|
-
return `${result}
|
|
7569
|
-
|
|
7570
|
-
[budget: ${remaining} of ${this.maxToolIters} tool calls left this turn \u2014 wrap up soon]`;
|
|
7571
|
-
}
|
|
7572
|
-
return result;
|
|
7573
|
-
});
|
|
7574
|
-
}
|
|
7575
7640
|
this.sessionName = opts.session ?? null;
|
|
7576
7641
|
if (this.sessionName) {
|
|
7577
7642
|
const prior = loadSessionMessages(this.sessionName);
|
|
@@ -7657,6 +7722,10 @@ var CacheFirstLoop = class {
|
|
|
7657
7722
|
}
|
|
7658
7723
|
this.scratch.reset();
|
|
7659
7724
|
this._inflight.clear();
|
|
7725
|
+
this.stats.reset();
|
|
7726
|
+
this._turn = 0;
|
|
7727
|
+
this._turnFailures.reset();
|
|
7728
|
+
this._budgetWarned = false;
|
|
7660
7729
|
let systemRebuilt = false;
|
|
7661
7730
|
if (this._rebuildSystem) {
|
|
7662
7731
|
try {
|
|
@@ -7666,6 +7735,29 @@ var CacheFirstLoop = class {
|
|
|
7666
7735
|
}
|
|
7667
7736
|
return { dropped, archived, systemRebuilt };
|
|
7668
7737
|
}
|
|
7738
|
+
/** `/cwd` follow-through — archives the previous session, drops in-memory state, repoints sessionName, and rebuilds the system prompt against whatever the rebuilder closure now resolves (the caller is expected to have already updated the root the closure reads). */
|
|
7739
|
+
switchWorkspace(opts) {
|
|
7740
|
+
const dropped = this.log.length;
|
|
7741
|
+
let archived = null;
|
|
7742
|
+
if (this.sessionName) {
|
|
7743
|
+
try {
|
|
7744
|
+
archived = archiveSession(this.sessionName);
|
|
7745
|
+
if (archived === null) rewriteSession(this.sessionName, []);
|
|
7746
|
+
} catch {
|
|
7747
|
+
}
|
|
7748
|
+
}
|
|
7749
|
+
this.log.compactInPlace([]);
|
|
7750
|
+
this.scratch.reset();
|
|
7751
|
+
this._inflight.clear();
|
|
7752
|
+
this.sessionName = opts.sessionName;
|
|
7753
|
+
if (this._rebuildSystem) {
|
|
7754
|
+
try {
|
|
7755
|
+
this.prefix.replaceSystem(this._rebuildSystem());
|
|
7756
|
+
} catch {
|
|
7757
|
+
}
|
|
7758
|
+
}
|
|
7759
|
+
return { dropped, archived };
|
|
7760
|
+
}
|
|
7669
7761
|
configure(opts) {
|
|
7670
7762
|
if (opts.model !== void 0) this.model = opts.model;
|
|
7671
7763
|
if (opts.stream !== void 0) {
|
|
@@ -7821,6 +7913,7 @@ ${reason}`
|
|
|
7821
7913
|
return userText;
|
|
7822
7914
|
}
|
|
7823
7915
|
async *step(userInput) {
|
|
7916
|
+
this._steerConsumed = false;
|
|
7824
7917
|
if (this.budgetUsd !== null) {
|
|
7825
7918
|
const spent = this.stats.totalCost;
|
|
7826
7919
|
if (spent >= this.budgetUsd) {
|
|
@@ -7854,7 +7947,6 @@ ${reason}`
|
|
|
7854
7947
|
this._turnSelfCorrected = false;
|
|
7855
7948
|
this._escalateThisTurn = false;
|
|
7856
7949
|
this._foldedThisTurn = false;
|
|
7857
|
-
this._toolDispatchesThisStep = 0;
|
|
7858
7950
|
let armedConsumed = false;
|
|
7859
7951
|
if (this._proArmedForNextTurn) {
|
|
7860
7952
|
this._escalateThisTurn = true;
|
|
@@ -7872,16 +7964,15 @@ ${reason}`
|
|
|
7872
7964
|
content: t("loop.proArmed")
|
|
7873
7965
|
};
|
|
7874
7966
|
}
|
|
7875
|
-
|
|
7967
|
+
this.appendAndPersist({ role: "user", content: userInput });
|
|
7968
|
+
let pendingUser = null;
|
|
7876
7969
|
const toolSpecs = this.prefix.tools();
|
|
7877
|
-
|
|
7878
|
-
let warnedForIterBudget = false;
|
|
7879
|
-
for (let iter = 0; iter < this.maxToolIters; iter++) {
|
|
7970
|
+
for (let iter = 0; ; iter++) {
|
|
7880
7971
|
if (signal.aborted) {
|
|
7881
7972
|
yield {
|
|
7882
7973
|
turn: this._turn,
|
|
7883
7974
|
role: "warning",
|
|
7884
|
-
content: t("loop.abortedAtIter", { iter
|
|
7975
|
+
content: t("loop.abortedAtIter", { iter })
|
|
7885
7976
|
};
|
|
7886
7977
|
const stoppedMsg = "[aborted by user (Esc) \u2014 no summary produced. Ask again or /retry when ready; prior tool output is still in the log.]";
|
|
7887
7978
|
this.appendAndPersist(buildSyntheticAssistantMessage(stoppedMsg, this.model));
|
|
@@ -7902,15 +7993,20 @@ ${reason}`
|
|
|
7902
7993
|
content: t("loop.toolUploadStatus")
|
|
7903
7994
|
};
|
|
7904
7995
|
}
|
|
7905
|
-
|
|
7906
|
-
|
|
7996
|
+
let messages = this.buildMessages(pendingUser);
|
|
7997
|
+
if (this._steer !== null) {
|
|
7998
|
+
const steer = this._steer;
|
|
7999
|
+
this._steer = null;
|
|
8000
|
+
this._steerConsumed = true;
|
|
8001
|
+
this.appendAndPersist({ role: "user", content: steer });
|
|
8002
|
+
messages = this.buildMessages(pendingUser);
|
|
8003
|
+
pendingUser = null;
|
|
7907
8004
|
yield {
|
|
7908
8005
|
turn: this._turn,
|
|
7909
|
-
role: "
|
|
7910
|
-
content:
|
|
8006
|
+
role: "steer",
|
|
8007
|
+
content: steer
|
|
7911
8008
|
};
|
|
7912
8009
|
}
|
|
7913
|
-
let messages = this.buildMessages(pendingUser);
|
|
7914
8010
|
{
|
|
7915
8011
|
const decision2 = this.context.decidePreflight(messages, this.prefix.toolSpecs, this.model);
|
|
7916
8012
|
if (decision2.needsAction) {
|
|
@@ -7918,23 +8014,29 @@ ${reason}`
|
|
|
7918
8014
|
yield {
|
|
7919
8015
|
turn: this._turn,
|
|
7920
8016
|
role: "status",
|
|
7921
|
-
content: t("loop.
|
|
8017
|
+
content: t("loop.preflightTruncateStatus")
|
|
7922
8018
|
};
|
|
7923
|
-
const result =
|
|
8019
|
+
const result = this.context.mechanicalTruncate(this.model, {
|
|
8020
|
+
allowEmpty: pendingUser !== null
|
|
8021
|
+
});
|
|
7924
8022
|
if (result.folded) {
|
|
8023
|
+
messages = this.buildMessages(pendingUser);
|
|
8024
|
+
const after = this.context.decidePreflight(messages, this.prefix.toolSpecs, this.model);
|
|
8025
|
+
const stillFull = after.needsAction;
|
|
7925
8026
|
yield {
|
|
7926
8027
|
turn: this._turn,
|
|
7927
8028
|
role: "warning",
|
|
7928
|
-
content: t(
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
8029
|
+
content: t(
|
|
8030
|
+
stillFull ? "loop.preflightTruncatedStillFull" : "loop.preflightTruncated",
|
|
8031
|
+
{
|
|
8032
|
+
estimate: after.estimateTokens.toLocaleString(),
|
|
8033
|
+
ctxMax: after.ctxMax.toLocaleString(),
|
|
8034
|
+
pct: Math.round(after.estimateTokens / after.ctxMax * 100),
|
|
8035
|
+
beforeMessages: result.beforeMessages,
|
|
8036
|
+
afterMessages: result.afterMessages
|
|
8037
|
+
}
|
|
8038
|
+
)
|
|
7936
8039
|
};
|
|
7937
|
-
messages = this.buildMessages(pendingUser);
|
|
7938
8040
|
} else {
|
|
7939
8041
|
yield {
|
|
7940
8042
|
turn: this._turn,
|
|
@@ -8091,10 +8193,6 @@ ${reason}`
|
|
|
8091
8193
|
this.modelForCurrentCall(),
|
|
8092
8194
|
usage ?? new Usage()
|
|
8093
8195
|
);
|
|
8094
|
-
if (pendingUser !== null) {
|
|
8095
|
-
this.appendAndPersist({ role: "user", content: pendingUser });
|
|
8096
|
-
pendingUser = null;
|
|
8097
|
-
}
|
|
8098
8196
|
this.scratch.reasoning = reasoningContent || null;
|
|
8099
8197
|
const { calls: repairedCalls, report } = this.repair.process(
|
|
8100
8198
|
toolCalls,
|
|
@@ -8288,20 +8386,6 @@ ${reason}`
|
|
|
8288
8386
|
}
|
|
8289
8387
|
}
|
|
8290
8388
|
}
|
|
8291
|
-
if (this.onIterBudgetExhausted === "pause") {
|
|
8292
|
-
const partial = await summarizePartialProgress(this.summaryContext());
|
|
8293
|
-
yield {
|
|
8294
|
-
turn: this._turn,
|
|
8295
|
-
role: "paused",
|
|
8296
|
-
content: "",
|
|
8297
|
-
sessionName: this.sessionName ?? void 0,
|
|
8298
|
-
pausedAtIter: this.maxToolIters,
|
|
8299
|
-
partialSummary: partial?.summary
|
|
8300
|
-
};
|
|
8301
|
-
yield { turn: this._turn, role: "done", content: "" };
|
|
8302
|
-
return;
|
|
8303
|
-
}
|
|
8304
|
-
yield* forceSummaryAfterIterLimit(this.summaryContext(), { reason: "budget" });
|
|
8305
8389
|
}
|
|
8306
8390
|
summaryContext() {
|
|
8307
8391
|
return {
|
|
@@ -8310,8 +8394,7 @@ ${reason}`
|
|
|
8310
8394
|
buildMessages: () => this.buildMessages(null),
|
|
8311
8395
|
appendAndPersist: (m) => this.appendAndPersist(m),
|
|
8312
8396
|
recordStats: (model, usage) => this.stats.record(this._turn, model, usage),
|
|
8313
|
-
turn: this._turn
|
|
8314
|
-
maxToolIters: this.maxToolIters
|
|
8397
|
+
turn: this._turn
|
|
8315
8398
|
};
|
|
8316
8399
|
}
|
|
8317
8400
|
async run(userInput, onEvent) {
|
|
@@ -9144,6 +9227,7 @@ var DEFAULT_TOPK = 5;
|
|
|
9144
9227
|
var FETCH_MAX_BYTES = 10 * 1024 * 1024;
|
|
9145
9228
|
var USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
|
9146
9229
|
var MOJEEK_ENDPOINT = "https://www.mojeek.com/search";
|
|
9230
|
+
var METASO_ENDPOINT = "https://metaso.cn/api/v1";
|
|
9147
9231
|
function searchStatusError(status) {
|
|
9148
9232
|
if (status === 429) return t("webErrors.rateLimit429");
|
|
9149
9233
|
if (status === 403) return t("webErrors.forbidden403");
|
|
@@ -9157,6 +9241,9 @@ function fetchStatusError(status, url) {
|
|
|
9157
9241
|
return t("webErrors.fetchStatus", { status, url });
|
|
9158
9242
|
}
|
|
9159
9243
|
async function webSearch(query, opts = {}) {
|
|
9244
|
+
if (opts.engine === "metaso") {
|
|
9245
|
+
return searchMetaso(query, opts);
|
|
9246
|
+
}
|
|
9160
9247
|
if (opts.engine === "searxng") {
|
|
9161
9248
|
return searchSearxng(query, opts);
|
|
9162
9249
|
}
|
|
@@ -9232,6 +9319,68 @@ async function searchSearxng(query, opts = {}) {
|
|
|
9232
9319
|
}
|
|
9233
9320
|
return results;
|
|
9234
9321
|
}
|
|
9322
|
+
async function searchMetaso(query, opts = {}) {
|
|
9323
|
+
const topK = Math.max(1, Math.min(100, opts.topK ?? DEFAULT_TOPK));
|
|
9324
|
+
const apiKey = loadMetasoApiKey();
|
|
9325
|
+
let resp;
|
|
9326
|
+
try {
|
|
9327
|
+
resp = await fetch(`${METASO_ENDPOINT}/search`, {
|
|
9328
|
+
method: "POST",
|
|
9329
|
+
headers: {
|
|
9330
|
+
"Content-Type": "application/json",
|
|
9331
|
+
Accept: "application/json",
|
|
9332
|
+
Authorization: `Bearer ${apiKey}`
|
|
9333
|
+
},
|
|
9334
|
+
body: JSON.stringify({
|
|
9335
|
+
q: query,
|
|
9336
|
+
scope: "webpage",
|
|
9337
|
+
size: topK
|
|
9338
|
+
}),
|
|
9339
|
+
signal: opts.signal
|
|
9340
|
+
});
|
|
9341
|
+
} catch (err) {
|
|
9342
|
+
if (err instanceof TypeError && err.message.includes("fetch")) {
|
|
9343
|
+
throw new Error(t("webErrors.cannotReach", { endpoint: METASO_ENDPOINT }));
|
|
9344
|
+
}
|
|
9345
|
+
throw err;
|
|
9346
|
+
}
|
|
9347
|
+
const raw = await resp.text();
|
|
9348
|
+
let data;
|
|
9349
|
+
try {
|
|
9350
|
+
data = JSON.parse(raw);
|
|
9351
|
+
} catch {
|
|
9352
|
+
throw new Error(t("webErrors.metasoParseError", { status: resp.status }));
|
|
9353
|
+
}
|
|
9354
|
+
if (!resp.ok) {
|
|
9355
|
+
if (resp.status === 401 || resp.status === 403) {
|
|
9356
|
+
throw new Error(t("webErrors.metasoUnauthorized"));
|
|
9357
|
+
}
|
|
9358
|
+
if (resp.status === 429) {
|
|
9359
|
+
throw new Error(t("webErrors.metasoRateLimit"));
|
|
9360
|
+
}
|
|
9361
|
+
throw new Error(t("webErrors.metasoServerError", { status: resp.status }));
|
|
9362
|
+
}
|
|
9363
|
+
if (data.code === 3003) {
|
|
9364
|
+
throw new Error(t("webErrors.metasoDailyLimit"));
|
|
9365
|
+
}
|
|
9366
|
+
if (data.code === 2005) {
|
|
9367
|
+
throw new Error(t("webErrors.metasoUnauthorized"));
|
|
9368
|
+
}
|
|
9369
|
+
if (data.code && data.code !== 0) {
|
|
9370
|
+
throw new Error(
|
|
9371
|
+
t("webErrors.metasoApiError", { code: data.code, message: data.message ?? "" })
|
|
9372
|
+
);
|
|
9373
|
+
}
|
|
9374
|
+
const webpages = data.webpages ?? [];
|
|
9375
|
+
if (webpages.length === 0) {
|
|
9376
|
+
return [];
|
|
9377
|
+
}
|
|
9378
|
+
return webpages.slice(0, topK).map((wp) => ({
|
|
9379
|
+
title: wp.title,
|
|
9380
|
+
url: wp.link,
|
|
9381
|
+
snippet: wp.snippet ?? wp.summary ?? ""
|
|
9382
|
+
}));
|
|
9383
|
+
}
|
|
9235
9384
|
function parseSearxngHtmlResults(html) {
|
|
9236
9385
|
const root = (0, import_node_html_parser.parse)(html);
|
|
9237
9386
|
const results = [];
|
|
@@ -9450,7 +9599,7 @@ function registerWebTools(registry, opts = {}) {
|
|
|
9450
9599
|
const maxFetchChars = opts.maxFetchChars ?? DEFAULT_FETCH_MAX_CHARS;
|
|
9451
9600
|
registry.register({
|
|
9452
9601
|
name: "web_search",
|
|
9453
|
-
description: "Search the public web. Returns ranked results with title, url, and snippet. Call this when the answer's correctness depends on current state \u2014 anything that changes over time (events, prices, releases, status of a thing in the real world). Composing such answers from training memory invents stale numbers; search first, then ground the answer in the results. For evergreen / definitional questions you don't need this. To change the backend, use /
|
|
9602
|
+
description: "Search the public web. Returns ranked results with title, url, and snippet. Call this when the answer's correctness depends on current state \u2014 anything that changes over time (events, prices, releases, status of a thing in the real world). Composing such answers from training memory invents stale numbers; search first, then ground the answer in the results. For evergreen / definitional questions you don't need this. To change the backend, use /search-engine mojeek|searxng|metaso.",
|
|
9454
9603
|
readOnly: true,
|
|
9455
9604
|
parallelSafe: true,
|
|
9456
9605
|
parameters: {
|
|
@@ -9519,6 +9668,51 @@ var import_picomatch2 = __toESM(require_picomatch(), 1);
|
|
|
9519
9668
|
import { promises as fs3 } from "fs";
|
|
9520
9669
|
import * as pathMod4 from "path";
|
|
9521
9670
|
|
|
9671
|
+
// src/memory/subdir.ts
|
|
9672
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
9673
|
+
import { dirname, join as join2, relative as relative2, resolve as resolve2 } from "path";
|
|
9674
|
+
function findSubdirMemoryAncestors(absPath, rootDir) {
|
|
9675
|
+
const root = resolve2(rootDir);
|
|
9676
|
+
const target = resolve2(absPath);
|
|
9677
|
+
const rel = relative2(root, target);
|
|
9678
|
+
if (!rel || rel.startsWith("..")) return [];
|
|
9679
|
+
const found = [];
|
|
9680
|
+
let cur = dirname(target);
|
|
9681
|
+
while (cur !== root) {
|
|
9682
|
+
const r = relative2(root, cur);
|
|
9683
|
+
if (!r || r.startsWith("..")) break;
|
|
9684
|
+
for (const name of PROJECT_MEMORY_FILES) {
|
|
9685
|
+
const path = join2(cur, name);
|
|
9686
|
+
if (existsSync2(path)) {
|
|
9687
|
+
found.push(path);
|
|
9688
|
+
break;
|
|
9689
|
+
}
|
|
9690
|
+
}
|
|
9691
|
+
const parent = dirname(cur);
|
|
9692
|
+
if (parent === cur) break;
|
|
9693
|
+
cur = parent;
|
|
9694
|
+
}
|
|
9695
|
+
return found;
|
|
9696
|
+
}
|
|
9697
|
+
function readSubdirMemoryContent(path) {
|
|
9698
|
+
let raw;
|
|
9699
|
+
try {
|
|
9700
|
+
raw = readFileSync2(path, "utf8");
|
|
9701
|
+
} catch {
|
|
9702
|
+
return null;
|
|
9703
|
+
}
|
|
9704
|
+
const trimmed = raw.trim();
|
|
9705
|
+
if (!trimmed) return null;
|
|
9706
|
+
if (trimmed.length <= PROJECT_MEMORY_MAX_CHARS) return trimmed;
|
|
9707
|
+
return `${trimmed.slice(0, PROJECT_MEMORY_MAX_CHARS)}
|
|
9708
|
+
\u2026 (truncated ${trimmed.length - PROJECT_MEMORY_MAX_CHARS} chars)`;
|
|
9709
|
+
}
|
|
9710
|
+
function formatSubdirMemorySection(displayPath, content) {
|
|
9711
|
+
return `[module memory: ${displayPath}]
|
|
9712
|
+
|
|
9713
|
+
${content}`;
|
|
9714
|
+
}
|
|
9715
|
+
|
|
9522
9716
|
// src/tools/fs/glob.ts
|
|
9523
9717
|
var import_picomatch = __toESM(require_picomatch(), 1);
|
|
9524
9718
|
import { promises as fs } from "fs";
|
|
@@ -9593,6 +9787,18 @@ var RUST_DECL_RE = /^(?:pub(?:\([^)]+\))?\s+)?(?:async\s+)?(?:unsafe\s+)?(fn|str
|
|
|
9593
9787
|
var RUST_IMPL_RE = /^(?:unsafe\s+)?impl(?:\s*<[^>]+>)?\s+(?:[^{]+\s+for\s+)?(\w+)/;
|
|
9594
9788
|
var MD_HEADING_RE = /^(#{1,6})\s+(.+?)\s*$/;
|
|
9595
9789
|
var MD_FENCE_RE = /^```/;
|
|
9790
|
+
var PROTO_TOP_RE = /^(message|service|enum|extend)\s+(\w+)/;
|
|
9791
|
+
var PROTO_RPC_RE = /^\s+rpc\s+(\w+)/;
|
|
9792
|
+
var CN_NUM = "[\\d\u96F6\u4E00\u4E8C\u4E09\u56DB\u4E94\u516D\u4E03\u516B\u4E5D\u5341\u767E\u5343\u4E07\uFF10-\uFF19]+";
|
|
9793
|
+
var TXT_CHAPTER_PATTERNS = [
|
|
9794
|
+
new RegExp(`^\u7B2C${CN_NUM}[\u7AE0\u8282\u56DE].{0,80}$`),
|
|
9795
|
+
new RegExp(`^\u5377${CN_NUM}.{0,80}$`),
|
|
9796
|
+
/^(?:序章|楔子|番外篇?|前言|后记|尾声|引子)(?:[\s\u3000::、—\-.].{0,80})?$/,
|
|
9797
|
+
/^Chapter\s+(?:\d+|[IVXLCDMivxlcdm]+|[A-Za-z]+)\b.{0,80}$/,
|
|
9798
|
+
/^CHAPTER\s+.{1,80}$/,
|
|
9799
|
+
/^Part\s+(?:\d+|[IVXLCDMivxlcdm]+)\b.{0,80}$/,
|
|
9800
|
+
/^PART\s+.{1,80}$/
|
|
9801
|
+
];
|
|
9596
9802
|
var EXT_TO_LANG = {
|
|
9597
9803
|
".ts": "ts",
|
|
9598
9804
|
".tsx": "ts",
|
|
@@ -9608,7 +9814,10 @@ var EXT_TO_LANG = {
|
|
|
9608
9814
|
".rs": "rust",
|
|
9609
9815
|
".md": "md",
|
|
9610
9816
|
".markdown": "md",
|
|
9611
|
-
".mdx": "md"
|
|
9817
|
+
".mdx": "md",
|
|
9818
|
+
".proto": "proto",
|
|
9819
|
+
".txt": "txt",
|
|
9820
|
+
".text": "txt"
|
|
9612
9821
|
};
|
|
9613
9822
|
function extractOutline(filename, lines) {
|
|
9614
9823
|
const ext = pathMod2.extname(filename).toLowerCase();
|
|
@@ -9625,6 +9834,10 @@ function extractOutline(filename, lines) {
|
|
|
9625
9834
|
return extractRust(lines);
|
|
9626
9835
|
case "md":
|
|
9627
9836
|
return extractMarkdown(lines);
|
|
9837
|
+
case "proto":
|
|
9838
|
+
return extractProto(lines);
|
|
9839
|
+
case "txt":
|
|
9840
|
+
return extractText(lines);
|
|
9628
9841
|
}
|
|
9629
9842
|
}
|
|
9630
9843
|
function extractTs(lines) {
|
|
@@ -9676,6 +9889,36 @@ function extractRust(lines) {
|
|
|
9676
9889
|
}
|
|
9677
9890
|
return out;
|
|
9678
9891
|
}
|
|
9892
|
+
function extractProto(lines) {
|
|
9893
|
+
const out = [];
|
|
9894
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9895
|
+
const line = lines[i];
|
|
9896
|
+
if (!line.startsWith(" ") && !line.startsWith(" ")) {
|
|
9897
|
+
const m = PROTO_TOP_RE.exec(line);
|
|
9898
|
+
if (m) {
|
|
9899
|
+
out.push({ line: i + 1, text: `${m[1]} ${m[2]}` });
|
|
9900
|
+
continue;
|
|
9901
|
+
}
|
|
9902
|
+
}
|
|
9903
|
+
const rpc = PROTO_RPC_RE.exec(line);
|
|
9904
|
+
if (rpc) out.push({ line: i + 1, text: `rpc ${rpc[1]}` });
|
|
9905
|
+
}
|
|
9906
|
+
return out;
|
|
9907
|
+
}
|
|
9908
|
+
function extractText(lines) {
|
|
9909
|
+
const out = [];
|
|
9910
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9911
|
+
const line = lines[i].trim();
|
|
9912
|
+
if (line.length === 0 || line.length > 100) continue;
|
|
9913
|
+
for (const re of TXT_CHAPTER_PATTERNS) {
|
|
9914
|
+
if (re.test(line)) {
|
|
9915
|
+
out.push({ line: i + 1, text: line });
|
|
9916
|
+
break;
|
|
9917
|
+
}
|
|
9918
|
+
}
|
|
9919
|
+
}
|
|
9920
|
+
return out;
|
|
9921
|
+
}
|
|
9679
9922
|
function extractMarkdown(lines) {
|
|
9680
9923
|
const out = [];
|
|
9681
9924
|
let inFence = false;
|
|
@@ -9923,17 +10166,25 @@ async function searchContent(ctx, startAbs, args) {
|
|
|
9923
10166
|
}
|
|
9924
10167
|
|
|
9925
10168
|
// src/tools/filesystem.ts
|
|
9926
|
-
var
|
|
10169
|
+
var DEFAULT_OUTLINE_THRESHOLD_BYTES = 512 * 1024;
|
|
9927
10170
|
var DEFAULT_MAX_LIST_BYTES = 256 * 1024;
|
|
9928
|
-
var
|
|
9929
|
-
var
|
|
9930
|
-
var AUTO_PREVIEW_TAIL_LINES = 40;
|
|
9931
|
-
var OUTLINE_MAX_ENTRIES2 = 30;
|
|
10171
|
+
var HARD_MAX_FILE_BYTES = 32 * 1024 * 1024;
|
|
10172
|
+
var OUTLINE_HEAD_LINES = 80;
|
|
9932
10173
|
var SKIP_DIR_NAMES = new Set(DEFAULT_INDEX_EXCLUDES.dirs);
|
|
9933
10174
|
var BINARY_EXTENSIONS = new Set(DEFAULT_INDEX_EXCLUDES.exts);
|
|
9934
10175
|
function displayRel3(rootDir, full) {
|
|
9935
10176
|
return pathMod4.relative(rootDir, full).replaceAll("\\", "/");
|
|
9936
10177
|
}
|
|
10178
|
+
function looksLikeAbsoluteSystemPath(raw) {
|
|
10179
|
+
if (/^[A-Za-z]:[\\/]/.test(raw)) return true;
|
|
10180
|
+
return /^\/(?:home|Users|etc|var|opt|tmp|usr|mnt|Library|Volumes|proc|sys|dev|run|srv|media|Applications|System|root|boot|private)(?:[/\\]|$)/.test(
|
|
10181
|
+
raw
|
|
10182
|
+
);
|
|
10183
|
+
}
|
|
10184
|
+
function pathIsUnder(child, parent) {
|
|
10185
|
+
const rel = pathMod4.relative(parent, child);
|
|
10186
|
+
return rel === "" || !rel.startsWith("..") && !pathMod4.isAbsolute(rel);
|
|
10187
|
+
}
|
|
9937
10188
|
var GLOB_METACHARS = /[*?{[]/;
|
|
9938
10189
|
function compileNameFilter(filter) {
|
|
9939
10190
|
if (!filter) return null;
|
|
@@ -9950,24 +10201,45 @@ function isLikelyBinaryByName(name) {
|
|
|
9950
10201
|
if (dot < 0) return false;
|
|
9951
10202
|
return BINARY_EXTENSIONS.has(name.slice(dot).toLowerCase());
|
|
9952
10203
|
}
|
|
10204
|
+
function looksBinary(buf) {
|
|
10205
|
+
const end = Math.min(buf.length, 8192);
|
|
10206
|
+
for (let i = 0; i < end; i++) {
|
|
10207
|
+
if (buf[i] === 0) return true;
|
|
10208
|
+
}
|
|
10209
|
+
return false;
|
|
10210
|
+
}
|
|
10211
|
+
function formatBytes(n) {
|
|
10212
|
+
if (n < 1024) return `${n} B`;
|
|
10213
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KiB`;
|
|
10214
|
+
if (n < 1024 * 1024 * 1024) return `${(n / (1024 * 1024)).toFixed(1)} MiB`;
|
|
10215
|
+
return `${(n / (1024 * 1024 * 1024)).toFixed(2)} GiB`;
|
|
10216
|
+
}
|
|
9953
10217
|
function registerFilesystemTools(registry, opts) {
|
|
9954
10218
|
const rootDir = pathMod4.resolve(opts.rootDir);
|
|
9955
10219
|
const allowWriting = opts.allowWriting !== false;
|
|
9956
|
-
const
|
|
10220
|
+
const outlineThresholdBytes = opts.outlineThresholdBytes ?? DEFAULT_OUTLINE_THRESHOLD_BYTES;
|
|
9957
10221
|
const maxListBytes = opts.maxListBytes ?? DEFAULT_MAX_LIST_BYTES;
|
|
9958
10222
|
const normRoot = pathMod4.resolve(rootDir);
|
|
9959
10223
|
const sessionApproved = /* @__PURE__ */ new Set();
|
|
9960
|
-
const
|
|
9961
|
-
function
|
|
9962
|
-
|
|
9963
|
-
|
|
9964
|
-
|
|
9965
|
-
|
|
9966
|
-
|
|
9967
|
-
|
|
9968
|
-
|
|
9969
|
-
|
|
10224
|
+
const shownSubdirMemory = /* @__PURE__ */ new Set();
|
|
10225
|
+
function withSubdirMemory(absPath, body) {
|
|
10226
|
+
if (!memoryEnabled()) return body;
|
|
10227
|
+
const ancestors = findSubdirMemoryAncestors(absPath, rootDir);
|
|
10228
|
+
if (ancestors.length === 0) return body;
|
|
10229
|
+
const sections = [];
|
|
10230
|
+
for (const memPath of [...ancestors].reverse()) {
|
|
10231
|
+
if (shownSubdirMemory.has(memPath)) continue;
|
|
10232
|
+
const content = readSubdirMemoryContent(memPath);
|
|
10233
|
+
if (!content) continue;
|
|
10234
|
+
shownSubdirMemory.add(memPath);
|
|
10235
|
+
sections.push(formatSubdirMemorySection(displayRel3(rootDir, memPath), content));
|
|
10236
|
+
}
|
|
10237
|
+
if (sections.length === 0) return body;
|
|
10238
|
+
return `${sections.join("\n\n")}
|
|
10239
|
+
|
|
10240
|
+
${body}`;
|
|
9970
10241
|
}
|
|
10242
|
+
const inflightGate = /* @__PURE__ */ new Map();
|
|
9971
10243
|
async function ensureOutsideSandboxAllowed(abs, intent, toolName, ctx) {
|
|
9972
10244
|
for (const dir of loadProjectPathAllowed(rootDir)) {
|
|
9973
10245
|
if (pathIsUnder(abs, dir)) return;
|
|
@@ -10032,11 +10304,11 @@ function registerFilesystemTools(registry, opts) {
|
|
|
10032
10304
|
registry.register({
|
|
10033
10305
|
name: "read_file",
|
|
10034
10306
|
parallelSafe: true,
|
|
10035
|
-
description: `Read a file under the sandbox root.
|
|
10036
|
-
- head: N \u2192 first N lines (imports
|
|
10037
|
-
- tail: N \u2192 last N lines (
|
|
10038
|
-
- range: "A-B" \u2192 inclusive
|
|
10039
|
-
|
|
10307
|
+
description: `Read a file under the sandbox root. Default behaviour returns FULL CONTENT for files at or under ${Math.round(DEFAULT_OUTLINE_THRESHOLD_BYTES / 1024)} KiB \u2014 trust the prompt cache, don't pre-truncate. Optional scoping:
|
|
10308
|
+
- head: N \u2192 first N lines (cheap probe of imports / config head)
|
|
10309
|
+
- tail: N \u2192 last N lines (recent-tail of a log)
|
|
10310
|
+
- range: "A-B" \u2192 inclusive 1-indexed range (e.g. "120-180" around an edit site)
|
|
10311
|
+
Files OVER the threshold auto-switch to outline mode: file metadata + first ${OUTLINE_HEAD_LINES} lines + a top-level symbol outline (TS/JS exports, Python def/class, Go func/type, Rust fn/struct/impl/trait, Markdown headings, Protobuf message/service/rpc, plain-text chapter markers) + concrete next-step commands. No middle bytes \u2014 drill in with range / search_content. Files over ${Math.round(HARD_MAX_FILE_BYTES / (1024 * 1024))} MiB are refused entirely (use grep / range). Binary files are refused \u2014 use get_file_info if you only need stat.`,
|
|
10040
10312
|
readOnly: true,
|
|
10041
10313
|
stormExempt: true,
|
|
10042
10314
|
parameters: {
|
|
@@ -10054,22 +10326,31 @@ When none of these is given AND the file is longer than ${DEFAULT_AUTO_PREVIEW_L
|
|
|
10054
10326
|
},
|
|
10055
10327
|
fn: async (args, ctx) => {
|
|
10056
10328
|
const abs = await safePath(args.path, "read_file", ctx);
|
|
10329
|
+
const rel = displayRel3(rootDir, abs);
|
|
10057
10330
|
const fh = await fs3.open(abs, "r");
|
|
10058
10331
|
let raw;
|
|
10332
|
+
let sizeBytes;
|
|
10059
10333
|
try {
|
|
10060
10334
|
const stat2 = await fh.stat();
|
|
10061
10335
|
if (stat2.isDirectory()) {
|
|
10062
10336
|
throw new Error(`not a file: ${args.path} (it's a directory)`);
|
|
10063
10337
|
}
|
|
10338
|
+
sizeBytes = stat2.size;
|
|
10339
|
+
if (sizeBytes > HARD_MAX_FILE_BYTES) {
|
|
10340
|
+
return [
|
|
10341
|
+
`[refused: ${rel} is ${formatBytes(sizeBytes)} (> ${formatBytes(HARD_MAX_FILE_BYTES)} hard ceiling) \u2014 too large to load]`,
|
|
10342
|
+
"Use one of:",
|
|
10343
|
+
` - search_content path:"${rel}" pattern:"<your regex>" \u2014 grep within the file`,
|
|
10344
|
+
` - read_file path:"${rel}" range:"A-B" \u2014 read a specific 1-indexed line range`,
|
|
10345
|
+
` - read_file path:"${rel}" head:N / tail:N \u2014 read N lines at the start or end`
|
|
10346
|
+
].join("\n");
|
|
10347
|
+
}
|
|
10064
10348
|
raw = await fh.readFile();
|
|
10065
10349
|
} finally {
|
|
10066
10350
|
await fh.close();
|
|
10067
10351
|
}
|
|
10068
|
-
if (raw
|
|
10069
|
-
|
|
10070
|
-
return `${headBytes}
|
|
10071
|
-
|
|
10072
|
-
[\u2026truncated ${raw.length - maxReadBytes} bytes \u2014 file is ${raw.length} B, cap ${maxReadBytes} B. Retry with head/tail/range for targeted view.]`;
|
|
10352
|
+
if (looksBinary(raw)) {
|
|
10353
|
+
return `[refused: ${rel} appears to be binary (${formatBytes(sizeBytes)}) \u2014 read_file returns text only. Use get_file_info for stat.]`;
|
|
10073
10354
|
}
|
|
10074
10355
|
const text = raw.toString("utf8");
|
|
10075
10356
|
let lines = text.split(/\r?\n/);
|
|
@@ -10081,8 +10362,8 @@ When none of these is given AND the file is longer than ${DEFAULT_AUTO_PREVIEW_L
|
|
|
10081
10362
|
const end = Math.min(totalLines, Math.max(start, rawEnd ?? totalLines));
|
|
10082
10363
|
const slice = lines.slice(start - 1, end);
|
|
10083
10364
|
const label = `[range ${start}-${end} of ${totalLines} lines]`;
|
|
10084
|
-
return `${label}
|
|
10085
|
-
${slice.join("\n")}
|
|
10365
|
+
return withSubdirMemory(abs, `${label}
|
|
10366
|
+
${slice.join("\n")}`);
|
|
10086
10367
|
}
|
|
10087
10368
|
if (typeof args.head === "number" && args.head > 0) {
|
|
10088
10369
|
const count = Math.min(args.head, totalLines);
|
|
@@ -10090,7 +10371,7 @@ ${slice.join("\n")}`;
|
|
|
10090
10371
|
const marker = count < totalLines ? `
|
|
10091
10372
|
|
|
10092
10373
|
[\u2026head ${count} of ${totalLines} lines \u2014 call again with range / tail for more]` : "";
|
|
10093
|
-
return slice.join("\n") + marker;
|
|
10374
|
+
return withSubdirMemory(abs, slice.join("\n") + marker);
|
|
10094
10375
|
}
|
|
10095
10376
|
if (typeof args.tail === "number" && args.tail > 0) {
|
|
10096
10377
|
const count = Math.min(args.tail, totalLines);
|
|
@@ -10098,25 +10379,26 @@ ${slice.join("\n")}`;
|
|
|
10098
10379
|
const marker = count < totalLines ? `[\u2026tail ${count} of ${totalLines} lines \u2014 call again with range / head for more]
|
|
10099
10380
|
|
|
10100
10381
|
` : "";
|
|
10101
|
-
return marker + slice.join("\n");
|
|
10382
|
+
return withSubdirMemory(abs, marker + slice.join("\n"));
|
|
10102
10383
|
}
|
|
10103
|
-
if (
|
|
10104
|
-
const head = lines.slice(0,
|
|
10105
|
-
const tail = lines.slice(totalLines - AUTO_PREVIEW_TAIL_LINES).join("\n");
|
|
10106
|
-
const omitted = totalLines - AUTO_PREVIEW_HEAD_LINES - AUTO_PREVIEW_TAIL_LINES;
|
|
10384
|
+
if (sizeBytes <= outlineThresholdBytes) return withSubdirMemory(abs, lines.join("\n"));
|
|
10385
|
+
const head = lines.slice(0, Math.min(OUTLINE_HEAD_LINES, totalLines)).join("\n");
|
|
10107
10386
|
const outline = formatOutline(extractOutline(abs, lines));
|
|
10108
10387
|
const parts = [
|
|
10109
|
-
`[
|
|
10388
|
+
`[large file: ${formatBytes(sizeBytes)}, ${totalLines} lines \u2014 outline mode (threshold ${formatBytes(outlineThresholdBytes)})]`,
|
|
10389
|
+
"",
|
|
10390
|
+
`[head ${Math.min(OUTLINE_HEAD_LINES, totalLines)} lines for orientation]`,
|
|
10110
10391
|
head
|
|
10111
10392
|
];
|
|
10112
10393
|
if (outline) parts.push("", outline);
|
|
10113
10394
|
parts.push(
|
|
10114
|
-
|
|
10115
|
-
[
|
|
10116
|
-
`,
|
|
10117
|
-
tail
|
|
10395
|
+
"",
|
|
10396
|
+
"[to read more, call one of:",
|
|
10397
|
+
` - read_file path:"${rel}" range:"A-B" \u2014 1-indexed line range`,
|
|
10398
|
+
` - read_file path:"${rel}" head:N / tail:N \u2014 first/last N lines`,
|
|
10399
|
+
` - search_content path:"${rel}" pattern:"..." \u2014 grep within this file]`
|
|
10118
10400
|
);
|
|
10119
|
-
return parts.join("\n");
|
|
10401
|
+
return withSubdirMemory(abs, parts.join("\n"));
|
|
10120
10402
|
}
|
|
10121
10403
|
});
|
|
10122
10404
|
registry.register({
|
|
@@ -10857,7 +11139,7 @@ var VERIFY_SYSTEM = `You are a verify subagent. Narrow check \u2014 return YES /
|
|
|
10857
11139
|
How to operate:
|
|
10858
11140
|
- Read only what's needed to verify the specific claim. No exploration past the claim.
|
|
10859
11141
|
- Use search_content / read_file to confirm the exact behavior, type, or call site in question.
|
|
10860
|
-
-
|
|
11142
|
+
- If a focused round of reads can't verify it, return INCONCLUSIVE plus what's missing \u2014 don't keep digging.
|
|
10861
11143
|
|
|
10862
11144
|
Final answer:
|
|
10863
11145
|
- Lead with VERIFIED / NOT VERIFIED / INCONCLUSIVE.
|
|
@@ -10868,8 +11150,8 @@ ${NEGATIVE_CLAIM_RULE}
|
|
|
10868
11150
|
|
|
10869
11151
|
${TUI_FORMATTING_RULES}`;
|
|
10870
11152
|
var TYPES = {
|
|
10871
|
-
explore: { system: EXPLORE_SYSTEM
|
|
10872
|
-
verify: { system: VERIFY_SYSTEM
|
|
11153
|
+
explore: { system: EXPLORE_SYSTEM },
|
|
11154
|
+
verify: { system: VERIFY_SYSTEM }
|
|
10873
11155
|
};
|
|
10874
11156
|
var SUBAGENT_TYPE_NAMES = Object.freeze(
|
|
10875
11157
|
Object.keys(TYPES)
|
|
@@ -10893,18 +11175,12 @@ ${NEGATIVE_CLAIM_RULE}
|
|
|
10893
11175
|
|
|
10894
11176
|
${TUI_FORMATTING_RULES}`;
|
|
10895
11177
|
var DEFAULT_MAX_RESULT_CHARS2 = 8e3;
|
|
10896
|
-
var DEFAULT_PAUSE_EVERY = 16;
|
|
10897
|
-
var BUDGET_WARN_THRESHOLD = 3;
|
|
10898
|
-
function budgetParagraph(maxToolIters) {
|
|
10899
|
-
return `Tool budget: you have ${maxToolIters} tool call${maxToolIters === 1 ? "" : "s"} for this task. The cap is enforced from outside \u2014 the call after #${maxToolIters} is refused. Pace yourself: if you can't fully resolve the task within the budget, stop early and return what you have plus what's missing, rather than burning the budget on one branch.`;
|
|
10900
|
-
}
|
|
10901
11178
|
var DEFAULT_SUBAGENT_MODEL = "deepseek-v4-flash";
|
|
10902
11179
|
var DEFAULT_SUBAGENT_EFFORT = "high";
|
|
10903
11180
|
var SUBAGENT_TOOL_NAME = "spawn_subagent";
|
|
10904
11181
|
var NEVER_INHERITED_TOOLS = /* @__PURE__ */ new Set([SUBAGENT_TOOL_NAME, "submit_plan"]);
|
|
10905
11182
|
async function spawnSubagent(opts) {
|
|
10906
11183
|
const model = opts.model ?? DEFAULT_SUBAGENT_MODEL;
|
|
10907
|
-
const maxToolIters = opts.maxToolIters ?? DEFAULT_PAUSE_EVERY;
|
|
10908
11184
|
const maxResultChars = opts.maxResultChars ?? DEFAULT_MAX_RESULT_CHARS2;
|
|
10909
11185
|
const sink = opts.sink;
|
|
10910
11186
|
const skillName = opts.skillName;
|
|
@@ -10957,26 +11233,8 @@ async function spawnSubagent(opts) {
|
|
|
10957
11233
|
new Set(opts.allowedTools),
|
|
10958
11234
|
NEVER_INHERITED_TOOLS
|
|
10959
11235
|
) : forkRegistryExcluding(opts.parentRegistry, NEVER_INHERITED_TOOLS);
|
|
10960
|
-
let dispatchCount = 0;
|
|
10961
|
-
childTools.setResultAugmenter((_name, _args, result) => {
|
|
10962
|
-
dispatchCount++;
|
|
10963
|
-
const remaining = maxToolIters - dispatchCount;
|
|
10964
|
-
if (remaining <= 0) {
|
|
10965
|
-
return `${result}
|
|
10966
|
-
|
|
10967
|
-
[budget: 0 of ${maxToolIters} tool calls left \u2014 finalize NOW; the next tool call will be refused]`;
|
|
10968
|
-
}
|
|
10969
|
-
if (remaining <= BUDGET_WARN_THRESHOLD) {
|
|
10970
|
-
return `${result}
|
|
10971
|
-
|
|
10972
|
-
[budget: ${remaining} of ${maxToolIters} tool call${remaining === 1 ? "" : "s"} left \u2014 wrap up soon]`;
|
|
10973
|
-
}
|
|
10974
|
-
return result;
|
|
10975
|
-
});
|
|
10976
11236
|
const childPrefix = new ImmutablePrefix({
|
|
10977
|
-
system:
|
|
10978
|
-
|
|
10979
|
-
${budgetParagraph(maxToolIters)}`,
|
|
11237
|
+
system: opts.system,
|
|
10980
11238
|
toolSpecs: childTools.specs()
|
|
10981
11239
|
});
|
|
10982
11240
|
const childLoop = new CacheFirstLoop({
|
|
@@ -10988,11 +11246,9 @@ ${budgetParagraph(maxToolIters)}`,
|
|
|
10988
11246
|
// task is already narrow by construction, and `high` cuts output
|
|
10989
11247
|
// tokens substantially vs `max`.
|
|
10990
11248
|
reasoningEffort: DEFAULT_SUBAGENT_EFFORT,
|
|
10991
|
-
maxToolIters,
|
|
10992
11249
|
hooks: [],
|
|
10993
11250
|
stream: true,
|
|
10994
|
-
session: sessionName
|
|
10995
|
-
onIterBudgetExhausted: "pause"
|
|
11251
|
+
session: sessionName
|
|
10996
11252
|
});
|
|
10997
11253
|
const onParentAbort = () => childLoop.abort();
|
|
10998
11254
|
if (opts.parentSignal?.aborted) {
|
|
@@ -11004,13 +11260,9 @@ ${budgetParagraph(maxToolIters)}`,
|
|
|
11004
11260
|
let errorMessage;
|
|
11005
11261
|
let toolIter = 0;
|
|
11006
11262
|
let summarisingEmitted = false;
|
|
11007
|
-
let
|
|
11008
|
-
let partialSummary;
|
|
11009
|
-
const taskForLoop = opts.resumeSession ? `[Resume: your tool-call budget has been refreshed with ${maxToolIters} new calls. Earlier "wrap up" / "finalize NOW" budget hints in this conversation referred to the previous window \u2014 they no longer apply. Continue the work you were given.]
|
|
11010
|
-
|
|
11011
|
-
${opts.task}` : opts.task;
|
|
11263
|
+
let forcedSummaryFired = false;
|
|
11012
11264
|
try {
|
|
11013
|
-
for await (const ev of childLoop.step(
|
|
11265
|
+
for await (const ev of childLoop.step(opts.task)) {
|
|
11014
11266
|
sink?.current?.({ kind: "inner", runId, task: taskPreview, skillName, model, inner: ev });
|
|
11015
11267
|
if (ev.role === "tool") {
|
|
11016
11268
|
toolIter++;
|
|
@@ -11040,7 +11292,12 @@ ${opts.task}` : opts.task;
|
|
|
11040
11292
|
}
|
|
11041
11293
|
if (ev.role === "assistant_final") {
|
|
11042
11294
|
if (ev.forcedSummary) {
|
|
11043
|
-
|
|
11295
|
+
if (opts.parentSignal?.aborted) {
|
|
11296
|
+
errorMessage = ev.content?.trim() || "subagent aborted before producing an answer";
|
|
11297
|
+
} else {
|
|
11298
|
+
final = ev.content ?? "";
|
|
11299
|
+
forcedSummaryFired = true;
|
|
11300
|
+
}
|
|
11044
11301
|
} else {
|
|
11045
11302
|
final = ev.content ?? "";
|
|
11046
11303
|
}
|
|
@@ -11048,17 +11305,13 @@ ${opts.task}` : opts.task;
|
|
|
11048
11305
|
if (ev.role === "error") {
|
|
11049
11306
|
errorMessage = ev.error ?? "subagent error";
|
|
11050
11307
|
}
|
|
11051
|
-
if (ev.role === "paused") {
|
|
11052
|
-
paused = true;
|
|
11053
|
-
if (ev.partialSummary) partialSummary = ev.partialSummary;
|
|
11054
|
-
}
|
|
11055
11308
|
}
|
|
11056
11309
|
} catch (err) {
|
|
11057
11310
|
errorMessage = err.message;
|
|
11058
11311
|
} finally {
|
|
11059
11312
|
opts.parentSignal?.removeEventListener("abort", onParentAbort);
|
|
11060
11313
|
}
|
|
11061
|
-
if (!errorMessage && !final
|
|
11314
|
+
if (!errorMessage && !final) {
|
|
11062
11315
|
errorMessage = opts.parentSignal?.aborted ? "subagent aborted before producing an answer" : "subagent ended without producing an answer";
|
|
11063
11316
|
}
|
|
11064
11317
|
const elapsedMs = Date.now() - startedAt;
|
|
@@ -11083,7 +11336,7 @@ ${opts.task}` : opts.task;
|
|
|
11083
11336
|
usage
|
|
11084
11337
|
});
|
|
11085
11338
|
return {
|
|
11086
|
-
success: !errorMessage,
|
|
11339
|
+
success: !errorMessage && !forcedSummaryFired,
|
|
11087
11340
|
output: errorMessage ? "" : truncated,
|
|
11088
11341
|
error: errorMessage,
|
|
11089
11342
|
turns,
|
|
@@ -11093,9 +11346,7 @@ ${opts.task}` : opts.task;
|
|
|
11093
11346
|
model,
|
|
11094
11347
|
skillName,
|
|
11095
11348
|
usage,
|
|
11096
|
-
|
|
11097
|
-
pausedSession: paused ? sessionName : void 0,
|
|
11098
|
-
partialSummary: paused ? partialSummary : void 0
|
|
11349
|
+
forcedSummary: forcedSummaryFired || void 0
|
|
11099
11350
|
};
|
|
11100
11351
|
}
|
|
11101
11352
|
function aggregateChildUsage(loop) {
|
|
@@ -11110,16 +11361,16 @@ function aggregateChildUsage(loop) {
|
|
|
11110
11361
|
return agg;
|
|
11111
11362
|
}
|
|
11112
11363
|
function formatSubagentResult(r) {
|
|
11113
|
-
if (r.
|
|
11364
|
+
if (r.forcedSummary) {
|
|
11114
11365
|
return JSON.stringify({
|
|
11115
11366
|
success: false,
|
|
11116
|
-
|
|
11117
|
-
|
|
11367
|
+
partial: true,
|
|
11368
|
+
output: r.output,
|
|
11369
|
+
turns: r.turns,
|
|
11118
11370
|
tool_iters: r.toolIters,
|
|
11119
11371
|
elapsed_ms: r.elapsedMs,
|
|
11120
11372
|
cost_usd: r.costUsd,
|
|
11121
|
-
|
|
11122
|
-
note: `Subagent reached its pause-every interval (${r.toolIters} tool calls) without producing a final answer. Read partial_summary above to see what was done / left / blocked, then decide: resume by calling spawn_subagent again with resume_session="${r.pausedSession}" (the task arg becomes a continuation nudge \u2014 e.g. "finish what you started"), or accept the partial work and proceed with what you already know.`
|
|
11373
|
+
note: "Subagent was force-summarized (storm-breaker or context-guard fired). `output` carries the partial synthesis the model produced before being stopped \u2014 useful but not a complete answer. Decide whether to accept the partial, narrow the task and re-spawn, or fall back to direct tools."
|
|
11123
11374
|
});
|
|
11124
11375
|
}
|
|
11125
11376
|
if (!r.success) {
|
|
@@ -11169,18 +11420,18 @@ function forkRegistryWithAllowList(parent, allow, alsoExclude) {
|
|
|
11169
11420
|
// src/code/edit-blocks.ts
|
|
11170
11421
|
import {
|
|
11171
11422
|
closeSync,
|
|
11172
|
-
existsSync as
|
|
11423
|
+
existsSync as existsSync3,
|
|
11173
11424
|
fstatSync,
|
|
11174
11425
|
ftruncateSync,
|
|
11175
11426
|
mkdirSync,
|
|
11176
11427
|
openSync,
|
|
11177
|
-
readFileSync as
|
|
11428
|
+
readFileSync as readFileSync3,
|
|
11178
11429
|
readSync,
|
|
11179
11430
|
unlinkSync,
|
|
11180
11431
|
writeFileSync,
|
|
11181
11432
|
writeSync
|
|
11182
11433
|
} from "fs";
|
|
11183
|
-
import { dirname as
|
|
11434
|
+
import { dirname as dirname3, resolve as resolve4 } from "path";
|
|
11184
11435
|
var BLOCK_RE = /^(\S[^\n]*)\n<{7} SEARCH\n([\s\S]*?)\n?={7}\n([\s\S]*?)\n?>{7} REPLACE/gm;
|
|
11185
11436
|
function parseEditBlocks(text) {
|
|
11186
11437
|
const out = [];
|
|
@@ -11198,8 +11449,8 @@ function parseEditBlocks(text) {
|
|
|
11198
11449
|
return out;
|
|
11199
11450
|
}
|
|
11200
11451
|
function applyEditBlock(block, rootDir) {
|
|
11201
|
-
const absRoot =
|
|
11202
|
-
const absTarget =
|
|
11452
|
+
const absRoot = resolve4(rootDir);
|
|
11453
|
+
const absTarget = resolve4(absRoot, block.path);
|
|
11203
11454
|
if (absTarget !== absRoot && !absTarget.startsWith(`${absRoot}${sep()}`)) {
|
|
11204
11455
|
return {
|
|
11205
11456
|
path: block.path,
|
|
@@ -11210,7 +11461,7 @@ function applyEditBlock(block, rootDir) {
|
|
|
11210
11461
|
const searchEmpty = block.search.length === 0;
|
|
11211
11462
|
if (searchEmpty) {
|
|
11212
11463
|
try {
|
|
11213
|
-
mkdirSync(
|
|
11464
|
+
mkdirSync(dirname3(absTarget), { recursive: true });
|
|
11214
11465
|
const fd = openSync(absTarget, "wx");
|
|
11215
11466
|
try {
|
|
11216
11467
|
writeSync(fd, block.replace);
|
|
@@ -11286,11 +11537,11 @@ function applyEditBlocks(blocks, rootDir) {
|
|
|
11286
11537
|
return blocks.map((b) => applyEditBlock(b, rootDir));
|
|
11287
11538
|
}
|
|
11288
11539
|
function toWholeFileEditBlock(path, content, rootDir) {
|
|
11289
|
-
const abs =
|
|
11540
|
+
const abs = resolve4(rootDir, path);
|
|
11290
11541
|
let search = "";
|
|
11291
|
-
if (
|
|
11542
|
+
if (existsSync3(abs)) {
|
|
11292
11543
|
try {
|
|
11293
|
-
search =
|
|
11544
|
+
search = readFileSync3(abs, "utf8");
|
|
11294
11545
|
} catch {
|
|
11295
11546
|
search = "";
|
|
11296
11547
|
}
|
|
@@ -11298,19 +11549,19 @@ function toWholeFileEditBlock(path, content, rootDir) {
|
|
|
11298
11549
|
return { path, search, replace: content, offset: 0 };
|
|
11299
11550
|
}
|
|
11300
11551
|
function snapshotBeforeEdits(blocks, rootDir) {
|
|
11301
|
-
const absRoot =
|
|
11552
|
+
const absRoot = resolve4(rootDir);
|
|
11302
11553
|
const seen = /* @__PURE__ */ new Set();
|
|
11303
11554
|
const snapshots = [];
|
|
11304
11555
|
for (const b of blocks) {
|
|
11305
11556
|
if (seen.has(b.path)) continue;
|
|
11306
11557
|
seen.add(b.path);
|
|
11307
|
-
const abs =
|
|
11308
|
-
if (!
|
|
11558
|
+
const abs = resolve4(absRoot, b.path);
|
|
11559
|
+
if (!existsSync3(abs)) {
|
|
11309
11560
|
snapshots.push({ path: b.path, prevContent: null });
|
|
11310
11561
|
continue;
|
|
11311
11562
|
}
|
|
11312
11563
|
try {
|
|
11313
|
-
snapshots.push({ path: b.path, prevContent:
|
|
11564
|
+
snapshots.push({ path: b.path, prevContent: readFileSync3(abs, "utf8") });
|
|
11314
11565
|
} catch {
|
|
11315
11566
|
snapshots.push({ path: b.path, prevContent: null });
|
|
11316
11567
|
}
|
|
@@ -11318,9 +11569,9 @@ function snapshotBeforeEdits(blocks, rootDir) {
|
|
|
11318
11569
|
return snapshots;
|
|
11319
11570
|
}
|
|
11320
11571
|
function restoreSnapshots(snapshots, rootDir) {
|
|
11321
|
-
const absRoot =
|
|
11572
|
+
const absRoot = resolve4(rootDir);
|
|
11322
11573
|
return snapshots.map((snap) => {
|
|
11323
|
-
const abs =
|
|
11574
|
+
const abs = resolve4(absRoot, snap.path);
|
|
11324
11575
|
if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep()}`)) {
|
|
11325
11576
|
return {
|
|
11326
11577
|
path: snap.path,
|
|
@@ -11330,7 +11581,7 @@ function restoreSnapshots(snapshots, rootDir) {
|
|
|
11330
11581
|
}
|
|
11331
11582
|
try {
|
|
11332
11583
|
if (snap.prevContent === null) {
|
|
11333
|
-
if (
|
|
11584
|
+
if (existsSync3(abs)) unlinkSync(abs);
|
|
11334
11585
|
return {
|
|
11335
11586
|
path: snap.path,
|
|
11336
11587
|
status: "applied",
|
|
@@ -11369,6 +11620,8 @@ export {
|
|
|
11369
11620
|
detectAtPicker,
|
|
11370
11621
|
rankPickerCandidates,
|
|
11371
11622
|
expandAtMentions,
|
|
11623
|
+
looksLikeAbsoluteSystemPath,
|
|
11624
|
+
pathIsUnder,
|
|
11372
11625
|
registerFilesystemTools,
|
|
11373
11626
|
registerMemoryTools,
|
|
11374
11627
|
registerChoiceTool,
|
|
@@ -11389,4 +11642,4 @@ export {
|
|
|
11389
11642
|
he/he.js:
|
|
11390
11643
|
(*! https://mths.be/he v1.2.0 by @mathias | MIT license *)
|
|
11391
11644
|
*/
|
|
11392
|
-
//# sourceMappingURL=chunk-
|
|
11645
|
+
//# sourceMappingURL=chunk-OPFUUYHL.js.map
|