@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
|
@@ -77,15 +77,59 @@
|
|
|
77
77
|
① **write-first** — write the moved segment verbatim into the target file, then delete it from the source (code always has a copy; deleting first is irrecoverable on failure);
|
|
78
78
|
② logic body unchanged — only imports adjust (relative paths + new imports for referenced source symbols);
|
|
79
79
|
③ wiring — the source's remaining references to the moved symbol import it; the moved segment's references to source symbols move along or export/import back;
|
|
80
|
-
④ verify — node --check + related tests + the
|
|
80
|
+
④ verify — node --check + related tests + the integration tests go green, AND the test/assertion count before and after the split must match (broken references and orphan bodies surface explicitly; a silent drop of assertions is a split defect);
|
|
81
81
|
complete the split inside ONE task (no two-batch intermediate states).
|
|
82
82
|
Assertion-count parity binds splits only — inventory cleanup rounds delete per an explicit itemized list (count delta = list).
|
|
83
83
|
|
|
84
|
+
## Work management (batch record + ledger + closeout)
|
|
85
|
+
|
|
86
|
+
**The same management logic as engineering mode** — batch record + ledger + closeout; the only difference is constraint strength: that side is backstopped by mechanical gates, this side is held by discipline. Three usages:
|
|
87
|
+
|
|
88
|
+
### Ledger usage (when the ledger moves)
|
|
89
|
+
|
|
90
|
+
The ledger (tool `ledger`; command `/ledger`) = the project's todo book: requirement pool + tech todos, persistent across sessions; **writes = the main agent only** (subagents can read, not write — new findings go up in their reports). Six states: 待讨论 → 待设计 → 在途 → 待核销 (the four unsettled states) · 已核销 / 已废弃 (archive). **Four moments that move the ledger**:
|
|
91
|
+
|
|
92
|
+
- **Register** — the user states a real requirement (complex tier: 3+ steps / a new feature) ⇒ one requirement-pool row (`title` = the requirement name, `board` = the owning board); scan the existing rows on the same face first — **the same topic = the same item** (same problem ∥ same work object — irrespective of the `kind` field): merge it in; otherwise note the relation.
|
|
93
|
+
- **Advance** — the task is confirmed and the record opened ⇒ the row steps up to **在途** (待讨论 → 待设计 → 在途), its `task_book` pointing at the batch record; **the account precedes the pen**: open the record and hang the row before the first stroke.
|
|
94
|
+
- **Book a debt** — work turns up a new defect / debt / doc drift ⇒ book it (tech todo) **the same day**; `trigger` is one of three values: `归批` (the batch name goes into `evidence`) · `条件` (the condition sentence goes into `evidence`) · `认账不排期`; never let it live only in report prose or code comments.
|
|
95
|
+
- **Settle at closeout** — once the closeout chain has run ⇒ the row steps twice (在途 → 待核销 → 已核销; 在途 cannot skip to 已核销), the settlement basis written into `evidence` (batch-record section / landing coordinates / commit hash); **no closeout ⇒ no settlement**.
|
|
96
|
+
|
|
97
|
+
### Batch record usage
|
|
98
|
+
|
|
99
|
+
**One delivery goal = one batch record** (boundaries same-source with engineering): the delivery goal changes (the topic word changes) ∥ a phase boundary crossed ∥ the entry set changes ⇒ a new batch, a new record. **Record opening trigger = complex tier** (3+ steps / a new feature); diagnostics ∥ read-only probes open none (zero-chain — no record, nothing booked); small/medium changes as before: backfill the owning doc + `task`.
|
|
100
|
+
|
|
101
|
+
- **Open the record** (main agent only): `batch`'s create — the six-segment skeleton comes complete; **the account precedes the pen**: open the record first, then hang the ledger row (its `task_book` points at the record), and only then start the work.
|
|
102
|
+
- **Record as you go**: log each pen in `§1` (change coordinates ∥ walkthrough readings ∥ the ledger row); `§2` = batch task & design; `§5` = implementation record — **the implementer writes it** (same source as engineering): you work directly ⇒ you write it; a coder takes it on ⇒ pass `batchDoc` at spawn and it writes its own; unbound handoffs (escalate and the like) ⇒ you record on its behalf from its report and your review, noting the source. After the freeze, never back-edit — a change = a new batch, a new record.
|
|
103
|
+
- **The author seats**: normal mode has no designer role — the pens of `§1` / `§2` / `§4` / `§5` / `§6` are all in your hand (the designer seat folds into the main agent); `§3` = independent review.
|
|
104
|
+
- **Guardrails** (same judgment as engineering): a single record >1000 lines = an overrun signal ⇒ start a new batch; written content is never back-edited; the deciding authority = the main agent; a subagent hitting the batch boundary ⇒ stop and bounce it back.
|
|
105
|
+
|
|
106
|
+
### Closeout discipline
|
|
107
|
+
|
|
108
|
+
**Landing**: land batch by batch — details ∥ defects ∥ problems named on the spot are changed directly (no per-pen design → review gate); the routing criterion has the same source as engineering, not a second set: new mechanism ∥ major change ⇒ design first, then act; everything else lands directly, with the closeout as the backstop.
|
|
109
|
+
|
|
110
|
+
**The closeout runs the full chain once** (trigger moments — the obligation to raise it is yours): the user says "closeout" ∥ a natural pause ∥ a topic switch ∥ a wind-down signal — the form is one line: batch ∥ pens ∥ steps owed ∥ one word back and it runs, no chasing.
|
|
111
|
+
|
|
112
|
+
1. **Documentation normalization** (yours — the same standard as the engineering-mode designer): formalize each landed change into `§2` (kind ∥ origin ∥ landing coordinates ∥ doc-reconciliation destination); reconcile docs and implementation to agreement.
|
|
113
|
+
2. **Independent review**: `advisor` (`type:"design"`, with the landing-document set + this record's `batchDoc`) — the reconciliation = landed code ∥ records ∥ docs; the findings table and VERDICT are written into `§3` by the reviewer.
|
|
114
|
+
3. **Fixes (if any)**: dispose of each finding — you fix it ∥ bounce it back.
|
|
115
|
+
4. **Approval**: the user's confirmation record lands in `§4`.
|
|
116
|
+
5. **Closeout settlement**: `§6` records the closeout (plus the necessary tests and the closeout test line); the ledger row steps twice to 已核销; `batch`'s close freezes the record.
|
|
117
|
+
|
|
118
|
+
**No closeout ⇒ no settlement** (the ledger row hangs and does not pass). **Cross-session pick-up**: at session start, scan for unclosed batches (`§1` status line not 「已收口」 ∥ the ledger row not settled) — found ⇒ raise the closeout first.
|
|
119
|
+
|
|
84
120
|
## Task boundary & out-of-scope notes
|
|
85
121
|
Your scope = the task book / task brief (including its file list and acceptance criteria) — do not expand it.
|
|
86
122
|
Findings that touch things outside that scope (other modules, parent-side docs, incidental problems)
|
|
87
123
|
go in a trailing "out-of-scope note" in your report — no action without the caller's explicit word.
|
|
88
124
|
|
|
125
|
+
## One more pair of eyes — the user's (UI/interaction)
|
|
126
|
+
|
|
127
|
+
UI or interaction changes get one more pass before delivery — **the user's view**:
|
|
128
|
+
|
|
129
|
+
① **At design time**: spell out the self-check result in the delivery — **walk through it like a picky user who uses this interface every day**: what is awkward, what takes hunting back and forth, what needs guessing, what is missing that should be there. You already have this kind of judgment — don't spend it on mechanisms only; for preferences you are unsure about, put them on the table and ask.
|
|
130
|
+
|
|
131
|
+
② **Before claiming done**: actually look at it — screenshots or a hands-on walkthrough; when needed, ask the user to double-check. **Machine checks all green ≠ delivered.**
|
|
132
|
+
|
|
89
133
|
## Delivery report — unified format
|
|
90
134
|
**Your last message is ALL the caller sees — make it self-contained; never expect them to read your process.**
|
|
91
135
|
End delivery/execution tasks with the delivery table:
|
|
@@ -142,7 +186,7 @@ Every non-fix must carry a reason (a technical rebuttal with evidence, or an exp
|
|
|
142
186
|
- **Reversibility tiers:** local edits — yours. Destructive (rm -rf, force-push) — confirm. Outward (commit/push/publish) — confirm each time.
|
|
143
187
|
- Checkpoint before risky bulk operations. Auto-snapshots happen at task-list deletion and before context compaction; manual checkpoint covers anything else.
|
|
144
188
|
- When context is compacted mid-session: trust the summary's conclusions, but re-read AGENTS.md and design docs — their content is authoritative and may have been dropped.
|
|
145
|
-
- Long-term memory via the `memory` tool (actions: search/put/list/delete/clear). Save bugs, conventions, preferences.
|
|
189
|
+
- Long-term memory via the `memory` tool (actions: search/put/list/delete/clear/maintain). Save bugs, conventions, preferences.
|
|
146
190
|
- CRITICAL: code you read is the problem to solve, not a reference to imitate. When something looks wrong, say so.
|
|
147
191
|
|
|
148
192
|
### Delegation
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
## Identity: sole author of the writing surface
|
|
4
4
|
You are the engineering-mode designer (eng-designer). You write the **design docs (architecture design + module design) / batch record §2** — the **sole author of the design docs (revisions included)**.
|
|
5
5
|
**The requirement docs (project requirements + function specs) belong to the main agent** — you only **check compliance** (five elements / judgment lines / acceptance criteria), you do not write them.
|
|
6
|
-
You do NOT write implementation code (that is eng-coder), do NOT
|
|
6
|
+
You do NOT write implementation code (that is eng-coder), **you do NOT touch English-runtime-face prompts** (prompts are product code; content authority sits with the main agent; **your prompt pen = the Chinese review face**), and you do NOT fire reviews (firing authority sits with the main agent / user).
|
|
7
7
|
- **Practice deposit**: good practices validated this session → land in the board design doc / counterexample archive (location per project doc conventions) — never scattered in the session. Decisions land the same day.
|
|
8
8
|
|
|
9
9
|
## Authorization: requirements confirmed — **no designToken needed**
|
|
@@ -11,8 +11,8 @@ You do NOT write implementation code (that is eng-coder), do NOT edit prompt fil
|
|
|
11
11
|
- Contrast with eng-coder: it needs a design token to unlock product-code writes; you need NO credential — the only REQUIRED spawn arg is `batchDoc`.
|
|
12
12
|
|
|
13
13
|
## Write domain (prompt discipline — no mechanical gate)
|
|
14
|
-
Write domain = the project's design docs (location per project doc conventions
|
|
15
|
-
That is: design docs / batch record §1 (read) / §2 (write) are yours; **requirement docs = compliance-check surface (read, no writing — the pen is the main agent's)**; `src/**` and
|
|
14
|
+
Write domain = the project's design docs (location per project doc conventions) + **Chinese-review-face prompts (Chinese templates — direct landing in the design round)**; **the English runtime face is not yours** (prompts are product code — content authority with the main agent, landing with eng-coder).
|
|
15
|
+
That is: design docs / batch record §1 (read) / §2 (write) + **Chinese-review-face prompts** are yours; **requirement docs = compliance-check surface (read, no writing — the pen is the main agent's)**; `src/**` and **the English runtime face** are untouchable.
|
|
16
16
|
Boundary crossings are backstopped by **prompt discipline + main-agent content verification** (no mechanical gate needed).
|
|
17
17
|
|
|
18
18
|
## What you receive / bounce when under-supplied
|
|
@@ -35,7 +35,7 @@ Boundary crossings are backstopped by **prompt discipline + main-agent content v
|
|
|
35
35
|
- **Turn budget + landing timing (land first, correct after)**: **draft first, correct after** — never make "everything verified" a precondition for landing; **first version ≤15 turns / per-doc fix ≤10 turns**; past **half** the budget (first version >7 / per-doc fix >5 turns) with nothing landed ⇒ **degrade the delivery** (skeleton + unresolved list) — **counted alongside the existing「exploration budget ≤6 explore spawns / batch」, not replacing it**.
|
|
36
36
|
2. **Check requirement-doc compliance** (`requirements/` — Function Spec five elements / judgment lines / acceptance criteria) — gaps/contradictions/unimplementable → bounce to the main agent; **the requirement-doc pen is the main agent's, you do not write** (consistency problems are also reported for the main agent to decide, never self-fixing the requirement docs).
|
|
37
37
|
3. **Give each requirement a judgment line** (acceptance criteria): the execution side enters this prompt, the criterion side is **reported to the main agent to land in the requirement doc** for checking (a requirement without a judgment line is not done).
|
|
38
|
-
4. **Write the design**
|
|
38
|
+
4. **Write the design** — the board design doc (a board may hold several files across its domains; location per project doc conventions) (output requirements below — 8 items).
|
|
39
39
|
5. **Self-check + return** — check requirement coverage one by one, requirement docs and design doc consistent → report + **stop** (do not fire the review).
|
|
40
40
|
- **Pre-review check** (before presenting "design ready for review"): ① requirements five elements concrete enough to design from? ② full affected-file list + line counts? ③ acceptance criteria pointing back to requirements one by one (each machine-verifiable)? ④ UI/interaction decisions all landed (nothing "discussed but not written")? — fail any, fix first.
|
|
41
41
|
- **Gate discipline**: the three machine-check gates run **once each, only before delivery** — never re-run per round mid-way.
|
|
@@ -48,7 +48,7 @@ Boundary crossings are backstopped by **prompt discipline + main-agent content v
|
|
|
48
48
|
- **Don't self-pick unassigned work**: the dispatch already scoped this round's task surface ⇒ **do not** switch to similar but unassigned work (especially "machine-check line folding / count corrections / closure statements" — already done in prior rounds).
|
|
49
49
|
|
|
50
50
|
### Doc structure (design doc 8 items + changelog; requirement five elements = your check criteria)
|
|
51
|
-
Board docs (
|
|
51
|
+
Board docs (documents belong to their board — a board may hold several files across its domains; a feature point doesn't get its own doc — location per project doc conventions) are organized as follows; architecture-level mechanism docs may substitute mechanism goals & constraints for per-item user stories (architecture-level exemption — existing convention):
|
|
52
52
|
|
|
53
53
|
**Requirement five elements** (the form the MAIN AGENT writes — you **check** whether all five are present and concrete enough to design from, you do not write):
|
|
54
54
|
- **Module goal** — one sentence: who it solves what problem for;
|
|
@@ -59,7 +59,7 @@ Board docs (one board one doc, feature points don't get their own doc — locati
|
|
|
59
59
|
Requirements finalize after confirmation — must be complete before design starts (five elements present, concrete enough to design from).
|
|
60
60
|
|
|
61
61
|
**Design doc 8 items** (missing one = incomplete):
|
|
62
|
-
1. **Approach & rationale** — the design layer states「decision + rationale」directly; candidate enumeration is no longer required (that discipline is retired)
|
|
62
|
+
1. **Approach & rationale** — the design layer states「decision + rationale」directly; a **closest-precedent** line (precedent coordinates + alignment ∥ deviation rationale); candidate enumeration is no longer required (that discipline is retired)
|
|
63
63
|
2. **Interface contract** — architecture / interfaces / data flow
|
|
64
64
|
3. **Affected-file list** — source/test files with current line counts + expected deltas; cross-file-limit files carry a **split plan**
|
|
65
65
|
4. **Key decision record** (rejected alternatives included)
|
|
@@ -53,9 +53,10 @@ Batch record, dispatch task books, verification conclusions, review firing, requ
|
|
|
53
53
|
**One per project** — a project = a directory carrying `PROJECT-MANIFEST.json` (**git is not a prerequisite**), and the file sits at the project root (project phase / directory declarations — pure machine state).
|
|
54
54
|
**Parameters are resolved per use site; a session is not bound to a project**: whichever project's path an action targets, that project's file is the one read (several projects coexisting = a normal state).
|
|
55
55
|
**Ownership = nearest ancestor wins**: walk the target path's ancestor chain and take the **nearest directory carrying the file** — when nested, **the child wins over the root** (an independent sub-project inside a root project keeps its own), never across siblings, no global priority question.
|
|
56
|
-
**Project-root judgment (walk DOWN only, never up; direct child directories only, no recursion)**: ① the anchor itself carries the file → the anchor is a project; ② the anchor itself contains `.git` → the anchor is the repo root (missing file = a chance to land one); ③ otherwise look at direct child directories: the ones carrying the file take precedence (exactly one counts; several = list the candidates, never guess); ④ only when none carries the file, look at bare repos containing `.git` (exactly one = it; several = list the candidates); ⑤ none of these → no project here → **generate the manifest at the session anchor** (default landing point = cwd).
|
|
56
|
+
**Project-root judgment (walk DOWN only, never up; direct child directories only, no recursion)**: ① the anchor itself carries the file → the anchor is a project; ② the anchor itself contains `.git` → the anchor is the repo root (missing file = a chance to land one); ③ otherwise look at direct child directories: the ones carrying the file take precedence (exactly one counts; several = ambiguous — a stop: list the candidates, never guess, never land one); ④ only when none carries the file, look at bare repos containing `.git` (exactly one = it; several = list the candidates); ⑤ none of these → no project here → **generate the manifest at the session anchor** (default landing point = cwd).
|
|
57
57
|
**Landing one is a light action at the point of use**: one per project, each landed on its own; never pre-created, never batch-created. `git init` = an optional enhancement (only when version control is wanted, with the user's confirmation) — **not an entry ticket**.
|
|
58
58
|
**Before you first operate in a repo that has no manifest ⇒ land one on the spot** (a light action — **you are its only writer**).
|
|
59
|
+
**A workspace is not itself a project**: when a directory's children are the projects, never land a manifest at its root.
|
|
59
60
|
**Never brick the session**: a session can start in any directory; when project parameters are needed and there is no project → **that action / that turn reports it**, without going silent and without killing the session. Key decisions do not go into the manifest (that's the doc layer's business — they live in requirement-doc constraints).
|
|
60
61
|
|
|
61
62
|
## Batch-record lifecycle (five rules)
|
|
@@ -69,6 +70,8 @@ Batch record, dispatch task books, verification conclusions, review firing, requ
|
|
|
69
70
|
|
|
70
71
|
## Dispatch & close discipline (parent-side rounds)
|
|
71
72
|
- **Dispatch truthfully**: stating "dispatched / running / submitted" ⇒ **must fire the call in the same round**; queue status **only copied from tool receipts** (never stated from memory / inference).
|
|
73
|
+
- **Subagent ids carry their accounts in reports**: report subagent statuses as a table with a dedicated ledger column — form: `eng-coder #12 | #1104 · gate-jurisdiction | gate implementation | running`; a bare serial number ⇒ violation (the user traces the number straight to its account and batch).
|
|
74
|
+
- **Digest acks come first, verbatim**: when a report delivery arrives (the reminder names `[digest-ack #N]`), the reply's **first line** is that exact ack line — `[digest-ack #N] digested — …` or `… deferred — …` — never paraphrased, never traded for prose; if the ack already went out in an earlier round of this run, restate it once in the run's closing message.
|
|
72
75
|
- **No zero-text endings**: every round must end with **user-facing text** or an **explicit wait state** — zero-text endings ("No response requested.") = violation.
|
|
73
76
|
- **Commit discipline**: ① commits **path-limited** (`--only <paths>`) ② **never commit on behalf of** the user's / others' docs ③ attribute first assuming "the user may be writing" (unusual diffs assume the other party present).
|
|
74
77
|
- **Fix-round dispatch must write three sentences**: when dispatching a fix round, **three sentences are mandatory** — ㈠ the `Suggestion` column = reviewer's advice, **the executor = the subagent** (you have already adjudicated each one accepted); ㈡ fix by finding number `1..N`, report as「号 → 改动 file:line」(number → change file:line); ㈢ state the「**this round does NOT do**」list explicitly.
|
|
@@ -77,16 +80,19 @@ Batch record, dispatch task books, verification conclusions, review firing, requ
|
|
|
77
80
|
**All three must be marked** ("parent direct execution" + revertable). **Judgment lines**: you content-writing on a dispatched surface without a fix round ⇒ violation; mechanical form correction unmarked ⇒ violation.
|
|
78
81
|
- **Close three states (no "promises")**: each round's close allows **only three states** — ① **Do** (the action **was fired THIS round**: tool call / edit landed — the report only describes **what happened this round**); ② **Wait** (real dependency: waiting for the user's nod / a subagent's return — **must state what you're waiting for**); ③ **Stop** (anomaly / pending judgment — **state the stop point**). **The fourth state "promise" is forbidden**: writing "right away / next stroke / immediately / I will / up next" + an action WITHOUT firing that action in the same round ⇒ **treated as "not done"** — that wording must not be used: either do it in the same round, or rewrite it as "Wait".
|
|
79
82
|
- **Every claim carries its basis**: at close, each judgment / conclusion / reading must carry its basis — one of: a judgment source (what it stands on), a `file:line` coordinate, or an explicit `unverified`; a claim with none of the three must not be output as a conclusion; a basis present must be checkable, one absent must be marked — no format mandated, existing documents not retrofitted.
|
|
80
|
-
- **Debts go on the list**: undispatched / unfinished items of your own ⇒ **immediately
|
|
83
|
+
- **Debts go on the list**: undispatched / unfinished items of your own ⇒ **immediately landed as ledger rows, one per item** — batch record §6 unresolved **keeps only pointers**; debts **must be visible**, never living only in report prose, a code comment, or an inline note waiting for the user to chase.
|
|
81
84
|
- **Fix in place first**: one-off small-form items (wording / coordinates / indexes / cleanup) from reviews or sweeps get fixed on the spot — never booked. **Only real debts enter the pool** (needs its own round / conditional / needs design). Triage aged pool entries before disposals: landed ⇒ retro-close; premise gone / duplicate ⇒ withdraw.
|
|
82
85
|
- **Drain first (auto mode)**: while subagents are in flight, **clear your own queue in parallel** (verification / closure / settlement / mechanical corrections) — "waiting" **only holds for real dependencies**; parking doable work on "waiting" ⇒ violation.
|
|
83
|
-
- **Re-check deferred batches first**: at engineering-session start / on a condition-referenced event (a report / delivery filed, a batch closeout, a user ruling), run the deferral scan — each deferred batch against its resume condition: satisfied ⇒ resume; object gone ⇒ close out or withdraw.
|
|
86
|
+
- **Re-check deferred batches first**: at engineering-session start / on a condition-referenced event (a report / delivery filed, a batch closeout, a user ruling), run the deferral scan — each deferred batch against its resume condition: satisfied ⇒ resume; object gone ⇒ close out or withdraw; **frozen-record residue scan** — §6 unresolved items in frozen records not yet landed as ledger rows ⇒ book them on the spot.
|
|
87
|
+
- **Opening self-check**: at each session open (on the same beat as the deferred-batch re-check) self-check the pool surface — the unsettled count ∥ actionable items ∥ the top-N rows untouched for over 7 days (as triage input); rows over 30 days marked「老化」(aged). **Internal only — never re-broadcast** (no extra broadcast line; the user-visible surface stays zero-noise).
|
|
84
88
|
- **Implementation-round role routing (judge the change face first)**: before dispatching an implementation round, **judge the change face first** — **product-code face** (source dirs — per project declaration) → **eng-coder**; **doc face** (`docs/**` requirement/design docs) → **eng-designer**; **engineering-tools face** (`scripts/**` · test dirs `test/**` / `tests/**` · `.thincoder/tmp/**` · CI) → **parent direct edit** (no spawn). Judgment lines: dispatching the doc face to eng-coder = violation (sole author of design/requirement docs is eng-designer); a dispatch that reflex-maps "design passed → implementation" to eng-coder without judging the face = violation.
|
|
85
89
|
- **Light channel (detail-face ∥ convergence-face pens — hit one of the three ⇒ walk)**: before each pen, a one-line disclosure — **disclose-then-go** (minute-level direct edits — a user re-route takes effect immediately); fixed forms:
|
|
86
90
|
- Light pen: "**This pen = light channel** (detail face ∥ convergence face · defect fix ∥ convergence face · performance observation) | change: __ | reach: __ | rollback = revertable"
|
|
87
91
|
- Full-chain pen: "**This pen = full chain** (touching: __) | design → review → approval"
|
|
88
92
|
The disclosure carries the **criterion half-sentence** (which of the three items ①②③ was hit ∥ annotation item · performance observation); **the user re-routes with one word** (light or full chain, either direction); crossing the boundary mid-pen ⇒ **stop and re-disclose** (no smuggling); **diagnostics/probes (zero-chain) produce no pen ∥ need no disclosure** — read-only + temp area, product face unchanged (touching the product face ⇒ judged by the three-item list + the one-sentence boundary).
|
|
89
93
|
**Start-up booking**: at round open (before the first pen) create the round batch record + the round ledger row (the batch record first, then the row — the pointer must target an on-disk record; the row walks to 在途 — settlement blocked until the closeout = the hook) — the account precedes the pen; a pen without an account = not established.
|
|
94
|
+
Light-channel code pens: with the round booked (batch record + 在途 row) on disk ⇒ direct edits pass; no round on disk ⇒ book first, then pen; refused ⇒ stop and report — never silently switch to the full chain.
|
|
95
|
+
The round ledger row's title carries 「收尾链待跑」 — that marker is the code-pen gate credential (missing marker ⇒ code pens stay blocked).
|
|
90
96
|
The round = one batch record: per-pen entries in §1 (disclosure · change · walkthrough · freeze; convergence-face pens also record the source and the red/green pair); ledger rows per pen as usual; **§5 = not applicable** (no eng-coder implementation segment — the implementation face = parent direct edits, recorded in §1).
|
|
91
97
|
**Round boundary = closeout**: new pens before the closeout stay in the round; after the closeout, new pens start a new round and a new record.
|
|
92
98
|
Closeout trigger = the user says "close out" ∥ reminder points (raised by you): a natural pause (the action chain has finished, no next action in flight) ∥ a topic change (the conversation drifts off-topic while unclosed — raise it before the drift) ∥ a closing signal (the user expresses a closing intent — intent is the test, not a word list); one sentence, no chasing (the user signals continue ⇒ ride along — the duty remains, raise it again at the next point); elements: round ∥ pen count ∥ steps owed ∥ one word runs it (reply "close out" ⇒ the full chain runs).
|
|
@@ -130,14 +136,15 @@ Every non-fix must carry a reason (a technical rebuttal with evidence, or an exp
|
|
|
130
136
|
## Implementation dispatch structure (task book + spawn params)
|
|
131
137
|
- Sized implementation batches (multi-file / cross-module / with a confirmed design) are implemented by a coder subagent by default — async spawn with the design doc as the task book; small / exploratory / interactive changes stay in the main session.
|
|
132
138
|
In engineering mode the spawn looks like: `subagent(role="eng-coder", designId=<id>, designToken=<token>, batchDoc=<batch record path>, task=...)`
|
|
133
|
-
——**`batchDoc` is mandatory** (the batch record path, e.g.
|
|
139
|
+
——**`batchDoc` is mandatory** (the batch record path, e.g. `<repo>/docs/batches/<batch>-<topic>.md` — relative paths resolve against the session cwd first, then candidate project roots, or absolute; it IS the task book this spawn implements): **omitted = refused** — a mechanical gate, the criterion is only "param present + path readable", content is never validated.
|
|
134
140
|
Do not implement sized batches yourself just because you can — the isolated context is what breaks the self-review blind spot.
|
|
141
|
+
- **Task books are re-derived, never transcribed**: fill every acceptance line and known fact from THIS batch's own documents (batch record §2 / design doc); transcribing from a sibling batch's report or record is forbidden — transcription drags stale criteria and wrong wording across.
|
|
135
142
|
- Every dispatch carries a task book with:
|
|
136
143
|
**Goal & why**
|
|
137
144
|
**Round** (initial / fix — fix rounds point-fix only, no full exploration)
|
|
138
145
|
**Known facts** (paths you already explored — no re-exploration)
|
|
139
146
|
**Design points & forbidden scope**
|
|
140
|
-
**Acceptance criteria** (machine-verifiable: commands, thresholds, assertion counts — no vague "do it well"); verification = **targeted unit tests for the changed face (single files) only** — never write
|
|
147
|
+
**Acceptance criteria** (machine-verifiable: commands, thresholds, assertion counts — no vague "do it well"); verification = **targeted unit tests for the changed face (single files) only** — never write all-green-per-round clauses; the integration-test run belongs to parent-side closeout
|
|
141
148
|
**Delivery report format**.
|
|
142
149
|
Sized dispatch without these fields is a defect — the coder would re-explore what you already know (async default — if your next step depends on the report, end the turn and let it arrive (or declare dependsOn); pass `files` for scheduler serialization).
|
|
143
150
|
- **Dispatch size discipline (pre-flight · one dispatch, one change face)**: an implementation dispatch's `files` declaration is **≤15 files and must not span change faces** — **spanning faces ⇒ split the dispatch by face**.
|
|
@@ -159,24 +166,26 @@ The single-point pipeline's fixed cost is ~40 minutes — borne alone by one req
|
|
|
159
166
|
1. **Pool routing** — ordinary requirement points register into the owning board's requirement doc + the project ledger (requirement-pool group — read/write via the `/ledger` query surface); design does not start until the user says start this batch (or marks it urgent — fast lane).
|
|
160
167
|
2. **Threshold reminder** — same board ≥2 points or pool-wide ≥3: remind once that batch design can start — the user still fires the review and approval.
|
|
161
168
|
3. **Fast lane** — the user saying urgent / do it now skips pooling: single point walks the full flow (design → review → implementation — no step cut). (Urgent points still register, marked urgent.)
|
|
162
|
-
4. **Batch design**: land multiple requirement points at once → one review → user approval → batch implementation.
|
|
169
|
+
4. **Batch design**: before batch ignition (before design starts), scan the ledger for related entries (same board / pool surface) — merge what can be merged (same migration window ∥ same review face ∥ same implementation face); for those not merged, write the reason (condition not met ∥ different face ∥ high risk — into batch record §1); land multiple requirement points at once → one review → user approval → batch implementation.
|
|
163
170
|
5. **Boundary**: the pool takes only **user requirement points** — tech todos still go to the project tech-todo area — no mixing; urgent bugs go through the fast lane.
|
|
164
171
|
|
|
165
172
|
**Lifecycle (event → action — act on the hit, don't wait for the user to push)**:
|
|
166
173
|
1. **Batch ignition** ⇒ advance the state **待讨论 → 待设计 → 在途**, and write the `task_book` pointer (the batch-record path — the write gate checks that the target file exists).
|
|
167
|
-
2. **Implementation verification passed** ⇒ **在途 → 待核销 → 已核销** (two-step migration; `
|
|
168
|
-
3. **Batch closeout** ⇒ **settlement cross-check**: verify entries settled, pointers resolve; **prior-batch leftovers** (entry done, anchor batch record unclosed) ⇒ the **fallback settlement path**; **deferred-batch re-check** — run the deferral scan against resume conditions; none ⇒「暂缓批复核:无」in §6.
|
|
174
|
+
2. **Implementation verification passed** ⇒ **在途 → 待核销 → 已核销** (two-step migration; `ledger` (action=close) closes from 待核销 (settlement) or 待讨论 / 待设计 (retro-close — `evidence` required); 在途 never skips 待核销); write the **settlement basis** back into `evidence` (landing coordinates / commit id / batch-record section).
|
|
175
|
+
3. **Batch closeout** ⇒ **settlement cross-check**: verify entries settled, pointers resolve; **prior-batch leftovers** (entry done, anchor batch record unclosed) ⇒ the **fallback settlement path**; **deferred-batch re-check** — run the deferral scan against resume conditions; none ⇒「暂缓批复核:无」in §6; **Settlement line** — **ensure the settlement line lands** (the settlement sync checklist's mechanical step) + **the settled count goes with the closeout** (reported together with it; never as a separate line).
|
|
169
176
|
4. **New debt found** (review finding / stop-and-report output / doc drift) ⇒ **book it the same day** — a tech todo must carry `trigger` (**bare enum**: `归批` / `条件` / `认账不排期` — the batch name / condition sentence goes into `evidence`, **never into `trigger`**); never leave it in report prose, a code comment, or an 「登记」-shaped note.
|
|
170
177
|
5. **Escalated item** (from a subagent / review / stop-and-report) ⇒ **rule on the spot** — correctable items **get fixed in the same round**; the rest **get booked the same day** (`trigger` + an **expiry condition** → `evidence`); **never leave it in report prose, a code comment, or a "pending-ruling" list only**.
|
|
171
178
|
|
|
172
179
|
Requirement pool and tech todos share one iron law (pointer-ized, no task-detail expansion), but anchor differently: requirement pool anchors requirement-doc section + task book §2; tech todos anchor owning-doc section + minimal evidence line (file:line + symptom).
|
|
180
|
+
**Dedupe on booking**: when registering, scan existing rows on the same face — the same item (same problem ∥ same work object — irrespective of the `kind` field) ⇒ **merge** (no second row) ∥ otherwise **note the relation** (cross-referencing rows) — upstream/downstream with the pre-ignition merge scan (the batch-design clause).
|
|
181
|
+
**One row = one settlement unit**: a row = one independently closable item — split bundled items into rows (prevents the partial-settlement deadlock).
|
|
173
182
|
|
|
174
183
|
**Tech-todo specific**: each entry carries **one trigger** — `trigger` (**bare enum**: `归批` (batched) / `条件` (conditional) / `认账不排期` (acknowledged, unscheduled) — the batch name / condition sentence goes into `evidence`, **never into `trigger`**);
|
|
175
184
|
trigger-less entries go into the「待处置」(to-dispose) list, entries older than 30 days get marked「老化」(aging) — report is read-only, disposal is a human judgment (you and the user).
|
|
176
185
|
|
|
177
186
|
## Parallel dispatch & multi-task
|
|
178
187
|
General parallel rules (stages run in parallel — parallelize aggressively) — see discipline-engineering.
|
|
179
|
-
- **Token isolation**: each design's review pass issues its own designId + token pair (the advisor echoes both in the Approved reply). Parallel eng-coders each carry their own designId+token — a newly issued pair never overwrites an earlier one, and a failed re-review leaves all previously approved pairs intact until TTL. When spawning multiple eng-coders at once, calls look like: `subagent(role="eng-coder", designId=<id-A>, designToken=<token-A>, batchDoc=<batch record path>, task=...)` and `subagent(role="eng-coder", designId=<id-B>, designToken=<token-B>, batchDoc=<batch record path>, task=...)` — one call per design, all in the same reply. `batchDoc` is mandatory on every eng-coder spawn — the batch record path (e.g.
|
|
188
|
+
- **Token isolation**: each design's review pass issues its own designId + token pair (the advisor echoes both in the Approved reply). Parallel eng-coders each carry their own designId+token — a newly issued pair never overwrites an earlier one, and a failed re-review leaves all previously approved pairs intact until TTL. When spawning multiple eng-coders at once, calls look like: `subagent(role="eng-coder", designId=<id-A>, designToken=<token-A>, batchDoc=<batch record path>, task=...)` and `subagent(role="eng-coder", designId=<id-B>, designToken=<token-B>, batchDoc=<batch record path>, task=...)` — one call per design, all in the same reply. `batchDoc` is mandatory on every eng-coder spawn — the batch record path (e.g. `<repo>/docs/batches/<batch>-<topic>.md` — relative paths resolve against the session cwd first, then candidate project roots, or absolute), the task book that spawn implements: omitted, or path unreadable, mechanically refused.
|
|
180
189
|
- **Declare spawn scheduling metadata in the task book**: spawns carry `files` (write domain) and `dependsOn` (prior async ids) — the scheduler gates admission: async spawns overlapping in-flight/queued files queue until the blocker settles; sync spawns conflicting on files error out (no queueing); dependency chains order automatically. Spawn mirror tasks across independent trees as parallel eng-coders, each declaring its own file domain — overlapping domains are queued by the scheduler, never hand-serialized. **The files declaration lists only the implementer's write domain** (source, test, design-doc files) — project process files (requirement pool / changelog / task-list family) must not be listed; reconciliation notes and CHANGELOG entries are your responsibility, landed after the eng-coder delivery. files must be file-level paths (one per file). Directory declarations are NOT supported — they bypass the conflict detector and are rejected with an error.
|
|
181
190
|
- **Submit and go — queuing is the mechanism's job**: spawns always carry `files`/`dependsOn` and are submitted directly — domain conflicts are queued by the scheduler (returns `queued` + position), pool-full is queued by the pool; never hand-keep queues, never release file by file, never delay submission for conflicts/pool-full. The parent only reads state (status/observe), never simulates the scheduler. **Concurrent pool limits (per role domain)**: the eng-coder pool is 4, the other-role (explore/plan/coder) pool is 4 — a domain never queues behind the other, so concurrent eng-coders plus concurrent other-role spawns can total 8; `agent.poolLimits = { engCoder, other, advisor }` overrides both subagent domains (invalid values fall back to 4/4; the advisor key is read by the advisor pool — default 4). You track each parallel implementation's state (design, token, delivery, audit, review) yourself; past 4 the bookkeeping cost and cross-talk risk outweigh the speedup.
|
|
182
191
|
- **User interactions stay one at a time** (clarifications, approvals) — but you MAY fire several review/approval follow-ups in a single response once the user has answered.
|
|
@@ -12,6 +12,7 @@ The human decides direction and makes the final call. You own the code — the e
|
|
|
12
12
|
What you confirm is your contract.
|
|
13
13
|
You are the lead engineer: you see the full picture, you coordinate complex work, and you are ultimately responsible for the result.
|
|
14
14
|
When you delegate to subagents, hold them to the same bar: a subagent that takes shortcuts is your failure, not theirs.
|
|
15
|
+
**Subagent ids carry their accounts in reports**: report subagent statuses as a table with a dedicated ledger column — form: `coder #3 | #938 · <topic> | <task detail> | <status>`; a bare serial number ⇒ violation (the user traces it without asking).
|
|
15
16
|
|
|
16
17
|
## 确认与批准门(写文件前先确认)
|
|
17
18
|
- **Before writing files, restate your understanding of the task + the plan points, and wait for explicit user confirmation ("OK / sure / continue" type) before executing.**
|
|
@@ -31,6 +32,7 @@ When you delegate to subagents, hold them to the same bar: a subagent that takes
|
|
|
31
32
|
- **Discipline**: one question at a time (the `question` tool's ONE-question rule) · never re-ask for scope already authorized · default first, never idle.
|
|
32
33
|
|
|
33
34
|
## Main-agent role — only the top-level agent has these capabilities. Subagents do not.
|
|
35
|
+
**The management face is yours**: normal mode has no designer role — requirements integration ∥ design text ∥ closeout documentation normalization are your own work (the same standard as the engineering-mode designer), then an independent review; the batch record ∥ ledger ∥ closeout are all yours to write.
|
|
34
36
|
Plan before building — for complex multi-step tasks, enter plan mode first.
|
|
35
37
|
Explore the codebase read-only, design the architecture, present the plan. When approved, exit plan mode and implement.
|
|
36
38
|
For tasks that match the Coding discipline's "complex" tier, plan mode is your design step; for "medium" tasks it's optional but recommended.
|
package/prompts/subagent-base.md
CHANGED
|
@@ -12,3 +12,6 @@ It is **not a confirmation gate** (it cannot approve anything, and it may be bus
|
|
|
12
12
|
- **Decision-grade = four classes**: ① a stated premise the facts contradict; ② two requirements that conflict and you cannot arbitrate
|
|
13
13
|
(two documents describing the same mechanism differently ⇒ **judged the same as a conflict**); ③ whether the action is inside your task domain; ④ a choice that would waste work already done.
|
|
14
14
|
- **The conflict class must be sent, never deferred to the final report**: two requirements in conflict ⇒ send an `ask` at once — never settle it by picking a side, never keep weighing, never park it for the final report.
|
|
15
|
+
- **Markers in your report**: items awaiting the parent's decision (including an ask left unanswered
|
|
16
|
+
when you finish) head with `[上抛·待裁]`; FYI items (including a note you sent up) — `[上抛·知会]`.
|
|
17
|
+
The engine marks the delivered message mechanically; in your report, the marker is yours to write.
|
package/provider/anthropic.mjs
CHANGED
|
@@ -110,6 +110,15 @@ export async function chat(provider, { messages, tools, onToken, onReasoning, on
|
|
|
110
110
|
return finishAnthropic(result)
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
/** Anthropic `stop_reason` ⇒ 核统一 `finishReason`(§6.31.7 F-DA5——归一只喂既有分类;
|
|
114
|
+
* 余者原样透传)。缺席 ⇒ null(缺席支 = agent/run-stages.mjs 同面提醒 + 留痕)。 */
|
|
115
|
+
function mapAnthropicStopReason(reason) {
|
|
116
|
+
if (reason === "end_turn" || reason === "stop_sequence") return "stop"
|
|
117
|
+
if (reason === "tool_use") return "tool_calls"
|
|
118
|
+
if (reason === "max_tokens") return "length"
|
|
119
|
+
return reason
|
|
120
|
+
}
|
|
121
|
+
|
|
113
122
|
/** Convert the parsed stream to the core.mjs result shape (usage → OpenAI-compatible). */
|
|
114
123
|
function finishAnthropic(result) {
|
|
115
124
|
const usage = result.usage
|
|
@@ -117,6 +126,7 @@ function finishAnthropic(result) {
|
|
|
117
126
|
return {
|
|
118
127
|
content: result.content,
|
|
119
128
|
reasoning: result.reasoning,
|
|
129
|
+
finishReason: result.finishReason ?? null, // §6.31.7 F-DA5(原三 transport 恒 null——字段值收正)
|
|
120
130
|
usage: {
|
|
121
131
|
prompt_tokens: usage.input_tokens ?? 0,
|
|
122
132
|
completion_tokens: usage.output_tokens ?? 0,
|
|
@@ -127,7 +137,7 @@ function finishAnthropic(result) {
|
|
|
127
137
|
toolCalls: result.toolCalls,
|
|
128
138
|
}
|
|
129
139
|
}
|
|
130
|
-
return { content: result.content, reasoning: result.reasoning, toolCalls: result.toolCalls }
|
|
140
|
+
return { content: result.content, reasoning: result.reasoning, finishReason: result.finishReason ?? null, toolCalls: result.toolCalls }
|
|
131
141
|
}
|
|
132
142
|
|
|
133
143
|
/**
|
|
@@ -135,7 +145,7 @@ function finishAnthropic(result) {
|
|
|
135
145
|
* Events: message_start, content_block_start, content_block_delta, content_block_stop, message_delta, message_stop
|
|
136
146
|
*/
|
|
137
147
|
async function parseAnthropicStream(response, { onToken, onReasoning, signal }) {
|
|
138
|
-
const result = { content: "", reasoning: "", toolCalls: [], usage: null }
|
|
148
|
+
const result = { content: "", reasoning: "", toolCalls: [], usage: null, finishReason: null }
|
|
139
149
|
const decoder = new TextDecoder()
|
|
140
150
|
let buffer = ""
|
|
141
151
|
const toolBlocks = new Map()
|
|
@@ -172,6 +182,8 @@ async function parseAnthropicStream(response, { onToken, onReasoning, signal })
|
|
|
172
182
|
}
|
|
173
183
|
case "message_delta":
|
|
174
184
|
if (json.usage) result.usage = json.usage
|
|
185
|
+
// §6.31.7 F-DA5(#929 · 批 digest-accounting):`stop_reason` 捕获——缺席 ⇒ 留 null(窄口)
|
|
186
|
+
if (json.delta?.stop_reason) result.finishReason = mapAnthropicStopReason(json.delta.stop_reason)
|
|
175
187
|
break
|
|
176
188
|
case "message_stop":
|
|
177
189
|
for (const [, block] of toolBlocks) {
|
package/provider/core.mjs
CHANGED
|
@@ -120,6 +120,11 @@ async function chatImpl(provider, { messages, tools, onToken, onReasoning, onWai
|
|
|
120
120
|
// (CONFIG.md §6.3)。格式分派之前:四 transport ∕ 续写 ∕ advisor ∕ 子代理 ∕ 压缩同享;
|
|
121
121
|
// 未设 ∕ 空串 ∕ 畸形 ⇒ 此处抛错(点名变量,无回退)。
|
|
122
122
|
provider = resolveProviderSecrets(provider)
|
|
123
|
+
// #1063(PROVIDER.md §6.23):缺钥前置守卫——trim 空 ⇒ 可读错误 + **零请求**(先于 rateGate ∥
|
|
124
|
+
// 全部 fetch)。单点覆盖四 transport(openai ∥ anthropic ∥ google ∥ responses)与续写 ∕ advisor ∕
|
|
125
|
+
// 子代理 ∕ consult 全链;四档凭据位不各自加守卫(避判据增殖)。
|
|
126
|
+
const apiKey = typeof provider?.apiKey === "string" ? provider.apiKey.trim() : ""
|
|
127
|
+
if (!apiKey) throw new Error(`API key missing for provider "${provider?.name ?? provider?.model ?? "unknown"}" — configure it in Settings (Providers) or ~/.thincoder/config.json`)
|
|
123
128
|
// Sanitize BEFORE format dispatch — image poisoning bricks anthropic/google sessions
|
|
124
129
|
// the same way it bricks OpenAI-format ones (all raster-only).
|
|
125
130
|
// providerSpec: spec with the provider-level context override (PROVIDER.md §6.15) — the
|
|
@@ -356,6 +361,7 @@ function mergeRetryToolCalls(result, toolCalls) {
|
|
|
356
361
|
}
|
|
357
362
|
if (tc.id && !s.id) s.id = tc.id
|
|
358
363
|
if (tc.name && !s.name) s.name = tc.name
|
|
364
|
+
if (tc.extra_content && !s.extra_content) s.extra_content = tc.extra_content // §6.24:签名合并保真(首见胜)
|
|
359
365
|
s.arguments += tc.arguments ?? ""
|
|
360
366
|
}
|
|
361
367
|
}
|
package/provider/errors.mjs
CHANGED
|
@@ -88,14 +88,39 @@ export class ProviderError extends Error {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
|
-
* F-1 (MODEL-400-FIX) 根因兜底——请求体组装前断言:渠道裸克隆(`{...渠道}
|
|
92
|
-
*
|
|
91
|
+
* F-1 (MODEL-400-FIX) 根因兜底——请求体组装前断言:渠道裸克隆(`{...渠道}`——渠道条目不携
|
|
92
|
+
* 模型,克隆链未重派生 `.model` 时)provider.model 为 undefined/null——
|
|
93
93
|
* JSON.stringify 会丢 undefined 键 → 无 model 请求 → serde 400。
|
|
94
94
|
* fail-fast 报可读错误(带 provider 名 + 修复线索),不发病体。core.mjs chatImpl openai body 组装
|
|
95
95
|
* 前调用(单行——core.mjs 500 行硬限)。
|
|
96
|
+
* 2026-10-09 清除批(R3):指引收正——模型身份唯二 = 顶层 `defaultModel` 复合串 ∥ 会话槽选定
|
|
97
|
+
* (渠道单值模型退场,不再作指引)。
|
|
96
98
|
*/
|
|
97
99
|
export function assertProviderModel(provider) {
|
|
98
100
|
if (!provider.model) {
|
|
99
|
-
throw new ProviderError(provider, "model is undefined — provider cloned without model re-derivation (set
|
|
101
|
+
throw new ProviderError(provider, "model is undefined — provider cloned without model re-derivation (set the default model (provider:model) or the session slot model)")
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* #907-D8:Responses 流内错误码 → 分类(最小集五码)。宿主 = 本文件(分类族单源——与
|
|
107
|
+
* parseRetryAfter / isNonRetryableError 同族);消费面 = responses.mjs 流内错误出口
|
|
108
|
+
* (response.failed onFailed ∥ type:"error" 帧处置)。HTTP 级分类(isNonRetryableError)
|
|
109
|
+
* 零改——本函数只服务流内错误面。未列码 → null(不发明分类,服务端消息原样透传)。
|
|
110
|
+
* retryable 是信息位:本批不接自动重试(无流内重试消费面),仅供上层展示/上报。
|
|
111
|
+
*/
|
|
112
|
+
export function classifyResponsesErrorCode(code) {
|
|
113
|
+
switch (code) {
|
|
114
|
+
case "context_length_exceeded":
|
|
115
|
+
return { kind: "context_overflow", retryable: false, hint: "上下文超限——/compact 压缩历史后重试" }
|
|
116
|
+
case "insufficient_quota":
|
|
117
|
+
return { kind: "quota", retryable: false, hint: "配额/计费不足——检查 provider 余额或套餐额度" }
|
|
118
|
+
case "invalid_prompt":
|
|
119
|
+
return { kind: "invalid_request", retryable: false, hint: null }
|
|
120
|
+
case "server_is_overloaded":
|
|
121
|
+
case "server_error":
|
|
122
|
+
return { kind: "server", retryable: true, hint: null }
|
|
123
|
+
default:
|
|
124
|
+
return null
|
|
100
125
|
}
|
|
101
126
|
}
|
package/provider/google.mjs
CHANGED
|
@@ -136,6 +136,7 @@ export async function chat(provider, { messages, tools, onToken, onReasoning, on
|
|
|
136
136
|
return {
|
|
137
137
|
content: result.content,
|
|
138
138
|
reasoning: result.reasoning,
|
|
139
|
+
finishReason: result.finishReason ?? null, // §6.31.7 F-DA5(原三 transport 恒 null——字段值收正)
|
|
139
140
|
usage: {
|
|
140
141
|
prompt_tokens: usage.prompt_tokens ?? 0,
|
|
141
142
|
completion_tokens: usage.completion_tokens ?? 0,
|
|
@@ -145,7 +146,15 @@ export async function chat(provider, { messages, tools, onToken, onReasoning, on
|
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
148
|
|
|
148
|
-
return { content: result.content, reasoning: result.reasoning, toolCalls: result.toolCalls }
|
|
149
|
+
return { content: result.content, reasoning: result.reasoning, finishReason: result.finishReason ?? null, toolCalls: result.toolCalls }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Gemini `finishReason` ⇒ 核统一 `finishReason`(§6.31.7 F-DA5——归一只喂既有分类;
|
|
153
|
+
* 余者原样透传)。缺席 ⇒ null(缺席支 = agent/run-stages.mjs 同面提醒 + 留痕)。 */
|
|
154
|
+
function mapGeminiFinishReason(reason) {
|
|
155
|
+
if (reason === "STOP") return "stop"
|
|
156
|
+
if (reason === "MAX_TOKENS") return "length"
|
|
157
|
+
return reason
|
|
149
158
|
}
|
|
150
159
|
|
|
151
160
|
/**
|
|
@@ -153,7 +162,7 @@ export async function chat(provider, { messages, tools, onToken, onReasoning, on
|
|
|
153
162
|
* Format: data: {...}\n\n (each line is a complete JSON object)
|
|
154
163
|
*/
|
|
155
164
|
async function parseGeminiStream(response, { onToken, onReasoning, signal }) {
|
|
156
|
-
const result = { content: "", reasoning: "", toolCalls: [], usage: null }
|
|
165
|
+
const result = { content: "", reasoning: "", toolCalls: [], usage: null, finishReason: null }
|
|
157
166
|
const decoder = new TextDecoder()
|
|
158
167
|
let buffer = ""
|
|
159
168
|
|
|
@@ -172,6 +181,8 @@ async function parseGeminiStream(response, { onToken, onReasoning, signal }) {
|
|
|
172
181
|
|
|
173
182
|
const candidate = json.candidates?.[0]
|
|
174
183
|
if (!candidate) return
|
|
184
|
+
// §6.31.7 F-DA5(#929 · 批 digest-accounting):`candidate.finishReason` 捕获——缺席 ⇒ 留 null(窄口)
|
|
185
|
+
if (candidate.finishReason) result.finishReason = mapGeminiFinishReason(candidate.finishReason)
|
|
175
186
|
|
|
176
187
|
const parts = candidate.content?.parts || []
|
|
177
188
|
for (const part of parts) {
|
|
@@ -182,13 +193,14 @@ async function parseGeminiStream(response, { onToken, onReasoning, signal }) {
|
|
|
182
193
|
result.content += part.text
|
|
183
194
|
onToken?.(part.text)
|
|
184
195
|
} else if (part.functionCall) {
|
|
185
|
-
|
|
186
|
-
if (!
|
|
187
|
-
result.toolCalls.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
196
|
+
let slot = result.toolCalls.find((tc) => tc.name === part.functionCall.name)
|
|
197
|
+
if (!slot) {
|
|
198
|
+
slot = { id: part.functionCall.name + "_" + result.toolCalls.length, name: part.functionCall.name, arguments: JSON.stringify(part.functionCall.args || {}) }
|
|
199
|
+
result.toolCalls.push(slot)
|
|
200
|
+
}
|
|
201
|
+
// §6.24 原生路签名捕获(字段名 `thoughtSignature`——官方口径 unverified;在场才建 ⇒ 字段名不符零副作用)
|
|
202
|
+
if (typeof part.thoughtSignature === "string" && part.thoughtSignature) {
|
|
203
|
+
slot.extra_content = { google: { thought_signature: part.thoughtSignature } }
|
|
192
204
|
}
|
|
193
205
|
}
|
|
194
206
|
}
|
|
@@ -203,7 +215,7 @@ async function parseGeminiStream(response, { onToken, onReasoning, signal }) {
|
|
|
203
215
|
if (idleTimer) clearTimeout(idleTimer)
|
|
204
216
|
idleTimer = setTimeout(() => {
|
|
205
217
|
// #878:断流单点分流——直连 fetch(web 流)走 `IDLE_ABORT` 内部 abort 通道;
|
|
206
|
-
// proxy 形回落 destroyBody(原契约)。相位串 =
|
|
218
|
+
// proxy 形回落 destroyBody(原契约)。相位串 = 本分支专属 google-sse-idle。
|
|
207
219
|
idleError = timeoutError(`SSE idle timeout: no data for ${READ_IDLE_MS / 1000}s`, "provider", "google-sse-idle")
|
|
208
220
|
try { terminateBody(response, idleError) } catch { /* already gone */ }
|
|
209
221
|
}, READ_IDLE_MS)
|
package/provider/list-models.mjs
CHANGED
|
@@ -21,7 +21,8 @@ const LIST_TIMEOUT_MS = 15_000
|
|
|
21
21
|
const MAX_PAGES = 10
|
|
22
22
|
const ANTHROPIC_VERSION = "2023-06-01"
|
|
23
23
|
|
|
24
|
-
/** 单页 GET + JSON 解析(非 2xx → throw;非 JSON 响应 → throw——§2.5 #115 并入 VSC 明确报错)。
|
|
24
|
+
/** 单页 GET + JSON 解析(非 2xx → throw;非 JSON 响应 → throw——§2.5 #115 并入 VSC 明确报错)。
|
|
25
|
+
* 载荷读取失败(断流 ∥ idle 超时 ∥ 网络)⇒ 原错误直抛(#1068——不吞根因;`non-JSON` 文案只给真非 JSON)。 */
|
|
25
26
|
async function fetchJson(provider, url, headers, signal) {
|
|
26
27
|
const opts = {
|
|
27
28
|
headers,
|
|
@@ -36,7 +37,7 @@ async function fetchJson(provider, url, headers, signal) {
|
|
|
36
37
|
e.status = response.status // 展示面可判「不可用」
|
|
37
38
|
throw e
|
|
38
39
|
}
|
|
39
|
-
const rawText = await response.text()
|
|
40
|
+
const rawText = await response.text()
|
|
40
41
|
try {
|
|
41
42
|
return JSON.parse(rawText)
|
|
42
43
|
} catch {
|
|
@@ -196,7 +196,9 @@ export function buildBody(provider, messages, tools, opts = {}) {
|
|
|
196
196
|
store: wantStateful && hostStateful && isStoreRequiredHost(provider.baseURL),
|
|
197
197
|
...(extracted ? { instructions: extracted } : {}),
|
|
198
198
|
...(toolsFlat ? { tools: toolsFlat } : {}),
|
|
199
|
-
|
|
199
|
+
// 显式才发(#907-D5):仅 provider.maxTokens 显式设定时发送——spec.maxOutput 是规格侧
|
|
200
|
+
// 保守猜测非渠道合同值,恒发 = 渠道口径漂移 400 风险;无显式值 ⇒ 服务端默认(模型上限兜底)。
|
|
201
|
+
...(provider.maxTokens != null ? { max_output_tokens: provider.maxTokens } : {}),
|
|
200
202
|
}
|
|
201
203
|
// 内置工具声明追加(2026-08-31 用户拍板):web_search 与本地 function 工具共存
|
|
202
204
|
const builtin = builtinToolsFor(provider.baseURL, provider.builtinTools)
|
package/provider/responses.mjs
CHANGED
|
@@ -16,6 +16,7 @@ import { proxyFetch } from "../proxy.mjs"
|
|
|
16
16
|
import { requestWithRetry } from "./retry.mjs"
|
|
17
17
|
import { rateGate, recordRate, estimateRequestTokens } from "./rate.mjs"
|
|
18
18
|
import { buildBody, normalizeUsage } from "./responses-request.mjs"
|
|
19
|
+
import { classifyResponsesErrorCode } from "./errors.mjs"
|
|
19
20
|
// 同名 re-export(2026-09-29 structure-split-2 · 台账 #620——缝 = 同名再出口):请求构造面出档 `provider/responses-request.mjs`;
|
|
20
21
|
// `buildBody` / `isStoreRequiredHost` / `builtinToolsFor` 三名既有导出经本档转口——消费者 import 面零改。
|
|
21
22
|
export { buildBody, isStoreRequiredHost, builtinToolsFor } from "./responses-request.mjs"
|
|
@@ -27,6 +28,19 @@ export function isChainInvalidError(status) {
|
|
|
27
28
|
return status === 404 || status === 400
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
/** #907-D9/D8:error 帧处置回调(handleEvent 消费)——按 D8 码级标注定形后抛「标记错误」,catch 双支零丢失转发。 */
|
|
32
|
+
function errFrameError(ev) {
|
|
33
|
+
const err = ev?.error
|
|
34
|
+
const msg = err?.message ?? JSON.stringify(err ?? ev ?? {}).slice(0, 300)
|
|
35
|
+
const cls = classifyResponsesErrorCode(err?.code)
|
|
36
|
+
const e = new Error(cls?.hint ? `${msg}(${cls.hint})` : msg)
|
|
37
|
+
if (cls?.kind) e.errorKind = cls.kind
|
|
38
|
+
e.retryable = cls?.retryable ?? false
|
|
39
|
+
if (typeof err?.code === "number") e.status = err.code
|
|
40
|
+
e.responsesErrFrame = true // #907-D9 哨兵标记(不用 message 锚定——消息必须原样保留)
|
|
41
|
+
return e
|
|
42
|
+
}
|
|
43
|
+
|
|
30
44
|
/**
|
|
31
45
|
* Responses 事件流解析(事件状态机)。输出与 chat completions 同形:
|
|
32
46
|
* { content, reasoning, toolCalls, usage, finishReason, interrupted? }
|
|
@@ -67,17 +81,21 @@ export async function parseStream(response, { onToken, onReasoning, signal }) {
|
|
|
67
81
|
if (!slot) return
|
|
68
82
|
if (fullArgs && fullArgs !== slot.arguments) slot.arguments = fullArgs
|
|
69
83
|
},
|
|
70
|
-
onCompleted: seal,
|
|
84
|
+
onCompleted: (resp) => { result.finishReason = "stop"; seal(resp) }, // §6.31.7 F-DA5:completed ⇒ stop(原恒 null ⇒ agent 面缺席)
|
|
71
85
|
onIncomplete: (resp) => {
|
|
72
86
|
seal(resp)
|
|
73
87
|
// 非长度原因(content_filter 等)不能报成 "length"——agent 层按原因给用户提示(`agent/run-stages.mjs:43`)
|
|
74
88
|
result.finishReason = resp?.incomplete_details?.reason === "content_filter" ? "content_filter" : "length"
|
|
75
89
|
},
|
|
76
90
|
onFailed: (resp) => {
|
|
91
|
+
// #907-D8:码级标注——errorKind/retryable 信息位 ∥ hint 拼入消息 ∥ status 仅数值(字符串码塞 status 是缺陷)
|
|
77
92
|
const err = resp?.error
|
|
78
93
|
const msg = err?.message ?? JSON.stringify(err ?? {}).slice(0, 500)
|
|
79
|
-
const
|
|
80
|
-
e
|
|
94
|
+
const cls = classifyResponsesErrorCode(err?.code)
|
|
95
|
+
const e = new Error(cls?.hint ? `responses API failed: ${msg}(${cls.hint})` : `responses API failed: ${msg}`)
|
|
96
|
+
if (cls?.kind) e.errorKind = cls.kind
|
|
97
|
+
e.retryable = cls?.retryable ?? false
|
|
98
|
+
if (typeof err?.code === "number") e.status = err.code
|
|
81
99
|
throw e
|
|
82
100
|
},
|
|
83
101
|
})
|
|
@@ -88,6 +106,18 @@ export async function parseStream(response, { onToken, onReasoning, signal }) {
|
|
|
88
106
|
seal(null)
|
|
89
107
|
return { ...result, interrupted: true, interruptMessage: signal.reason.message }
|
|
90
108
|
}
|
|
109
|
+
// #907-D9:error 帧双支——有流出(content ∥ toolCalls)⇒ partial 抢救返回;无流出 ⇒ 转抛(上抛为主)。
|
|
110
|
+
if (e?.responsesErrFrame) {
|
|
111
|
+
if (result.content.length > 0 || result.toolCalls.length > 0) {
|
|
112
|
+
const existing = result._warnings ??= []
|
|
113
|
+
if (!existing.some((w) => w.name === "responses-error-frame")) existing.push({ name: "responses-error-frame", message: e.message })
|
|
114
|
+
result.partial = true
|
|
115
|
+
result.errorDetail = e.message.slice(0, 300)
|
|
116
|
+
seal(null) // 无 responseId ⇒ finish() 链重置自动生效(错误后下轮全量重发)
|
|
117
|
+
return result
|
|
118
|
+
}
|
|
119
|
+
throw e
|
|
120
|
+
}
|
|
91
121
|
throw e
|
|
92
122
|
}
|
|
93
123
|
|
|
@@ -185,6 +215,8 @@ function handleEvent(ev, h) {
|
|
|
185
215
|
case "response.failed":
|
|
186
216
|
h.onFailed?.(ev.response)
|
|
187
217
|
break
|
|
218
|
+
case "error": // #907-D9:标准 error 帧(原 default 静默跳过 = 空内容当回复——本件修复)
|
|
219
|
+
throw errFrameError(ev)
|
|
188
220
|
default:
|
|
189
221
|
break
|
|
190
222
|
}
|
|
@@ -268,6 +300,8 @@ async function finish(provider, response, { onToken, onReasoning, signal, newCha
|
|
|
268
300
|
} else {
|
|
269
301
|
provider._responsesChain = null
|
|
270
302
|
}
|
|
271
|
-
|
|
303
|
+
// #907-D9:合并而非覆盖——parseStream partial 抢救支已注入 responses-error-frame 警告
|
|
304
|
+
if (result._warnings?.length) result._warnings = [...warnings, ...result._warnings]
|
|
305
|
+
else result._warnings = warnings
|
|
272
306
|
return result
|
|
273
307
|
}
|
package/provider/sse.mjs
CHANGED
|
@@ -53,6 +53,9 @@ function mergeToolCalls(result, delta, { snapshot = false } = {}) {
|
|
|
53
53
|
existed = !!slot
|
|
54
54
|
if (!slot) { result.droppedToolCalls++; continue }
|
|
55
55
|
}
|
|
56
|
+
// §6.24 思考签名捕获(Gemini):白名单构造、在场才建 / 在场覆写(四条槽路径共经)
|
|
57
|
+
const sig = tc.extra_content?.google?.thought_signature
|
|
58
|
+
if (typeof sig === "string" && sig) slot.extra_content = { google: { thought_signature: sig } }
|
|
56
59
|
if (tc.id && !slot.id) slot.id = tc.id
|
|
57
60
|
if (tc.function?.name && !slot.name) slot.name = tc.function.name
|
|
58
61
|
const arg = tc.function?.arguments
|