@thincoder/core 0.9.5 → 0.10.1
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 +36 -0
- package/README.md +1 -1
- package/abort-provenance.mjs +10 -10
- package/advisor/citations.mjs +5 -5
- package/advisor/compaction.mjs +16 -15
- package/advisor/loop.mjs +12 -12
- package/advisor/messages.mjs +6 -6
- package/advisor/project-context.mjs +5 -5
- package/advisor/repos.mjs +3 -3
- package/advisor/run.mjs +22 -12
- package/advisor.mjs +4 -4
- package/agent/assemble.mjs +111 -0
- package/agent/chat-call.mjs +68 -0
- package/agent/child-marks.mjs +2 -2
- package/agent/completion.mjs +9 -2
- package/agent/continue-decision.mjs +25 -0
- package/agent/dispatch-gates.mjs +140 -0
- package/agent/dispatch-run.mjs +167 -0
- package/agent/dispatch.mjs +54 -296
- package/agent/family-tools.mjs +5 -5
- package/agent/helpers.mjs +53 -3
- package/agent/live-beat.mjs +49 -0
- package/agent/post-turn.mjs +4 -8
- package/agent/record-results.mjs +23 -20
- package/agent/relay-prefix.mjs +2 -2
- package/agent/run-stages.mjs +15 -11
- package/agent/run-start.mjs +111 -0
- package/agent/setup-reminders.mjs +9 -9
- package/agent/setup.mjs +32 -4
- package/agent/spawn-child.mjs +9 -9
- package/agent/suspension.mjs +89 -29
- package/agent/timers.mjs +113 -0
- package/agent/turn-loop.mjs +244 -0
- package/agent/write-gate.mjs +103 -6
- package/agent-tools/advisor-async.mjs +9 -9
- package/agent-tools/advisor-settle.mjs +12 -12
- package/agent-tools/advisor.mjs +36 -41
- package/agent-tools/async-discard.mjs +44 -1
- package/agent-tools/async-settle.mjs +21 -18
- package/agent-tools/audit-block.mjs +7 -7
- package/agent-tools/bash-async.mjs +281 -0
- package/agent-tools/batch-lifecycle.mjs +57 -18
- package/agent-tools/batch-paths.mjs +52 -8
- package/agent-tools/batch-skeleton.mjs +19 -1
- package/agent-tools/batch.mjs +12 -65
- package/agent-tools/checkpoint.mjs +121 -0
- package/agent-tools/child-permission.mjs +5 -3
- package/agent-tools/consult.mjs +56 -56
- package/agent-tools/context.mjs +4 -20
- package/agent-tools/design-token.mjs +3 -3
- package/agent-tools/digest-budget.mjs +1 -1
- package/agent-tools/eng.mjs +5 -5
- package/agent-tools/escalate-async.mjs +16 -11
- package/agent-tools/goal.mjs +3 -10
- package/agent-tools/panel-blocks.mjs +2 -2
- package/agent-tools/parent-channel.mjs +3 -13
- package/agent-tools/plan.mjs +3 -4
- package/agent-tools/read-history.mjs +12 -28
- package/agent-tools/recent-changes.mjs +4 -5
- package/agent-tools/review-facts.mjs +1 -1
- package/agent-tools/settings.mjs +47 -20
- package/agent-tools/skill.mjs +3 -5
- package/agent-tools/spawn-gates.mjs +3 -3
- package/agent-tools/subagent-actions-query.mjs +270 -0
- package/agent-tools/subagent-actions.mjs +21 -265
- package/agent-tools/subagent-async.mjs +25 -20
- package/agent-tools/subagent-panel.mjs +33 -4
- package/agent-tools/subagent-run.mjs +36 -21
- package/agent-tools/subagent-scheduler.mjs +28 -27
- package/agent-tools/subagent-spawn.mjs +45 -14
- package/agent-tools/subagent.mjs +33 -53
- package/agent-tools/task.mjs +3 -9
- package/agent-tools/timer.mjs +12 -12
- package/agent-tools/verify.mjs +14 -13
- package/agent-tools.mjs +0 -1
- package/agent.mjs +18 -355
- package/attachments.mjs +125 -0
- package/auto-think.mjs +1 -1
- package/config-presets.mjs +11 -6
- package/config-watch.mjs +83 -0
- package/config.mjs +17 -4
- package/context-degrade.mjs +81 -0
- package/context-echo.mjs +88 -0
- package/context-push.mjs +51 -0
- package/context.mjs +8 -187
- package/conventions.mjs +110 -161
- package/declaration.mjs +194 -0
- package/embedding.mjs +3 -1
- package/env-ref.mjs +81 -0
- package/explore-distill.mjs +2 -3
- package/file-links.mjs +53 -0
- package/file-refs.mjs +103 -0
- package/generate-title.mjs +28 -4
- package/history-window.mjs +17 -2
- package/i18n.mjs +2 -0
- package/index-discover.mjs +7 -7
- package/ledger-cmd.mjs +34 -126
- package/ledger-db.mjs +10 -2
- package/ledger-tools.mjs +188 -0
- package/ledger.mjs +9 -4
- package/log.mjs +12 -7
- package/manifest-discovery.mjs +131 -0
- package/manifest-schema.mjs +166 -0
- package/manifest.mjs +18 -217
- package/mcp.mjs +4 -1
- package/memory/code-search.mjs +149 -0
- package/memory/code-sync.mjs +89 -230
- package/memory/core.mjs +47 -32
- package/memory/docs.mjs +63 -262
- package/memory/file-list.mjs +89 -0
- package/memory/file-walk.mjs +7 -4
- package/memory/memory-tool.mjs +229 -0
- package/memory/schema.mjs +18 -3
- package/memory/sweep.mjs +289 -0
- package/memory/sync-tail.mjs +81 -0
- package/memory-status.mjs +69 -0
- package/memory.mjs +3 -0
- package/model-specs.mjs +16 -7
- package/notify-policy.mjs +51 -0
- package/package.json +1 -1
- package/peer-claims.mjs +2 -2
- package/peer-domains.mjs +7 -11
- package/peer-instances.mjs +9 -10
- package/process-probe-exec.mjs +187 -0
- package/process-probe.mjs +27 -175
- package/prompt-overlays.mjs +11 -10
- package/prompts/advisor-design.md +2 -2
- package/prompts/advisor-round2.md +1 -1
- package/prompts/advisor-round3.md +1 -1
- package/prompts/common.md +16 -8
- package/prompts/discipline-engineering.md +57 -16
- package/prompts/discipline-normal.md +6 -3
- package/prompts/persona-coder.md +0 -1
- package/prompts/persona-eng-coder.md +1 -5
- package/prompts/persona-eng-designer.md +2 -3
- package/prompts/persona-engineering.md +32 -11
- package/prompts/persona-explore.md +0 -1
- package/prompts/persona-normal.md +1 -1
- package/prompts/persona-plan.md +3 -4
- package/prompts/subagent-base.md +14 -0
- package/provider/core.mjs +11 -48
- package/provider/list-models.mjs +3 -0
- package/provider/responses-request.mjs +237 -0
- package/provider/responses.mjs +6 -228
- package/provider-flows.mjs +248 -0
- package/queued.mjs +90 -0
- package/rules.mjs +209 -2
- package/session-guard.mjs +27 -7
- package/session-lifecycle.mjs +90 -18
- package/session-rename.mjs +5 -1
- package/session-slot-claims.mjs +230 -0
- package/session-slot-scan.mjs +299 -0
- package/session-slot-verify.mjs +304 -0
- package/session-slot-write.mjs +74 -8
- package/session-slots-manifest.mjs +163 -241
- package/session-slots.mjs +99 -71
- package/session.mjs +30 -10
- package/shell-candidates.mjs +78 -0
- package/text-budget.mjs +2 -2
- package/think-off.mjs +49 -0
- package/token-ttl.mjs +15 -4
- package/tool-args.mjs +73 -0
- package/tool-docs/advisor.md +9 -0
- package/tool-docs/batch.md +1 -0
- package/tool-docs/code_search.md +1 -0
- package/tool-docs/consult_start.md +5 -0
- package/tool-docs/consult_stop.md +3 -0
- package/tool-docs/context.md +7 -0
- package/tool-docs/doc_search.md +1 -0
- package/tool-docs/edit.md +2 -13
- package/tool-docs/eng.md +1 -0
- package/tool-docs/execute.md +1 -9
- package/tool-docs/git.md +25 -51
- package/tool-docs/goal.md +6 -0
- package/tool-docs/ledger_add.md +1 -0
- package/tool-docs/ledger_close.md +1 -0
- package/tool-docs/ledger_count.md +1 -0
- package/tool-docs/ledger_query.md +1 -0
- package/tool-docs/ledger_update.md +1 -0
- package/tool-docs/main_history.md +1 -0
- package/tool-docs/memory.md +10 -0
- package/tool-docs/notify_parent.md +9 -0
- package/tool-docs/peer_instances.md +1 -0
- package/tool-docs/plan.md +5 -0
- package/tool-docs/process.md +7 -3
- package/tool-docs/read_history.md +7 -0
- package/tool-docs/recent_changes.md +1 -0
- package/tool-docs/repo_outline.md +1 -0
- package/tool-docs/settings.md +6 -0
- package/tool-docs/skill.md +5 -0
- package/tool-docs/subagent.md +31 -0
- package/tool-docs/task.md +7 -0
- package/tool-docs/timer.md +6 -0
- package/tool-docs/verify.md +1 -0
- package/tool-docs/wait_for.md +2 -1
- package/tools/bash.mjs +28 -4
- package/tools/edit-batch.mjs +4 -4
- package/tools/edit-diff.mjs +4 -23
- package/tools/exec-run.mjs +102 -4
- package/tools/execute.mjs +5 -2
- package/tools/file.mjs +4 -9
- package/tools/git-run.mjs +1 -1
- package/tools/git.mjs +0 -24
- package/tools/index.mjs +2 -1
- package/tools/linter.mjs +2 -1
- package/tools/node-child.mjs +21 -0
- package/tools/ops.mjs +50 -13
- package/tools/patch.mjs +4 -2
- package/tools/repomap.mjs +20 -9
- package/tools/shared.mjs +8 -6
- package/tools/web.mjs +5 -2
- package/tools/write-path.mjs +1 -1
- package/traces/trace-store.mjs +17 -14
- package/vision-reader.mjs +76 -0
- package/agent-tools/batch-segment.mjs +0 -18
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent/live-beat.mjs — 存活投影心跳(拍间隔 ∕ 单拍 ∕ 起 ∕ 停 —— 幂等 · 清点)——「桌面处理流 · VSC 对齐」批 R6 上提产物。
|
|
3
|
+
*
|
|
4
|
+
* 上提源(迁移前坐标)= VSC `thincoder-vscode/src/extension/panel-messages.mjs:45`(`LIVE_HEARTBEAT_MS = 2000`)
|
|
5
|
+
* `:50`(单拍 `liveHeartbeatBeat`)`:62-75`(起 ∕ 停 `startLiveHeartbeat` ∕ `stopLiveHeartbeat`);
|
|
6
|
+
* 桌面先例 = `thincoder-desktop/src/main/subagent-face.mjs`(拍体逐键清点)。**纯搬 + 转口,零语义改**:
|
|
7
|
+
* 拍间隔 ∕ 单拍 ∕ 起停 ∕ 幂等 ∕ 清点入核。两处 VSC 端面判据**随拍体留端**(非本档语义):
|
|
8
|
+
* 「未就绪不拍」前置(`panel._wvReady`)与拍日志(`ev:subreassert`)。
|
|
9
|
+
*
|
|
10
|
+
* 拍体(`beat` 注入)= 各端存活再断言本体:VSC = `reassertLiveChildren(panel)`(`thincoder-vscode/src/
|
|
11
|
+
* extension/suspension.mjs:148` 投影面);桌面 = 逐键 `bridge(key).reassertLive(agent)`(只发在飞实例)。
|
|
12
|
+
* 回值 = 本拍投递条数(**清点**读数 —— 拍体自清点,本档原样透传)。
|
|
13
|
+
* **`setInterval` ∕ `unref` 转注入缝**:`timer(fn, ms) ⇒ handle` ∕ `clear(handle)` 由调用方注入
|
|
14
|
+
* (缺省 = 平台 `setInterval` ∕ `clearInterval`;句柄 `unref` = 可选面 —— 在场即调,不阻进程退出)。
|
|
15
|
+
* 消费面 = 桌面 `thincoder-desktop/src/main/subagent-face.mjs` · VSC `panel-messages.mjs` 拍家族——已随 parity-b4
|
|
16
|
+
* 迁移轮改指本档(2026-09-29,双写窗口收口;两处 VSC 端面判据随拍体留端)。
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** 拍间隔(毫秒)——单源(VSC 同值 `panel-messages.mjs:45`):起拍即按此周期。 */
|
|
20
|
+
export const LIVE_HEARTBEAT_MS = 2000
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 起 ∕ 停 ∕ 单拍(幂等):
|
|
24
|
+
* - `beat()` = 单拍直驱(回值 = 本拍投递条数 —— 测试直驱面 ∕ 手动补拍);
|
|
25
|
+
* - `start()` = 起拍(**单拍幂等**:已起 ⇒ 返既有句柄、零重起);回值 = 定时器句柄;
|
|
26
|
+
* - `stop()` = 停拍(未起 ⇒ 零动作);停后 `start()` 可再起(新句柄 —— 起 ∕ 停两向)。
|
|
27
|
+
* `timer` ∕ `clear` = 定时器注入缝(缺省 = 平台全局;假钟注入 ⇒ 平 node 直测)。`beat` 缺 ∕ 非函数 ⇒ 抛
|
|
28
|
+
* (fail-loud —— 拍体未接线不得静默空转成「零投拍」)。
|
|
29
|
+
*/
|
|
30
|
+
export function createLiveBeat({ beat, timer = (fn, ms) => setInterval(fn, ms), clear = (handle) => clearInterval(handle) } = {}) {
|
|
31
|
+
if (typeof beat !== "function") {
|
|
32
|
+
throw new TypeError("createLiveBeat: beat seam missing — inject the beat function (the projection must not silently idle)")
|
|
33
|
+
}
|
|
34
|
+
let handle = null
|
|
35
|
+
return {
|
|
36
|
+
beat,
|
|
37
|
+
start() {
|
|
38
|
+
if (handle !== null) return handle
|
|
39
|
+
handle = timer(beat, LIVE_HEARTBEAT_MS)
|
|
40
|
+
handle?.unref?.() // 不阻进程退出(句柄可选面)
|
|
41
|
+
return handle
|
|
42
|
+
},
|
|
43
|
+
stop() {
|
|
44
|
+
if (handle === null) return
|
|
45
|
+
clear(handle)
|
|
46
|
+
handle = null
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
}
|
package/agent/post-turn.mjs
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* stall detection, and goal status tracking.
|
|
5
5
|
*/
|
|
6
6
|
import { escapeXml, tryCanonicalize, DEFAULT_GOAL_TURNS } from "./helpers.mjs"
|
|
7
|
+
import { takeExpiredTimers, injectTimerReminders } from "./timers.mjs"
|
|
7
8
|
|
|
8
9
|
export const STALL_WINDOW_SIZE = 5
|
|
9
10
|
export const STALL_THRESHOLD = 3
|
|
@@ -15,14 +16,9 @@ export const GOAL_BUDGET_WARN_RATIO = 0.75
|
|
|
15
16
|
*/
|
|
16
17
|
export function injectPostTurn(agent, results, recentCallSigs, callbacks, turn) {
|
|
17
18
|
// Expired timers — inject reminders when thinking budget is up
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
agent._pendingTimers = agent._pendingTimers.filter((t) => t.expiresAt > now)
|
|
22
|
-
for (const t of expired) {
|
|
23
|
-
agent.history.push({ role: "user", content: `[System reminder: ⏰ timer — ${t.message}]` })
|
|
24
|
-
}
|
|
25
|
-
}
|
|
19
|
+
// AGENT-LOOP-ASYNC-POOL.md §6.30.2:过滤 / 注入语义单源于 `agent/timers.mjs`(CLI 空闲闩 /
|
|
20
|
+
// 挂起窗同读);本处 = 回合边界轮询调用点——行为零变(最近到期 / 出列幂等 / 逐字形态逐条不变)。
|
|
21
|
+
injectTimerReminders(agent, takeExpiredTimers(agent))
|
|
26
22
|
|
|
27
23
|
// Pending reminders
|
|
28
24
|
if (agent._pendingReminders.length > 0) {
|
package/agent/record-results.mjs
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import { pushReal } from "../context.mjs"
|
|
27
27
|
import { specForModel } from "../config.mjs"
|
|
28
|
-
import { FILE_MUTATORS } from "./helpers.mjs"
|
|
28
|
+
import { FILE_MUTATORS, toolTouchPaths } from "./helpers.mjs"
|
|
29
29
|
import { resolve } from "node:path"
|
|
30
30
|
import { advisorRuns, stripApprovedSuffix } from "../agent-tools/advisor-async.mjs"
|
|
31
31
|
import { looksLikeReviewOutput, advisorIncompleteMarker } from "../advisor/run.mjs"
|
|
@@ -78,7 +78,7 @@ export async function recordToolResults(agent, toolByName, results) {
|
|
|
78
78
|
// Direct file edit — code was changed. The prior advisor review and
|
|
79
79
|
// verify are stale: a review that ran before the edit no longer
|
|
80
80
|
// covers the current file state.
|
|
81
|
-
//
|
|
81
|
+
// fix A(2026-09-07):mutation-seq 记账已移到 dispatch
|
|
82
82
|
// runOne 执行成功即刻(唯一记账点——取代本批后段 + agent.mjs 中断分支——不双计)——
|
|
83
83
|
// 此处仅剩 guard 标志失效(顺序语义:批内同消息的 sync advisor 提交仍在其后置位)。
|
|
84
84
|
agent._mutatedThisRun = true
|
|
@@ -100,7 +100,7 @@ export async function recordToolResults(agent, toolByName, results) {
|
|
|
100
100
|
}
|
|
101
101
|
if (toolCall.name === "verify") agent._verifiedThisRun = true
|
|
102
102
|
if (toolCall.name === "advisor") {
|
|
103
|
-
// §
|
|
103
|
+
// AGENT-LOOP-ASYNC-POOL.md §6.10 D-24b (settle accounting split — fix #2): an ASYNC launch returns an
|
|
104
104
|
// ack and settles later — the settle callback owns its called/round/token
|
|
105
105
|
// accounting. Only the SYNC path (depth>0 / explicit async:false) accounts
|
|
106
106
|
// here — per-review instance round++ (marker-keyed by tool call id) + the
|
|
@@ -117,7 +117,7 @@ export async function recordToolResults(agent, toolByName, results) {
|
|
|
117
117
|
} else {
|
|
118
118
|
const reviewId = agent._advisorSyncCalls?.get(toolCall.id)
|
|
119
119
|
const run = reviewId !== undefined ? advisorRuns(agent).get(reviewId) : undefined
|
|
120
|
-
// F16 同步面(第 13
|
|
120
|
+
// F16 同步面(第 13 批——ADVISOR-GUARDS.md §6):未完成尾 + 非设计面
|
|
121
121
|
// (含类型不可判——run 缺失的 legacy 直调)⇒ 不置「已覆盖」(与 settle 面
|
|
122
122
|
// 逐条 parity;设计评审保持计入——无代码面)。
|
|
123
123
|
const incomplete = advisorIncompleteMarker(String(result))
|
|
@@ -127,7 +127,7 @@ export async function recordToolResults(agent, toolByName, results) {
|
|
|
127
127
|
run.round++
|
|
128
128
|
agent._advisorRound = run.round
|
|
129
129
|
// Prior of round 2+ = the last REVIEW-LOOKING output (run.mjs parity).
|
|
130
|
-
// F2e
|
|
130
|
+
// F2e: the sync settle's engine-approved suffix (stored on the
|
|
131
131
|
// run by settleDesignReview) is stripped with exact truncation — the
|
|
132
132
|
// prior never carries the raw token / designId.
|
|
133
133
|
if (looksLikeReviewOutput(result)) {
|
|
@@ -145,23 +145,26 @@ export async function recordToolResults(agent, toolByName, results) {
|
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
if (FILE_MUTATORS.has(toolCall.name)) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
_reindexFile
|
|
148
|
+
try {
|
|
149
|
+
const args = JSON.parse(toolCall.arguments)
|
|
150
|
+
const paths = toolTouchPaths(tool, args)
|
|
151
|
+
for (const p of paths) {
|
|
152
|
+
if (typeof p !== "string" || !p) continue // 非字符串项不拖累合法路径(与 dispatch / VSC 消费面同口径)
|
|
153
|
+
const abs = resolve(agent.cwd, p)
|
|
154
|
+
if (!agent._touchedFiles.includes(abs)) agent._touchedFiles.push(abs)
|
|
155
|
+
if (agent.memory) {
|
|
156
|
+
// Fire-and-forget: don't block the agent loop on indexing.
|
|
157
|
+
// Reuses a single cached import; errors surface as pending reminders on next turn.
|
|
158
|
+
if (!_reindexFile) {
|
|
159
|
+
const mod = await import("../memory.mjs")
|
|
160
|
+
_reindexFile = mod.reindexFile
|
|
161
|
+
}
|
|
162
|
+
_reindexFile(agent.memory, agent.cwd, abs).catch((e) => {
|
|
163
|
+
agent._pendingReminders.push(`[System reminder: background indexing failed for ${toolCall.name} on ${abs}: ${e.message}. This does not affect your work — the code index will catch up on next reindex.]`)
|
|
164
|
+
})
|
|
159
165
|
}
|
|
160
|
-
_reindexFile(agent.memory, agent.cwd, abs).catch((e) => {
|
|
161
|
-
agent._pendingReminders.push(`[System reminder: background indexing failed for ${toolCall.name} on ${abs}: ${e.message}. This does not affect your work — the code index will catch up on next reindex.]`)
|
|
162
|
-
})
|
|
163
166
|
}
|
|
164
|
-
}
|
|
167
|
+
} catch { /* #327:畸形 args(含 null)/ 畸形路径不再裸抛——touchedFiles 尽力而为 */ }
|
|
165
168
|
}
|
|
166
169
|
}
|
|
167
170
|
}
|
package/agent/relay-prefix.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* relay-prefix.mjs — relay 前缀(`role#id/`)文法**单一权威**(第 27
|
|
2
|
+
* relay-prefix.mjs — relay 前缀(`role#id/`)文法**单一权威**(第 27 批——R-A2.3)。
|
|
3
3
|
*
|
|
4
4
|
* 零依赖(不 import 任何模块)——TUI(`src/tui/`)与 ACP(`src/acp/`)双向可导入,无环;
|
|
5
5
|
* 生成侧 `src/agent/spawn-child.mjs` 再导出(枢纽)。文法 = 自
|
|
6
|
-
* `src/tui/subagent-blocks.mjs` 逐字迁入(第 27
|
|
6
|
+
* `src/tui/subagent-blocks.mjs` 逐字迁入(第 27 批——零语义改动)。
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
/** `role#id/` prefix router — hyphen included since the eng-coder fix (2026-08-21). */
|
package/agent/run-stages.mjs
CHANGED
|
@@ -15,11 +15,12 @@ import { logEvent, errText } from "../log.mjs"
|
|
|
15
15
|
// ASYNC-RESULT-CONTAINER.md D1:池 accessor(absorb 双池——advisor 独立池无队列)
|
|
16
16
|
import { getAsyncPool, releaseSettledEntry } from "../agent-tools/async-settle.mjs"
|
|
17
17
|
// 批 4 CLI-ASYNC-DISCARD(AGENT-LOOP-ASYNC-POOL.md §6.20):中止分支「只清已死」收尾单点
|
|
18
|
-
|
|
18
|
+
// (#9:+ 后台 bash 任务族——逐条杀树 + 出池 + 墓碑)
|
|
19
|
+
import { discardAbortedPool, discardAbortedAdvisors, discardAbortedBgTasks } from "../agent-tools/async-discard.mjs"
|
|
19
20
|
// R10 L3 (MULTI-INSTANCE-COLLAB §2a.5 D-L3a):回合末域登记 flush(写工具钩子累积 →
|
|
20
21
|
// 整写一次本实例 peers 文件——无写入跳过;失败容忍不抛)
|
|
21
22
|
import { flushPeerDomains } from "../peer-domains.mjs"
|
|
22
|
-
// 第 30 批 D1(Stop 钩子——AGENT-LOOP.md §
|
|
23
|
+
// 第 30 批 D1(Stop 钩子——AGENT-LOOP.md §6.13):主会话 run 终止事件(触发块见 finalizeAgentTurn)
|
|
23
24
|
import { runHooks } from "../hooks.mjs"
|
|
24
25
|
|
|
25
26
|
/**
|
|
@@ -164,14 +165,14 @@ export async function finalizeAgentTurn(agent, ctx) {
|
|
|
164
165
|
},
|
|
165
166
|
}).catch(() => {})
|
|
166
167
|
}
|
|
167
|
-
// R17(
|
|
168
|
+
// R17(CONSULTATION.md §6.2): consultation sessions are cross-turn
|
|
168
169
|
// background work now — NO unconditional turn-end cleanup (the old rule aborted
|
|
169
170
|
// leftover consult children at every turn end because check-loop consumption was
|
|
170
171
|
// turn-scoped). Sessions stay alive across normal turn ends (and ContinueError
|
|
171
172
|
// resumes); the Ctrl+C abort branch below is the only path that aborts them
|
|
172
173
|
// (cleanupConsultSessions — marked stopped → no digest), and the suspension
|
|
173
174
|
// driver aborts them on its own abort unwind.
|
|
174
|
-
// Async subagent turn-end handling (AGENT-LOOP.md §
|
|
175
|
+
// Async subagent turn-end handling (AGENT-LOOP-SUBAGENT.md §6.7.3 D-A3 + AGENT-LOOP-ASYNC-POOL.md §6.8 D-S1). Lifecycle:
|
|
175
176
|
// - Ctrl+C (plain abort): children were aborted with the parent signal — discard dead
|
|
176
177
|
// entries (tombstone/out-of-pool/notice — AGENT-LOOP-ASYNC-POOL.md §6.20; no stale
|
|
177
178
|
// errors injected — user explicitly stopped); consultation sessions are cross-turn
|
|
@@ -188,12 +189,15 @@ export async function finalizeAgentTurn(agent, ctx) {
|
|
|
188
189
|
if (signal?.aborted && !signal?.reason?.interrupt) {
|
|
189
190
|
const subPool = getAsyncPool(agent, "subagent")
|
|
190
191
|
const advPool = getAsyncPool(agent, "advisor")
|
|
191
|
-
|
|
192
|
-
|
|
192
|
+
const bgPool = getAsyncPool(agent, "bg") // #9:后台 bash 任务池(第三域)
|
|
193
|
+
if ((subPool?.size ?? 0) > 0 || (advPool?.size ?? 0) > 0 || (bgPool?.size ?? 0) > 0) {
|
|
194
|
+
logEvent("ev:stopped", { poolN: (subPool?.size ?? 0) + (advPool?.size ?? 0) + (bgPool?.size ?? 0), where: "turn-end-abort" })
|
|
193
195
|
}
|
|
194
|
-
// §6.20:只清已死条目(存活/已 settle 留池——收尾见 async-discard.mjs
|
|
196
|
+
// §6.20:只清已死条目(存活/已 settle 留池——收尾见 async-discard.mjs;原无条件清池已弃);
|
|
197
|
+
// #9:后台 bash 任务同面(杀 ⟺ 控制器已中止——逐条杀树 + 出池 + 墓碑)。
|
|
195
198
|
discardAbortedPool(agent)
|
|
196
199
|
discardAbortedAdvisors(agent)
|
|
200
|
+
discardAbortedBgTasks(agent)
|
|
197
201
|
// R17: the consult family dies with the user stop (marked stopped — no digest).
|
|
198
202
|
cleanupConsultSessions(agent)
|
|
199
203
|
// ASYNC-RESULT-CONTAINER.md D2:pending 单容器——中止丢弃 consult/escalate 族停靠
|
|
@@ -204,7 +208,7 @@ export async function finalizeAgentTurn(agent, ctx) {
|
|
|
204
208
|
// keep _asyncSubagents/_asyncAdvisors/_consultSessions — the resumed run continues them
|
|
205
209
|
} else {
|
|
206
210
|
const injectedAdvisor = await collectSettledAsync(agent, { suspDriven })
|
|
207
|
-
// §
|
|
211
|
+
// AGENT-LOOP-ASYNC-POOL.md §6.10 D-24b: a normally-ended USER run with no review/child activity closes
|
|
208
212
|
// the OPEN code instances — the converged/abandoned thread must not burn the
|
|
209
213
|
// 5-round cap of a later task (the next code review starts a fresh round 1).
|
|
210
214
|
// Digest auto-turns are exempt (their disposition precedes the fix round).
|
|
@@ -241,8 +245,8 @@ export async function finalizeAgentTurn(agent, ctx) {
|
|
|
241
245
|
* maybeRefillAsync runs in both modes (starts queued heads whose slot freed).
|
|
242
246
|
* Single ownership: entries settled inside a suspension session were moved to
|
|
243
247
|
* _pendingAsyncResults by the settle callback, so this only sees user-turn
|
|
244
|
-
* settles (no double inject — D-S3 points ①/②).
|
|
245
|
-
*
|
|
248
|
+
* settles (no double inject — D-S3 points ①/②). settle 回调只发 ⟦ev⟧settled;
|
|
249
|
+
* ⟦ev⟧done 冻结落消费窗(#746 · ASYNC-POOL §6.8)。
|
|
246
250
|
* 2026-09-05 实践轮:自 agent.mjs 迁入(agent.mjs 内仅 finalize 引用——随收尾同迁)。
|
|
247
251
|
*/
|
|
248
252
|
async function collectSettledAsync(agent, { suspDriven = false } = {}) {
|
|
@@ -257,7 +261,7 @@ async function collectSettledAsync(agent, { suspDriven = false } = {}) {
|
|
|
257
261
|
if (!e.done) continue // still running — stays in the pool (D-S1)
|
|
258
262
|
await injectAsyncResult(agent, e)
|
|
259
263
|
if (e.role === "advisor") injectedAdvisor = true
|
|
260
|
-
// TUI-OOM-ROOTCAUSE
|
|
264
|
+
// TUI-OOM-ROOTCAUSE(AGENT-LOOP.md §6.15 消费点①——回合尾收集):注入完成 → 释放条目持有
|
|
261
265
|
releaseSettledEntry(e)
|
|
262
266
|
map.delete(String(e.id))
|
|
263
267
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent/run-start.mjs — runAgent 起跑前段(2026-09-29 P2 三拆:agent.mjs runAgent 段 1
|
|
3
|
+
* verbatim 迁入——distill 等待 ∕ pendingAsync 注入 ∕ prepareRun ∕ 复位块 ∕ 域文本 ∕ 授权注释块)。
|
|
4
|
+
* 接口 = beginRun(agent, input, callbacks, opts) → 主循环装配面(`tools` 死绑定去——KD-8)。
|
|
5
|
+
*/
|
|
6
|
+
import { prepareRun } from "./setup.mjs"
|
|
7
|
+
import {
|
|
8
|
+
restoreGuard, // digest D-S6 读侧单点(AGENT-LOOP-ASYNC-POOL.md §6.8;P2 机制层端差批 §2.18——键清单归核)
|
|
9
|
+
AUTO_TURN_DIGEST_DOMAIN,
|
|
10
|
+
AUTO_TURN_DIGEST_DOMAIN_ENG, // §6.15.3(F10 第三面):工程模式 digest 基座变体(task 指针改批次档 + 台账)
|
|
11
|
+
UPSTREAM_TURN_DOMAIN, // §6.27.12.8:上行唤醒轮域文本(手动档——ask 轮不沿用 digest 域文本)
|
|
12
|
+
TIMER_TURN_DOMAIN, // §6.30.3 D-TW4:timer 唤醒轮域文本(第三变体——到期 timer 自动开轮)
|
|
13
|
+
} from "./helpers.mjs"
|
|
14
|
+
|
|
15
|
+
export async function beginRun(agent, input, callbacks, {
|
|
16
|
+
depth = 0, signal, overrideTurns, resume = false, autoTurn = false, upstreamTurn = false, timerTurn = false,
|
|
17
|
+
extraTools = null, injections = null, turnDomainText = null, toolDecorate = undefined,
|
|
18
|
+
} = {}) {
|
|
19
|
+
// Previous run's async exploration distillation must settle before this run pushes
|
|
20
|
+
// input (SEND-STALL-DISTILL §2.2 N1) — await first, or its history replace wipes it.
|
|
21
|
+
if (agent._pendingDistill) {
|
|
22
|
+
const p = agent._pendingDistill
|
|
23
|
+
agent._pendingDistill = null
|
|
24
|
+
await p
|
|
25
|
+
}
|
|
26
|
+
// AGENT-LOOP-ASYNC-POOL.md §6.8 D-S3: suspension-settled async results inject before EVERY run's prepareRun
|
|
27
|
+
// (user + auto-turn); spliced = consumed. collectSettledAsync owns a different
|
|
28
|
+
// container, so no double-inject across the two consumption points.
|
|
29
|
+
// ASYNC-RESULT-CONTAINER.md D2 (2026-09-08):pending 单容器 `_pendingAsyncResults`
|
|
30
|
+
// +role——四族(subagent/advisor/escalate/consult)统一停靠;注入器按 role 分发
|
|
31
|
+
// (consult → injectConsultResult;其余 → injectAsyncResult——族分支同 CONSULTATION.md §6.2 D-R17a/b)
|
|
32
|
+
// ——单容器一处清,不再逐族三段。
|
|
33
|
+
const pendingAsync = agent._pendingAsyncResults
|
|
34
|
+
if (pendingAsync?.length) {
|
|
35
|
+
const { injectAsyncResult } = await import("../agent-tools/subagent.mjs")
|
|
36
|
+
const { injectConsultResult } = await import("../agent-tools/consult.mjs")
|
|
37
|
+
// TUI-OOM-ROOTCAUSE(AGENT-LOOP.md §6.15 消费点②——run 起始 pending 注入):
|
|
38
|
+
// 注入完成后释放条目对子代理对象的持有(childAgent/report 置空——幂等 helper)。
|
|
39
|
+
const { releaseSettledEntry } = await import("../agent-tools/async-settle.mjs")
|
|
40
|
+
for (const e of pendingAsync.splice(0)) {
|
|
41
|
+
if (e.role === "consult") await injectConsultResult(agent, e)
|
|
42
|
+
else await injectAsyncResult(agent, e)
|
|
43
|
+
releaseSettledEntry(e)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
agent._inAutoTurn = autoTurn // spawn gate for manual-tier digests (AGENT-LOOP-ASYNC-POOL.md §6.8 D-S6/N3;上行唤醒轮同持 autoTurn——§6.27.12.4 ①)
|
|
47
|
+
const { maxTurns, threshold, toolSchemas, toolByName, systemPrompt } = await prepareRun(
|
|
48
|
+
// G1/G2(施工②):prompt 装配收口 prepareRun 内部(assemblePrompt——prompt-overlays.mjs
|
|
49
|
+
// 槽位常量,与子代理角色常量同源——单一权威锚 D1);本调用不再携带 prompt 常量。
|
|
50
|
+
agent, input, callbacks,
|
|
51
|
+
{ depth, signal, overrideTurns, resume: resume || autoTurn, extraTools, injections, toolDecorate },
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
// Exploration-distillation boundary (CONTEXT-COMPACTION §5): prepareRun already
|
|
55
|
+
// pushed input + injections — appended from here counts as "this run's" work.
|
|
56
|
+
agent._runStartHistoryLen = agent.history.length
|
|
57
|
+
|
|
58
|
+
// Per-run bookkeeping reset — PRESERVED on `resume` (ContinueError continuation):
|
|
59
|
+
// mutation/guard continuity and the convergence budget must survive a continuation.
|
|
60
|
+
// §2.3 链内段数(TURN-CAP-CONTINUE.md §4):首段 1、每次续跑 +1——复位条件同 `_turnSeq`
|
|
61
|
+
// (链起点复位、续跑不回退);消费面 = 检查点 ask 载荷 / 池条目留痕二元。
|
|
62
|
+
agent._continueSegments = resume ? (agent._continueSegments ?? 1) + 1 : 1
|
|
63
|
+
// #417(撞帽载荷「本段零落盘轮数」——只报数:零阈值常量 / 零自动动作):段起点复位(首段 /
|
|
64
|
+
// 续跑新段——与 `_continueSegments` 同点);段内累加,采集点在回合环(与 `_turnSeq` 同源面)。
|
|
65
|
+
agent._zeroWriteTurns = 0
|
|
66
|
+
if (!resume) {
|
|
67
|
+
// 第 19 批(TURN-ACROSS-SEGMENTS——设计 TURN-CAP-CONTINUE.md §4):链内累计编号
|
|
68
|
+
// 只在链起点复位——续跑(resume:true)不重置、不回退(编号帧公式见 helpers.mjs
|
|
69
|
+
// turnFrame)。与下方 mutation/guard 复位同条件同点(全档唯一复位点)。
|
|
70
|
+
agent._turnSeq = 0
|
|
71
|
+
// AGENT-LOOP-ASYNC-POOL.md §6.8 D-S6: an auto-turn's guard marks are inherited by the next USER run (not
|
|
72
|
+
// reset) so auto-turn changes never escape the guard silently.
|
|
73
|
+
const g = agent._inheritedGuard
|
|
74
|
+
if (g) {
|
|
75
|
+
restoreGuard(agent, g)
|
|
76
|
+
agent._inheritedGuard = null
|
|
77
|
+
} else {
|
|
78
|
+
agent._mutatedThisRun = false
|
|
79
|
+
agent._verifiedThisRun = false
|
|
80
|
+
agent._verifyPassed = undefined
|
|
81
|
+
agent._calledAdvisorThisRun = false
|
|
82
|
+
agent._touchedFiles = []
|
|
83
|
+
agent._verifyRetries = 0
|
|
84
|
+
agent._advisorRound = 0
|
|
85
|
+
agent._advisorSession = null // advisor session is per-run: discard when the task ends, next task starts fresh
|
|
86
|
+
agent._emptyRetries = 0 // empty-response retry budget is per-run: a fresh user turn restarts from zero
|
|
87
|
+
agent._compressFailures = 0 // compaction summary-failure counter is per-run: a fresh user turn restarts from zero
|
|
88
|
+
// F-CC2(§6.16.2):模型主动压缩的排队槽也是回合级——上一回合末尾未被安全点消费的请求不得跨回合生效
|
|
89
|
+
agent._pendingCompact = null
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// digest D-S6 manual tier(AGENT-LOOP-ASYNC-POOL.md §6.8): action-domain reminder (system-driven turn — organize only).
|
|
93
|
+
// §6.27.12.4 ②: an up-stream wake turn answers a RUNNING subagent waiting for the reply — it
|
|
94
|
+
// must not reuse the digest text ("no one is waiting" is the opposite of the truth).
|
|
95
|
+
// §6.30.3 D-TW4: timer wake turn (third auto-turn variant) — 到期 timer 自动开轮,仅作域文本选择
|
|
96
|
+
// (`upstreamTurn` 优先不回归:ask 轮是「有人在等回复」,比 timer 轮更紧)。
|
|
97
|
+
if ((autoTurn || upstreamTurn) && !agent.autoApprove) {
|
|
98
|
+
// §2.5-A3(P4-I):端壳经 `opts.turnDomainText` 供「核基座 + 端 overlay」组合串(组合点 =
|
|
99
|
+
// 端 `turn-domains.mjs` composeTurnDomain);缺省(null)⇒ 核基座逐字零变(选择序不变)。
|
|
100
|
+
const domainBase = upstreamTurn ? UPSTREAM_TURN_DOMAIN : timerTurn ? TIMER_TURN_DOMAIN : (agent.config?.agent?.engineering === true ? AUTO_TURN_DIGEST_DOMAIN_ENG : AUTO_TURN_DIGEST_DOMAIN)
|
|
101
|
+
agent.history.push({ role: "user", content: turnDomainText ?? domainBase, transient: true })
|
|
102
|
+
}
|
|
103
|
+
// eng-coder authorization (_engDesignReviewed) is eng-coder-only: set by subagent-spawn.mjs
|
|
104
|
+
// (spawn gate) / design-token.mjs (design review pass) BEFORE the child runAgent — the
|
|
105
|
+
// depth-0 parent never reads or writes it (the parent gate reads anyLiveDesignSlot; the
|
|
106
|
+
// depth-0 per-turn reset was removed 2026-09-08, ENG-SESSION-PROVIDER-CLEANUP D1.3).
|
|
107
|
+
// Design slots (_engDesignTokens Map) survive across turns (design review → approval →
|
|
108
|
+
// eng-coder spawn) — persisted to the session slot at settle time (DESIGN-TOKEN-
|
|
109
|
+
// SETTLEMENT D1); lifecycle: issued on a passing review, consumed by consume-design / TTL.
|
|
110
|
+
return { maxTurns, threshold, toolSchemas, toolByName, systemPrompt }
|
|
111
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* (SESSION.md §6.11 — 2026-09-06 需求池 R5/R8/R9/R11 合并设计; R10 L1 peer 注入同文件).
|
|
4
4
|
*
|
|
5
5
|
* One unified transient user reminder per turn covers the whole self-awareness
|
|
6
|
-
* family: env identity (R8 — §6.10 D-1
|
|
6
|
+
* family: env identity (R8 — §6.10 D-1 端名先例:值 = 进程端名(§6.20 端名缝 sessionEnd()),不做 cmdline 判别),
|
|
7
7
|
* engineering mode (R9), active model (R11), session slot (F1), restart awareness
|
|
8
8
|
* (R5 resumed). §6.11(2026-09-08):resumed 按会话跟踪——恢复事件由 session.mjs
|
|
9
9
|
* applySession 武装(agent._envResumed——载入历史非空 = 恢复);进程重启句独立走
|
|
@@ -29,16 +29,16 @@
|
|
|
29
29
|
*/
|
|
30
30
|
import { statSync } from "node:fs"
|
|
31
31
|
import { resolve } from "node:path"
|
|
32
|
-
import {
|
|
32
|
+
import { sessionEnd } from "../session-slots.mjs"
|
|
33
33
|
import { peerInstances } from "../peer-instances.mjs"
|
|
34
34
|
import { specForModel } from "../config.mjs"
|
|
35
35
|
import { manifestFilePath, readManifest, projectView } from "../manifest.mjs"
|
|
36
36
|
|
|
37
|
-
/** env-state line builder —
|
|
37
|
+
/** env-state line builder — 对入参 + 进程端名确定(§6.20 端名缝 sessionEnd();setSessionEnd 注入 ⇒ 单测可用)。
|
|
38
38
|
* §6.11(F1):slot 字段入行——位置在 model 后 resumed 前(N3:无绑定 → 显式 null——
|
|
39
39
|
* 不读 manifest active 共享指针——粘性 _slot 才是"本 agent 之槽")。 */
|
|
40
40
|
export function envStateLine({ mode, model, slot, resumed }) {
|
|
41
|
-
return `[System reminder: env: ${
|
|
41
|
+
return `[System reminder: env: ${sessionEnd()}, mode: ${mode}, model: ${model}, slot: ${slot}, resumed: ${resumed ? "yes" : "no"}.]`
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
@@ -63,16 +63,16 @@ export function pushEnvStateReminder(agent) {
|
|
|
63
63
|
/** 情境行前缀——本行族识别符(摘旧行 / 会话重建认领同用;行形逐字见 manifestStateLine)。 */
|
|
64
64
|
const MANIFEST_LINE_PREFIX = "[System reminder: project state: "
|
|
65
65
|
|
|
66
|
-
/** phase →
|
|
67
|
-
const
|
|
66
|
+
/** phase → rigor 标签映射(判据单源 = 需求 v2);未知值不在表内 → 无标签。 */
|
|
67
|
+
const MANIFEST_RIGOR = Object.freeze({ "initial-dev": "light", production: "strict" })
|
|
68
68
|
|
|
69
69
|
/** 情境行构造 — pure, unit-testable(模块契约 `docs/core/design/MANIFEST.md` §2.6,与
|
|
70
70
|
* envStateLine 同族)。行形逐字(**单字段**——2026-09-17 裁撤批后):
|
|
71
|
-
* `[System reminder: project state: phase: <值> (
|
|
71
|
+
* `[System reminder: project state: phase: <值> (rigor: <light|strict>).]`
|
|
72
72
|
* 未知 phase → 只出值、不编判据(无标签)。 */
|
|
73
73
|
export function manifestStateLine({ phase }) {
|
|
74
|
-
const
|
|
75
|
-
return `${MANIFEST_LINE_PREFIX}phase: ${phase}${
|
|
74
|
+
const rigor = MANIFEST_RIGOR[phase]
|
|
75
|
+
return `${MANIFEST_LINE_PREFIX}phase: ${phase}${rigor ? ` (rigor: ${rigor})` : ""}.]`
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/**
|
package/agent/setup.mjs
CHANGED
|
@@ -10,14 +10,18 @@ import { search as memorySearch, docSearch } from "../memory.mjs"
|
|
|
10
10
|
import { pushReal } from "../context.mjs"
|
|
11
11
|
import { toOpenAISchema } from "../tools/index.mjs"
|
|
12
12
|
import { loadSkills, formatSkillListing } from "../skills.mjs"
|
|
13
|
+
import { scopedRulesBlock } from "../rules.mjs"
|
|
13
14
|
import { assemblePrompt } from "../prompt-overlays.mjs"
|
|
14
15
|
import {
|
|
15
16
|
escapeXml, repairHistory, listWorkDir,
|
|
16
17
|
collectGitContext, loadProjectInstructions, OUTLINE_INJECT_PREFIX,
|
|
17
18
|
DEFAULT_MAX_TURNS, ensureAutoReminder,
|
|
18
19
|
} from "./helpers.mjs"
|
|
19
|
-
import { pushEnvStateReminder, pushPeerReminder } from "./setup-reminders.mjs"
|
|
20
|
+
import { pushEnvStateReminder, pushPeerReminder, pushInjections } from "./setup-reminders.mjs"
|
|
20
21
|
import { assembleFamilyTools } from "./family-tools.mjs"
|
|
22
|
+
import { normalizeOrigin } from "../memory/origin.mjs"
|
|
23
|
+
import { searchOrigins } from "../memory/code-search.mjs"
|
|
24
|
+
import { INDEX_ORIGIN_ROW_WARN } from "../memory/schema.mjs"
|
|
21
25
|
|
|
22
26
|
const DEFAULT_COMPACT_THRESHOLD = 100_000
|
|
23
27
|
const DOC_SEARCH_LIMIT = 5
|
|
@@ -41,6 +45,10 @@ const MEMORY_SEARCH_LIMIT = 3
|
|
|
41
45
|
*/
|
|
42
46
|
export async function prepareRun(agent, input, callbacks, {
|
|
43
47
|
depth = 0, signal, overrideTurns, resume, extraTools = null,
|
|
48
|
+
// P4-I(批次档 §2.5 A1 + 父侧 2026-09-29 裁定①):端壳装配缝——注入串 ∕ 工具装饰;
|
|
49
|
+
// 两项缺省(null ∕ undefined)⇒ 现行行为逐字零变(CLI ∕ desktop 不传即零变)。
|
|
50
|
+
// (A2 提示尾块载位已退役——B7 3a:核自持 `.cursor/rules` 尾块,见下方 depth-0 块。)
|
|
51
|
+
injections = null, toolDecorate = undefined,
|
|
44
52
|
} = {}) {
|
|
45
53
|
const maxTurns = overrideTurns ?? agent.config?.agent?.maxTurns ?? DEFAULT_MAX_TURNS
|
|
46
54
|
const threshold = agent.config?.agent?.compactThreshold ?? DEFAULT_COMPACT_THRESHOLD
|
|
@@ -91,7 +99,8 @@ export async function prepareRun(agent, input, callbacks, {
|
|
|
91
99
|
if (agent.memory && !agent.history.some((m) => typeof m.content === "string" && m.content.startsWith(OUTLINE_INJECT_PREFIX))) {
|
|
92
100
|
try {
|
|
93
101
|
const { buildSummary } = await import("../tools/repomap.mjs")
|
|
94
|
-
|
|
102
|
+
// §6.14 B4:origin 限定 + 文件数上界(越界 ⇒ 提示行 + 跳过大纲构建——见 repomap.mjs)
|
|
103
|
+
const summary = await buildSummary(agent.memory.db, agent.cwd, { origin: normalizeOrigin(agent.memory.codeOrigin) })
|
|
95
104
|
if (summary && !summary.startsWith("(no indexed")) {
|
|
96
105
|
agent.history.push({ role: "user", content: `${OUTLINE_INJECT_PREFIX}\n${summary}]`, transient: true })
|
|
97
106
|
}
|
|
@@ -103,8 +112,16 @@ export async function prepareRun(agent, input, callbacks, {
|
|
|
103
112
|
if (agent.memory && depth === 0) {
|
|
104
113
|
const docs = await docSearch(agent.memory, input, { limit: DOC_SEARCH_LIMIT })
|
|
105
114
|
if (docs.length > 0) {
|
|
106
|
-
|
|
107
|
-
const
|
|
115
|
+
// §6.14 B3 + §6.15:origin 集限定 + 有界计数(子查询 LIMIT = WARN+1 形——越界回「20000+」;跨 origin 全扫不再回升)
|
|
116
|
+
const origins = searchOrigins(agent.memory)
|
|
117
|
+
const bound = INDEX_ORIGIN_ROW_WARN + 1
|
|
118
|
+
const countFilter = origins.length === 1 ? " WHERE origin = ?" : origins.length > 1 ? ` WHERE origin IN (${origins.map(() => "?").join(", ")})` : ""
|
|
119
|
+
const countRow = agent.memory.db.prepare(
|
|
120
|
+
`SELECT COUNT(*) AS n FROM (SELECT 1 FROM doc_chunks${countFilter} LIMIT ${bound})`,
|
|
121
|
+
).get(...origins)
|
|
122
|
+
const count = Number(countRow?.n ?? 0)
|
|
123
|
+
const countLabel = count >= bound ? `${INDEX_ORIGIN_ROW_WARN}+` : String(count)
|
|
124
|
+
const more = count > docs.length ? ` (${countLabel} chunks indexed total — call doc_search if you need more)` : ""
|
|
108
125
|
agent.history.push({
|
|
109
126
|
role: "user",
|
|
110
127
|
content:
|
|
@@ -137,6 +154,9 @@ export async function prepareRun(agent, input, callbacks, {
|
|
|
137
154
|
pushEnvStateReminder(agent)
|
|
138
155
|
await pushPeerReminder(agent)
|
|
139
156
|
}
|
|
157
|
+
// A1(§2.5——端壳编辑器上下文注入,序位 = env/peer 之后、time 之前):端采集内容经
|
|
158
|
+
// `pushInjections` 单点落机器线(transient + 同文去重);缺省 null ⇒ 零行为。
|
|
159
|
+
pushInjections(agent.history, injections)
|
|
140
160
|
|
|
141
161
|
// Time grounding for EVERY agent depth AND every resume, pushed LAST (after the user
|
|
142
162
|
// input): transient on the HUMAN line — dropped on persist; on the MACHINE line — kept
|
|
@@ -165,6 +185,9 @@ export async function prepareRun(agent, input, callbacks, {
|
|
|
165
185
|
engineering: agent.config?.agent?.engineering === true,
|
|
166
186
|
consultModels: agent.config?.agent?.consultModels ?? [],
|
|
167
187
|
batchDoc: agent._batchDoc ?? null,
|
|
188
|
+
// 裁定①(P4-I · 父侧 2026-09-29):端壳装饰体透传(`assembleFamilyTools` 既有 `decorate`
|
|
189
|
+
// 参数面——缺省 undefined ⇒ 核默认形态逐字零变,CLI ∕ desktop 不传即零变)。
|
|
190
|
+
decorate: toolDecorate,
|
|
168
191
|
})
|
|
169
192
|
// 两段式:绑定值(基础集)→ 家族段 → caller 注入面(§2.3F 不相交义务在注入方)。
|
|
170
193
|
const tools = [...agent.tools, ...familyTools, ...(Array.isArray(extraTools) ? extraTools : [])]
|
|
@@ -232,6 +255,11 @@ export async function prepareRun(agent, input, callbacks, {
|
|
|
232
255
|
const skills = await loadSkills(agent.cwd)
|
|
233
256
|
const listing = formatSkillListing(skills)
|
|
234
257
|
if (listing) systemPrompt += `\n\n${listing}`
|
|
258
|
+
// B7 3a:`.cursor/rules` 常驻集尾块(原端壳 A2 载位退役——核自持;序位 = skills 清单之后)。
|
|
259
|
+
// 尾块 ∕ JIT 两缝同门 `depth === 0`(子代理不携——B1 §2.5-A2 在册态);同点缓存 `agent._rules`
|
|
260
|
+
// (JIT 判定只读该缓存——`loadScopedRules` 本 run 唯一一次目录读取)。
|
|
261
|
+
const scopedBlock = scopedRulesBlock(agent, agent.cwd)
|
|
262
|
+
if (scopedBlock) systemPrompt += scopedBlock
|
|
235
263
|
}
|
|
236
264
|
|
|
237
265
|
ensureAutoReminder(agent)
|
package/agent/spawn-child.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* spawn-child.mjs — 生成侧统一子代理管线(AGENT-LOOP.md §7.2
|
|
2
|
+
* spawn-child.mjs — 生成侧统一子代理管线(AGENT-LOOP-SUBAGENT.md §6.7.2)。
|
|
3
3
|
*
|
|
4
4
|
* 收编 subagent/escalate/consult 三份机械同构的重复实现:
|
|
5
5
|
* - allocRelay / emitRelayModel: relay 取号段 ∥ 出生声明(`[model]`)发射段——两段分离
|
|
@@ -20,8 +20,8 @@ import { specForModel } from "../config.mjs"
|
|
|
20
20
|
import { ContinueError } from "../agent.mjs"
|
|
21
21
|
import { relayPrefixOf } from "./relay-prefix.mjs"
|
|
22
22
|
import { appendCappedText } from "../text-budget.mjs"
|
|
23
|
-
// 第 27
|
|
24
|
-
// 生成侧枢纽再导出(TUI/ACP
|
|
23
|
+
// 第 27 批:relay 前缀文法单一权威模块(`src/agent/relay-prefix.mjs`)——
|
|
24
|
+
// 生成侧枢纽再导出(TUI/ACP 消费方可经此导入;消费方直连模块亦可)。
|
|
25
25
|
export { RELAY_PREFIX_RE, parseRelayPath, relayPrefixOf } from "./relay-prefix.mjs"
|
|
26
26
|
|
|
27
27
|
/** 事件 token 哨兵串(D1)——LLM 正常内容混淆概率极低;字段分隔用 RS (\x1e)。 */
|
|
@@ -35,7 +35,7 @@ const RS = "\x1e"
|
|
|
35
35
|
export { TURN_CAP_MARK, STOPPED_MARK } from "./child-marks.mjs"
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
38
|
+
* D-E3 工程子代理内部 spawn 机械门(AGENT-LOOP-SUBAGENT.md §6.7.6 后备 +
|
|
39
39
|
* ENGINEERING-MODE.md §2.15 D):eng-coder(偏差审计)与 **eng-designer(自己勘察)**
|
|
40
40
|
* 同为受限通道——depth>0 且父角色 ∈ 集合时,内部 spawn 只允许 role='explore'、
|
|
41
41
|
* async 强制同步。
|
|
@@ -109,7 +109,7 @@ export function makeRelay(parent, label, emit, model) {
|
|
|
109
109
|
// Single source for the event grammar branch lists (consult P3, 2026-08-30):
|
|
110
110
|
// stripEventToken (display) and stripEventTokensForCapture (capture) shared them
|
|
111
111
|
// literally — extending the event set meant touching both regexes.
|
|
112
|
-
// "done" = §
|
|
112
|
+
// "done" = AGENT-LOOP-SUBAGENT.md §6.7.3 D-A3 async-child completion event (emitted by the parent's
|
|
113
113
|
// turn-end collection, not by children — listed so the grammar stays honest).
|
|
114
114
|
const EVENT_PHASE = "turn|approval|done"
|
|
115
115
|
const EVENT_TYPE = "llm|tool|approval|done"
|
|
@@ -139,7 +139,7 @@ export function stripEventTokensForCapture(text) {
|
|
|
139
139
|
* - onToolOutput 带**已加前缀**的 name 走父 onToolOutput(name 形如
|
|
140
140
|
* "coder#1/bash",消费端剥前缀路由进对应区块;chunk 对象/裸串原样透传)。
|
|
141
141
|
* 父回调缺省时不包装(headless 嵌入)。
|
|
142
|
-
* §
|
|
142
|
+
* docs/cli/design/TUI.md §6.8.2 R23 D-R23c1:包装产物在 onToken 上留 `_relayPrefix` 标记——同步收尾段据此
|
|
143
143
|
* 判定"ctx.callbacks 已是嵌套 wrapper"(本 spawn 处于更深一层——eng-coder 内
|
|
144
144
|
* explore)→ emitNestedChildEvent 补发射内层完成事件(T-R23c.2b 断言源)。
|
|
145
145
|
*/
|
|
@@ -163,7 +163,7 @@ export function wrapChildCallbacks(relayPrefix, parentCallbacks = {}) {
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
/**
|
|
166
|
-
* §
|
|
166
|
+
* §6.8.2 R23 D-R23c1(生成侧补发射——评审 #1 🅰):sync spawn 同步收尾时若父回调已是
|
|
167
167
|
* 嵌套 wrapper(onToken 带 `_relayPrefix` 标记——即本 spawn 的父本身是子代理,如
|
|
168
168
|
* eng-coder 内 explore 审计)→ 发内层 done/stopped 事件(带完整嵌套前缀——wrapper
|
|
169
169
|
* 链自动补外层前缀)→ 主 TUI routeSubToken 路由到子块定格。非嵌套(depth-0 直连主
|
|
@@ -178,7 +178,7 @@ export function emitNestedChildEvent(ctx, relayPrefix, kind) {
|
|
|
178
178
|
return true
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
/** 捕获额度(TUI-OOM-ROOTCAUSE——AGENT-LOOP.md §
|
|
181
|
+
/** 捕获额度(TUI-OOM-ROOTCAUSE——AGENT-LOOP.md §6.15:滞后水位截断):
|
|
182
182
|
* `_capturedOutput` 是子代理流式文本的第二份全量拷贝(原无上限——勘察 C2);超 hard 即
|
|
183
183
|
* 裁至头 16K + 标记 + 尾 48K(摊还 O(1))——消费面读时已各自 slice(0, 2000/4000),头尾
|
|
184
184
|
* 保真覆盖;停止报告内联场景出现截断标记(可断言——D-SM1)。纯函数本体住
|
|
@@ -235,7 +235,7 @@ export async function runWithContinue(runner, child, input, callbacks, runOpts,
|
|
|
235
235
|
// Review #4 fix: strip sentinel/control chars from the capture — `output` feeds
|
|
236
236
|
// onDeclined's partial-output return, which lands in the PARENT LLM history where
|
|
237
237
|
// the display-layer sanitizeDisplay backstop does not apply.
|
|
238
|
-
// TUI-OOM-ROOTCAUSE
|
|
238
|
+
// TUI-OOM-ROOTCAUSE(AGENT-LOOP.md §6.15):捕获滞后水位截断——超 CAPTURE_CAP_OPTS.hard 裁至
|
|
239
239
|
// 头 + 标记 + 尾(_capturedOutput 有界;续跑同闭包累积,语义一致)。
|
|
240
240
|
const capture = callbacks?.onToken
|
|
241
241
|
? (t) => { output = appendCappedText(output, stripEventTokensForCapture(String(t)), CAPTURE_CAP_OPTS); child._capturedOutput = output; callbacks.onToken(t) }
|