@thincoder/core 0.10.3 → 0.10.5
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 +62 -0
- package/README.md +1 -1
- package/advisor/citations.mjs +29 -3
- package/advisor/compaction.mjs +31 -7
- package/advisor/history.mjs +8 -6
- package/advisor/loop.mjs +29 -27
- package/advisor/messages.mjs +2 -1
- package/advisor/run.mjs +9 -5
- package/advisor/timeline.mjs +26 -0
- package/advisor.mjs +4 -2
- package/agent/assemble.mjs +1 -1
- package/agent/chat-call.mjs +1 -1
- package/agent/child-marks.mjs +2 -2
- package/agent/completion.mjs +3 -1
- package/agent/digest-account.mjs +189 -0
- package/agent/dispatch-gates.mjs +8 -0
- package/agent/dispatch.mjs +39 -18
- package/agent/family-tools.mjs +27 -16
- package/agent/helpers.mjs +12 -3
- package/agent/light-round.mjs +66 -0
- package/agent/post-turn.mjs +8 -4
- package/agent/record-results.mjs +4 -0
- package/agent/run-stages.mjs +32 -7
- package/agent/run-start.mjs +50 -3
- package/agent/setup-reminders.mjs +1 -1
- package/agent/setup.mjs +18 -3
- package/agent/subagent-event.mjs +1 -1
- package/agent/suspension.mjs +37 -12
- package/agent/timers.mjs +56 -5
- package/agent/turn-loop.mjs +30 -5
- package/agent/write-gate.mjs +20 -32
- package/agent-tools/advisor-async.mjs +15 -7
- package/agent-tools/advisor-settle.mjs +2 -2
- package/agent-tools/advisor.mjs +34 -33
- package/agent-tools/async-discard.mjs +34 -0
- package/agent-tools/async-settle.mjs +32 -7
- package/agent-tools/audit-block.mjs +1 -1
- package/agent-tools/bash-async.mjs +1 -1
- package/agent-tools/batch-lifecycle.mjs +59 -17
- package/agent-tools/batch-paths.mjs +24 -4
- package/agent-tools/batch-skeleton.mjs +59 -43
- package/agent-tools/batch.mjs +39 -18
- package/agent-tools/browser-async.mjs +198 -0
- package/agent-tools/checkpoint.mjs +1 -1
- package/agent-tools/design-slots.mjs +232 -0
- package/agent-tools/design-token.mjs +20 -0
- package/agent-tools/digest-budget.mjs +1 -1
- package/agent-tools/escalate-async.mjs +4 -2
- package/agent-tools/manifest.mjs +193 -0
- package/agent-tools/parent-channel.mjs +7 -4
- package/agent-tools/review-facts.mjs +143 -16
- package/agent-tools/settings.mjs +1 -1
- package/agent-tools/skill.mjs +1 -1
- package/agent-tools/subagent-actions-query.mjs +13 -6
- package/agent-tools/subagent-async.mjs +31 -15
- package/agent-tools/subagent-panel.mjs +2 -1
- package/agent-tools/subagent-scheduler.mjs +8 -5
- package/agent-tools/subagent-spawn.mjs +30 -56
- package/agent-tools/subagent.mjs +21 -8
- package/agent-tools/timer.mjs +186 -12
- package/agent-tools/zero-write-watch.mjs +77 -0
- package/agent-tools.mjs +2 -1
- package/agent.mjs +2 -2
- package/attachments.mjs +1 -1
- package/config-io.mjs +18 -13
- package/config-migrate.mjs +13 -29
- package/config-presets.mjs +33 -29
- package/config.mjs +68 -25
- package/context-degrade.mjs +1 -1
- package/context-echo.mjs +1 -1
- package/context-push.mjs +2 -2
- package/context.mjs +1 -1
- package/conventions.mjs +28 -8
- package/declaration.mjs +18 -2
- package/escape.mjs +1 -1
- package/file-links.mjs +1 -1
- package/file-refs.mjs +1 -1
- package/history-window.mjs +1 -1
- package/i18n.mjs +7 -0
- package/ledger-cmd.mjs +62 -16
- package/ledger-db.mjs +6 -0
- package/ledger-surface.mjs +49 -5
- package/ledger-tools.mjs +158 -87
- package/ledger-variant-notice.mjs +47 -0
- package/ledger.mjs +11 -18
- package/log.mjs +1 -1
- package/manifest-discovery.mjs +1 -1
- package/manifest-schema.mjs +7 -5
- package/manifest.mjs +5 -3
- package/mcp/transport-stdio.mjs +1 -1
- package/mcp.mjs +1 -1
- package/memory/code-index.mjs +1 -1
- package/memory/code-search.mjs +1 -1
- package/memory/code-sync.mjs +1 -1
- package/memory/docs.mjs +6 -2
- package/memory/file-list.mjs +1 -1
- package/memory/file-walk.mjs +17 -5
- package/memory/maintain.mjs +410 -0
- package/memory/memory-tool.mjs +21 -6
- package/memory/schema.mjs +19 -7
- package/memory/sweep.mjs +8 -5
- package/memory/sync-tail.mjs +16 -10
- package/model-ref.mjs +16 -13
- package/model-specs.mjs +21 -9
- package/package.json +1 -1
- package/peer-instances.mjs +1 -1
- package/permission.mjs +33 -0
- package/process-probe.mjs +2 -2
- package/prompts/advisor-design.md +4 -2
- package/prompts/advisor-round1.md +1 -1
- package/prompts/advisor-round2.md +1 -1
- package/prompts/advisor-round3.md +1 -1
- package/prompts/common.md +38 -4
- package/prompts/consult-base.md +1 -1
- package/prompts/discipline-engineering.md +32 -15
- package/prompts/discipline-normal.md +46 -2
- package/prompts/persona-eng-designer.md +6 -6
- package/prompts/persona-engineering.md +18 -9
- package/prompts/persona-normal.md +2 -0
- package/prompts/subagent-base.md +3 -0
- package/provider/anthropic.mjs +14 -2
- package/provider/core.mjs +6 -0
- package/provider/errors.mjs +28 -3
- package/provider/google.mjs +22 -10
- package/provider/list-models.mjs +3 -2
- package/provider/responses-request.mjs +3 -1
- package/provider/responses.mjs +38 -4
- package/provider/sse.mjs +3 -0
- package/provider-flows.mjs +14 -15
- package/proxy-chunked.mjs +102 -0
- package/proxy-target.mjs +54 -0
- package/proxy-transport.mjs +298 -0
- package/proxy.mjs +17 -248
- package/session-gc.mjs +2 -2
- package/session-lifecycle.mjs +18 -14
- package/session-slot-claims.mjs +2 -2
- package/session-slots-manifest.mjs +1 -1
- package/session-slots.mjs +3 -3
- package/shell-identity.mjs +107 -0
- package/team-ledger.mjs +282 -0
- package/team.mjs +223 -0
- package/tool-docs/advisor.md +1 -1
- package/tool-docs/bash.md +2 -1
- package/tool-docs/batch.md +1 -1
- package/tool-docs/browser.md +48 -0
- package/tool-docs/delete.md +7 -5
- package/tool-docs/ledger.md +6 -0
- package/tool-docs/manifest.md +16 -0
- package/tool-docs/memory.md +1 -0
- package/tool-docs/notify_parent.md +5 -0
- package/tool-docs/process.md +4 -4
- package/tool-docs/question.md +1 -0
- package/tool-docs/subagent.md +14 -3
- package/tool-docs/team_ledger.md +9 -0
- package/tool-docs/timer.md +6 -0
- package/tool-docs/wait_for.md +1 -0
- package/tools/bash.mjs +9 -4
- package/tools/browser.mjs +161 -0
- package/tools/git.mjs +1 -1
- package/tools/index.mjs +4 -4
- package/tools/ops.mjs +26 -10
- package/tools/patch.mjs +8 -2
- package/tools/repomap.mjs +1 -1
- package/tools/write-path.mjs +8 -6
- package/traces/trace-cleanup.mjs +1 -1
- package/traces/trace-store.mjs +1 -1
- package/vision-reader.mjs +10 -16
- package/tool-docs/ledger_add.md +0 -1
- package/tool-docs/ledger_close.md +0 -1
- package/tool-docs/ledger_count.md +0 -1
- package/tool-docs/ledger_query.md +0 -1
- package/tool-docs/ledger_update.md +0 -1
package/config-presets.mjs
CHANGED
|
@@ -8,52 +8,56 @@
|
|
|
8
8
|
* - `presetToEntry` = 取 VSC 侧独有导出(CLI 侧对应实现在
|
|
9
9
|
* `cli/setup-wizard.mjs`,同字节语义)。
|
|
10
10
|
*
|
|
11
|
-
* 预先声明(
|
|
12
|
-
*
|
|
11
|
+
* 预先声明(2026-10-09 清除批:渠道单值 `model` 退场——PROVIDER.md §6.16 M3):预置**不携带**
|
|
12
|
+
* 默认模型(新装种子 = 零播种);模型身份唯二 = 顶层 `defaultModel` 复合串 ∥ 会话槽选定;
|
|
13
|
+
* 候选清单字段亦不预置——运行期从 provider 拉取(`GET /models`)。
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
/** Built-in provider presets: shared by /provider add <preset> and first-run wizard. */
|
|
16
17
|
export const PROVIDER_PRESETS = {
|
|
17
|
-
deepseek: { baseURL: "https://api.deepseek.com",
|
|
18
|
-
kimi: { baseURL: "https://api.moonshot.cn/v1",
|
|
19
|
-
"kimi-code": { baseURL: "https://api.kimi.com/coding/v1",
|
|
20
|
-
glm: { baseURL: "https://open.bigmodel.cn/api/paas/v4",
|
|
21
|
-
"glm-code": { baseURL: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
22
|
-
qwen: { baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
23
|
-
qwenplan: { baseURL: "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1",
|
|
24
|
-
mimo: { baseURL: "https://api.xiaomimimo.com/v1",
|
|
25
|
-
mimoplan: { baseURL: "https://token-plan-cn.xiaomimimo.com/v1",
|
|
26
|
-
minimax: { baseURL: "https://api.minimaxi.com/v1",
|
|
27
|
-
openai: { baseURL: "https://api.openai.com/v1",
|
|
28
|
-
claude: { baseURL: "https://api.anthropic.com/v1",
|
|
29
|
-
gemini: { baseURL: "https://generativelanguage.googleapis.com/v1beta",
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
18
|
+
deepseek: { baseURL: "https://api.deepseek.com", thinking: { type: "enabled" }, reasoningEffort: "max", maxTokens: 384_000, desc: "DeepSeek" },
|
|
19
|
+
kimi: { baseURL: "https://api.moonshot.cn/v1", thinking: null, reasoningEffort: "max", maxTokens: 131072, desc: "Kimi / Moonshot" },
|
|
20
|
+
"kimi-code": { baseURL: "https://api.kimi.com/coding/v1", thinking: null, reasoningEffort: "max", maxTokens: 131072, desc: "Kimi For Coding (platform.kimi.com — sk-kimi- keys; NOT interchangeable with Moonshot)" },
|
|
21
|
+
glm: { baseURL: "https://open.bigmodel.cn/api/paas/v4", thinking: { type: "enabled" }, reasoningEffort: "max", maxTokens: 128000, desc: "Zhipu GLM" },
|
|
22
|
+
"glm-code": { baseURL: "https://open.bigmodel.cn/api/coding/paas/v4", thinking: { type: "enabled" }, reasoningEffort: "max", maxTokens: 128000, desc: "Zhipu GLM Coding Plan (coding endpoint — same key as GLM; server-forced thinking)" },
|
|
23
|
+
qwen: { baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1", reasoningEffort: "high", maxTokens: 131072, desc: "Qwen / Alibaba" },
|
|
24
|
+
qwenplan: { baseURL: "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1", reasoningEffort: "high", maxTokens: 131072, desc: "Qwen Token Plan (百炼套餐)" },
|
|
25
|
+
mimo: { baseURL: "https://api.xiaomimimo.com/v1", thinking: { type: "enabled" }, maxTokens: 131072, desc: "MiMo (Xiaomi)" },
|
|
26
|
+
mimoplan: { baseURL: "https://token-plan-cn.xiaomimimo.com/v1", thinking: { type: "enabled" }, maxTokens: 131072, desc: "MiMo Token Plan (小米套餐 — tp- keys; 与按量付费 sk- 密钥不通用)" },
|
|
27
|
+
minimax: { baseURL: "https://api.minimaxi.com/v1", thinking: { type: "adaptive" }, maxTokens: 128000, chatPath: "/text/chatcompletion_v2", desc: "MiniMax" },
|
|
28
|
+
openai: { baseURL: "https://api.openai.com/v1", desc: "OpenAI" },
|
|
29
|
+
claude: { baseURL: "https://api.anthropic.com/v1", format: "anthropic", maxTokens: 8192, desc: "Claude (Anthropic)" },
|
|
30
|
+
gemini: { baseURL: "https://generativelanguage.googleapis.com/v1beta", format: "google", maxTokens: 8192, desc: "Gemini (Google)" },
|
|
31
|
+
// Gemini OpenAI 兼容端点(Google 官方——2026-10-09 server 预设批 · 台账 #1128):与 `gemini`(原生
|
|
32
|
+
// `format:"google"`)同 key、两形态并存——本键 = OpenAI `/chat/completions` 侧(PROVIDER.md §6.11)。
|
|
33
|
+
// **待验(无 key——未实拉 `/models`;接入后按实拉复核)**;`thinking` / `reasoningEffort` / `maxTokens` 不设 = 不发(载荷面未测——D-13)。
|
|
34
|
+
"gemini-openai": { baseURL: "https://generativelanguage.googleapis.com/v1beta/openai", desc: "Gemini (Google) — OpenAI-compatible side (same key as gemini)" },
|
|
35
|
+
grok: { baseURL: "https://api.x.ai/v1", maxTokens: 64_000, desc: "Grok (xAI)" },
|
|
36
|
+
mistral: { baseURL: "https://api.mistral.ai/v1", maxTokens: 32_000, desc: "Mistral" },
|
|
37
|
+
volcengine: { baseURL: "https://ark.cn-beijing.volces.com/api/v3", maxTokens: 131072, desc: "Volcengine Ark (豆包)" },
|
|
38
|
+
hunyuan: { baseURL: "https://api.hunyuan.cloud.tencent.com/v1", maxTokens: 32_000, desc: "Hunyuan (腾讯混元)" },
|
|
34
39
|
// TokenHub = Tencent MaaS 聚合网关(另一主机;`hy3` 在本规格表有行)。`thinking` / `reasoningEffort` /
|
|
35
40
|
// `maxTokens` 一律**不设 = 不发**(两新渠道的 thinking 载荷与 max_tokens 行为未测 —— MODEL-SPECS §9.6 D-13)。
|
|
36
|
-
tokenhub: { baseURL: "https://tokenhub.tencentmaas.com/v1",
|
|
41
|
+
tokenhub: { baseURL: "https://tokenhub.tencentmaas.com/v1", desc: "Tencent TokenHub (腾讯混元网关)" },
|
|
37
42
|
// Huawei Cloud MaaS(ModelArts Studio)——OpenAI 兼容 `…/openai/v1`(区域端点变体走自定义渠道
|
|
38
|
-
// 自助路径——PROVIDER.md §6.21
|
|
39
|
-
// QS 示例模型 · 官方 API 样例 `model` 逐字)· **待验(无 key——未实拉 /models;接入后按实拉取值复核)**;
|
|
43
|
+
// 自助路径——PROVIDER.md §6.21)。**待验(无 key——未实拉 /models;接入后按实拉取值复核)**;
|
|
40
44
|
// `thinking` / `reasoningEffort` / `maxTokens` 不设 = 不发(载荷面未测——D-13)。
|
|
41
|
-
huawei: { baseURL: "https://api.modelarts-maas.com/openai/v1",
|
|
42
|
-
siliconflow: { baseURL: "https://api.siliconflow.cn/v1",
|
|
43
|
-
openrouter: { baseURL: "https://openrouter.ai/api/v1",
|
|
44
|
-
groq: { baseURL: "https://api.groq.com/openai/v1",
|
|
45
|
+
huawei: { baseURL: "https://api.modelarts-maas.com/openai/v1", desc: "Huawei Cloud ModelArts Studio (华为云 MaaS)" },
|
|
46
|
+
siliconflow: { baseURL: "https://api.siliconflow.cn/v1", maxTokens: 32_000, desc: "SiliconFlow (硅基流动)" },
|
|
47
|
+
openrouter: { baseURL: "https://openrouter.ai/api/v1", maxTokens: 32_000, desc: "OpenRouter" },
|
|
48
|
+
groq: { baseURL: "https://api.groq.com/openai/v1", maxTokens: 32_000, desc: "Groq" },
|
|
45
49
|
// OpenCode Go 订阅网关(opencode.ai/zen 取 sk- key ∥ 无 OAuth)——官方档(go.mdx)双协议混装 ⇒ 拆双预设
|
|
46
50
|
// (PROVIDER.md §6.11):本键 = OpenAI 兼容 `/chat/completions` 侧;`opencode-go-anthropic` = Anthropic
|
|
47
51
|
// `/messages` 侧(同 key——模型按侧选)。字段集 = 最小面:`thinking` / `reasoningEffort` 不设 = 不发
|
|
48
52
|
// (D-13 同口径);`maxTokens` 仅 anthropic 侧设 `65536`(该 transport `max_tokens` 必发——不设即落规格
|
|
49
53
|
// 行值 131072,超渠道口径)。**待验(无 key——未实拉 `/models` ∕ 载荷未实测;接入后按实拉复核)**;
|
|
50
54
|
// `x-opencode-session` 头 = 可选遥测——**不发**(亦不动 `providers[].headers` 面)。
|
|
51
|
-
"opencode-go": { baseURL: "https://opencode.ai/zen/go/v1",
|
|
52
|
-
"opencode-go-anthropic": { baseURL: "https://opencode.ai/zen/go/v1",
|
|
55
|
+
"opencode-go": { baseURL: "https://opencode.ai/zen/go/v1", desc: "OpenCode Go subscription (sk- key from opencode.ai/zen; OpenAI-compatible models — MiniMax/Qwen via opencode-go-anthropic)" },
|
|
56
|
+
"opencode-go-anthropic": { baseURL: "https://opencode.ai/zen/go/v1", format: "anthropic", maxTokens: 65536, desc: "OpenCode Go — Anthropic Messages side (same sk- key as opencode-go; MiniMax/Qwen models)" },
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
/** Build the stored provider entry from a preset — strip the display field, keep the rest
|
|
56
|
-
*
|
|
60
|
+
* (2026-10-09 清除批:不产 `model` 键——渠道不携模型;候选清单不再由预设播种)。 */
|
|
57
61
|
export function presetToEntry(name) {
|
|
58
62
|
const preset = PROVIDER_PRESETS[name]
|
|
59
63
|
if (!preset) return null
|
package/config.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* config.mjs — configuration loading and saving
|
|
3
|
-
* Model-merge schema v2 (2026-09-10 MODEL-SELECTION):
|
|
4
|
-
*
|
|
5
|
-
* candidates list field models[] is gone — the available-model list is fetched from
|
|
6
|
-
* provider at runtime (`GET /models`, PROVIDER.md §16). config.defaultModel (top level,
|
|
3
|
+
* Model-merge schema v2 (2026-09-10 MODEL-SELECTION; 2026-10-09 清除批:渠道单值模型退场):
|
|
4
|
+
* providers[] carry NO channel default model (`providers[].model` retired — 渠道不是模型选择
|
|
5
|
+
* 单位); the candidates list field models[] is gone — the available-model list is fetched from
|
|
6
|
+
* the provider at runtime (`GET /models`, PROVIDER.md §16). config.defaultModel (top level,
|
|
7
7
|
* "provider:model" composite) is the new-session starting point; activeProvider/activeModel
|
|
8
8
|
* are gone from config (session slots keep their own double fields). Legacy fields migrate
|
|
9
9
|
* on load (write-back failure never blocks startup).
|
|
@@ -35,7 +35,7 @@ export const DEFAULTS = {
|
|
|
35
35
|
agent: {
|
|
36
36
|
maxTurns: 200,
|
|
37
37
|
subagentTurns: 100,
|
|
38
|
-
subagentModel: null, // default subagent model: "provider:model" |
|
|
38
|
+
subagentModel: null, // default subagent model: "provider:model" | model name (parent provider); null = inherit parent provider
|
|
39
39
|
subagentModels: {}, // per-type override: { explore, plan, coder, "eng-coder" } — priority: subagent tool model arg > this[role] > subagentModel > parent provider
|
|
40
40
|
goalTurns: 200,
|
|
41
41
|
compactThreshold: 100000,
|
|
@@ -69,6 +69,8 @@ export const DEFAULTS = {
|
|
|
69
69
|
dbPath: join(configDir, "memory.db"),
|
|
70
70
|
projectDir: ".thincoder/memory",
|
|
71
71
|
team: null,
|
|
72
|
+
// 索引自维护(§6.14 面⑤ · 台账 #1096):维护型备份保留枚数(轮转保最新 N——引擎钳 ≥ 1)
|
|
73
|
+
maintain: { backupKeep: 2 },
|
|
72
74
|
},
|
|
73
75
|
shell: null, // bash tool shell executable (e.g. "C:\\Program Files\\Git\\bin\\bash.exe" or "pwsh"); null = system default (cmd on Windows, /bin/sh elsewhere)
|
|
74
76
|
embedding: {
|
|
@@ -82,11 +84,14 @@ export const DEFAULTS = {
|
|
|
82
84
|
// Structured search via Tavily when a key is set — empty apiKey → Bing RSS/HTML fallback (zero-config).
|
|
83
85
|
apiKey: "", // Tavily key (tvly-...) — optional
|
|
84
86
|
},
|
|
87
|
+
// 浏览器工具(BROWSER-TOOL.md §3.2):非空 ⇒ navigate 目标 host ∥ click/evaluate/type 的当前页 host 须命中
|
|
88
|
+
//(全等(大小写不敏感)∥ `*.` 前缀 = 子域通配)——收束面非沙箱;缺省空 = 不设限。
|
|
89
|
+
browser: { allowDomains: [] },
|
|
85
90
|
traces: {
|
|
86
91
|
enabled: false, // §18.6 D-TR6 修订(2026-09-05 用户裁定——发布隐私:"不希望用户那边也采集"):轨迹存档默认 OFF——新用户零采集;本地调试分析可显式开(~/.thincoder/config.json traces.enabled:true)
|
|
87
92
|
retentionHours: 24, // D-TR10:轨迹文件保留小时数——CLI 启动时删除超过该时长的文件(默认 24h)
|
|
88
93
|
},
|
|
89
|
-
// 诊断开关(用户面 ① 配置键——CONFIG.md §6.2 名录):`heapWatch` 默认开(设 false
|
|
94
|
+
// 诊断开关(用户面 ① 配置键——CONFIG.md §6.2 名录):`heapWatch` 默认开(设 false 关闭)——自愈分支
|
|
90
95
|
// 遥测;`heapSnapshot` 默认关——采集 opt-in(堆转储敏感度 ≥ traces ⇒ 对齐发布隐私先例 D-TR6;
|
|
91
96
|
// 2026-09-30 采集收网批;桌面端运行期热读 ∥ CLI 本进程下次启动)
|
|
92
97
|
diagnostics: { heapWatch: true, heapSnapshot: false },
|
|
@@ -99,11 +104,34 @@ import { specForModel, providerSpec, specMatch, assistantToolCallMessage } from
|
|
|
99
104
|
export { specForModel, providerSpec, specMatch, assistantToolCallMessage }
|
|
100
105
|
|
|
101
106
|
|
|
102
|
-
// Window utilization threshold: compacts at 60%
|
|
103
|
-
//
|
|
104
|
-
//
|
|
107
|
+
// Window utilization threshold: compacts at 60% of the USABLE window (window − output
|
|
108
|
+
// reservation — providers require input + max_tokens ≤ window; 2026-10-06 user ruling:
|
|
109
|
+
// the reservation folds into the budget so compaction fires first, never a provider
|
|
110
|
+
// rejection), the remaining 40% stays as headroom for injected context (directory tree,
|
|
111
|
+
// git context, outline, project instructions, memory/doc search results — 30-50K tokens
|
|
112
|
+
// per turn).
|
|
105
113
|
const COMPACT_RATIO = 0.6
|
|
106
114
|
|
|
115
|
+
/** Output reservation the request counts against the window: explicit `provider.maxTokens`
|
|
116
|
+
* (the value actually sent — `provider/core.mjs`) when set, else the spec `maxOutput`, else 0. */
|
|
117
|
+
export function outputReserve(provider, spec) {
|
|
118
|
+
const explicit = provider?.maxTokens
|
|
119
|
+
if (Number.isFinite(explicit) && explicit > 0) return explicit
|
|
120
|
+
return spec?.maxOutput ?? 0
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Usable input window = window − output reservation. Degenerate guard: a reservation
|
|
124
|
+
* ≥ the window is non-physical (no input could ever fit — e.g. the doubao spec
|
|
125
|
+
* `maxOutput` 524_288 > `context` 256_000 ∥ the test harness's 1K window vs the default
|
|
126
|
+
* spec); collapsing the budget to 0 would be instant `context_limit` death — the very
|
|
127
|
+
* failure the reservation fold exists to prevent. Fall back to half the window.
|
|
128
|
+
* (2026-10-06——收口核验捕获:advisor-loop-split B5a 红。) */
|
|
129
|
+
export function usableWindow(context, reserve) {
|
|
130
|
+
const ctx = context ?? 0
|
|
131
|
+
const usable = ctx - reserve
|
|
132
|
+
return usable > 0 ? usable : Math.floor(ctx / 2)
|
|
133
|
+
}
|
|
134
|
+
|
|
107
135
|
/** Derive compaction threshold; explicit is the value explicitly set in config file (takes priority), otherwise auto-computed from model.
|
|
108
136
|
* Second param accepts EITHER a model name string (pure spec lookup — legacy caller:
|
|
109
137
|
* first-run wizard) OR a provider object (providerSpec — the providers[].context
|
|
@@ -112,7 +140,8 @@ export function resolveCompactThreshold(explicit, modelOrProvider) {
|
|
|
112
140
|
if (explicit != null) return { value: explicit, auto: false }
|
|
113
141
|
const provider = typeof modelOrProvider === "string" ? { model: modelOrProvider } : (modelOrProvider ?? {})
|
|
114
142
|
const spec = providerSpec(provider)
|
|
115
|
-
const
|
|
143
|
+
const usable = usableWindow(spec.context, outputReserve(provider, spec))
|
|
144
|
+
const value = Math.floor(usable * COMPACT_RATIO)
|
|
116
145
|
return { value, auto: true }
|
|
117
146
|
}
|
|
118
147
|
|
|
@@ -191,7 +220,7 @@ function warnConsultModelsFiltered(dropped, path) {
|
|
|
191
220
|
/** #861(AGENT-LOOP-SUBAGENT.md §6.7.1):subagent 模型覆盖加载期清洗——单源纯函数(核 loadConfig ∥ VSC 端壳 raw 读点同消费);非法形态不 throw(`subagentModel` 非法 ⇒ null、`subagentModels` 非对象 ⇒ {}、值非法 ⇒ 剔除该键)。 */
|
|
192
221
|
export function sanitizeSubagentModel(v) {
|
|
193
222
|
const ok = typeof v === "string" && v.trim()
|
|
194
|
-
const dropped = ok || v == null ? [] : [`agent.subagentModel must be a non-empty string ("provider:model" |
|
|
223
|
+
const dropped = ok || v == null ? [] : [`agent.subagentModel must be a non-empty string ("provider:model" | model) — got ${Array.isArray(v) ? "array" : typeof v} — ignored (inherits the parent provider)`]
|
|
195
224
|
return { value: ok ? v : null, dropped }
|
|
196
225
|
}
|
|
197
226
|
export function sanitizeSubagentModels(v) {
|
|
@@ -200,7 +229,7 @@ export function sanitizeSubagentModels(v) {
|
|
|
200
229
|
const value = {}, dropped = []
|
|
201
230
|
for (const [role, val] of Object.entries(v)) {
|
|
202
231
|
if (typeof val === "string" && val.trim()) { value[role] = val; continue }
|
|
203
|
-
dropped.push(`agent.subagentModels.${role} must be a non-empty string ("provider:model" |
|
|
232
|
+
dropped.push(`agent.subagentModels.${role} must be a non-empty string ("provider:model" | model) — got ${Array.isArray(val) ? "array" : typeof val} — dropped`)
|
|
204
233
|
}
|
|
205
234
|
return { value, dropped }
|
|
206
235
|
}
|
|
@@ -210,12 +239,12 @@ function warnSubagentModelsFiltered(dropped, path) {
|
|
|
210
239
|
if (warnedSubagentModels || dropped.length === 0) return
|
|
211
240
|
warnedSubagentModels = true
|
|
212
241
|
console.warn(`[config] agent.subagentModel/subagentModels: ${dropped.length} invalid entr${dropped.length === 1 ? "y ignored" : "ies ignored"} (filtered — startup continues; no crash):\n` +
|
|
213
|
-
`${dropped.map((d) => ` - ${d}`).join("\n")}\n Fix: clean it in ${path} — "provider:model" |
|
|
242
|
+
`${dropped.map((d) => ` - ${d}`).join("\n")}\n Fix: clean it in ${path} — "provider:model" | model.`)
|
|
214
243
|
}
|
|
215
244
|
|
|
216
245
|
/**
|
|
217
246
|
* Find provider by name in providers[].
|
|
218
|
-
* Throws if name is non-empty but not found — a typo in
|
|
247
|
+
* Throws if name is non-empty but not found — a typo in the caller-supplied name (e.g. `defaultModel`) silently falling to the first provider would use the wrong key on the wrong endpoint.
|
|
219
248
|
* Returns the first provider when name is empty.
|
|
220
249
|
*/
|
|
221
250
|
export function findProvider(providers, name) {
|
|
@@ -223,24 +252,35 @@ export function findProvider(providers, name) {
|
|
|
223
252
|
const found = providers.find((p) => p.name === name)
|
|
224
253
|
if (found) return found
|
|
225
254
|
const available = providers.map((p) => p.name).join(", ") || "(empty)"
|
|
226
|
-
throw new Error(`
|
|
255
|
+
throw new Error(`provider "${name}" not in providers list (available: ${available}); check for a typo in: ${configPath}`)
|
|
227
256
|
}
|
|
228
|
-
return providers[0] ?? { name: "default", baseURL: ""
|
|
257
|
+
return providers[0] ?? { name: "default", baseURL: "" }
|
|
229
258
|
}
|
|
230
259
|
|
|
231
|
-
/** Normalize proxy config to { uri, web
|
|
260
|
+
/** Normalize proxy config to { uri, web } or undefined (uri/url both accepted; invalid types dropped) */
|
|
232
261
|
export function normalizeProxy(proxy) {
|
|
233
|
-
if (typeof proxy === "string") return proxy ? { uri: proxy, web: true
|
|
262
|
+
if (typeof proxy === "string") return proxy ? { uri: proxy, web: true } : undefined
|
|
234
263
|
if (!proxy || typeof proxy !== "object" || Array.isArray(proxy)) return undefined
|
|
235
264
|
const uri = proxy.uri || proxy.url || ""
|
|
236
265
|
if (typeof uri !== "string" || !uri) return undefined
|
|
237
|
-
return { uri, web: proxy.web !== false
|
|
266
|
+
return { uri, web: proxy.web !== false }
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** 团队登录段加载归一(B1 批——`docs/core/design/TEAM.md` §2.1 ∥ CONFIG.md §6.4):非对象 ⇒ null(软失败
|
|
270
|
+
* 不阻启动——沿 proxy 段先例);键级 = trim 非空字符串(非串 ∥ 空 ⇒ null);`member` 浅形 `{ username, name }`。
|
|
271
|
+
* **不入 `DEFAULTS`**(settings 工具面零涉——agent 不可写;`team.token` 经读面到达 = 敏感段词表遮罩)。 */
|
|
272
|
+
export function normalizeTeamSection(value) {
|
|
273
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return null
|
|
274
|
+
const str = (v) => (typeof v === "string" && v.trim() !== "" ? v.trim() : null)
|
|
275
|
+
const raw = value.member
|
|
276
|
+
const member = raw !== null && typeof raw === "object" && !Array.isArray(raw) ? { username: str(raw.username), name: str(raw.name) } : null
|
|
277
|
+
return { server: str(value.server), member, label: str(value.label), token: str(value.token) }
|
|
238
278
|
}
|
|
239
279
|
|
|
240
280
|
/**
|
|
241
281
|
* Load configuration.
|
|
242
282
|
* No env-var overrides — config.json is the single source of truth
|
|
243
|
-
* (API keys, baseURL,
|
|
283
|
+
* (API keys, baseURL, defaultModel all come from the file).
|
|
244
284
|
*/
|
|
245
285
|
/** Keep only { header: "string value" } pairs from a provider's headers field — anything
|
|
246
286
|
* else (null, arrays, nested objects) is dropped so it can never reach a fetch call.
|
|
@@ -289,6 +329,7 @@ export function loadConfig() {
|
|
|
289
329
|
agent: { ...DEFAULTS.agent, ...config.agent },
|
|
290
330
|
memory: { ...DEFAULTS.memory, ...config.memory },
|
|
291
331
|
embedding: { ...DEFAULTS.embedding, ...config.embedding },
|
|
332
|
+
browser: { ...DEFAULTS.browser, ...config.browser },
|
|
292
333
|
traces: { ...DEFAULTS.traces, ...config.traces },
|
|
293
334
|
diagnostics: { ...DEFAULTS.diagnostics },
|
|
294
335
|
}
|
|
@@ -306,10 +347,9 @@ export function loadConfig() {
|
|
|
306
347
|
const _diagRaw = config.diagnostics && typeof config.diagnostics === "object" && !Array.isArray(config.diagnostics) ? config.diagnostics : {}
|
|
307
348
|
for (const key of ["heapWatch", "heapSnapshot"]) if (typeof _diagRaw[key] === "boolean") merged.diagnostics[key] = _diagRaw[key]
|
|
308
349
|
|
|
309
|
-
// providers[].model
|
|
310
|
-
//
|
|
350
|
+
// providers[].model 一律删除(2026-10-09 清除批——渠道单值模型退场:渠道不携模型,不落任何
|
|
351
|
+
// 回退;模型身份唯二 = 顶层 defaultModel 复合串 ∥ 会话槽选定——PROVIDER.md §6.16 M3)。
|
|
311
352
|
for (const p of merged.providers) {
|
|
312
|
-
if (typeof p.model === "string" && p.model.trim()) continue
|
|
313
353
|
if (p.model !== undefined) delete p.model
|
|
314
354
|
}
|
|
315
355
|
|
|
@@ -351,10 +391,13 @@ export function loadConfig() {
|
|
|
351
391
|
if (p.baseURL) p.baseURL = p.baseURL.replace(/\/+$/, "")
|
|
352
392
|
}
|
|
353
393
|
|
|
354
|
-
// Normalize proxy: string → { uri, web:true
|
|
394
|
+
// Normalize proxy: string → { uri, web:true }; object 补默认值;非法类型丢弃。
|
|
355
395
|
// 保证 agent.config.proxy 永远是规范形态或 undefined
|
|
356
396
|
merged.proxy = normalizeProxy(merged.proxy)
|
|
357
397
|
|
|
398
|
+
// 团队登录段(B1 批——TEAM.md §2.1):加载归一(形不符 ⇒ null——软失败不阻启动);不入 DEFAULTS
|
|
399
|
+
merged.team = normalizeTeamSection(merged.team)
|
|
400
|
+
|
|
358
401
|
// Runtime provider = 统一解析(#841——resolveProviderPlan 单源:回退序 + 三态;PROVIDER.md §6.22)。
|
|
359
402
|
// 不可运行(渠表空 ∥ 全表无 key)⇒ {} + providerInvalidReason(D-S1 处置不 throw——make-agent 打
|
|
360
403
|
// _providerInvalid 标记 → TUI 首帧弹选择 / headless 报可读错误;同 2026-09-02 Q1 语义——不复用
|
|
@@ -387,7 +430,7 @@ export function loadConfig() {
|
|
|
387
430
|
/**
|
|
388
431
|
* MCP.md §5 D-3 (2026-09-01): re-read config.json and replace ONLY the agent's mcp section
|
|
389
432
|
* — the agent 代配 closed loop (agent edits config.json with its edit tool, /mcp picks it
|
|
390
|
-
* up). Never touches other config sections (providers/
|
|
433
|
+
* up). Never touches other config sections (providers/defaultModel stay as loaded).
|
|
391
434
|
*
|
|
392
435
|
* Malformed disk config → memory state kept, { ok:false, error } returned (the /mcp menu
|
|
393
436
|
* shows "⚠ disk config unreadable"). Never throws.
|
package/context-degrade.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* context-degrade.mjs — 确定性降级面(无 LLM 的有损收缩 · 自 `context.mjs` 迁出 · 2026-10-01 core 拆分批 #755 ∥ #786)。
|
|
3
3
|
* 内容 = `pruneStub` ∥ `pruneStaleToolOutputs`(F-CC3 陈旧工具输出清理)∥ `OVERSIZE_CONTENT_LIMIT` ∥
|
|
4
4
|
* `shrinkOversized`(splitHistory 无中间段时的单条超限收缩)——迁出块逐字。
|
|
5
|
-
* 原档 `context.mjs` 经 `export { … } from` 转口保名(消费面 /
|
|
5
|
+
* 原档 `context.mjs` 经 `export { … } from` 转口保名(消费面 / 单元测试档 import 面零改);
|
|
6
6
|
* 跨档被引的原私有面(`shrinkOversized`)补 `export` 关键字。
|
|
7
7
|
*/
|
|
8
8
|
import { collectStaleToolOutputs } from "./token-window.mjs"
|
package/context-echo.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* context-echo.mjs — 回声安全面(D-CC18 ∥ D-CC19 恢复面归并 · 自 `context.mjs` 迁出 · 2026-10-01 core 拆分批 #755 ∥ #786)。
|
|
3
3
|
* 内容 = `COMPACTION_PLACEHOLDER` ∥ 前缀器族(`prefixContent` / `withPlaceholderPrefix`)∥ 回声对归并
|
|
4
4
|
* (`isAssistantEchoPair` / `contentTextOf` / `absorbEchoContent` / `mergeAdjacentAssistantEchoes`)——迁出块逐字。
|
|
5
|
-
* 原档 `context.mjs` 经 `export { … } from` 转口保名(消费面 /
|
|
5
|
+
* 原档 `context.mjs` 经 `export { … } from` 转口保名(消费面 / 单元测试档 import 面零改);
|
|
6
6
|
* 跨档被引的原私有面(`COMPACTION_PLACEHOLDER` ∥ `withPlaceholderPrefix`)补 `export` 关键字。
|
|
7
7
|
*/
|
|
8
8
|
|
package/context-push.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* context-push.mjs — 消息双线写入单点(写缝面 · 自 `context.mjs` 迁出 · 2026-10-01 core 拆分批 #755 ∥ #786)。
|
|
3
3
|
* `pushReal` = 真实消息写缝(机器线 `agent.history` + 人读线 `agent._fullHistory` 同源双写);
|
|
4
4
|
* `pushRecord` = 人读线留档记录单点(机器线零触)。迁出块逐字;原档 `context.mjs` 经
|
|
5
|
-
* `export { … } from` 转口保名(消费面 /
|
|
5
|
+
* `export { … } from` 转口保名(消费面 / 单元测试档 import 面零改)。
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -40,7 +40,7 @@ export function pushReal(agent, msg) {
|
|
|
40
40
|
* pushRecord — 人读线留档记录单点(消化生命周期面 · #726;形 ∥ 写缝 ∥ 判据单源 = SESSION.md §6.26)。
|
|
41
41
|
* `pushReal` 双胞(直复用——零算法副本):`ts` 打点 ∥ 记录存储追加(`_recordStore?.append`——§6.14)∥
|
|
42
42
|
* 尾窗驱逐三面同源;**机器线零触** = `history` 以一次性弃数组承接 ⇒ 记录不入 `agent.history` /
|
|
43
|
-
* `contextHistory`(不喂模型)。未绑定(`_recordStore` 缺)⇒ 人读线追加照常 ∥
|
|
43
|
+
* `contextHistory`(不喂模型)。未绑定(`_recordStore` 缺)⇒ 人读线追加照常 ∥ 存储分支空转(零抛——模式 F
|
|
44
44
|
* 零回归);载体缺位(无活跃会话)⇒ 零动作(零抛——日志归端侧)。
|
|
45
45
|
*/
|
|
46
46
|
export function pushRecord(agent, record) {
|
package/context.mjs
CHANGED
|
@@ -250,7 +250,7 @@ export function compressFallback(agent) {
|
|
|
250
250
|
|
|
251
251
|
// ─── 迁出面(import 面保持:TUI / verify-compress / VSC 对拍经本档取——先例 = 下方 explore-distill 再导出)──
|
|
252
252
|
export { estimateTokens }
|
|
253
|
-
// ─── 拆分转口(2026-10-01 拆分批 · #755 ∥ #786):写缝 ∥ 回声 ∥ 降级三面外提——消费面 /
|
|
253
|
+
// ─── 拆分转口(2026-10-01 拆分批 · #755 ∥ #786):写缝 ∥ 回声 ∥ 降级三面外提——消费面 / 单元测试档 import 面零改 ───
|
|
254
254
|
export { pushReal, pushRecord } from "./context-push.mjs"
|
|
255
255
|
export { isAssistantEchoPair, mergeAdjacentAssistantEchoes } from "./context-echo.mjs"
|
|
256
256
|
export { pruneStaleToolOutputs } from "./context-degrade.mjs"
|
package/conventions.mjs
CHANGED
|
@@ -26,9 +26,10 @@
|
|
|
26
26
|
* code — inside a declared code segment (default: the path segment `src`), or the
|
|
27
27
|
* fallback; doc — documentation extension outside any code segment and not
|
|
28
28
|
* scratch; temp — tmp-* name or .tmp/.temp extension; aux — a default
|
|
29
|
-
* auxiliary-path segment sequence (test / tests / scripts / .thincoder/tmp —
|
|
30
|
-
*
|
|
31
|
-
*
|
|
29
|
+
* auxiliary-path segment sequence (test / tests / scripts / .thincoder/tmp — F9)
|
|
30
|
+
* that matched no code segment and neither doc nor temp; state — a project state
|
|
31
|
+
* file (`PROJECT-MANIFEST.json` basename ∥ `.thincoder/conventions.json` pair — #941).
|
|
32
|
+
* Precedence: code segment → temp → doc → aux → state → fallback code.
|
|
32
33
|
*
|
|
33
34
|
* Segment matching runs on the PROJECT-ROOT-RELATIVE FACE of a path (D17 · 2026-09-28): the
|
|
34
35
|
* machine layout ABOVE the project root never participates. An absolute path outside the
|
|
@@ -128,18 +129,32 @@ function hasAuxSegment(p, conv) {
|
|
|
128
129
|
return hasSegmentSequence(p, DEFAULT_AUX_PATHS, conv?.root, false)
|
|
129
130
|
}
|
|
130
131
|
|
|
131
|
-
/**
|
|
132
|
+
/** Project state-file markers (data — #941 · PORTABILITY D19): manifest basename (any layer) ∥
|
|
133
|
+
* retired-carrier pair; single sources: `MANIFEST_REL` ∥ `RETIRED_REL_PATH`. */
|
|
134
|
+
const STATE_BASENAME = "project-manifest.json"
|
|
135
|
+
const STATE_PAIR = ".thincoder/conventions.json"
|
|
136
|
+
|
|
137
|
+
/** True when `s` names a project state file (basename any layer ∥ segment pair; case-insensitive — no face, D17). */
|
|
138
|
+
function isStateFile(s) {
|
|
139
|
+
const segs = segmentsOf(s)
|
|
140
|
+
if (segs.length > 0 && lowerSeg(segs[segs.length - 1]) === STATE_BASENAME) return true
|
|
141
|
+
return hasSequenceIn(segs, [STATE_PAIR])
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** "code" | "doc" | "temp" | "aux" | "state" — the single classification decision.
|
|
132
145
|
* Precedence: code segment first (src/** stays product code even when the name
|
|
133
146
|
* looks scratch — the pre-existing unconditional-src rule), then temp, then a
|
|
134
147
|
* documentation extension, then an auxiliary-path segment sequence (test / tests
|
|
135
|
-
* / scripts / .thincoder/tmp — F9),
|
|
136
|
-
*
|
|
148
|
+
* / scripts / .thincoder/tmp — F9), then a project state file (#941), else code
|
|
149
|
+
* (anything not doc/temp/aux/state is product code). Segment checks face-bound (D17);
|
|
150
|
+
* state matches by name marker. */
|
|
137
151
|
export function classifyPath(p, conv) {
|
|
138
152
|
const s = String(p ?? "")
|
|
139
153
|
if (hasCodeSegment(s, conv)) return "code"
|
|
140
154
|
if (TEMP_FILE.test(s)) return "temp"
|
|
141
155
|
if (DOC_FILE.test(s)) return "doc"
|
|
142
156
|
if (hasAuxSegment(s, conv)) return "aux"
|
|
157
|
+
if (isStateFile(s)) return "state"
|
|
143
158
|
return "code"
|
|
144
159
|
}
|
|
145
160
|
|
|
@@ -165,8 +180,13 @@ export function isAuxPath(p, conv) {
|
|
|
165
180
|
return classifyPath(p, conv) === "aux"
|
|
166
181
|
}
|
|
167
182
|
|
|
183
|
+
/** True when the path is a project state file (`classifyPath === "state"` — #941; derived form). */
|
|
184
|
+
export function isStatePath(p, conv) {
|
|
185
|
+
return classifyPath(p, conv) === "state"
|
|
186
|
+
}
|
|
187
|
+
|
|
168
188
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
169
189
|
// 迁出面(2026-10-01 拆分批 · #755 ∥ #786):声明装载面外提 `declaration.mjs`——本档经
|
|
170
|
-
// 转口保名(消费面 /
|
|
190
|
+
// 转口保名(消费面 / 单元测试档 import 面零改;`DEFAULT_DECLARATION` 另供本档分类裁判缺省取用)。
|
|
171
191
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
172
|
-
export { loadProjectDeclaration, DEFAULT_DECLARATION, isExcludedRelPath, declaredPublicRoots, clearDeclarationCache } from "./declaration.mjs"
|
|
192
|
+
export { loadProjectDeclaration, declarationForTarget, DEFAULT_DECLARATION, isExcludedRelPath, declaredPublicRoots, clearDeclarationCache } from "./declaration.mjs"
|
package/declaration.mjs
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* · 自 `conventions.mjs` 迁出 · 2026-10-01 core 拆分批 #755 ∥ #786。
|
|
4
4
|
* 投影单源 = `manifest.mjs`(ONE reader / ONE root resolution——`readManifest` + `manifestFilePath`;
|
|
5
5
|
* name / shape / defaults = schema 面 `docs/core/design/MANIFEST.md` §2.2)。迁出块逐字;
|
|
6
|
-
* 原档 `conventions.mjs` 经 `export { … } from` 转口保名(消费面 /
|
|
6
|
+
* 原档 `conventions.mjs` 经 `export { … } from` 转口保名(消费面 / 单元测试档 import 面零改)。
|
|
7
7
|
*/
|
|
8
8
|
import { existsSync, statSync } from "node:fs"
|
|
9
9
|
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path"
|
|
10
|
-
import { DEFAULT_MANIFEST, manifestFilePath, readManifest } from "./manifest.mjs"
|
|
10
|
+
import { DEFAULT_MANIFEST, manifestFilePath, owningProject, readManifest } from "./manifest.mjs"
|
|
11
11
|
import { logEvent } from "./log.mjs"
|
|
12
12
|
|
|
13
13
|
/** Retired declaration carrier (project-root relative). Its EXISTENCE is checked, its
|
|
@@ -216,3 +216,19 @@ export function loadProjectDeclaration(cwd) {
|
|
|
216
216
|
_cache.set(file, decl)
|
|
217
217
|
return decl
|
|
218
218
|
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* 辖域单点(F11 · 2026-10-08 · #1104 · PORTABILITY §3.2 行 8 / §3.9 / D20):目标**绝对路径**
|
|
222
|
+
* ⇒ 沿祖先链取**最近**带档目录(`owningProject` 归属形——nearest wins ∕ 纯 fs)⇒ 该项目的声明
|
|
223
|
+
* 对象(`loadProjectDeclaration` 本体零改——按档路径缓存);祖先链无档 ⇒ `null` = **出辖**
|
|
224
|
+
* (不在任何 manifest 树内——父侧门不判不拦)。
|
|
225
|
+
* **发现梯(`discoverProjects`)不参与**——无候选 ∥ 歧义均出辖(不在无档面凭空落脚)。
|
|
226
|
+
* 相对形由消费面按会话 cwd 先解析(判据 = 目标所属项目,非会话 cwd)。
|
|
227
|
+
* @param {string} targetAbs 目标绝对路径(目录 / 文件)
|
|
228
|
+
* @returns {Readonly<object>|null} 所属项目的声明对象 / null = 出辖
|
|
229
|
+
*/
|
|
230
|
+
export function declarationForTarget(targetAbs) {
|
|
231
|
+
const root = owningProject(targetAbs)
|
|
232
|
+
if (!root) return null
|
|
233
|
+
return loadProjectDeclaration(root)
|
|
234
|
+
}
|
package/escape.mjs
CHANGED
|
@@ -52,7 +52,7 @@ export function escapeLiteralEscapes(text) {
|
|
|
52
52
|
return out
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
/** 净化孤立 UTF-16 代理对(毒源②):高代理无低代理跟随 / 低代理无高代理前置 →
|
|
55
|
+
/** 净化孤立 UTF-16 代理对(毒源②):高代理无低代理跟随 / 低代理无高代理前置 → 剥除(出串零该码元——码 :66/:68 实读为准)。 */
|
|
56
56
|
export function sanitizeLoneSurrogates(text) {
|
|
57
57
|
text = String(text ?? "")
|
|
58
58
|
let out = ""
|
package/file-links.mjs
CHANGED
|
@@ -22,7 +22,7 @@ export const MAX_LINKS = 50
|
|
|
22
22
|
/**
|
|
23
23
|
* 工具结果文本 ⇒ 验存文件链接(去重 + 封顶)。`cwd` = 项目根(相对 token 的解析基);缺 ∕ 空 ⇒ 相对 token
|
|
24
24
|
* 一律不检(禁假链接);绝对 token 与 cwd 无关,照检。`probe` = 盘面探针注入缝(`{ existsSync(p), statSync(p) }`——
|
|
25
|
-
* `node:fs` 同名面;桌面端侧注入)。零文本(`""` ∕
|
|
25
|
+
* `node:fs` 同名面;桌面端侧注入)。零文本(`""` ∕ 非串)先于探针校验早退(零判据 —— 探针不检)。
|
|
26
26
|
*/
|
|
27
27
|
export function extractFileLinks(cwd, text, probe) {
|
|
28
28
|
if (typeof text !== "string" || text === "") return []
|
package/file-refs.mjs
CHANGED
|
@@ -26,7 +26,7 @@ function probeOf(probe) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/** Scan text for @path references and replace with inline file content.
|
|
29
|
-
* 零文本(非串 ∥
|
|
29
|
+
* 零文本(非串 ∥ 空串)先于探针校验早退(零判据——探针不检,同序沿核 `file-links.mjs:28`)。 */
|
|
30
30
|
export function injectAtRefs(text, cwd, probe) {
|
|
31
31
|
if (typeof text !== "string" || text === "") return text
|
|
32
32
|
const io = probeOf(probe)
|
package/history-window.mjs
CHANGED
|
@@ -112,7 +112,7 @@ function pairToolEntries(history) {
|
|
|
112
112
|
* `opts.records === true`(opt-in —— 留档批 · #719)⇒ `digest` ∕ `subagent` 两型
|
|
113
113
|
* 记录原样入窗(`{ ...record, idx }`,字段零改名)并占条目位(游标按条目推进);
|
|
114
114
|
* 记录不参与 turn ∕ 可见性谓词(`visible` 面语义不动)。**默认关** ⇒ 输出与改前
|
|
115
|
-
*
|
|
115
|
+
* 逐字等价(负控分支);缺 `opts` ∕ 非 `true` ⇒ 同默认径。
|
|
116
116
|
*/
|
|
117
117
|
export function historyWindow(history, before, pageSize = HISTORY_PAGE_SIZE, opts = {}) {
|
|
118
118
|
const total = Array.isArray(history) ? history.length : 0
|
package/i18n.mjs
CHANGED
|
@@ -41,6 +41,9 @@ export const CORE_MESSAGES = Object.freeze({
|
|
|
41
41
|
"digest.turnLabelAsk": { en: "[auto-turn: answering ${from}: ${msg}]", zh: "自动回合:答复 ${from}:${msg}" },
|
|
42
42
|
"digest.capAuto": { en: "[auto-turn: continuing past turn cap…]", zh: "自动回合:越过轮次上限,继续推进…" },
|
|
43
43
|
"digest.capStop": { en: "[auto-turn stopped at ${turns} turns — partial digest; finished reports stay in history]", zh: "自动回合在 ${turns} 轮处停止——部分消化;已完成的报告保留在历史中" },
|
|
44
|
+
// 消化账务批 · 2026-10-05 · 台账 #930(§6.31.6 端面残余行——判据 = `unsettledCount(carrier)`;
|
|
45
|
+
// 消费面 = CLI 痕行 ∥ 桌面 / VSC 行元素;三端共读本键)
|
|
46
|
+
"digest.residue": { en: "${n} background report(s) not accounted — they will be re-delivered", zh: "有 ${n} 份后台报告未销账——将自动重投" },
|
|
44
47
|
// ── 限流 / 供应商状态行 ──
|
|
45
48
|
"status.rateWait": { en: "TPM throttle wait ~${s}s", zh: "TPM 限流等待 ~${s}s" },
|
|
46
49
|
"status.rateLimited": { en: "Rate-limited 429, retry in ${s}s", zh: "限流 429,${s}s 后重试" },
|
|
@@ -67,8 +70,12 @@ export const CORE_MESSAGES = Object.freeze({
|
|
|
67
70
|
"status.stopped": { en: "[stopped]", zh: "[已停止]" },
|
|
68
71
|
"status.thinking": { en: "Thinking", zh: "思考中" },
|
|
69
72
|
"status.turn": { en: "turn ${n}/${m}", zh: "轮次 ${n}/${m}" },
|
|
73
|
+
// ── 团队登录态(登录面补全批 · 2026-10-10 · 台账 #1231——TEAM.md §2.6:已失效段 ∥ 面板失效行;图形端经核字典投影直取)──
|
|
74
|
+
"status.team.invalid": { en: "Session expired — log in again", zh: "已失效——重新登录" },
|
|
70
75
|
"tool.running": { en: "running…", zh: "执行中…" },
|
|
71
76
|
"tool.done": { en: "done", zh: "完成" },
|
|
77
|
+
// ── 台账(ledger)· F-LX3 变体键库首跑提示(批 2026-10-05-ledger-variant-db-notice · 台账 #935 · LEDGER.md §12.2 逐字;消费面 = CLI TUI 启动行)
|
|
78
|
+
"ledger.variantDbNotice": { en: "Legacy variant-key ledger DB found for this project (pre-upgrade; rows may be outside the current ledger): thincoder ledger audit to inspect; thincoder ledger migrate to reconcile", zh: "检测到本项目的存量变体键台账库(升级遗留,数据可能未并入当前库):thincoder ledger audit 查看;thincoder ledger migrate 收正" },
|
|
72
79
|
})
|
|
73
80
|
// 深度冻结(机器消费面冻结——嵌套 { en, zh } 也冻结;浅冻结下值可被静默改写)。
|
|
74
81
|
for (const entry of Object.values(CORE_MESSAGES)) Object.freeze(entry)
|