thincoder 0.12.61 → 0.12.63
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 +34 -1
- package/README.md +13 -12
- package/bin/thincoder.mjs +63 -28
- package/package.json +6 -5
- package/src/acp/bridge.mjs +35 -15
- package/src/acp/client-caps.mjs +86 -0
- package/src/acp/ext.mjs +86 -0
- package/src/acp/handlers-session.mjs +240 -0
- package/src/acp/handlers-slots.mjs +196 -0
- package/src/acp/login.mjs +48 -0
- package/src/acp/session.mjs +6 -4
- package/src/acp.mjs +67 -371
- package/src/cli/distill-command.mjs +3 -3
- package/src/cli/make-agent.mjs +59 -17
- package/src/cli/memory-command.mjs +3 -3
- package/src/cli/permission.mjs +4 -48
- package/src/cli/setup-wizard.mjs +1 -1
- package/src/completions.mjs +3 -1
- package/src/crash-reports.mjs +32 -10
- package/src/distill.mjs +4 -4
- package/src/heap-watch.mjs +88 -0
- package/src/prompt-injections.mjs +20 -0
- package/src/tui/agent-turn.mjs +40 -9
- package/src/tui/cmd-advisor.mjs +5 -5
- package/src/tui/cmd-clear.mjs +2 -0
- package/src/tui/cmd-config.mjs +8 -8
- package/src/tui/cmd-eng.mjs +25 -9
- package/src/tui/cmd-mcp.mjs +9 -8
- package/src/tui/cmd-model.mjs +1 -1
- package/src/tui/cmd-new.mjs +10 -5
- package/src/tui/cmd-reindex.mjs +1 -1
- package/src/tui/cmd-restore.mjs +2 -2
- package/src/tui/cmd-session.mjs +31 -4
- package/src/tui/cmd-skills.mjs +1 -1
- package/src/tui/cmd-think.mjs +22 -9
- package/src/tui/config-helpers.mjs +1 -1
- package/src/tui/display-budget.mjs +206 -0
- package/src/tui/index.mjs +40 -12
- package/src/tui/interaction.mjs +16 -7
- package/src/tui/key-handler-search.mjs +9 -1
- package/src/tui/key-modes.mjs +9 -4
- package/src/tui/ledger-surface.mjs +85 -0
- package/src/tui/model-catalog.mjs +4 -4
- package/src/tui/model-picker.mjs +8 -7
- package/src/tui/mouse.mjs +11 -6
- package/src/tui/pickers.mjs +15 -2
- package/src/tui/render-conversation.mjs +1 -1
- package/src/tui/render-frame.mjs +17 -6
- package/src/tui/render-loop.mjs +1 -1
- package/src/tui/render-segments.mjs +3 -1
- package/src/tui/slash-commands.mjs +1 -1
- package/src/tui/startup.mjs +49 -17
- package/src/tui/subagent-blocks.mjs +21 -3
- package/src/tui/subagent-children.mjs +86 -14
- package/src/tui/subagent-freeze.mjs +80 -3
- package/src/tui/suspension-drive.mjs +48 -22
- package/src/tui/tool-args.mjs +5 -2
- package/src/tui/tool-display.mjs +16 -2
- package/src/tui/tool-events.mjs +64 -22
- package/src/tui/tui-lifecycle.mjs +9 -2
- package/src/tui/wizard.mjs +3 -3
- package/src/tui/wrapped-spawn.mjs +21 -5
- package/src/abort-provenance.mjs +0 -116
- package/src/advisor/citations.mjs +0 -139
- package/src/advisor/compaction.mjs +0 -174
- package/src/advisor/convergence.mjs +0 -80
- package/src/advisor/history.mjs +0 -77
- package/src/advisor/loop.mjs +0 -293
- package/src/advisor/messages.mjs +0 -299
- package/src/advisor/project-context.mjs +0 -194
- package/src/advisor/repos.mjs +0 -150
- package/src/advisor/run.mjs +0 -293
- package/src/advisor/truncate.mjs +0 -57
- package/src/advisor.mjs +0 -290
- package/src/agent/completion.mjs +0 -146
- package/src/agent/dispatch.mjs +0 -489
- package/src/agent/helpers.mjs +0 -384
- package/src/agent/post-turn.mjs +0 -70
- package/src/agent/record-results.mjs +0 -174
- package/src/agent/relay-prefix.mjs +0 -39
- package/src/agent/run-stages.mjs +0 -242
- package/src/agent/setup-reminders.mjs +0 -69
- package/src/agent/setup.mjs +0 -354
- package/src/agent/spawn-child.mjs +0 -228
- package/src/agent-tools/advisor-async.mjs +0 -346
- package/src/agent-tools/advisor-settle.mjs +0 -231
- package/src/agent-tools/advisor.mjs +0 -260
- package/src/agent-tools/async-settle.mjs +0 -191
- package/src/agent-tools/batch-segment.mjs +0 -195
- package/src/agent-tools/consult.mjs +0 -468
- package/src/agent-tools/design-token.mjs +0 -117
- package/src/agent-tools/digest-budget.mjs +0 -76
- package/src/agent-tools/eng.mjs +0 -67
- package/src/agent-tools/escalate-async.mjs +0 -289
- package/src/agent-tools/goal.mjs +0 -119
- package/src/agent-tools/plan.mjs +0 -81
- package/src/agent-tools/read-history.mjs +0 -294
- package/src/agent-tools/recent-changes.mjs +0 -24
- package/src/agent-tools/review-streak.mjs +0 -93
- package/src/agent-tools/settings.mjs +0 -265
- package/src/agent-tools/skill.mjs +0 -47
- package/src/agent-tools/subagent-actions.mjs +0 -479
- package/src/agent-tools/subagent-async.mjs +0 -434
- package/src/agent-tools/subagent-panel.mjs +0 -160
- package/src/agent-tools/subagent-run.mjs +0 -205
- package/src/agent-tools/subagent-scheduler.mjs +0 -392
- package/src/agent-tools/subagent-spawn.mjs +0 -453
- package/src/agent-tools/subagent.mjs +0 -404
- package/src/agent-tools/task.mjs +0 -87
- package/src/agent-tools/timer.mjs +0 -46
- package/src/agent-tools/verify.mjs +0 -271
- package/src/agent-tools.mjs +0 -17
- package/src/agent.mjs +0 -413
- package/src/auto-think.mjs +0 -115
- package/src/config-migrate.mjs +0 -70
- package/src/config.mjs +0 -496
- package/src/context.mjs +0 -381
- package/src/conventions.mjs +0 -223
- package/src/embedding.mjs +0 -120
- package/src/escape.mjs +0 -152
- package/src/expand-home.mjs +0 -16
- package/src/explore-distill.mjs +0 -155
- package/src/generate-title.mjs +0 -83
- package/src/git/checkpoint.mjs +0 -448
- package/src/git/gitmem.mjs +0 -100
- package/src/hooks.mjs +0 -97
- package/src/log.mjs +0 -195
- package/src/markdown.mjs +0 -106
- package/src/mcp/helpers.mjs +0 -51
- package/src/mcp/transport-http.mjs +0 -248
- package/src/mcp/transport-stdio.mjs +0 -140
- package/src/mcp/transport-ws.mjs +0 -122
- package/src/mcp.mjs +0 -295
- package/src/memory/code-index.mjs +0 -219
- package/src/memory/code-sync.mjs +0 -413
- package/src/memory/core.mjs +0 -300
- package/src/memory/delete.mjs +0 -236
- package/src/memory/docs.mjs +0 -417
- package/src/memory/file-walk.mjs +0 -109
- package/src/memory/schema.mjs +0 -452
- package/src/memory.mjs +0 -21
- package/src/model-ref.mjs +0 -66
- package/src/model-specs.mjs +0 -179
- package/src/peer-domains.mjs +0 -265
- package/src/peer-instances.mjs +0 -231
- package/src/prompt-overlays.mjs +0 -82
- package/src/prompts/advisor-design.md +0 -41
- package/src/prompts/advisor-round1.md +0 -41
- package/src/prompts/advisor-round2.md +0 -46
- package/src/prompts/advisor-round3.md +0 -42
- package/src/prompts/common.md +0 -115
- package/src/prompts/consult-base.md +0 -19
- package/src/prompts/discipline-engineering.md +0 -217
- package/src/prompts/discipline-normal.md +0 -179
- package/src/prompts/persona-coder.md +0 -21
- package/src/prompts/persona-eng-coder.md +0 -37
- package/src/prompts/persona-eng-designer.md +0 -55
- package/src/prompts/persona-engineering.md +0 -54
- package/src/prompts/persona-explore.md +0 -15
- package/src/prompts/persona-normal.md +0 -27
- package/src/prompts/persona-plan.md +0 -26
- package/src/provider/anthropic.mjs +0 -225
- package/src/provider/core.mjs +0 -476
- package/src/provider/errors.mjs +0 -101
- package/src/provider/google.mjs +0 -257
- package/src/provider/index.mjs +0 -7
- package/src/provider/list-models.mjs +0 -93
- package/src/provider/normalize.mjs +0 -81
- package/src/provider/rate.mjs +0 -108
- package/src/provider/responses.mjs +0 -495
- package/src/provider/retry.mjs +0 -88
- package/src/provider/sse.mjs +0 -264
- package/src/proxy.mjs +0 -261
- package/src/rules.mjs +0 -53
- package/src/session-gc.mjs +0 -214
- package/src/session-guard.mjs +0 -47
- package/src/session-migrate.mjs +0 -48
- package/src/session-rename.mjs +0 -38
- package/src/session-slots.mjs +0 -489
- package/src/session.mjs +0 -475
- package/src/skills.mjs +0 -153
- package/src/token-ttl.mjs +0 -274
- package/src/tools/apply_patch.md +0 -15
- package/src/tools/bash.md +0 -37
- package/src/tools/bash.mjs +0 -268
- package/src/tools/checklist-sync.mjs +0 -181
- package/src/tools/checklist.md +0 -13
- package/src/tools/checklist.mjs +0 -299
- package/src/tools/delete.md +0 -13
- package/src/tools/edit-batch.mjs +0 -191
- package/src/tools/edit-diff.mjs +0 -348
- package/src/tools/edit.md +0 -30
- package/src/tools/execute.md +0 -21
- package/src/tools/execute.mjs +0 -228
- package/src/tools/fetch.md +0 -12
- package/src/tools/file.mjs +0 -469
- package/src/tools/file_ops.md +0 -17
- package/src/tools/get_current_time.md +0 -8
- package/src/tools/git-checkpoint.mjs +0 -143
- package/src/tools/git-ext.mjs +0 -173
- package/src/tools/git.md +0 -54
- package/src/tools/git.mjs +0 -356
- package/src/tools/glob-dialect.mjs +0 -130
- package/src/tools/glob.md +0 -11
- package/src/tools/grep.md +0 -19
- package/src/tools/hashline_edit.md +0 -14
- package/src/tools/index.mjs +0 -36
- package/src/tools/insert_after.md +0 -15
- package/src/tools/lint.md +0 -10
- package/src/tools/linter.mjs +0 -128
- package/src/tools/ls.md +0 -12
- package/src/tools/lsp.md +0 -10
- package/src/tools/lsp.mjs +0 -316
- package/src/tools/ops.mjs +0 -299
- package/src/tools/patch.mjs +0 -282
- package/src/tools/process.md +0 -10
- package/src/tools/question.md +0 -16
- package/src/tools/question.mjs +0 -26
- package/src/tools/read.md +0 -20
- package/src/tools/read_image.md +0 -8
- package/src/tools/repomap.mjs +0 -314
- package/src/tools/search.mjs +0 -236
- package/src/tools/shared.mjs +0 -446
- package/src/tools/tree.md +0 -14
- package/src/tools/tree.mjs +0 -66
- package/src/tools/wait_for.md +0 -22
- package/src/tools/web.mjs +0 -224
- package/src/tools/websearch.md +0 -16
- package/src/tools/write.md +0 -11
- package/src/traces/trace-store.mjs +0 -224
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { isAbsolute, join } from "node:path"
|
|
2
|
-
import { loadConfig } from "
|
|
2
|
+
import { loadConfig } from "@thincoder/core/config.mjs"
|
|
3
3
|
import { teamConfig } from "./make-agent.mjs"
|
|
4
|
-
import { put, search, list } from "
|
|
5
|
-
import { deleteByUid } from "
|
|
4
|
+
import { put, search, list } from "@thincoder/core/memory/core.mjs"
|
|
5
|
+
import { deleteByUid } from "@thincoder/core/memory/delete.mjs"
|
|
6
6
|
|
|
7
7
|
/** thincoder memory <list|search|put|remove> subcommands.
|
|
8
8
|
* opts.dirs: { project, team } layer directories for project/team file deletion (tests inject their own);
|
package/src/cli/permission.mjs
CHANGED
|
@@ -1,48 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
const s = JSON.stringify(toolArgs)
|
|
6
|
-
return s.length > 120 ? s.slice(0, 120) + "..." : s
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/** Permission request key info (per-tool customized), aligned with TUI formatPermission. name may include sub-agent prefix ("coder/bash") — extract basename for matching */
|
|
10
|
-
export function formatPermission(name, args) {
|
|
11
|
-
const cap = (s, n = 1000) => (s.length > n ? `${s.slice(0, n)}…(共 ${s.length} 字符)` : s)
|
|
12
|
-
const base = name.includes("/") ? name.split("/").pop() : name
|
|
13
|
-
if (base === "bash") return cap(args.command ?? "")
|
|
14
|
-
if (base === "write") return `${args.path}(写入 ${(args.content ?? "").length} 字符)\n${cap(args.content ?? "", 1000)}`
|
|
15
|
-
if (base === "edit") {
|
|
16
|
-
const oldLines = cap(args.old_string ?? "", 500).split("\n").map((l) => `- ${l}`).join("\n")
|
|
17
|
-
const newLines = cap(args.new_string ?? "", 500).split("\n").map((l) => `+ ${l}`).join("\n")
|
|
18
|
-
return `${args.path}\n${oldLines}\n ↓\n${newLines}`
|
|
19
|
-
}
|
|
20
|
-
if (base === "delete") return `${args.path}${args.force ? "(force:跟踪文件也删)" : ""}`
|
|
21
|
-
if (base === "subagent") return cap(args.task ?? "", 500)
|
|
22
|
-
if (base === "memory") {
|
|
23
|
-
// §6 action-routed preview: put shows content, batch delete/clear show the gate args
|
|
24
|
-
const action = String(args.action ?? "")
|
|
25
|
-
if (action === "put") return `[${args.type ?? ""}] ${args.title ?? ""}\n${cap(args.content ?? "", 500)}`
|
|
26
|
-
if (action === "delete") return args.id ? `id=${args.id}${args.layer ? ` layer=${args.layer}` : ""}` : `batch delete layer=${args.layer ?? ""} type=${args.type ?? ""} keyword=${args.keyword ?? ""} confirm=${args.confirm}`
|
|
27
|
-
if (action === "clear") return `clear layer=${args.layer ?? ""} confirm=${args.confirm}`
|
|
28
|
-
return cap(summarize(args), 300)
|
|
29
|
-
}
|
|
30
|
-
return cap(summarize(args), 300)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Permission confirmation: interactive y/n on TTY; non-interactive defaults to deny (safety-first) */
|
|
34
|
-
export async function askPermission(name, toolArgs) {
|
|
35
|
-
if (!process.stdin.isTTY) {
|
|
36
|
-
console.error(`\n[deny] ${name} (non-interactive, side-effect tools require a TTY)`)
|
|
37
|
-
return false
|
|
38
|
-
}
|
|
39
|
-
const rl = createInterface({ input: process.stdin, output: process.stderr })
|
|
40
|
-
try {
|
|
41
|
-
const answer = await new Promise((resolve) => {
|
|
42
|
-
rl.question(`\n[allow?] ${name}\n${formatPermission(name, toolArgs)}\n(y/N) `, resolve)
|
|
43
|
-
})
|
|
44
|
-
return answer.trim().toLowerCase() === "y"
|
|
45
|
-
} finally {
|
|
46
|
-
rl.close()
|
|
47
|
-
}
|
|
48
|
-
}
|
|
1
|
+
/** permission.mjs — CLI 转口档(P2 机制层端差批 §2.20):闸语义 + 每工具请示文案单源 = 核
|
|
2
|
+
* `permission.mjs`(#165 落点——本端零第二实现);展示面由核的 `io.ask` 缝承载。
|
|
3
|
+
* 导出面与核逐字对齐,保既有 import 面(`bin/thincoder.mjs` · `distill-command.mjs`)。 */
|
|
4
|
+
export { summarize, formatPermission, askPermission } from "@thincoder/core/permission.mjs"
|
package/src/cli/setup-wizard.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createInterface } from "node:readline"
|
|
2
|
-
import { configPath, writeConfigAtomic, PROVIDER_PRESETS } from "
|
|
2
|
+
import { configPath, writeConfigAtomic, PROVIDER_PRESETS } from "@thincoder/core/config.mjs"
|
|
3
3
|
import { probeChannelModels } from "../tui/model-catalog.mjs"
|
|
4
4
|
|
|
5
5
|
/** First-time setup (TTY chat / distill): ask a few questions to configure a provider, save to disk, return runtime provider. Cancel returns null. */
|
package/src/completions.mjs
CHANGED
|
@@ -21,7 +21,7 @@ export function printCompletion(shell) {
|
|
|
21
21
|
distill) COMPREPLY=( \\$(compgen -W "--yes --layer=" -- "\\$cur") ) ;;
|
|
22
22
|
completion) COMPREPLY=( \\$(compgen -W "bash zsh fish" -- "\\$cur") ) ;;
|
|
23
23
|
*)
|
|
24
|
-
COMPREPLY=( \\$(compgen -W "chat acp memory sync reindex distill upgrade completion session -v --version -h --help" -- "\\$cur") ) ;;
|
|
24
|
+
COMPREPLY=( \\$(compgen -W "tui chat acp memory sync reindex distill upgrade completion session -v --version -h --help" -- "\\$cur") ) ;;
|
|
25
25
|
esac
|
|
26
26
|
}
|
|
27
27
|
complete -F _thincoder thincoder
|
|
@@ -39,6 +39,7 @@ _thincoder() {
|
|
|
39
39
|
case "\\$state" in
|
|
40
40
|
cmd)
|
|
41
41
|
_values 'command' \\
|
|
42
|
+
'tui[Launch the interactive TUI]' \\
|
|
42
43
|
'chat[One-shot agent run with tools]' \\
|
|
43
44
|
'acp[Agent Client Protocol server for IDEs]' \\
|
|
44
45
|
'memory[Manage long-term memory]' \\
|
|
@@ -69,6 +70,7 @@ _thincoder
|
|
|
69
70
|
complete -c thincoder -f
|
|
70
71
|
|
|
71
72
|
# Subcommands
|
|
73
|
+
complete -c thincoder -a tui -d 'Launch the interactive TUI'
|
|
72
74
|
complete -c thincoder -a chat -d 'One-shot agent run with tools'
|
|
73
75
|
complete -c thincoder -a memory -d 'Manage long-term memory'
|
|
74
76
|
complete -c thincoder -a sync -d 'Sync team memory repo'
|
package/src/crash-reports.mjs
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* crash-reports.mjs —
|
|
3
|
-
*
|
|
2
|
+
* crash-reports.mjs — 崩溃捕获与取证(docs/design/CRASH-REPORTS.md——F1/F2 为 R25 与
|
|
3
|
+
* TUI-STDERR-CAPTURE 存量迁移;F3 近堆上限堆快照 = TUI-OOM-FORENSICS 批新增)。
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* - prepareCrashReporting()(F-R25b
|
|
5
|
+
* 能力(与设计逐项对应):
|
|
6
|
+
* - prepareCrashReporting()(F-R25b + F3①):入口最前调用——mkdir 预建 ~/.thincoder/crash-reports/
|
|
7
7
|
* + process.report 代码内启用(reportOnFatalError + directory)——V8 OOM/原生 fatal
|
|
8
8
|
* 自动写 report.*.json。实现批实测(2026-09-07):目录缺失时 Node 对 fatal 静默不写
|
|
9
9
|
* 报告——预建是必要动作而非"零成本保险"。
|
|
10
|
+
* F3①:同点武装近堆上限堆快照(v8.setHeapSnapshotNearHeapLimit——对象级证据,回答
|
|
11
|
+
* "谁在持内存";全路径单源,默认开、THINCODER_HEAP_SNAPSHOT 可关——F3②)。
|
|
10
12
|
* - writeCrashRecord()(F-R25a):JS 异常钩子同步落盘 crash-{ts}-{pid}.json(ts = epoch ms
|
|
11
13
|
* UTC + pid——跨进程同 ms 防覆盖——复审 #3)——权限 0600(config.json 先例)。
|
|
12
14
|
* - recentCrashHint()(F-R25c):启动扫描 24h 窗内记录(两类文件模式定死——评审 #8:
|
|
@@ -23,7 +25,12 @@
|
|
|
23
25
|
*/
|
|
24
26
|
import { chmodSync, existsSync, mkdirSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs"
|
|
25
27
|
import { join } from "node:path"
|
|
26
|
-
import
|
|
28
|
+
// F3① 命名空间 import:API 缺失(旧 Node)降级为调用期异常并被武装 try 吞掉——不做 import 期硬失败
|
|
29
|
+
import * as v8 from "node:v8"
|
|
30
|
+
import { configDir } from "@thincoder/core/config.mjs"
|
|
31
|
+
|
|
32
|
+
/** F3② 关值集合(trim + 大小写不敏感);其余取值(未设 / 空串 / 未知串)默认开——fail-open 向取证。 */
|
|
33
|
+
const HEAP_SNAPSHOT_OFF_VALUES = new Set(["0", "false", "off", "no"])
|
|
27
34
|
|
|
28
35
|
/** crash-reports 运行时目录(~/.thincoder/crash-reports——非仓内——写时自清理)。 */
|
|
29
36
|
export function crashReportsDir() {
|
|
@@ -32,13 +39,15 @@ export function crashReportsDir() {
|
|
|
32
39
|
|
|
33
40
|
/** 记录文件模式(评审 #8 定死):crash-*.json = 自写(F-R25a);report.*.json = Node
|
|
34
41
|
* fatal(默认命名 report.YYYYMMDD.HHMMSS.<pid>.<seq>.json——2026-09-07 实现批实测)。
|
|
35
|
-
* purge 另含 tui-stderr-*.log(TUI-STDERR-CAPTURE F-2——30
|
|
36
|
-
*
|
|
42
|
+
* purge 另含 tui-stderr-*.log(TUI-STDERR-CAPTURE F-2——30 天同族淘汰)与
|
|
43
|
+
* Heap.*.heapsnapshot(CRASH-REPORTS F3③——快照 GB 级、TUI 落点须自动兜底)——但
|
|
44
|
+
* recentCrashHint 两类均不计(N3):tui-stderr 每次 TUI 启动都生成(正常退出也留档)、
|
|
45
|
+
* 快照非「异常终止」证据类——计入即正常会话误报。 */
|
|
37
46
|
function isCrashRecordName(name) {
|
|
38
47
|
return /^crash-.+\.json$/.test(name) || /^report\..+\.json$/.test(name)
|
|
39
48
|
}
|
|
40
49
|
function isPurgeRecordName(name) {
|
|
41
|
-
return isCrashRecordName(name) || /^tui-stderr-.+\.log$/.test(name)
|
|
50
|
+
return isCrashRecordName(name) || /^tui-stderr-.+\.log$/.test(name) || /^Heap\..+\.heapsnapshot$/.test(name)
|
|
42
51
|
}
|
|
43
52
|
|
|
44
53
|
/** >30 天淘汰(评审 #6)——写时自清理;搭车点 = 写 / 入口 mkdir / 启动扫描(复审 #4)。 */
|
|
@@ -54,18 +63,31 @@ function purgeOldCrashReports(dir) {
|
|
|
54
63
|
}
|
|
55
64
|
}
|
|
56
65
|
|
|
66
|
+
/** F3② 武装判定(单点——包装器不判 env,防两处判定漂移):关值集合命中 → 不武装;其余 → 武装。 */
|
|
67
|
+
function heapSnapshotEnabled(env) {
|
|
68
|
+
return !HEAP_SNAPSHOT_OFF_VALUES.has(String(env?.THINCODER_HEAP_SNAPSHOT ?? "").trim().toLowerCase())
|
|
69
|
+
}
|
|
70
|
+
|
|
57
71
|
/**
|
|
58
72
|
* F-R25b:入口最前调用(一切重活前——缩编程期窗口)——预建目录 + process.report 启用。
|
|
59
73
|
* 任何失败不阻断启动(尽力面——record 路径自带降级)。返回目录路径。
|
|
74
|
+
* F3①:同点武装近堆上限堆快照(默认开——全路径单源;环境开关见 heapSnapshotEnabled)。
|
|
75
|
+
* @param {object} [opts] 注入缝(默认参数保 bin 入口调用点零改)
|
|
76
|
+
* @param {string} [opts.dir] 目录注入(测试用——同时服务 mkdir / report / purge / 返回)
|
|
77
|
+
* @param {object} [opts.env] env 注入(测试用)——仅服务 F3② 判定
|
|
78
|
+
* @param {(n: number) => void} [opts.armHeapSnapshot] 武装实现注入(测试用)——默认真实 node:v8 API
|
|
60
79
|
*/
|
|
61
|
-
export function prepareCrashReporting() {
|
|
62
|
-
const dir = crashReportsDir()
|
|
80
|
+
export function prepareCrashReporting({ dir = crashReportsDir(), env = process.env, armHeapSnapshot = v8.setHeapSnapshotNearHeapLimit } = {}) {
|
|
63
81
|
try {
|
|
64
82
|
mkdirSync(dir, { recursive: true })
|
|
65
83
|
// 代码内启用:shebang 入口无法携带启动参数(env 单参数限制 + execArgv 仅子进程——评审 #1)
|
|
66
84
|
process.report.directory = dir
|
|
67
85
|
process.report.reportOnFatalError = true
|
|
68
86
|
} catch { /* mkdir/启用失败不阻断启动——尽力面 */ }
|
|
87
|
+
// F3① 武装(独立 try——失败不阻断启动、不影响上方 F1 既有步骤;尽力面静默)
|
|
88
|
+
if (heapSnapshotEnabled(env)) {
|
|
89
|
+
try { armHeapSnapshot(1) } catch { /* 武装失败不阻断——N1 */ }
|
|
90
|
+
}
|
|
69
91
|
purgeOldCrashReports(dir) // F-R25b 入口 mkdir 搭车清理(复审 #4——纯 fatal 序列也触发)
|
|
70
92
|
return dir
|
|
71
93
|
}
|
package/src/distill.mjs
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* Absolutely no automatic storage at session end.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { chat } from "
|
|
8
|
-
import { loadConfig } from "
|
|
9
|
-
import { put, putMarkdown } from "
|
|
10
|
-
import { commitAndPush } from "
|
|
7
|
+
import { chat } from "@thincoder/core/provider/index.mjs"
|
|
8
|
+
import { loadConfig } from "@thincoder/core/config.mjs"
|
|
9
|
+
import { put, putMarkdown } from "@thincoder/core/memory.mjs"
|
|
10
|
+
import { commitAndPush } from "@thincoder/core/git/gitmem.mjs"
|
|
11
11
|
|
|
12
12
|
const DISTILL_PROMPT = `You are a knowledge extractor. Read the following agent work session log and extract knowledge worth remembering across sessions.
|
|
13
13
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* heap-watch.mjs — 堆遥测/看门狗(TUI-OOM-ROOTCAUSE 批——CRASH-REPORTS.md §8)。
|
|
3
|
+
*
|
|
4
|
+
* 事故会话堆爬升 ~19 分钟无任何事前信号(「炸了才知道」);本模块补「事前预警」:
|
|
5
|
+
* 60s 采样 + 堆上限比例双档(70% / 85%)边缘触发 → stderr 行 + TUI 行(订阅制)+
|
|
6
|
+
* 事件日志。**只看不治**(不强制 GC、不阻断、不触发快照——D-HW2)。
|
|
7
|
+
*
|
|
8
|
+
* 关口(§8.3):阈值 = `heapUsed / v8.getHeapStatistics().heap_size_limit`(比例——可移植,
|
|
9
|
+
* 无绝对 MB 常量);双档边缘触发(每档一次/进程——不重复刷屏);零常态开销
|
|
10
|
+
* (60s 一次 memoryUsage(),无输出——D-HW5)。
|
|
11
|
+
*
|
|
12
|
+
* 开关单点:`THINCODER_HEAP_WATCH`——关值集合 {`0`,`false`,`off`,`no`}(trim + 大小写
|
|
13
|
+
* 不敏感)→ 不启动;未设/空串/其他值 → 启动(默认开——与 F3② 同约定)。
|
|
14
|
+
*
|
|
15
|
+
* 注入缝(N5 可测):`sample` / `heapLimit` / `timer`——测试以假实现 + `checkNow()`
|
|
16
|
+
* 直驱(零等待);失败面全吞(采样抛错不阻断——样本失败静默跳过本次)。
|
|
17
|
+
*/
|
|
18
|
+
import { getHeapStatistics } from "node:v8"
|
|
19
|
+
import { logEvent } from "@thincoder/core/log.mjs"
|
|
20
|
+
|
|
21
|
+
/** 关值集合(单点判定——§3.2 同约定复用)。 */
|
|
22
|
+
export const HEAP_WATCH_OFF = new Set(["0", "false", "off", "no"])
|
|
23
|
+
|
|
24
|
+
/** 开关判定(单点——grep 面:仅本模块读 env.THINCODER_HEAP_WATCH)。 */
|
|
25
|
+
export function heapWatchEnabled(env = process.env) {
|
|
26
|
+
const v = String(env.THINCODER_HEAP_WATCH ?? "").trim().toLowerCase()
|
|
27
|
+
return !HEAP_WATCH_OFF.has(v)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 订阅表(TUI 行接线——§8.3 订阅制;模块级:武装在 bin、订阅在 startTUI)
|
|
31
|
+
const _subscribers = new Set()
|
|
32
|
+
|
|
33
|
+
/** 订阅预警行(返回退订函数)。 */
|
|
34
|
+
export function onHeapWarn(cb) {
|
|
35
|
+
if (typeof cb !== "function") return () => {}
|
|
36
|
+
_subscribers.add(cb)
|
|
37
|
+
return () => _subscribers.delete(cb)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** 预警行格式化(逐字——§8.3;U/L 一位小数、P 取整)。 */
|
|
41
|
+
export function heapWarnLine(used, limit, ratio) {
|
|
42
|
+
const gb = (n) => (n / 1024 / 1024 / 1024).toFixed(1)
|
|
43
|
+
return `[heap] warning: heapUsed ${gb(used)} GB / ${gb(limit)} GB heap limit (${Math.round(ratio * 100)}%) — long session; consider /new to reset context`
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 启动看门狗(返回 `{ stop(), checkNow() }`——checkNow 直驱一次采样并返回本轮新发预警行)。
|
|
48
|
+
* 关值(§8.3)→ 不注册定时器(返回惰性句柄)。
|
|
49
|
+
*/
|
|
50
|
+
export function startHeapWatch({
|
|
51
|
+
intervalMs = 60_000,
|
|
52
|
+
ratios = [0.7, 0.85],
|
|
53
|
+
sample = process.memoryUsage,
|
|
54
|
+
heapLimit = () => getHeapStatistics().heap_size_limit,
|
|
55
|
+
env = process.env,
|
|
56
|
+
timer = setInterval,
|
|
57
|
+
} = {}) {
|
|
58
|
+
const warned = new Set()
|
|
59
|
+
const checkNow = () => {
|
|
60
|
+
const out = []
|
|
61
|
+
try {
|
|
62
|
+
const used = sample()?.heapUsed ?? 0
|
|
63
|
+
const limit = heapLimit()
|
|
64
|
+
if (!(limit > 0)) return out
|
|
65
|
+
const ratio = used / limit
|
|
66
|
+
for (const r of ratios) {
|
|
67
|
+
if (ratio >= r && !warned.has(r)) {
|
|
68
|
+
warned.add(r) // 每档一次/进程
|
|
69
|
+
const line = heapWarnLine(used, limit, ratio)
|
|
70
|
+
out.push(line)
|
|
71
|
+
try { console.error(line) } catch { /* 输出失败不阻断 */ } // stderr(headless 主面——§8.3)
|
|
72
|
+
for (const cb of _subscribers) { try { cb(line) } catch { /* 订阅者失败不阻断 */ } }
|
|
73
|
+
try { logEvent("heap-warn", { kind: "heap-warn", used, limit, ratio: Number(ratio.toFixed(3)) }) } catch { /* 日志失败不阻断(D-HW4) */ }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
} catch { /* 失败面全吞(N5——采样/上限抛错不阻断、状态保持) */ }
|
|
77
|
+
return out
|
|
78
|
+
}
|
|
79
|
+
if (!heapWatchEnabled(env)) return { stop() {}, checkNow }
|
|
80
|
+
const handle = timer(checkNow, intervalMs)
|
|
81
|
+
try { handle?.unref?.() } catch { /* unref 失败不阻断(一次性命令自然退出) */ }
|
|
82
|
+
return {
|
|
83
|
+
stop() {
|
|
84
|
+
try { clearInterval(handle) } catch { /* 已停/不可清——静默(尽力面) */ }
|
|
85
|
+
},
|
|
86
|
+
checkNow,
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* prompt-injections.mjs — the CLI end's prompt-anchor value table.
|
|
3
|
+
*
|
|
4
|
+
* The core's single resolution face (`@thincoder/core/prompt-files.mjs`) owns the anchors
|
|
5
|
+
* (`{{inject:<name>}}` in `thincoder-core/prompts/` + `thincoder-core/tool-docs/`); this end
|
|
6
|
+
* supplies the values. The table is registered ONCE at the process entry (`bin/thincoder.mjs`)
|
|
7
|
+
* — before any assembly — via `configurePromptInjections`.
|
|
8
|
+
*
|
|
9
|
+
* 2026-09-17 消端差:提示词面(persona / discipline / common)注入锚全消——导航注记删
|
|
10
|
+
* (正文自足)、端特有段通用化并入核内正文、文档地图路径统一写死 `docs/README.md`。
|
|
11
|
+
* 剩 = 工具面 2 锚(bash-terminal-face / question-ui-face——真实工具差异,待工具面 review 处置)。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** 工具面 2 锚(锚名 → 本端取值)。 */
|
|
15
|
+
export const CLI_PROMPT_INJECTIONS = {
|
|
16
|
+
// bash 宿主终端面——CLI 无 terminal 参数(工具面 review 再统一)
|
|
17
|
+
"bash-terminal-face": "",
|
|
18
|
+
// question 工具可用性行(核内正文该行已移出正文、锚原位替换——本端填回 CLI 措辞)
|
|
19
|
+
"question-ui-face": "- Availability: this tool needs an interactive UI — in contexts without one (headless runs, subagent children) it returns an error instead of asking; put the question in your reply text instead.",
|
|
20
|
+
}
|
package/src/tui/agent-turn.mjs
CHANGED
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
* 递归回本文件(函数级静态环——模块求值期无顶层调用,环安全——session-slots ↔
|
|
15
15
|
* session.mjs 同款先例)。
|
|
16
16
|
*/
|
|
17
|
-
import { runAgent, ContinueError } from "
|
|
18
|
-
import { saveSession } from "
|
|
17
|
+
import { runAgent, ContinueError } from "@thincoder/core/agent.mjs"
|
|
18
|
+
import { saveSession } from "@thincoder/core/session.mjs"
|
|
19
19
|
import { ansi, C } from "./ansi.mjs"
|
|
20
20
|
import { buildToolCallbacks, sweepToolBlocks } from "./tool-events.mjs"
|
|
21
21
|
import { freezeAllSubTasks } from "./subagent-blocks.mjs" // freezeReclaimDigestedBlocks 随 §17 段迁 suspension-drive.mjs
|
|
22
|
-
import { ensureSessionTitle } from "
|
|
23
|
-
import { logEvent, errText } from "
|
|
22
|
+
import { ensureSessionTitle } from "@thincoder/core/generate-title.mjs"
|
|
23
|
+
import { logEvent, errText } from "@thincoder/core/log.mjs"
|
|
24
24
|
import { suspensionSession, poolLive } from "./suspension-drive.mjs"
|
|
25
25
|
|
|
26
26
|
/** Exit-flush bound for the async end-of-run distillation (SEND-STALL-DISTILL §2.5):
|
|
@@ -28,6 +28,10 @@ import { suspensionSession, poolLive } from "./suspension-drive.mjs"
|
|
|
28
28
|
* never let shutdown hang on the background summary call. */
|
|
29
29
|
const DISTILL_FLUSH_TIMEOUT_MS = 5000
|
|
30
30
|
|
|
31
|
+
/** Provider 失败面 URL 脱敏(X8——判据与 VSC `panel-turn-loop.mjs:153` 同式;两端各自持有
|
|
32
|
+
* 字面 = 双写登记——单源化须动 VSC/核写域,出本批射程,见批档 §2.3)。 */
|
|
33
|
+
const PROVIDER_URL_RE = /https?:\/\/[^\s,)"]+/g
|
|
34
|
+
|
|
31
35
|
/**
|
|
32
36
|
* 第 33 批(TUI §14.3(d)——纯函数,供测试直驱):回合链尾「需要用户」谓词——agent 已停
|
|
33
37
|
* 且无自动续跑 ⇒ 置 attention 位。排除项语义(F13):`skipSession`(digest / 会话内回合
|
|
@@ -45,6 +49,8 @@ export function userNeededAtTurnEnd(state, agent, skipSession) {
|
|
|
45
49
|
* user/auto;result ok/stopped/error)。内层经 _logOutcome 载具回传终止原因(中止/
|
|
46
50
|
* turn-cap 拒绝/错误 vs 正常完成)——嵌套回合(队列递归/挂起会话内 digest 轮)各自
|
|
47
51
|
* 新开载具(每次包装调用独立),互不串扰。err:internal = 逃出内层的未分类异常。
|
|
52
|
+
* @returns {Promise<"ok"|"stopped"|"error">} 本回合终态(同一载具外化——X9 消化轮
|
|
53
|
+
* 收尾可见行的 ok/aborted 判据;VSC digest end ok 旗标同口径)。
|
|
48
54
|
*/
|
|
49
55
|
export async function runAgentTurn(ctx, text, opts = {}) {
|
|
50
56
|
const kind = opts?.autoTurn ? "auto" : "user"
|
|
@@ -55,8 +61,8 @@ export async function runAgentTurn(ctx, text, opts = {}) {
|
|
|
55
61
|
const t0 = Date.now()
|
|
56
62
|
logEvent("turn:start", { kind })
|
|
57
63
|
try {
|
|
58
|
-
|
|
59
|
-
return result
|
|
64
|
+
await runAgentTurnInner(ctx, text, runOpts)
|
|
65
|
+
return carrier.result ?? "ok"
|
|
60
66
|
} catch (e) {
|
|
61
67
|
carrier.result = "error"
|
|
62
68
|
logEvent("err:internal", { msg: errText(e, 200), where: "runAgentTurn" })
|
|
@@ -68,7 +74,7 @@ export async function runAgentTurn(ctx, text, opts = {}) {
|
|
|
68
74
|
|
|
69
75
|
/** runAgentTurn 本体(LOGGING 包装之外——见上方包装器)。 */
|
|
70
76
|
async function runAgentTurnInner(ctx, text, opts) {
|
|
71
|
-
const { autoTurn = false, skipSession = false } = opts ?? {}
|
|
77
|
+
const { autoTurn = false, skipSession = false, upstreamTurn = false } = opts ?? {} // upstreamTurn:上行 ask 唤醒轮旗标(§6.27.12.5 D/I——透传核 runAgent,仅供域文本选择)
|
|
72
78
|
const { agent, state, pushLine, pushLabel, render, scheduleRender, ensureAssistantLabel, askPermission, askBatchPermission, askQuestion, handleSlash } = ctx
|
|
73
79
|
// 可注入覆盖(测试用);默认走真实实现
|
|
74
80
|
const runAgentImpl = ctx.runAgent ?? runAgent
|
|
@@ -144,7 +150,7 @@ async function runAgentTurnInner(ctx, text, opts) {
|
|
|
144
150
|
try {
|
|
145
151
|
for (let resume = false; ; resume = true) {
|
|
146
152
|
try {
|
|
147
|
-
await runAgentImpl(agent, text, callbacks, { signal: state.controller.signal, resume, autoTurn, suspDriven: true })
|
|
153
|
+
await runAgentImpl(agent, text, callbacks, { signal: state.controller.signal, resume, autoTurn, upstreamTurn, suspDriven: true })
|
|
148
154
|
flushStream()
|
|
149
155
|
break // Normal completion, exit loop
|
|
150
156
|
} catch (error) {
|
|
@@ -216,7 +222,32 @@ async function runAgentTurnInner(ctx, text, opts) {
|
|
|
216
222
|
state.controller = makeController()
|
|
217
223
|
continue
|
|
218
224
|
}
|
|
219
|
-
|
|
225
|
+
// X8(2026-09-20 端差·显示面消差批):provider 失败面 = 友好首行(判据同 VSC
|
|
226
|
+
// panel-turn-loop.mjs:152-153:原文首行 + URL 脱敏)+ 诊断两行(Provider/Model)+ Retry
|
|
227
|
+
// 询问(复用 permission 机制——同意 ⇒ 重建 controller + resume 重入,与 ContinueError
|
|
228
|
+
// 同法;拒绝 ⇒ 现状 break,[error] 行保留)。
|
|
229
|
+
const rawMsg = error?.message || String(error)
|
|
230
|
+
const errLine = rawMsg.split("\n")[0].replace(PROVIDER_URL_RE, "[endpoint]")
|
|
231
|
+
// X8 跟进②(用户 2026-09-20 裁定):原文余行 = **log-only**(表面零膨胀——不增 UI 行)。
|
|
232
|
+
// `head` 与 `err` **同过脱敏管道**(PROVIDER_URL_RE ⇒ `[endpoint]`——LOGGING.md §6.3
|
|
233
|
+
// D-LG4「URL 不入事件」)**先脱敏后截断**(防截断留 URL 残段),自限 ≤200(`err` 200 +
|
|
234
|
+
// `head` 200 + 固定字段 < 512——log.mjs NF-L2);无余行 ⇒ 传 `null`(勿传空串)。
|
|
235
|
+
const restRaw = rawMsg.split("\n").slice(1).map((l) => l.trim()).filter(Boolean).join(" / ")
|
|
236
|
+
const restSan = restRaw.replace(PROVIDER_URL_RE, "[endpoint]")
|
|
237
|
+
logEvent("err:provider", { err: errLine, head: restSan ? (restSan.length > 200 ? restSan.slice(0, 199) + "…" : restSan) : null })
|
|
238
|
+
pushLine(`[error] ${errLine}`, C.error)
|
|
239
|
+
pushLine(`→ Provider: ${agent.provider?.baseURL}`, C.dim)
|
|
240
|
+
pushLine(`→ Model: ${agent.provider?.model}`, C.dim)
|
|
241
|
+
const willRetry = await new Promise((resolve) => {
|
|
242
|
+
state.permission = { name: "retry", args: { message: errLine }, resolve }
|
|
243
|
+
state.status = "Retry after provider error?"
|
|
244
|
+
render()
|
|
245
|
+
})
|
|
246
|
+
state.permission = null
|
|
247
|
+
if (willRetry) {
|
|
248
|
+
state.controller = makeController()
|
|
249
|
+
continue
|
|
250
|
+
}
|
|
220
251
|
if (opts?._logOutcome) opts._logOutcome.result = "error"
|
|
221
252
|
break
|
|
222
253
|
}
|
package/src/tui/cmd-advisor.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* ctx: { agent, showPicker, pushLine, pushLabel, persistRaw } */
|
|
4
4
|
import { readFileSync } from "node:fs"
|
|
5
5
|
import { ansi, C } from "./ansi.mjs"
|
|
6
|
-
import { activeSlot, slotPath } from "
|
|
6
|
+
import { activeSlot, slotPath } from "@thincoder/core/session.mjs"
|
|
7
7
|
import { writeSessionFile } from "./cmd-eng.mjs"
|
|
8
8
|
|
|
9
9
|
export async function handleAdvisorCommand(ctx) {
|
|
@@ -98,7 +98,7 @@ export async function handleAdvisorCommand(ctx) {
|
|
|
98
98
|
pushLabel("❯ Advisor", ansi.bold + C.tool)
|
|
99
99
|
pushLine("Thinking: using main model settings", C.tool)
|
|
100
100
|
} else if (c.action === "think_on") {
|
|
101
|
-
const { specForModel } = await import("
|
|
101
|
+
const { specForModel } = await import("@thincoder/core/config.mjs")
|
|
102
102
|
const spec = specForModel(getEffectiveModel(agent, cfg))
|
|
103
103
|
cfg.thinking = { type: spec.thinkEnabledValue ?? "enabled" }
|
|
104
104
|
if (spec.thinkApi === "effort") delete cfg.thinking
|
|
@@ -106,7 +106,7 @@ export async function handleAdvisorCommand(ctx) {
|
|
|
106
106
|
pushLabel("❯ Advisor", ansi.bold + C.tool)
|
|
107
107
|
pushLine(`Thinking: ON`, C.tool)
|
|
108
108
|
} else if (c.action === "think_off") {
|
|
109
|
-
const { specForModel } = await import("
|
|
109
|
+
const { specForModel } = await import("@thincoder/core/config.mjs")
|
|
110
110
|
const spec = specForModel(getEffectiveModel(agent, cfg))
|
|
111
111
|
const isCustomThink = (spec.thinkEnabledValue ?? "enabled") !== "enabled"
|
|
112
112
|
cfg.thinking = isCustomThink ? null : { type: "disabled" }
|
|
@@ -179,7 +179,7 @@ function getEffectiveModel(agent, cfg) {
|
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
async function fetchAdvisorModels(agent) {
|
|
182
|
-
const { listModels } = await import("
|
|
182
|
+
const { listModels } = await import("@thincoder/core/provider/index.mjs")
|
|
183
183
|
const result = new Map()
|
|
184
184
|
await Promise.all((agent.providers || []).map(async (p) => {
|
|
185
185
|
try {
|
|
@@ -223,7 +223,7 @@ function buildModelEntries(agent, cfg, cache) {
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
async function buildThinkingEntries(agent, cfg) {
|
|
226
|
-
const { specForModel } = await import("
|
|
226
|
+
const { specForModel } = await import("@thincoder/core/config.mjs")
|
|
227
227
|
const providerForDefaults = cfg.provider
|
|
228
228
|
? agent.providers?.find(p => p.name === cfg.provider) || agent.provider
|
|
229
229
|
: agent.provider
|
package/src/tui/cmd-clear.mjs
CHANGED
|
@@ -9,12 +9,14 @@ export async function handleClearCommand(ctx) {
|
|
|
9
9
|
], { defaultIndex: 1 })
|
|
10
10
|
if (e?.action === "yes") {
|
|
11
11
|
state.lines = []
|
|
12
|
+
state._linesChars = 0 // TUI-OOM-ROOTCAUSE(§15.3.2):行集清空 → 字符账同步归零
|
|
12
13
|
state.streaming = ""
|
|
13
14
|
render()
|
|
14
15
|
}
|
|
15
16
|
return
|
|
16
17
|
}
|
|
17
18
|
state.lines = []
|
|
19
|
+
state._linesChars = 0 // TUI-OOM-ROOTCAUSE(§15.3.2):行集清空 → 字符账同步归零
|
|
18
20
|
state.streaming = ""
|
|
19
21
|
render()
|
|
20
22
|
}
|
package/src/tui/cmd-config.mjs
CHANGED
|
@@ -23,7 +23,7 @@ export function poolCur(pl, defaults) {
|
|
|
23
23
|
/** /config command: view and set agent/embedding/proxy config. */
|
|
24
24
|
export async function handleConfigCommand(ctx, args = []) {
|
|
25
25
|
const { agent, pushLine, pushLabel, showPicker, askQuestion, persistRaw, maskKey, pickModelForSlot } = ctx
|
|
26
|
-
const { configPath, DEFAULTS } = await import("
|
|
26
|
+
const { configPath, DEFAULTS } = await import("@thincoder/core/config.mjs")
|
|
27
27
|
let ac = agent.config?.agent ?? {}
|
|
28
28
|
let ec = agent.config?.embedding ?? {}
|
|
29
29
|
let tc = agent.config?.traces ?? {}
|
|
@@ -43,7 +43,7 @@ export async function handleConfigCommand(ctx, args = []) {
|
|
|
43
43
|
agent.config.embedding ??= {}
|
|
44
44
|
agent.config.embedding.apiKey = embKey
|
|
45
45
|
if (agent.memory) {
|
|
46
|
-
const { createEmbedder } = await import("
|
|
46
|
+
const { createEmbedder } = await import("@thincoder/core/embedding.mjs")
|
|
47
47
|
agent.memory.embedder = createEmbedder(agent.config.embedding)
|
|
48
48
|
}
|
|
49
49
|
pushLabel("❯ Config", ansi.bold + C.tool)
|
|
@@ -55,8 +55,8 @@ export async function handleConfigCommand(ctx, args = []) {
|
|
|
55
55
|
* MODEL-MERGE-SESSION 三支改写(cfg 无 active*):运行时由「会话槽复合仍在 providers 中 →
|
|
56
56
|
* 保持槽值」或「config defaultModel(新会话起点)」决定——不再有渠道默认字段可回退。 */
|
|
57
57
|
async function reloadConfig() {
|
|
58
|
-
const { loadConfig } = await import("
|
|
59
|
-
const { injectProxy } = await import("
|
|
58
|
+
const { loadConfig } = await import("@thincoder/core/config.mjs")
|
|
59
|
+
const { injectProxy } = await import("@thincoder/core/proxy.mjs")
|
|
60
60
|
const cfg = loadConfig()
|
|
61
61
|
injectProxy(cfg.providersList, cfg)
|
|
62
62
|
const sessionName = agent.activeProvider
|
|
@@ -82,7 +82,7 @@ export async function handleConfigCommand(ctx, args = []) {
|
|
|
82
82
|
// 运行时重建后同步注入结果(cfg.provider 系 loadConfig 内建——injectProxy 后建)
|
|
83
83
|
agent.provider.proxyUri = cfg.providersList.find((p) => p.name === agent.activeProvider)?.proxyUri
|
|
84
84
|
if (agent.config?.agent?.compactThresholdAuto) {
|
|
85
|
-
const { resolveCompactThreshold } = await import("
|
|
85
|
+
const { resolveCompactThreshold } = await import("@thincoder/core/config.mjs")
|
|
86
86
|
agent.config.agent.compactThreshold = resolveCompactThreshold(null, agent.provider).value
|
|
87
87
|
}
|
|
88
88
|
}
|
|
@@ -136,8 +136,8 @@ export async function handleConfigCommand(ctx, args = []) {
|
|
|
136
136
|
pushLabel("❯ Config", ansi.bold + C.tool)
|
|
137
137
|
pushLine(`proxy.${key} = ${!pc[key] ? "on" : "off"}`, C.tool)
|
|
138
138
|
} else if (c.action === "test") {
|
|
139
|
-
const { proxyFetch, resolveWebProxy } = await import("
|
|
140
|
-
const { UA } = await import("
|
|
139
|
+
const { proxyFetch, resolveWebProxy } = await import("@thincoder/core/proxy.mjs")
|
|
140
|
+
const { UA } = await import("@thincoder/core/tools/web.mjs")
|
|
141
141
|
const uri = resolveWebProxy({ agent })
|
|
142
142
|
pushLabel("❯ Config", ansi.bold + C.tool)
|
|
143
143
|
pushLine(`Testing ${uri ? `via proxy ${uri}` : "direct (no proxy)"}...`, C.dim)
|
|
@@ -170,7 +170,7 @@ export async function handleConfigCommand(ctx, args = []) {
|
|
|
170
170
|
|
|
171
171
|
/** 会诊/飞刀候选池子菜单:列出 / 添加 / 编辑 effort / 删除 consultModels 条目。 */
|
|
172
172
|
async function pickEffort(current, model) {
|
|
173
|
-
const { specForModel } = await import("
|
|
173
|
+
const { specForModel } = await import("@thincoder/core/config.mjs")
|
|
174
174
|
const enumList = model ? specForModel(model).reasoningEffortEnum : null
|
|
175
175
|
// The model's reasoning-effort enum is HETEROGENEOUS across providers (deepseek:
|
|
176
176
|
// low/high/max; qwen3.8-max: xhigh/medium/low; kimi: 7 levels). A fixed
|
package/src/tui/cmd-eng.mjs
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/** /eng command: toggle engineering mode.
|
|
2
|
-
* No prerequisites (FR11
|
|
3
|
-
* no
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* No retired-concept prerequisites (FR11 收正口径 — `docs/core/requirements/PORTABILITY.md:62`):
|
|
3
|
+
* no methodology file, no docs tree. The **repo-root anchor(git)= E2 既有入口前提**:
|
|
4
|
+
* 非锚 cwd 上 ON = fail-closed 拒翻 + 明示原因、模式保持 OFF(#41 —
|
|
5
|
+
* `docs/core/design/MANIFEST.md` §2.8 F2/F3;判定面 = 入口决策树单源)。
|
|
6
|
+
* ctx: { agent, pushLine, pushLabel } */
|
|
6
7
|
import { readFileSync, writeFileSync, mkdirSync, renameSync, unlinkSync } from "node:fs"
|
|
7
8
|
import { dirname } from "node:path"
|
|
8
9
|
import { ansi, C } from "./ansi.mjs"
|
|
9
|
-
import { activeSlot, slotPath } from "
|
|
10
|
-
import { purgeExpiredDesignTokens } from "
|
|
10
|
+
import { activeSlot, slotPath } from "@thincoder/core/session.mjs"
|
|
11
|
+
import { purgeExpiredDesignTokens } from "@thincoder/core/token-ttl.mjs"
|
|
12
|
+
import { resolveEngineeringManifest } from "@thincoder/core/manifest.mjs"
|
|
11
13
|
|
|
12
|
-
import { ENG_OFF_REMINDER } from "
|
|
14
|
+
import { ENG_OFF_REMINDER } from "@thincoder/core/agent.mjs"
|
|
13
15
|
|
|
14
16
|
/** Atomic slot write (same shape as session.mjs writeSessionFile — kept local to avoid a
|
|
15
17
|
* private-import; cmd-advisor's guard toggle shares this helper). */
|
|
@@ -29,8 +31,22 @@ export async function handleEngCommand(ctx) {
|
|
|
29
31
|
const { agent, pushLine, pushLabel } = ctx
|
|
30
32
|
agent.config.agent ??= {}
|
|
31
33
|
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
+
// No retired-concept prerequisite check (FR11 收正口径;退场原因见档头注)。ON 方向的门
|
|
35
|
+
// = 入口决策树(下方);OFF 方向恒放行(F2 表 OFF 行——零 manifest I/O)。
|
|
36
|
+
//
|
|
37
|
+
// #41 先判后翻(MANIFEST.md §2.8 F2/F3 · KD-M1-21):**ON 方向**先走入口决策树单源
|
|
38
|
+
// (resolveEngineeringManifest——KD-M1-20),判据通过才写态;OFF 方向零改(F2 表 OFF 行)。
|
|
39
|
+
// 拒 ⇒ warn 行 + **零标签**(标签 = 成功回显——拒时零标签防假成功)+ 零副作用
|
|
40
|
+
// (模式未翻 / token 未清 / `_advisorRuns` 未重置 / 提醒未入列 / 槽未写)。
|
|
41
|
+
// 准 ⇒ 缺档格就地建档(writer:'main')+ `agent.manifest` ← 结果(相位行当回合起活)。
|
|
42
|
+
if (!agent.config.agent.engineering) {
|
|
43
|
+
const r = resolveEngineeringManifest(agent.cwd ?? process.cwd(), { writer: "main" })
|
|
44
|
+
if (!r.ok) {
|
|
45
|
+
pushLine(`Engineering mode not enabled — ${r.message} (mode unchanged)`, C.warn)
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
agent.manifest = r.manifest
|
|
49
|
+
}
|
|
34
50
|
agent.config.agent.engineering = !agent.config.agent.engineering
|
|
35
51
|
// §11.2 D-24b: per-review instances die with the mode (fresh convergence cycles
|
|
36
52
|
// on the next toggle).
|