@thincoder/core 0.10.7 → 0.10.9
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/CHANGELOG.md +21 -0
- package/advisor/compaction.mjs +1 -1
- package/advisor/run.mjs +1 -1
- package/agent/chat-call.mjs +40 -27
- package/agent/child-marks.mjs +1 -1
- package/agent/dispatch-gates.mjs +7 -4
- package/agent/dispatch-run.mjs +1 -0
- package/agent/family-tools.mjs +13 -8
- package/agent/helpers.mjs +12 -10
- package/agent/image-heal.mjs +65 -0
- package/agent/light-round.mjs +4 -4
- package/agent/live-beat.mjs +4 -4
- package/agent/post-turn.mjs +34 -0
- package/agent/record-results.mjs +9 -1
- package/agent/run-stages.mjs +45 -10
- package/agent/setup-reminders.mjs +14 -13
- package/agent/setup.mjs +72 -48
- package/agent/spawn-child.mjs +1 -1
- package/agent/subagent-event.mjs +1 -1
- package/agent/suspension.mjs +2 -1
- package/agent/timers.mjs +8 -8
- package/agent/turn-loop.mjs +7 -1
- package/agent-tools/advisor.mjs +1 -1
- package/agent-tools/async-discard.mjs +40 -5
- package/agent-tools/async-settle.mjs +3 -1
- package/agent-tools/audit-block.mjs +5 -3
- package/agent-tools/batch-gate.mjs +198 -0
- package/agent-tools/batch-lifecycle.mjs +216 -36
- package/agent-tools/batch-paths.mjs +20 -20
- package/agent-tools/batch-skeleton.mjs +87 -0
- package/agent-tools/batch.mjs +11 -3
- package/agent-tools/checkpoint.mjs +1 -1
- package/agent-tools/context.mjs +28 -5
- package/agent-tools/design-slots.mjs +20 -7
- package/agent-tools/doc-check.mjs +75 -0
- package/agent-tools/eng.mjs +5 -3
- package/agent-tools/manifest.mjs +18 -18
- package/agent-tools/parent-channel.mjs +3 -3
- package/agent-tools/review-facts.mjs +2 -2
- package/agent-tools/settings.mjs +1 -1
- package/agent-tools/spawn-gates.mjs +108 -47
- package/agent-tools/ssh-session.mjs +486 -0
- package/agent-tools/ssh-transport.mjs +324 -0
- package/agent-tools/ssh-vault.mjs +291 -0
- package/agent-tools/subagent-scheduler.mjs +6 -5
- package/agent-tools/subagent-spawn.mjs +18 -11
- package/agent-tools/subagent.mjs +17 -9
- package/agent-tools/task.mjs +1 -1
- package/agent-tools/timer.mjs +8 -2
- package/agent-tools/zero-write-watch.mjs +2 -2
- package/agent-tools.mjs +3 -2
- package/attachments.mjs +4 -4
- package/browser/cdp.mjs +2 -2
- package/browser/launch.mjs +50 -4
- package/browser/snapshot.mjs +1 -1
- package/config-defaults.mjs +92 -0
- package/config-io.mjs +7 -2
- package/config-normalize.mjs +109 -0
- package/config-watch.mjs +2 -2
- package/config.mjs +88 -190
- package/declaration.mjs +5 -1
- package/doc-check/anchors.mjs +142 -0
- package/doc-check/index.mjs +210 -0
- package/doc-check/version.mjs +84 -0
- package/doc-check/width.mjs +132 -0
- package/file-refs.mjs +6 -6
- package/generate-title.mjs +1 -1
- package/hooks.mjs +1 -1
- package/i18n.mjs +13 -1
- package/ledger-cmd.mjs +314 -62
- package/ledger-db.mjs +122 -31
- package/ledger-migrate.mjs +18 -12
- package/ledger-read.mjs +6 -7
- package/ledger-tools.mjs +138 -77
- package/ledger.mjs +34 -62
- package/log.mjs +17 -3
- package/manifest-discovery.mjs +21 -18
- package/manifest-schema.mjs +17 -14
- package/manifest.mjs +37 -22
- package/mcp/transport-stdio.mjs +1 -1
- package/mcp.mjs +2 -2
- package/memory/core.mjs +1 -1
- package/memory/scan.mjs +1 -1
- package/memory/sweep.mjs +1 -1
- package/model-specs.mjs +7 -7
- package/notify-policy.mjs +6 -6
- package/package.json +2 -1
- package/permission.mjs +21 -1
- package/prompts/advisor-design.md +53 -23
- package/prompts/advisor-round1.md +34 -14
- package/prompts/advisor-round2.md +32 -19
- package/prompts/advisor-round3.md +34 -21
- package/prompts/common.md +148 -79
- package/prompts/consult-base.md +14 -8
- package/prompts/discipline-engineering.md +70 -66
- package/prompts/discipline-normal.md +86 -77
- package/prompts/persona-coder.md +6 -6
- package/prompts/persona-eng-coder.md +19 -18
- package/prompts/persona-eng-designer.md +40 -31
- package/prompts/persona-engineering.md +124 -92
- package/prompts/persona-explore.md +2 -2
- package/prompts/persona-normal.md +20 -17
- package/prompts/persona-plan.md +7 -7
- package/prompts/subagent-base.md +4 -4
- package/provider/anthropic.mjs +4 -2
- package/provider/core.mjs +4 -2
- package/provider/errors.mjs +8 -0
- package/provider/google.mjs +8 -1
- package/provider/normalize.mjs +42 -12
- package/provider/responses-request.mjs +4 -3
- package/provider/responses.mjs +2 -2
- package/provider/wait-status.mjs +1 -1
- package/provider-flows.mjs +1 -1
- package/queued.mjs +2 -2
- package/rules.mjs +4 -4
- package/session-gc.mjs +1 -1
- package/session-lifecycle.mjs +7 -7
- package/session-slot-write.mjs +2 -2
- package/session-slots-manifest.mjs +1 -1
- package/session.mjs +1 -1
- package/shell-candidates.mjs +2 -2
- package/shell-identity.mjs +1 -1
- package/team.mjs +63 -5
- package/think-off.mjs +3 -3
- package/token-ttl.mjs +1 -1
- package/token-window.mjs +1 -1
- package/tool-docs/bash.md +1 -1
- package/tool-docs/batch.md +1 -1
- package/tool-docs/browser.md +1 -1
- package/tool-docs/doc_check.md +54 -0
- package/tool-docs/fetch.md +3 -2
- package/tool-docs/ledger.md +7 -5
- package/tool-docs/manifest.md +14 -2
- package/tool-docs/ssh.md +51 -0
- package/tool-docs/subagent.md +6 -9
- package/tool-docs/timer.md +2 -1
- package/tool-docs/websearch.md +3 -1
- package/tools/browser.mjs +1 -1
- package/tools/execute.mjs +1 -1
- package/tools/git-run.mjs +2 -2
- package/tools/git.mjs +8 -8
- package/tools/index.mjs +3 -0
- package/tools/ops.mjs +1 -1
- package/tools/process-tree.mjs +2 -2
- package/tools/ssh-askpass.mjs +35 -0
- package/tools/ssh.mjs +105 -0
- package/tools/web-deepseek.mjs +172 -0
- package/tools/web.mjs +29 -4
- package/vision-reader.mjs +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,27 @@
|
|
|
3
3
|
All notable changes to the core package are documented here.
|
|
4
4
|
Format: Keep a Changelog · 中文 · 号在发布时定(CalVer——见 `docs/RELEASE.md` §4)。
|
|
5
5
|
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
## [0.10.9] — 2026-10-19
|
|
9
|
+
|
|
10
|
+
### Removed
|
|
11
|
+
|
|
12
|
+
- **`design-slots` 的 `reveal` 开关与掩蔽(token-visibility-simplify 批 2026-10-17)**:清点面改**直读**——逐槽行恒携 `designToken`(过期照列、`status` 自标态;记录畸形 ⇒ `—`);删净 `reveal` 开关 / 掩蔽 / 一次一槽限制(用户裁定「没必要把 token 藏起来,agent 要就告诉他」);签发链(mint / settle / TTL / persist)零改。
|
|
13
|
+
|
|
14
|
+
## [0.10.8] — 2026-10-16
|
|
15
|
+
|
|
16
|
+
> 0.10.7 → 0.10.8(月内 +1——发布时定号)
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **MCP 探活具名读数(#1431——2026-10-14 走查轮)**:`probeMcpServer` 返回值增 `tools`(工具名数组——原 `mcpTools` 到手即弃,控制台探活只见计数);服务端页面随正(具名读数形,缺名回落计数形——零回归)。
|
|
21
|
+
- **注入开关(#1420——injection-switches 批 2026-10-14)**:`agent.injectDocs` ∥ `agent.injectMemories` 两枚全局开关(DEFAULTS `true`;判据 `!== false`);关 = 阻断三块自动注入(项目指令文件块 ∥ 文档检索块 ∥ 记忆检索块——检索调用不执行),不删数据;`settings` 工具 + CLI `/config` 热应用(写后下一次组装生效);VSC/桌面设置界面各落两开关。
|
|
22
|
+
- **SSH 会话工具(#1436——ssh-session 批 2026-10-14)**:`ssh` 工具(`connect`/`send`/`read`/`secret_input`/`close`/`list`——多往返会话、状态保持、设备提示符与分页驱动);密钥 / agent 优先,需要密码走凭据弹窗(**值零入上下文**——`onSecretInput` 注入缝 + askpass 链);**零第三方依赖**(系统 openssh + `ssh -tt` 远端 PTY + `SSH_ASKPASS`);三端(CLI / VSC / 桌面)界面在位。
|
|
23
|
+
- **`design-slots` 回收读口(#1384——design-token-recovery 批 2026-10-14)**:`designId` 过滤单槽(trim 精确匹配;未知 ⇒ 拒 + 列在场 id)∥ `reveal:true` 揭值(必携 `designId`;只揭活槽——过期/畸形槽掩蔽注因;`null` 视同缺省——零揭值不 fail-open);`log.mjs` `SECRET_FORM` 增 token 值形(`uuid:epoch`)。
|
|
24
|
+
- **产品内建 `doc_check` 工具(#1586——builtin-doc-check 批 2026-10-16)**:文档纪律机检内建为产品工具(只读 · 报告态 · **非门禁**)——起步两条判据:锚(档 token ± `§N` ⇒ 档缺 ∥ 节缺;行号不作节指)∥ 行宽(>300 字符;四类谓词 + 区带豁免);声明面 = `PROJECT-MANIFEST.json` `checkConfig`(零硬编码路径);项目自建脚本退出判据链。
|
|
25
|
+
- **台账工具项目根读写同一推导(#1588——ledger-root-derivation 批 2026-10-16)**:`resolveLedgerRoot` 判据单源(自目标路径沿祖先链取最近「已注册」= 带档 ∨ 自身路径键控库在盘;工作区态不立为根);族发现取祖先支;无根契约 = 读面空账 ∥ 写面抛(零落盘);写面「抛错列候选」退场。
|
|
26
|
+
|
|
6
27
|
## [0.10.7] — 2026-10-14
|
|
7
28
|
|
|
8
29
|
> 0.10.6 → 0.10.7(月内 +1——发布时定号)
|
package/advisor/compaction.mjs
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* advisor-loop-split 批:loop 的压缩检查点归位本档(`compactContextIfNeeded`——`context_limit` 尾串生成点同迁;时间线面 ⇒ `advisor/timeline.mjs`)。
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { providerSpec, outputReserve, usableWindow } from "../config.mjs" // 第 25 批:预算派生(与 loop.mjs
|
|
13
|
+
import { providerSpec, outputReserve, usableWindow } from "../config.mjs" // 第 25 批:预算派生(与 loop.mjs 的 providerSpec 同源导入);`outputReserve`/`usableWindow` = 2026-10-06 预留裁定(#975)
|
|
14
14
|
// B4(群 B 批,ADVISOR-GUARDS.md §9——F32):CJK 加权单源(provider/rate.mjs 叶子向无环)
|
|
15
15
|
import { estimateText } from "../provider/rate.mjs"
|
|
16
16
|
|
package/advisor/run.mjs
CHANGED
|
@@ -37,7 +37,7 @@ export function resolveAdvisorProvider(agent) {
|
|
|
37
37
|
// chat 前 `assertProviderModel` fail-fast(不静默)。
|
|
38
38
|
const result = cfg.model ? { ...provider, model: cfg.model } : { ...provider, model: agent.provider?.model }
|
|
39
39
|
// 台账 #329 / `MODEL-SPECS.md §15.4-3`:off 形须**活着到达载荷层**——载荷层 off 门首款 = `provider.thinking === null`
|
|
40
|
-
// (`thincoder-core/provider/core.mjs
|
|
40
|
+
// (`thincoder-core/provider/core.mjs` 的 `chatImpl`);归一为 `undefined` 会让该门永不开(effort 族 advisor
|
|
41
41
|
// 关思考静默失效)。显式 off 同清**继承档**(渠条目自带 / 主 provider 的 `reasoningEffort` 不得随行——
|
|
42
42
|
// 随行则显式档支先命中、off 意图作废)。`false`(非法原值)仍归一 `undefined`(零变)。
|
|
43
43
|
if (cfg.thinking === null) {
|
package/agent/chat-call.mjs
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* 由 runAgent 的 catch/finally 收束。
|
|
6
6
|
*/
|
|
7
7
|
import { chat } from "../provider/index.mjs"
|
|
8
|
+
import { healImageRejection, clearImageFailStreak } from "./image-heal.mjs"
|
|
8
9
|
|
|
9
10
|
/** 流式输出门(§2.5 #78 并入——VSC 三态门,纯函数可直驱测试):depth 0 恒通;
|
|
10
11
|
* consult 子代理豁免(其输出进会诊面板);其余经 `streamOutput` 显式选择进入。 */
|
|
@@ -24,32 +25,36 @@ export async function callModelTurn(agent, {
|
|
|
24
25
|
await classifyAndApply(agent, turn).catch(() => {})
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
28
|
+
// 发送选项(首送 ∥ §6.26 自愈重发共用——两送除 messages 外逐字同参)
|
|
29
|
+
const sendOpts = {
|
|
30
|
+
tools: toolSchemas,
|
|
31
|
+
// §2.5 #78 并入(VSC onToken 三态门):depth 0 恒通;consult 子代理豁免(其输出进
|
|
32
|
+
// 会诊面板);escalate 等经 opts.streamOutput 显式选择进入;其余子代理不流式。
|
|
33
|
+
onToken: streamOutputAllowed(depth, agent._role, streamOutput) ? callbacks.onToken : null,
|
|
34
|
+
onReasoning: callbacks.onReasoning,
|
|
35
|
+
onWait: callbacks.onWait,
|
|
36
|
+
signal,
|
|
37
|
+
streamRules: agent.config.agent?.streamRules ?? [],
|
|
38
|
+
firedPatterns: streamRuleFired,
|
|
39
|
+
// LOGGING(LOGGING.md):llm:* 事件的语义上下文(stage=turn 主循环回合——含
|
|
40
|
+
// digest 消化轮 auto=true;child=子代理 id(spawn 时 stamp 于 child._logId))
|
|
41
|
+
// TRACES.md §6.1 D-TR4:轨迹元数据增补(role/depth/kind/session/cwd——trace-store 只读
|
|
42
|
+
// logCtx,签名不变);kind:depth>0 = subagent(consult 孩子 = consult)——子代理
|
|
43
|
+
// 对回靠 role+depth+child id(children 无 _sessionStart——不经 depth-0 设置——
|
|
44
|
+
// session 字段对子代理轨迹为 null——见 trace-store/agent.mjs 注释)。
|
|
45
|
+
logCtx: {
|
|
46
|
+
stage: "turn", turn: turn + 1, auto: autoTurn, child: agent._logId,
|
|
47
|
+
role: agent._role ?? null,
|
|
48
|
+
depth,
|
|
49
|
+
kind: depth > 0 ? (agent._role === "consult" ? "consult" : "subagent") : "turn",
|
|
50
|
+
session: agent._sessionStart ?? null,
|
|
51
|
+
cwd: agent.cwd,
|
|
52
|
+
traces: agent.config?.traces?.enabled !== false,
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
response = await chat(agent.provider, { messages, ...sendOpts })
|
|
53
58
|
} catch (e) {
|
|
54
59
|
// User interrupt (Ctrl+I): controller.abort({ interrupt: true, message }).
|
|
55
60
|
// Inject into history; the outer loop recreates the controller and resumes.
|
|
@@ -61,8 +66,16 @@ export async function callModelTurn(agent, {
|
|
|
61
66
|
agent.history.push({ role: "user", content: msg })
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
|
-
|
|
69
|
+
// §6.26 图片拒绝自愈(判定 ∥ 剥除 ∥ 注记 ∥ 记账 = agent/image-heal.mjs 单源):
|
|
70
|
+
// 未愈 ⇒ 原错照抛(非图像错误零误伤);已愈 ⇒ 载荷已修(历史无图 + 机读注记)单次重发。
|
|
71
|
+
const { healed } = healImageRejection(agent, e)
|
|
72
|
+
if (!healed) throw e
|
|
73
|
+
// 重建消息(§6.26 步 4——剥除后的 agent.history 含注记):再发一次(非重试——同请求
|
|
74
|
+
// 重发对图像拒绝恒无效);再败 ⇒ 本行抛新错(不循环、不二次剥除,历史保持已剥除态)。
|
|
75
|
+
response = await chat(agent.provider, { messages: [{ role: "system", content: systemPrompt }, ...agent.history], ...sendOpts })
|
|
65
76
|
}
|
|
77
|
+
// §6.26 清零调用点(记账单源 = image-heal.mjs):任一次成功调用清零(含自愈重发成功)
|
|
78
|
+
clearImageFailStreak(agent)
|
|
66
79
|
|
|
67
80
|
return response
|
|
68
81
|
}
|
package/agent/child-marks.mjs
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* 原样再导出 ⇒ 既有 import 面(核 agent-tools 族 / CLI `tool-events.mjs`)零改。
|
|
9
9
|
*
|
|
10
10
|
* 语义:消费端用 `includes()` 检出锚点 ⇒ 合成块头注记("work may be partial")——两端各一处:
|
|
11
|
-
* CLI = `thincoder-cli/src/tui/tool-events.mjs
|
|
11
|
+
* CLI = `thincoder-cli/src/tui/tool-events.mjs` 的 `onToolResult`;端壳 = `thincoder-vscode/src/extension/
|
|
12
12
|
* panel-callbacks.mjs`(onToolResult)。文案演进只改这里(与检测判据单源——消除文案/判据漂移面)。
|
|
13
13
|
*
|
|
14
14
|
* 零 import——任意层可引、无环。
|
package/agent/dispatch-gates.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import { writeFileSync, mkdirSync, existsSync } from "node:fs"
|
|
|
11
11
|
import { join, resolve } from "node:path"
|
|
12
12
|
import { homedir } from "node:os"
|
|
13
13
|
import { toolTouchPaths } from "./helpers.mjs"
|
|
14
|
+
import { redactArgs } from "../log.mjs" // #1432:args 落痕前过遮蔽链(单源 redactSecret 族;log.mjs 为叶——零环)
|
|
14
15
|
// fix A(2026-09-07):FILE_MUTATORS 的 mutation-seq 记账从
|
|
15
16
|
// 批后提交(record-results noteMutations)移到执行成功即刻——唯一记账点(取代批后段
|
|
16
17
|
// + agent.mjs 中断分支记账——不双计)——同消息 [写 + async advisor launch] 时 launch 前
|
|
@@ -32,10 +33,11 @@ export function logToolError(toolName, args, error) {
|
|
|
32
33
|
const dir = join(ERRORS_DIR, ymd)
|
|
33
34
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
|
|
34
35
|
const file = join(dir, `${ts}-${toolName.replace(/[/\\]/g, "_")}.log`)
|
|
36
|
+
const argsText = JSON.stringify(redactArgs(args), null, 2) // #1432:遮蔽先于截断(≤2000 截断零改)
|
|
35
37
|
const entry = [
|
|
36
38
|
`time: ${now.toISOString()}`,
|
|
37
39
|
`tool: ${toolName}`,
|
|
38
|
-
`args: ${
|
|
40
|
+
`args: ${argsText.slice(0, 2000)}${argsText.length > 2000 ? "… (truncated)" : ""}`,
|
|
39
41
|
`error: ${error?.message ?? String(error)}`,
|
|
40
42
|
error?.stack ? `stack:\n${error.stack}` : "",
|
|
41
43
|
].filter(Boolean).join("\n") + "\n"
|
|
@@ -60,8 +62,9 @@ export function logToolError(toolName, args, error) {
|
|
|
60
62
|
* AGENT-LOOP-SUBAGENT.md §6.7.2 panel (round1 #5): view 面归只读类(同 status——planMode 放行、免
|
|
61
63
|
* 审批、可批并行);freeze 面归控制类(同 cancel——planMode 放行、免权限审批、
|
|
62
64
|
* 批审批不入组、digest 内放行)。freeze 存在(非空 key)即控制类——否则只读类。
|
|
65
|
+
* #1178:泛化改名(原「子代理专用」谓词 ⇒ 通用动作级只读谓词——语义零改,旧标识符零余)——第二消费面 = agent/record-results.mjs 的 verify 失效链(动作级只读 ⇒ 不清 `_verifiedThisRun`)。
|
|
63
66
|
*/
|
|
64
|
-
export function
|
|
67
|
+
export function isReadonlyAction(toolName, args) {
|
|
65
68
|
// §6 memory 工具面重构(MEMORY.md §6 D-M5):memory search/list 是只读动作——与
|
|
66
69
|
// subagent status 同分类(planMode 放行/免审批——Phase-2 批并行只认工具级
|
|
67
70
|
// readonly/parallel,memory 无 parallel → 按非只读串行,见 MEMORY.md §6.4 实现注)。
|
|
@@ -92,7 +95,7 @@ export function isSubagentReadonlyAction(toolName, args) {
|
|
|
92
95
|
if (action === "observe") return true
|
|
93
96
|
// AGENT-LOOP-SUBAGENT.md §6.7.2 panel view 面(freeze 缺省/空 = 视图请求——readonly;非空 freeze 归控制类)
|
|
94
97
|
if (action === "panel" && (args.freeze === undefined || args.freeze === null || String(args.freeze) === "")) return true
|
|
95
|
-
// DESIGN-TOKEN-SETTLEMENT.md §
|
|
98
|
+
// DESIGN-TOKEN-SETTLEMENT.md §9 F-SL1(2026-10-07 批 ledger-tool):design-slots = 只读清点
|
|
96
99
|
// (同 status / observe——planMode 放行 / 免审批 / digest 放行;工程模式父侧门在执行器内)。
|
|
97
100
|
if (action === "design-slots") return true
|
|
98
101
|
return false
|
|
@@ -127,7 +130,7 @@ export function isSubagentEscalateAction(toolName, args) {
|
|
|
127
130
|
* 由此保形;核内工具零钩子 ⇒ CLI ∕ desktop 逐字零变(`undefined ?? 核谓词`)。
|
|
128
131
|
* 两谓词 = 上列两门禁位(planMode ∕ 权限短路)的公共输入(同址同用,防两处漂移)。 */
|
|
129
132
|
export function readonlyActionOf(tool, toolName, args) {
|
|
130
|
-
return tool?.isReadonlyAction?.(args) ??
|
|
133
|
+
return tool?.isReadonlyAction?.(args) ?? isReadonlyAction(toolName, args)
|
|
131
134
|
}
|
|
132
135
|
export function controlActionOf(tool, toolName, args) {
|
|
133
136
|
return tool?.isControlAction?.(args) ?? isSubagentControlAction(toolName, args)
|
package/agent/dispatch-run.mjs
CHANGED
|
@@ -105,6 +105,7 @@ export async function runPreparedItem(item, agent, depth, signal, callbacks) {
|
|
|
105
105
|
_toolCallId: item.toolCall.id,
|
|
106
106
|
onOutput: (chunk) => callbacks.onToolOutput?.(item.toolCall.name, chunk, item.toolCall.id),
|
|
107
107
|
onQuestion: callbacks.onQuestion,
|
|
108
|
+
onSecretInput: callbacks.onSecretInput,
|
|
108
109
|
onPermissionRequest: callbacks.onPermissionRequest,
|
|
109
110
|
}
|
|
110
111
|
try {
|
package/agent/family-tools.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* agent/family-tools.mjs — 家族矩阵单源(`assembleFamilyTools`)。
|
|
3
3
|
*
|
|
4
4
|
* 批次 `docs/batches/2026-09-15-vsc-tool-table-dup.md` §2.3A:家族矩阵(哪个运行面得哪些
|
|
5
|
-
* 家族工具)自 CLI 侧 `agent/setup.mjs
|
|
5
|
+
* 家族工具)自 CLI 侧 `agent/setup.mjs` 的 `assembleFamilyTools`(as-of 迁出前坐标)整体迁出——CLI 与 VSC 同调同一份实现
|
|
6
6
|
* (消灭「同一角色矩阵两份实现」= 本批缺陷的类根因);端差(VSC 装饰链 / settings 追加 /
|
|
7
7
|
* consult 池来源)经 `decorate` 注入——**不传 = 核默认形态**(CLI = 迁出前逐字)。
|
|
8
8
|
*
|
|
@@ -26,7 +26,7 @@ export async function assembleFamilyTools({
|
|
|
26
26
|
decorate = null, // object 端差面:{ subagent?, consultStart?, consultStop?, settings? }
|
|
27
27
|
} = {}) {
|
|
28
28
|
// CORE-UNIFICATION TOOLS #83:consult 家族随统一登记册自 `../agent-tools.mjs` 取用(单一来源)
|
|
29
|
-
const { planTool, subagentTool, taskTool, skillTool, goalTool, verifyTool, recentChangesTool, timerTool, advisorTool, engTool, readHistoryTool, contextTool, batchTool, manifestTool, consultStartTool, consultStopTool, parentChannelTool } = await import("../agent-tools.mjs")
|
|
29
|
+
const { planTool, subagentTool, taskTool, skillTool, goalTool, verifyTool, recentChangesTool, timerTool, advisorTool, engTool, readHistoryTool, contextTool, batchTool, manifestTool, consultStartTool, consultStopTool, parentChannelTool, docCheckTool } = await import("../agent-tools.mjs")
|
|
30
30
|
// 台账统一入口(2026-10-05 统一入口批——台账 #923):全量变体 = depth-0 段 ∥ 只读变体 = 全部
|
|
31
31
|
// depth>0 段。动态 import——ledger 链静态达 node:sqlite(W8 契约②);两变体同名单对象、schema 窄化。
|
|
32
32
|
const { ledgerTool, ledgerReadTool } = await import("../ledger.mjs")
|
|
@@ -125,8 +125,8 @@ export async function assembleFamilyTools({
|
|
|
125
125
|
...subagentTool,
|
|
126
126
|
name: "subagent",
|
|
127
127
|
description: designer
|
|
128
|
-
? "Spawn a read-only `explore` sub-agent to SURVEY the current state for the design: it reads code / docs / existing designs and reports evidence with file:line. BLOCKING ONLY (no async) and action:'spawn' ONLY — the survey channel is read-only; escalate/status/cancel/panel/consume-design/observe/send are not available. Survey budget: ≤6 explore spawns per batch — the main agent's survey result is reference only; do your own."
|
|
129
|
-
: "Spawn a read-only `explore` sub-agent to AUDIT your delivery against the design: it compares the delivered code with the design for divergence — partially implemented acceptance criteria, silent simplifications, doc drift, changes outside the approved file list. BLOCKING ONLY (no async — the audit report decides your next protocol step). action:'spawn' ONLY — the audit channel is a read-only spawn; escalate/status/cancel/panel/consume-design/observe/send are not available. The audit task book is appended MECHANICALLY — your own spawn task (docs involved / acceptance criteria / file list) plus the files you actually touched; never hand the audit a self-written file list (a self-report could omit exactly the out-of-scope file it must catch).",
|
|
128
|
+
? "Spawn a read-only `explore` sub-agent to SURVEY the current state for the design: it reads code / docs / existing designs and reports evidence with file:line. BLOCKING ONLY (no async) and action:'spawn' ONLY — the survey channel is read-only; escalate/status/cancel/panel/consume-design/observe/send are not available. Survey budget: ≤6 explore spawns per batch — the main agent's survey result is reference only; do your own. The spawn carries the five task-book parameters (four core: goal / knownFacts / acceptance / reportFormat + `scope`); a task-only spawn is refused."
|
|
129
|
+
: "Spawn a read-only `explore` sub-agent to AUDIT your delivery against the design: it compares the delivered code with the design for divergence — partially implemented acceptance criteria, silent simplifications, doc drift, changes outside the approved file list. BLOCKING ONLY (no async — the audit report decides your next protocol step). action:'spawn' ONLY — the audit channel is a read-only spawn; escalate/status/cancel/panel/consume-design/observe/send are not available. The audit task book is appended MECHANICALLY — your own spawn task (docs involved / acceptance criteria / file list) plus the files you actually touched; never hand the audit a self-written file list (a self-report could omit exactly the out-of-scope file it must catch). The spawn carries the five task-book parameters (four core: goal / knownFacts / acceptance / reportFormat + `scope`); a task-only spawn is refused.",
|
|
130
130
|
parameters: { ...subagentTool.parameters, properties: props },
|
|
131
131
|
}
|
|
132
132
|
})()
|
|
@@ -151,6 +151,9 @@ export async function assembleFamilyTools({
|
|
|
151
151
|
// manifest 工具(`MANIFEST.md` §2.10 · 2026-10-08 · 台账 #1098):主 agent 读写出口——
|
|
152
152
|
// **两模式同挂**(需求未设模式门);子代理各段零挂(未装配 = 权限面第一道)。
|
|
153
153
|
manifestTool,
|
|
154
|
+
// 文档纪律机检(`DOC-DISCIPLINE.md` §3.20 条目 S):只读报告态——depth-0 主 agent 段;
|
|
155
|
+
// 写文档 / 设计档的角色段(eng-coder / eng-designer)另挂(见下两分支)。
|
|
156
|
+
docCheckTool,
|
|
154
157
|
// 端差(decorate.settings——VSC depth-0 主 agent 面;缺省不追加):核默认形态里
|
|
155
158
|
// settings 住**基础集**(`tools/index.mjs` `assembleBuiltinTools`),端侧自持清单
|
|
156
159
|
// 无该面 ⇒ 端以 decorate 补位(收敛通道 = 将来去 decorate 项即归核位)。
|
|
@@ -175,12 +178,14 @@ export async function assembleFamilyTools({
|
|
|
175
178
|
// SUBAGENT-UPSTREAM-CHANNEL(AGENT-LOOP-UPSTREAM.md §6.27.4 装配接线):子代理上行通道
|
|
176
179
|
// (`notify_parent`)随 depth>0 段**前置**——4 处携带 = eng-coder / eng-designer / coder / 兜底段
|
|
177
180
|
// (未列名 depth>0 role 落同一兜底段 ⇒ 亦装配;语义 =「depth>0 且非 consult 皆装配」);
|
|
178
|
-
// 计数口径:`consult` 分支不入 ⇒ 「5
|
|
181
|
+
// 计数口径:`consult` 分支不入 ⇒ 「5 个插入点」读法已作废(实读本档 eng 两分支的 `batch` 挂载段)。
|
|
179
182
|
// consult 段不入(其角色语义 = 父发起的一次性会诊,父在其 settle 前不期望中途对话)。
|
|
180
183
|
// 台账只读变体(query / count)——全部 depth>0 角色段(含 consult ∕ 兜底;写三 schema 级不可达);
|
|
181
184
|
// 团队台账只读变体(team-ledger 批)同律。
|
|
182
|
-
: engChildRole === "eng-coder" ? [parentChannelTool, ledgerReadTool, teamLedgerReadTool, advisorTool, verifyTool, batchTool(batchDoc),
|
|
183
|
-
|
|
185
|
+
: engChildRole === "eng-coder" ? [parentChannelTool, ledgerReadTool, teamLedgerReadTool, advisorTool, verifyTool, batchTool(batchDoc), docCheckTool,
|
|
186
|
+
...(engChildSubagent ? [engChildSubagent] : [])]
|
|
187
|
+
: engChildRole === "eng-designer" ? [parentChannelTool, ledgerReadTool, teamLedgerReadTool, batchTool(batchDoc), docCheckTool,
|
|
188
|
+
...(engChildSubagent ? [engChildSubagent] : [])]
|
|
184
189
|
: role === "coder" ? [parentChannelTool, ledgerReadTool, teamLedgerReadTool, verifyTool, advisorTool, ...(batchDoc ? [batchTool(batchDoc)] : [])]
|
|
185
190
|
: role === "consult" ? [recentChangesTool, ledgerReadTool, teamLedgerReadTool]
|
|
186
191
|
: [parentChannelTool, ledgerReadTool, teamLedgerReadTool]
|
|
@@ -191,7 +196,7 @@ export async function assembleFamilyTools({
|
|
|
191
196
|
// execute 层另有机械拒兜底,见 `agent-tools/task.mjs`)。
|
|
192
197
|
// 工程模式固定段 = plan / task 皆**不入表**;普通模式固定段逐字不变(FR31 边界 / F10 边界)。
|
|
193
198
|
// 判据 = 单一模式位 `engineering`(不带深度分支——子代理面随同排除,role enum
|
|
194
|
-
//
|
|
199
|
+
// `subagentRoles` + spawn 门 / task execute 门已同向)。
|
|
195
200
|
return engineering
|
|
196
201
|
? [timerTool, ...depthOnly]
|
|
197
202
|
: [taskTool, planTool, timerTool, ...depthOnly]
|
package/agent/helpers.mjs
CHANGED
|
@@ -370,12 +370,14 @@ export function readonlyToolNames(tools) {
|
|
|
370
370
|
}
|
|
371
371
|
|
|
372
372
|
/** Depth>0 tool exclusions — subagents never get the interactive main-session tools (`question`:
|
|
373
|
-
* no user to ask; the
|
|
374
|
-
*
|
|
375
|
-
|
|
373
|
+
* no user to ask; `ssh`: the credential window is a user-facing event AND the session pool
|
|
374
|
+
* belongs to the main session — no UI at depth>0 and no session to hand back; the routing
|
|
375
|
+
* guidance already lives in every persona file). Single source for the repo: the shell-side
|
|
376
|
+
* depth>0 filter consumes this set (design = TOOLS.md §6.16 / D-TO11 + §6.22). */
|
|
377
|
+
export const SUBAGENT_TOOL_EXCLUSIONS = new Set(["question", "ssh"])
|
|
376
378
|
|
|
377
379
|
/** Apply the depth>0 exclusion set — always returns a NEW array (parent table untouched).
|
|
378
|
-
* #9(TOOLS.md §6.19):`bash` 的 `async` 参仅 depth-0——子代副本删参(照 `family-tools.mjs
|
|
380
|
+
* #9(TOOLS.md §6.19):`bash` 的 `async` 参仅 depth-0——子代副本删参(照 `family-tools.mjs` 的 `engChildSubagent`
|
|
379
381
|
* 先例;返回值仍恒新数组、未命中条目原对象直传)。运行期第二道在 `tools/bash.mjs` execute
|
|
380
382
|
* (照 `read_image`「schema 门 + 运行期第二道」双线先例);批 browser-async-fix(#1045)同线
|
|
381
383
|
* 扩至 `browser` 的 `async` 参(运行期第二道在 `tools/browser.mjs` execute)。 */
|
|
@@ -449,14 +451,14 @@ export const AUTO_TURN_DIGEST_DOMAIN =
|
|
|
449
451
|
/** Engineering-mode auto-turn digest domain variant (mode = `agent.config.agent.engineering`;
|
|
450
452
|
* clause 2 drops the task-tool pointer — F10: the task tool is disabled in engineering mode,
|
|
451
453
|
* the batch record + ledger are the tracking authority). Single line, verbatim from the
|
|
452
|
-
* design doc (TOOLS.md §6.15.3); mechanism = AGENT-LOOP-ASYNC-POOL.md §6.8. Content
|
|
454
|
+
* design doc (TOOLS.md §6.15.3); mechanism = AGENT-LOOP-ASYNC-POOL.md §6.8. Content basis = the mechanism doc; landing = eng-coder. */
|
|
453
455
|
export const AUTO_TURN_DIGEST_DOMAIN_ENG =
|
|
454
456
|
"[System reminder: auto-turn — background async subagents finished while there was no user message, and this turn runs automatically to digest their reports (the finished-report reminders above). No one is waiting for this reply, so organize only: 1) summarize each finished report's key points into this conversation for the user to read later; 2) (engineering mode: the task tool is disabled — no task-list update is expected this turn; the batch record + ledger are the tracking authority and are updated in real user turns); 3) write decision points with a suggested next step as text — do not execute it. FORBIDDEN this turn (mechanically enforced): modifying files, bash/execute/verify, spawning subagents, asking questions — those need a real user message. End the turn once the summaries are written.]"
|
|
455
457
|
|
|
456
458
|
/** Up-stream wake-turn domain (AGENT-LOOP-UPSTREAM.md §6.27.12.8): a running subagent sent
|
|
457
459
|
* an in-flight message and is waiting for the reply — the digest domain's "No one is waiting
|
|
458
460
|
* for this reply" is the opposite of the truth, so the wake turn gets its own text.
|
|
459
|
-
* Verbatim from the design doc (single line — no newlines). Content
|
|
461
|
+
* Verbatim from the design doc (single line — no newlines). Content basis = the mechanism doc; landing = eng-coder. */
|
|
460
462
|
export const UPSTREAM_TURN_DOMAIN =
|
|
461
463
|
"[System reminder: auto-turn — a running subagent sent you an in-flight message (shown below). No user message is waiting. Decide it now and reply with subagent action:'send' (id + message) — the child consumes the reply at its next turn boundary and keeps working on the unaffected parts; if the message needs no answer, say so in one line and move on. If finished subagent reports are also present above, summarize them as usual in the same turn. Do not start new work: FORBIDDEN this turn (mechanically enforced): modifying files, bash/execute/verify, spawning subagents, asking questions — those need a real user message. End the turn once the reply is sent.]"
|
|
462
464
|
|
|
@@ -464,16 +466,16 @@ export const UPSTREAM_TURN_DOMAIN =
|
|
|
464
466
|
* expired while the session was idle and the end's latch opened this turn automatically —
|
|
465
467
|
* neither the digest domain ("no one is waiting", organize-only) nor the up-stream domain
|
|
466
468
|
* (a subagent waiting for a reply) fits: the timer's own semantics is "act on what the
|
|
467
|
-
* timer was for". Verbatim from the
|
|
468
|
-
* Content
|
|
469
|
+
* timer was for". Verbatim from the mechanism doc (single line — no newlines).
|
|
470
|
+
* Content basis = the mechanism doc; landing = eng-coder. */
|
|
469
471
|
export const TIMER_TURN_DOMAIN =
|
|
470
472
|
"[System reminder: auto-turn — a timer you set earlier has expired (reminder above). No user message is waiting. Act on what the timer was for now: pull the data or run the check rather than continuing to reason in the abstract. Stay within the work that was in flight when the timer was set; do not open unrelated new work. Wrap up as soon as the action is done or blocked.]"
|
|
471
473
|
|
|
472
474
|
/** Digest-accounting requirement line (AGENT-LOOP-ASYNC-POOL.md §6.31.3 · batch digest-accounting ·
|
|
473
475
|
* 2026-10-05):消化轮账目要求——两档同发(手动 / AUTO);manual 域文本只覆盖手动档 ⇒ 要求行
|
|
474
476
|
* 独立常量承载,避免 AUTO 轮无要求。`<ids>` = 未销账 id 清单占位(动态拼 = digest-account.mjs
|
|
475
|
-
* `digestAccountRequirement`);注入面 = transient(机器线独有)。Verbatim from the
|
|
476
|
-
*
|
|
477
|
+
* `digestAccountRequirement`);注入面 = transient(机器线独有)。Verbatim from the mechanism doc
|
|
478
|
+
* (single line — no newlines). Content basis = the mechanism doc; landing = eng-coder. */
|
|
477
479
|
export const DIGEST_ACCOUNT_DOMAIN =
|
|
478
480
|
"[System reminder: digest accounting — for every report listed below as not yet accounted, emit exactly one line: \"[digest-ack #<id>] digested — <key points>\" (handled) or \"[digest-ack #<id>] deferred — <reason>\" (not handled). Not yet accounted: <ids>. Do not omit any.]"
|
|
479
481
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent/image-heal.mjs — 图片拒绝自愈(PROVIDER.md §6.26;批 image-history-400 · 台账 #1450)。
|
|
3
|
+
*
|
|
4
|
+
* 判定(T1 图像语义 400 ∥ T2 同态重复)→ 全剥历史图(copy-on-write,人读线 `_fullHistory`
|
|
5
|
+
* 零损)→ 机读注记 → `_imageFailStreak` 记账(单源本档)。返回 `{ healed, dropped }`;零抛
|
|
6
|
+
* (失败面不阻断回合驱动——落账为尽力面)。重发(一次,非重试)与「再败照抛」归
|
|
7
|
+
* agent/chat-call.mjs catch 支;清零接口 = clearImageFailStreak(调用点 = chat-call 成功路径)。
|
|
8
|
+
*/
|
|
9
|
+
import { looksLikeImageRejection } from "../provider/errors.mjs"
|
|
10
|
+
import { stripRejectedImages } from "../provider/normalize.mjs"
|
|
11
|
+
import { logEvent } from "../log.mjs"
|
|
12
|
+
|
|
13
|
+
/** 机读注记(§6.26 逐字;N = 剥除数)。 */
|
|
14
|
+
const healNote = (n) =>
|
|
15
|
+
`[System reminder: the provider rejected the image payload (HTTP 4xx) — ${n} image(s) were dropped from the context so the session can continue; the request was re-sent. Re-read a downscaled copy with read_image if still needed, and tell the user which image(s) were dropped.]`
|
|
16
|
+
|
|
17
|
+
/** 状态码提取(§6.26——技法同 T2 族:`LLM API error 4xx:` 构造位文本取码;非该族 ⇒ 0)。 */
|
|
18
|
+
function statusCodeOf(text) {
|
|
19
|
+
const m = String(text ?? "").match(/LLM API error (\d{3}):/)
|
|
20
|
+
return m ? Number(m[1]) : 0
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** 历史 image part 计数(T1/T2 的「史含图」判据 + dropped 取数)。 */
|
|
24
|
+
function countImageParts(messages) {
|
|
25
|
+
let n = 0
|
|
26
|
+
if (!Array.isArray(messages)) return 0
|
|
27
|
+
for (const m of messages) {
|
|
28
|
+
if (!Array.isArray(m?.content)) continue
|
|
29
|
+
for (const p of m.content) if (p?.type === "image_url") n++
|
|
30
|
+
}
|
|
31
|
+
return n
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 成功路径清零(§6.26——清零调用点 = chat-call.mjs;Abort 豁免 ∥ 失败不清零)。 */
|
|
35
|
+
export function clearImageFailStreak(agent) {
|
|
36
|
+
if (agent) agent._imageFailStreak = 0
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 判定 ∥ 剥除 ∥ 注记 ∥ streak 记账(§6.26 机制 1–3。判定先后:T2 读本回合自增前值)。
|
|
41
|
+
* @returns {{ healed: boolean, dropped: number }}
|
|
42
|
+
*/
|
|
43
|
+
export function healImageRejection(agent, error) {
|
|
44
|
+
try {
|
|
45
|
+
// Abort 类失败豁免:既不自增也不清零(中断不是「被拒」——重启后原样重发)
|
|
46
|
+
if (!agent || error?.name === "AbortError") return { healed: false, dropped: 0 }
|
|
47
|
+
const message = String(error?.message ?? "")
|
|
48
|
+
const code = statusCodeOf(message)
|
|
49
|
+
const is4xxNon429 = code >= 400 && code < 500 && code !== 429 // 429 归 provider 重试族
|
|
50
|
+
const prev = Number(agent._imageFailStreak) || 0
|
|
51
|
+
if (is4xxNon429) agent._imageFailStreak = prev + 1 // 自增(T2 判据读上值——见上方调用序)
|
|
52
|
+
const dropped = countImageParts(agent.history)
|
|
53
|
+
if (dropped === 0) return { healed: false, dropped: 0 } // 剥除后自熄面:两判据皆需「史含图」
|
|
54
|
+
const t1 = code === 400 && looksLikeImageRejection(message) // 图像语义 400(HTTP 400 合取)
|
|
55
|
+
const t2 = is4xxNon429 && prev >= 1 // 结构兜底:上一回合同类失败(防网关文案不在词表内)
|
|
56
|
+
if (!t1 && !t2) return { healed: false, dropped: 0 }
|
|
57
|
+
// 全剥(历史本体持久修正——后续回合不再重发毒图)+ 机读注记(模型知晓 + 复合指令)
|
|
58
|
+
agent.history = stripRejectedImages(agent.history)
|
|
59
|
+
agent.history.push({ role: "user", content: healNote(dropped) })
|
|
60
|
+
logEvent("llm:image-heal", { dropped, streak: agent._imageFailStreak, model: agent.provider?.model })
|
|
61
|
+
return { healed: true, dropped }
|
|
62
|
+
} catch {
|
|
63
|
+
return { healed: false, dropped: 0 } // §6.26 零抛:失败面不阻断回合驱动
|
|
64
|
+
}
|
|
65
|
+
}
|
package/agent/light-round.mjs
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* ② 该指针指向的轮次批档:`§1` 状态行 = 「进行中」(判定器单源 = `readBatchStatusLine`)。
|
|
7
7
|
* 查找面(纯向下)= 会话锚 + 其直接子目录一层(候选枚举单源 = `ledgerChildren`——与族发现同级判据
|
|
8
8
|
* 同源;不递归 ∥ 不取同胞):逐候选查行 ∥ 以各候选为基解析指针(台账写门同式)。歧义候选
|
|
9
|
-
* (`projectRootView` 判 `
|
|
9
|
+
* (`projectRootView` 判 `workspace`——如工作区锚自身)⇒ 跳过该候选、照查其余(既定观察态,非读错);
|
|
10
10
|
* 候选读错 ∥ 不可判 ⇒ 停本候选(总判不因他候选读错而失——开路凭据仍是「两件校验全过」)。
|
|
11
11
|
* 语义:开门动作 ≡ 开轮两件落盘(「启动即挂账」本体——无独立于账的开门面);单件不放行
|
|
12
12
|
* (轮档独在 ∥ 行独在 ∥ 无标记 ∥ 行非在途 ∥ 指针失据 ∥ 档已收口 ⇒ 皆 false);收口即自闭
|
|
@@ -38,9 +38,9 @@ export async function lightRoundOpen(agent) {
|
|
|
38
38
|
// 候选面 = 锚自身 + 直接子目录一层(纯向下;子项枚举单源 = `ledgerChildren`)。
|
|
39
39
|
const candidates = [anchor, ...ledgerChildren(anchor).map((p) => p.root)]
|
|
40
40
|
for (const cand of candidates) {
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
if (projectRootView(cand).state === "
|
|
41
|
+
// 工作区候选(下辖 ≥2 项目——`projectRootView`)⇒ 跳过、照查其余(工作区锚不充当项目 = AC-M2-19 同判;
|
|
42
|
+
// 该态判据 = 根推导单源「已注册」的库臂排除(键控库在盘 ≠ 可作项目)——既定观察态,非读错)。
|
|
43
|
+
if (projectRootView(cand).state === "workspace") continue
|
|
44
44
|
let rows
|
|
45
45
|
try { rows = ledgerQuery({ cwd: cand, status: "在途" }) } catch { continue } // 候选读错 ⇒ 停本候选
|
|
46
46
|
if (!Array.isArray(rows) || rows.length === 0) continue
|
package/agent/live-beat.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* agent/live-beat.mjs — 存活投影心跳(拍间隔 ∕ 单拍 ∕ 起 ∕ 停 —— 幂等 · 清点)——「桌面处理流 · VSC 对齐」批 R6 上提产物。
|
|
3
3
|
*
|
|
4
|
-
* 上提源(迁移前坐标)= VSC `thincoder-vscode/src/extension/panel-messages.mjs
|
|
5
|
-
*
|
|
4
|
+
* 上提源(迁移前坐标)= VSC `thincoder-vscode/src/extension/panel-messages.mjs`(`LIVE_HEARTBEAT_MS = 2000`)
|
|
5
|
+
* (单拍 `liveHeartbeatBeat`)·(起 ∕ 停 `startLiveHeartbeat` ∕ `stopLiveHeartbeat`);
|
|
6
6
|
* 桌面先例 = `thincoder-desktop/src/main/subagent-face.mjs`(拍体逐键清点)。**纯搬 + 转口,零语义改**:
|
|
7
7
|
* 拍间隔 ∕ 单拍 ∕ 起停 ∕ 幂等 ∕ 清点入核。两处 VSC 端面判据**随拍体留端**(非本档语义):
|
|
8
8
|
* 「未就绪不拍」前置(`panel._wvReady`)与拍日志(`ev:subreassert`)。
|
|
9
9
|
*
|
|
10
10
|
* 拍体(`beat` 注入)= 各端存活再断言本体:VSC = `reassertLiveChildren(panel)`(`thincoder-vscode/src/
|
|
11
|
-
* extension/suspension.mjs
|
|
11
|
+
* extension/suspension.mjs` 的 `reassertLiveChildren` 投影面);桌面 = 逐键 `bridge(key).reassertLive(agent)`(只发在飞实例)。
|
|
12
12
|
* 回值 = 本拍投递条数(**清点**读数 —— 拍体自清点,本档原样透传)。
|
|
13
13
|
* **`setInterval` ∕ `unref` 转注入缝**:`timer(fn, ms) ⇒ handle` ∕ `clear(handle)` 由调用方注入
|
|
14
14
|
* (缺省 = 平台 `setInterval` ∕ `clearInterval`;句柄 `unref` = 可选面 —— 在场即调,不阻进程退出)。
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* 迁移轮改指本档(2026-09-29,双写窗口收口;两处 VSC 端面判据随拍体留端)。
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
/** 拍间隔(毫秒)——单源(VSC 同值 `panel-messages.mjs
|
|
19
|
+
/** 拍间隔(毫秒)——单源(VSC 同值 `panel-messages.mjs` 的 `LIVE_HEARTBEAT_MS`):起拍即按此周期。 */
|
|
20
20
|
export const LIVE_HEARTBEAT_MS = 2000
|
|
21
21
|
|
|
22
22
|
/**
|
package/agent/post-turn.mjs
CHANGED
|
@@ -10,6 +10,31 @@ export const STALL_WINDOW_SIZE = 5
|
|
|
10
10
|
export const STALL_THRESHOLD = 3
|
|
11
11
|
export const GOAL_BUDGET_WARN_RATIO = 0.75
|
|
12
12
|
|
|
13
|
+
// 设计轮轮数注入阈值(AGENT-LOOP.md §6.6「设计轮轮数注入」· 台账 #1661):
|
|
14
|
+
// 5 = 在 persona 判定点(过 7 轮未落盘 ⇒ 降级)之前 3 轮给出计数;8 = 与 persona 同一判定点;
|
|
15
|
+
// 15 = persona 的首版预算(注入串里的 15 一律由它插值——改常量即改注入文本,不另留字面)。
|
|
16
|
+
export const DESIGN_TURN_NOTICE_AT = 5
|
|
17
|
+
export const DESIGN_TURN_SKELETON_AT = 8
|
|
18
|
+
export const DESIGN_FIRST_VERSION_TURNS = 15
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 设计轮轮数提醒(纯函数——注入串 | `null`;判据源 = 链内累计轮号 + 本设计轮是否落过盘)。
|
|
22
|
+
* 逐字注入形 = `AGENT-LOOP.md` §6.6「注入形逐字」;角色表当前一行(`eng-designer`),
|
|
23
|
+
* `eng-coder` 零注入(其零落笔面另由零落笔看门狗兜)。
|
|
24
|
+
* @param {{role?:string|null, turn?:number, wrote?:boolean}} o `turn` = 链内累计轮号(1 起)
|
|
25
|
+
* @returns {string|null}
|
|
26
|
+
*/
|
|
27
|
+
export function designTurnReminder({ role, turn, wrote } = {}) {
|
|
28
|
+
if (role !== "eng-designer") return null
|
|
29
|
+
const used = Number(turn ?? 0)
|
|
30
|
+
if (!Number.isFinite(used) || used < DESIGN_TURN_NOTICE_AT) return null
|
|
31
|
+
const budget = DESIGN_FIRST_VERSION_TURNS
|
|
32
|
+
if (used >= DESIGN_TURN_SKELETON_AT && !wrote) {
|
|
33
|
+
return `[System reminder: design round turn ${used}/${budget} — nothing has been written to disk yet. The 7-turn mark has passed with nothing landed: degrade the delivery NOW — write the skeleton (design doc + batch record §2) plus an unresolved list, then refine in place.]`
|
|
34
|
+
}
|
|
35
|
+
return `[System reminder: design round turn ${used}/${budget} — ${wrote ? "something has been written to disk" : "nothing has been written to disk yet"}. First version budget is ${budget} turns; past 7 turns with nothing landed the rule is a skeleton + unresolved list.]`
|
|
36
|
+
}
|
|
37
|
+
|
|
13
38
|
/**
|
|
14
39
|
* Inject all post-turn events into agent history.
|
|
15
40
|
* Must be called after every tool-execution turn within runAgent's loop.
|
|
@@ -66,5 +91,14 @@ export function injectPostTurn(agent, results, recentCallSigs, callbacks, turn,
|
|
|
66
91
|
})
|
|
67
92
|
}
|
|
68
93
|
|
|
94
|
+
// 设计轮轮数注入(AGENT-LOOP.md §6.6 · 台账 #1661):轮号取 `_currentTurn`(链内累计、续跑不重置),
|
|
95
|
+
// 写入痕迹取 `_touchedFiles`(在 resume 保留集内 ⇒ 续跑段沿用前段痕迹);段内 `turn` 形参不参与判据。
|
|
96
|
+
const designReminder = designTurnReminder({
|
|
97
|
+
role: agent._role,
|
|
98
|
+
turn: agent._currentTurn ?? 0,
|
|
99
|
+
wrote: (agent._touchedFiles ?? []).length > 0,
|
|
100
|
+
})
|
|
101
|
+
if (designReminder) agent.history.push({ role: "user", content: designReminder })
|
|
102
|
+
|
|
69
103
|
callbacks.onTurnEnd?.(agent, turn)
|
|
70
104
|
}
|
package/agent/record-results.mjs
CHANGED
|
@@ -32,9 +32,16 @@ import { advisorRuns, stripApprovedSuffix } from "../agent-tools/advisor-async.m
|
|
|
32
32
|
import { looksLikeReviewOutput, advisorIncompleteMarker } from "../advisor/run.mjs"
|
|
33
33
|
// #949(评审 history 串台面):同步面投递水印单点(叶档——零环)。
|
|
34
34
|
import { noteReviewDelivered } from "../agent-tools/review-facts.mjs"
|
|
35
|
+
// #1178:动作级只读分类(settings get ∥ manifest read 类)不失效 verify——谓词单源 = dispatch-gates。
|
|
36
|
+
import { isReadonlyAction } from "./dispatch-gates.mjs"
|
|
35
37
|
|
|
36
38
|
let _reindexFile = null
|
|
37
39
|
|
|
40
|
+
/** 动作级只读分类的入参(畸形 ⇒ null——谓词自守;解析口径同 dispatch 相位一)。 */
|
|
41
|
+
function actionArgsOf(toolCall) {
|
|
42
|
+
try { return JSON.parse(toolCall.arguments || "{}") } catch { return null }
|
|
43
|
+
}
|
|
44
|
+
|
|
38
45
|
export async function recordToolResults(agent, toolByName, results) {
|
|
39
46
|
// Multimodal user messages (injected images / not-injected reminders) must NOT be pushed
|
|
40
47
|
// between tool results of parallel calls — strict providers (DeepSeek) 400 when a tool
|
|
@@ -88,7 +95,7 @@ export async function recordToolResults(agent, toolByName, results) {
|
|
|
88
95
|
agent._calledAdvisorThisRun = false
|
|
89
96
|
agent._verifiedThisRun = false
|
|
90
97
|
agent._verifyPassed = undefined
|
|
91
|
-
} else if (!tool.readonly && !tool.sideEffectExempt) {
|
|
98
|
+
} else if (!tool.readonly && !isReadonlyAction(toolCall.name, actionArgsOf(toolCall)) && !tool.sideEffectExempt) {
|
|
92
99
|
// Non-mutating side-effect tools (bash, git): do NOT invalidate the
|
|
93
100
|
// advisor review — a review is triggered by CODE MUTATIONS only
|
|
94
101
|
// (user decision 2026-08-08: the guard rule is "review after code
|
|
@@ -96,6 +103,7 @@ export async function recordToolResults(agent, toolByName, results) {
|
|
|
96
103
|
// barred from writing files, so it cannot change the reviewed code).
|
|
97
104
|
// Verify IS invalidated: its state snapshot (git diff, file list)
|
|
98
105
|
// may be stale after git/shell operations.
|
|
106
|
+
// #1178:动作级只读(settings get ∥ manifest read 一类)非侧效——verify 保持有效。
|
|
99
107
|
if (agent._verifiedThisRun) {
|
|
100
108
|
agent._verifiedThisRun = false
|
|
101
109
|
agent._verifyPassed = undefined
|