From f07f8f15edbffa13731c864ec9e2b3cb1a786213 Mon Sep 17 00:00:00 2001 From: Turtle Date: Tue, 4 Aug 2026 00:11:45 +0800 Subject: [PATCH 1/7] docs: define hierarchy and document forms --- ...2026-07-04-doc-tiers-and-budgets.i18n.yaml | 4 +-- .../2026-07-04-doc-tiers-and-budgets.md | 14 ++++---- .../2026-07-04-doc-tiers-and-budgets.zh.md | 20 ++++++----- .agents/skills/dsh-doc-standards/SKILL.md | 20 +++++++---- docs/AGENTS.md | 34 +++++++++++-------- scripts/doc-budgets.manifest.json | 2 +- 6 files changed, 56 insertions(+), 38 deletions(-) diff --git a/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.i18n.yaml b/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.i18n.yaml index b66111ec6f..0d2231b449 100644 --- a/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.i18n.yaml +++ b/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.md -2026-07-04-doc-tiers-and-budgets.md: a52c40a9a147fd39fdec4c61079822f1b1115227 -2026-07-04-doc-tiers-and-budgets.zh.md: c26efe0e06d73ff0bd5608c7f44d54ebbe373bed +2026-07-04-doc-tiers-and-budgets.md: e7b3421d09a1ae5ab9a9373e8040832c1b0d4b97 +2026-07-04-doc-tiers-and-budgets.zh.md: 3e372f975943e8e4868aa6a386361ba324ef7109 diff --git a/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.md b/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.md index a52c40a9a1..e7b3421d09 100644 --- a/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.md +++ b/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.md @@ -1,4 +1,4 @@ -# Agent Note: Documentation tiers, budgets, and the ceiling gate +# Agent Note: Documentation structure, tiers, and budgets Status: implemented @@ -6,13 +6,14 @@ English | [中文](2026-07-04-doc-tiers-and-budgets.zh.md) ## Problem -Standing docs accumulated repeated rules, retold incidents, duplicated package maps, and stale Agent Note summaries despite existing writing guidance. Because review alone did not prevent that growth, the repository needed a mechanical budget alongside its documentation taxonomy. +Standing docs accumulated repeated rules, retold incidents, duplicated package maps, and stale Agent Note summaries despite existing writing guidance. That guidance also did not define how a document's place in the hierarchy limits its scope or how ordered teaching differs from lookup-oriented material. Because review alone did not prevent that growth, the repository needed a mechanical budget alongside its documentation taxonomy. ## Decision -- **A tier taxonomy with one home per fact.** [docs/AGENTS.md](../../../../docs/AGENTS.md) is the documentation standard: it assigns every Markdown tier a single job (standing orders, system map, type catalog, decision records, incident stories, how-tos, per-package contracts, generated catalogs, workflows), forbids restating a fact outside its home tier (link instead), and carries the slop checklist used when writing or reviewing any doc. +- **Structure follows the documentation tree.** [docs/AGENTS.md](../../../../docs/AGENTS.md) is the documentation standard: a document owns detail about its subject, summarizes only the purpose, responsibility, and high-level behavior of direct children, and links to deeper owners. [Agent Notes](../../README.md) remain outside this structural contract. Every human-facing document is a tutorial with an ordered outcome or a reference with an explicit lookup scope; a [postmortem](../../../../docs/postmortem/README.md) is an incident-scoped reference whose chronology records evidence. Tutorials introduce concepts in prerequisite order for the reader's starting knowledge. +- **A tier taxonomy with one home per fact.** The standard assigns every Markdown tier one job, forbids restating a fact outside its home tier, and carries the slop checklist used when writing or reviewing any doc. - **A narrow, hard budget gate.** [scripts/verify-doc-budgets.ts](../../../../scripts/verify-doc-budgets.ts) joins `doc-sync`: every doc listed in [scripts/doc-budgets.manifest.json](../../../../scripts/doc-budgets.manifest.json) must stay under its word ceiling (`wc -w` semantics, whole file), and a budgeted file that is missing fails the gate so a rename cannot silently orphan its budget. Scope is deliberately only the accretion-prone standing docs — the root and subtree `AGENTS.md` files, `architecture.md`, `packages/README.md`, and the standing policy docs they evict content into (`docs/testing.md`, `docs/defensive-patterns.md`). Reference docs, Agent Notes, and package READMEs are unbudgeted: length is legitimate there when every row is a fact, and review plus the slop checklist govern them. -- **Ceilings are an enforcement frontier that ratchets.** A ceiling sits at least 5% above the doc's current size — working headroom, so routine wording edits pass while real growth still trips the gate — and ratchets down, keeping that margin, as the doc is brought to its target budget (root `AGENTS.md` ≤ 1,500 words; `architecture.md` ≤ 1,800; subtree `AGENTS.md` ≤ 600; `packages/README.md` ≤ 600). When the gate goes red the fix is to relocate or condense per the taxonomy; raising a ceiling is permitted only with explicit justification in the PR description, the manifest diff being the reviewable act. +- **Ceilings are an enforcement frontier that ratchets.** A doc at or below its target keeps at least 5% headroom as its ceiling ratchets down; a doc above target keeps a frozen ceiling that prevents growth until it reaches the target (root `AGENTS.md` ≤ 1,600 words; `architecture.md` ≤ 1,800; subtree `AGENTS.md` ≤ 600 except `packages/AGENTS.md` ≤ 650 and `docs/AGENTS.md` ≤ 1,250; `packages/README.md` ≤ 600). When the gate goes red, relocate or condense; raise a ceiling only with explicit PR justification. - **A thin workflow skill, contracts in docs.** [.agents/skills/dsh-doc-standards](../../../skills/dsh-doc-standards/SKILL.md) carries the placement/audit/red-gate workflow and defers to the standard as its source of truth, the same split as [dsh-translate-docs](../../../skills/dsh-translate-docs/SKILL.md) over the i18n contract. ## Alternatives considered @@ -23,6 +24,7 @@ Standing docs accumulated repeated rules, retold incidents, duplicated package m ## Consequences -- Adding to a budgeted doc now requires displacement: relocate the addition to its taxonomy home with a pointer, or condense existing prose to pay for it. Growth without pruning fails CI. -- The bring-under-target rewrites land as stacked follow-ups that ratchet the manifest down as they merge; until each lands, its doc's frozen ceiling only prevents further growth. +- Adding to a budgeted doc requires displacement: relocate the addition to its taxonomy home with a pointer, or condense existing prose to pay for it. Growth without pruning fails CI. +- Structural review starts with ownership and document form before sentence-level editing, so lower-level detail moves to its owner instead of being polished in the wrong place. +- Budgeted docs that remain above target cannot grow; reaching the target restores the 5% working headroom. - Word count is a crude proxy accepted deliberately: it cannot judge quality, but it forces the relocation decision at exactly the moment content is being added, which is when the author has the context to place it correctly. diff --git a/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.zh.md b/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.zh.md index c26efe0e06..18bf98777c 100644 --- a/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.zh.md +++ b/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.zh.md @@ -1,4 +1,4 @@ -# Agent Note: 文档分层、预算与上限门禁 +# Agent Note: 文档结构、层级与预算 Status: implemented @@ -6,14 +6,15 @@ Status: implemented ## 问题 -尽管已有写作指导,常设文档仍不断累积重复规则、反复讲述的事故、重复的包映射,以及陈旧的 Agent Note 摘要。仅靠评审无法阻止这种增长,因此仓库需要在文档分类体系之外再配一套可自动执行的预算。 +尽管已有写作指导,常设文档仍不断累积重复规则、反复讲述的事件、重复的包(package)映射,以及陈旧的 Agent Note(agent 决策记录)摘要。该指导也未明确文档在层级中的位置如何限定其内容范围,以及按顺序引导读者学习的内容与面向查阅的材料有何不同。仅靠评审无法阻止这种增长,因此仓库需要在文档分类之外再配一套机械预算。 ## 决策 -- **每项事实只归属一处的层级分类体系。**[docs/AGENTS.md](../../../../docs/AGENTS.md) 是文档标准:它为每种 Markdown 层级分配单一职责(常设指令、系统图、类型目录、决策记录、事故叙事、操作指南、各包契约、生成的目录、工作流),禁止在事实归属层级之外重复陈述(应改为链接),并包含编写或评审任何文档时使用的赘余检查清单。 -- **范围窄且严格的预算门禁。**[scripts/verify-doc-budgets.ts](../../../../scripts/verify-doc-budgets.ts) 接入 `doc-sync`:[scripts/doc-budgets.manifest.json](../../../../scripts/doc-budgets.manifest.json) 列出的每份文档都不得超过其词数上限(采用 `wc -w` 语义,统计整个文件);预算内文件缺失也会使门禁失败,使文件重命名无法悄然摆脱预算约束。范围刻意只涵盖容易膨胀的常设文档——根目录和子树中的 `AGENTS.md` 文件、`architecture.md`、`packages/README.md`,以及它们将内容移入的常设策略文档(`docs/testing.md`、`docs/defensive-patterns.md`)。参考文档、Agent Note 和包 README 不设预算:只要每一行都是事实,长度在这些位置就是合理的;评审和赘余检查清单负责约束它们。 -- **上限是会逐步收紧的执行红线。** 上限设定为文档当前词数的至少 105%(留出工作余量,使日常措辞调整能通过,而真正的膨胀仍会触发门禁),并随着文档被精简到目标预算而同步下调、保持该余量(根 `AGENTS.md` ≤ 1,500 词;`architecture.md` ≤ 1,800;子树 `AGENTS.md` ≤ 600;`packages/README.md` ≤ 600)。门禁变红时,修复方式是按分类体系迁移或压缩内容;只有在 PR(Pull Request)描述中给出明确理由时才允许提高上限,manifest(元数据清单)的 diff 本身就是可供评审的变更。 -- **精简的工作流 skill(技能),契约归文档。**[.agents/skills/dsh-doc-standards](../../../skills/dsh-doc-standards/SKILL.md) 承载文档放置、审计和门禁失败处理工作流,并以文档标准为真源,与 [dsh-translate-docs](../../../skills/dsh-translate-docs/SKILL.md) 和 i18n 契约之间的分工相同。 +- **结构遵循文档树。**[docs/AGENTS.md](../../../../docs/AGENTS.md) 是文档标准:文档负责承载其主题的详细内容,仅概述直接子项的目的、职责和高层行为,并链接到更深层内容的归属文档。[Agent Note](../../README.md) 仍不受这一结构契约约束。每份面向人的文档要么是按顺序引导读者达成结果的教程(tutorial),要么是查阅范围明确的参考文档(reference);[事故复盘(postmortem)](../../../../docs/postmortem/README.md) 是范围限定于单个事件的参考文档,其时间线记录证据。教程结合读者的起始知识,按前置依赖顺序介绍概念。 +- **每项事实只归属一处的层级分类。**文档标准为每种 Markdown 层级分配单一职责,禁止在事实归属层级之外重复陈述,并包含编写或评审任何文档时使用的赘余检查清单。 +- **范围窄且严格的预算门禁。**[scripts/verify-doc-budgets.ts](../../../../scripts/verify-doc-budgets.ts) 接入 `doc-sync`:[scripts/doc-budgets.manifest.json](../../../../scripts/doc-budgets.manifest.json) 列出的每份文档都必须低于其字数上限(采用 `wc -w` 语义,统计整个文件);预算内文件缺失也会使门禁失败,使重命名无法悄然遗落其预算。范围刻意只涵盖容易膨胀的常设文档——根目录和子树中的 `AGENTS.md` 文件、`architecture.md`、`packages/README.md`,以及它们将内容移入的常设策略文档(`docs/testing.md`、`docs/defensive-patterns.md`)。参考文档、Agent Note 和包 README 不设预算:只要每一行都是事实,长度在这些位置就是合理的;评审和赘余检查清单负责约束它们。 +- **上限是只进不退的执行红线。** 达到或低于目标的文档在上限逐步下调时保留至少 5% 的余量;高于目标的文档则维持冻结的上限,在达到目标之前不得增长(根 `AGENTS.md` ≤ 1,600 词;`architecture.md` ≤ 1,800;子树 `AGENTS.md` ≤ 600,但 `packages/AGENTS.md` ≤ 650、`docs/AGENTS.md` ≤ 1,250;`packages/README.md` ≤ 600)。门禁变红时,迁移或压缩内容;只有在 PR(Pull Request)描述中给出明确理由时才提高上限。 +- **精简的工作流 skill(技能),契约归文档。**[.agents/skills/dsh-doc-standards](../../../skills/dsh-doc-standards/SKILL.md) 承载放置/审计/红灯门禁工作流,并以文档标准为真源,与 [dsh-translate-docs](../../../skills/dsh-translate-docs/SKILL.md) 和 i18n 契约之间的分工相同。 ## 曾考虑的替代方案 @@ -23,6 +24,7 @@ Status: implemented ## 后果 -- 向受预算约束的文档添加内容现在需要腾挪空间:将新增内容迁移到其分类体系归属地并留下链接,或压缩现有行文来腾出空间。只增不减会导致 CI 失败。 -- 精简到目标预算的重写以堆叠的后续 PR 落地,每次合并时同步下调 manifest 中的上限;在各自落地之前,文档冻结的上限仅阻止进一步膨胀。 -- 词数是一个粗糙的代理指标,这是有意接受的:它无法判断质量,但它在内容被添加的那一刻强制触发迁移决策,而那正是作者拥有足够上下文来正确放置内容的时刻。 +- 向受预算约束的文档添加内容需要置换:将新增内容迁移到其分类体系归属地并留下指针,或压缩现有行文来腾出空间。只增不减会导致 CI 失败。 +- 结构评审先检查归属关系和文档形式,再进行句子层面的编辑,使较低层级的细节迁移到其归属文档,而不是在错误的位置加以润色。 +- 仍高于目标的受预算约束文档不得增长;达到目标后,将恢复 5% 的工作余量。 +- 字数是一个粗糙的代理指标,这是有意接受的:它无法判断质量,但它在内容被添加的那一刻强制触发迁移决策,而那正是作者拥有足够上下文来正确放置内容的时刻。 diff --git a/.agents/skills/dsh-doc-standards/SKILL.md b/.agents/skills/dsh-doc-standards/SKILL.md index 202b4bcfd8..020768c200 100644 --- a/.agents/skills/dsh-doc-standards/SKILL.md +++ b/.agents/skills/dsh-doc-standards/SKILL.md @@ -1,6 +1,6 @@ --- name: dsh-doc-standards -description: 'Use when writing, moving, reviewing, or auditing documentation in the deepseek-harness repo — choosing where content belongs, trimming doc slop, responding to a verify-doc-budgets gate failure, or requests like "improve the docs", "audit the docs for slop", "where should this be documented", "this doc is too long".' +description: 'Use when writing, moving, reviewing, or auditing documentation in the deepseek-harness repo — choosing hierarchy and detail, separating tutorials from references, checking tutorial progression, trimming doc slop, responding to a verify-doc-budgets failure, or requests like "improve the docs", "audit the docs", "where should this be documented", or "this doc is too long".' --- # Applying the DeepSeek Harness Documentation Standard @@ -9,24 +9,32 @@ The contract lives in [docs/AGENTS.md](../../../docs/AGENTS.md). This workflow c ## Sources of truth (read, don't re-summarize) -- [docs/AGENTS.md](../../../docs/AGENTS.md) — the taxonomy ("one home per fact"), budgets, slop checklist. +- [docs/AGENTS.md](../../../docs/AGENTS.md) — hierarchy, tutorial/reference forms, taxonomy, budgets, and slop checklist. - [.agents/notes/README.md](../../notes/README.md) — when a decision earns an Agent Note, how to file it, and what goes inside one (the header block, per-lifecycle skeleton, and Alternatives-considered mandate, gated by `verify-agent-note-format`); [docs/postmortem/README.md](../../../docs/postmortem/README.md) — when an incident earns a postmortem. - [docs/i18n/README.md](../../../docs/i18n/README.md) — the bilingual pairing contract; editing either side of a pair obligates the counterpart in the same change. - Root [AGENTS.md](../../../AGENTS.md) — the standing orders whose budget discipline this skill protects. - [Archived Agent Notes](../../notes/archived/AGENTS.md) — frozen historical snapshots excluded from editorial maintenance and evolving documentation gates. -## Placing content +## Review structure before prose -Run the placement test in the standard's taxonomy table, then check the constraints that make a placement expensive or wrong: +Apply the standard's authoring order to every human-facing document in scope. Do not apply this structural pass to Agent Notes. Classify a postmortem as a reference scoped to one incident; preserve the chronological evidence required by its contract without treating chronology as a teaching sequence. + +1. Locate the document in the repository and navigation trees. State its own subject and identify its direct children. +2. Set the detail boundary. Keep full detail about the document's subject, summarize direct children by purpose, responsibility, and high-level behavior, and move deeper explanations to their owning descendants with links. Treat test infrastructure as descendant-owned unless it is the document's subject. +3. Classify the document from its intended use, not its path or title. A tutorial must lead through ordered work to an observable outcome; a reference must support lookup within an explicit scope without requiring sequential reading. +4. For a tutorial, privately classify the starting reader and concepts as beginner, intermediate, or advanced. Trace each concept to its prerequisites, reorder premature material, and move optional advanced detail to a later tutorial or reference. +5. Split substantial mixed forms. Keep a small secondary form only behind a clear structural boundary. + +Then check constraints that make placement expensive or wrong: - Paired docs (`pnpm run verify-translation-pairing --list`) cost a zh counterpart update and a `--write` re-record on every edit — prefer an unpaired home for content that will churn. - Generated catalogs are never hand-edited; if the fact belongs there, change the generator's source. - Before renaming or moving any doc, grep for inbound references: `verify-md-links` catches Markdown links, `verify-doc-refs` catches `docs/*.md` citations in TypeScript comments, but nothing catches heading-anchor fragments — grep `#the-heading` across the repo yourself (one anchor is hardcoded in `scripts/gen-cordis-catalog.ts`). - A move is atomic: remove from the old home, add to the new home, and fix every inbound link in the same change. -## Auditing the corpus +## Audit the corpus -The audit is a hunt for the standard's slop checklist, cheapest probes first. Verify and fetch the PR's live base, then run `pnpm --silent run change-scope --base ` to identify committed and dirty paths before applying semantic judgment. After a retarget or base merge, rerun the report and repeat the audit for prose introduced by the new base rather than relying on the earlier result. +After the structural pass, hunt the standard's slop checklist with the cheapest probes first. Verify and fetch the PR's live base, then run `pnpm --silent run change-scope --base ` to identify committed and dirty paths before applying semantic judgment. After a retarget or base merge, rerun the report and audit prose introduced by the new base. 1. Measure: `pnpm run verify-doc-budgets --list`, then `git ls-files '*.md' ':(exclude)vendor/**' | xargs wc -w | sort -rn | head -30` to spot unbudgeted outliers. 2. Hunt narrated history: `rg -n "no longer|used to|previously|was moved|renamed" --glob '*.md' --glob '*.ts' --glob '!vendor/**'` and keep only contrasts against a live alternative. Keep the vendor exclusion last so include globs cannot override it. diff --git a/docs/AGENTS.md b/docs/AGENTS.md index fb3b43e56c..cd4e8aaaa5 100644 --- a/docs/AGENTS.md +++ b/docs/AGENTS.md @@ -1,10 +1,20 @@ # AGENTS.md — The documentation standard -This file defines Markdown tiers, writing rules, and `verify-doc-budgets` ceilings. Use [dsh-doc-standards](../.agents/skills/dsh-doc-standards/SKILL.md) for placement and validation, and [dsh-prose-standard](../.agents/skills/dsh-prose-standard/SKILL.md) for required coverage and editorial judgment; the [doc-tiers Agent Note](../.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.md) owns rationale. +This file defines document structure, Markdown tiers, writing rules, and `verify-doc-budgets` ceilings. Use [dsh-doc-standards](../.agents/skills/dsh-doc-standards/SKILL.md) for placement and validation, and [dsh-prose-standard](../.agents/skills/dsh-prose-standard/SKILL.md) for required coverage and editorial judgment; the [doc-tiers Agent Note](../.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.md) owns rationale. + +## Document structure + +These rules apply to human-facing documentation; [Agent Notes](../.agents/notes/README.md) remain outside their scope. A [postmortem](postmortem/README.md) is an incident-scoped reference; chronology records evidence, not a teaching sequence. A document's subject and tree position fix its scope: describe its own subject at appropriate detail, describe direct children only by purpose, responsibility, and high-level behavior, and link to the owning descendant for lower-level detail. Document type does not widen that scope. A reference may be exhaustive only about its own subject. Testing mechanisms, fixtures, and harnesses belong at the lowest owning level; higher documents link there. + +Classify every in-scope document as a tutorial or reference. A tutorial follows an ordered path to an outcome and introduces only what each step needs. A reference defines a lookup scope and describes current behavior without depending on a teaching sequence. Separate substantial tutorial and reference content; use a clear structural boundary when either part is small. + +Before writing a tutorial, privately classify the reader's starting knowledge and each concept as beginner, intermediate, or advanced. Establish prerequisites before dependent concepts, increase difficulty gradually, and move unnecessary advanced material to a later tutorial or reference. + +Author in this order: locate the document in the tree; set its permitted detail; choose tutorial or reference; for a tutorial, order concepts by prerequisite and difficulty; relocate descendant-owned detail; replace lower-level explanations with links to their owners. ## The tier taxonomy: one home per fact -Each fact has one home: the tier whose job it is. Elsewhere, link to that home; `verify-md-links` keeps links resolving while duplicated prose drifts. +Each fact has one home: the tier whose job it is. Elsewhere, link to that home. | Tier | Job | Does NOT belong there | |---|---|---| @@ -12,17 +22,15 @@ Each fact has one home: the tier whose job it is. Elsewhere, link to that home; | Subtree `AGENTS.md` (`packages/`, `examples/`, `docs/`, `.agents/notes/`) | Orders specific to that subtree | Repo-wide rules the root file already carries | | [architecture.md](architecture.md) | The system map: services, the loop, extension seams — read before changing `packages/` | Type shapes (→ core-data-structures), per-package detail (→ package READMEs), decision rationale (→ Agent Notes), implementation-status annotations | | [core-data-structures/](core-data-structures/core.md) | The type catalog: literal shapes and semantics of the spine and seam vocabulary | Behavior narration (→ architecture.md) | -| [Agent Notes](../.agents/notes/README.md) | Active decision records: the why, what-was-given-up, and concise verification contract; `implemented/` notes describe shipped reality in present tense | Migration plans, acceptance-task checklists, fixture walkthroughs, and spec-speak ("should…") once the decision has shipped; archived notes are frozen history, never current authority | +| [Agent Notes](../.agents/notes/README.md) | Decision records under their own lifecycle contract | Migration plans, checklists, and spec-speak once implemented; archived notes are frozen history | | [postmortem/](postmortem/README.md) | Incident stories — the only tier where war-story narrative belongs | — | | [cookbook/](cookbook/adding-a-package.md) | Step-by-step how-tos with numbered verify steps | Design rationale (→ the Agent Note each guide links) | | [user/](user/index.md) | Product-facing guides published by the documentation website | Generated reference tables, contributor procedures, decision history | -| Package README | The per-package contract: config, semantics, limitations, extension points, and [Model Experience](cookbook/adding-a-package.md#4-write-the-package-readme) | JSDoc restatement, generated-catalog restatement (event/tool tables), other packages' concerns | -| [development.md](development.md) | First-stop contributor onboarding: local setup, daily workflow, and CI shape at summary level; a bilingual pair under the [i18n contract](i18n/README.md) | Runtime/version rationale (→ Agent Notes), gate-by-gate enumerations that drift from `package.json` scripts | +| Package README | Per-package config, semantics, limitations, extension points, and [Model Experience](cookbook/adding-a-package.md#4-write-the-package-readme) | JSDoc or catalog restatement, other packages' concerns | +| [development.md](development.md) | Contributor onboarding: setup, daily workflow, and CI shape at summary level | Runtime rationale (→ Agent Notes), drifting gate inventories | | Generated catalogs: [cordis events](cordis-catalog/events.md), [cordis services](cordis-catalog/services.md), [Cordis core API](cordis-catalog/core/context.md), [tool-catalog](tool-catalog.md), [config-catalog](config-catalog.md), [persistence-catalog](persistence-catalog.md), [module-graph.md](module-graph.md) | Exhaustive enumerations regenerated from source, freshness-gated | Hand edits of any kind | | Skills (`.agents/skills/`) | Reusable workflows and specialized decision standards | Product and runtime contracts (→ docs or source) | -Placement: bugs → postmortems; rationale → Agent Notes; procedures → cookbooks; type shapes → core data; package contracts → READMEs; standing orders → root `AGENTS.md` with a rationale link. - ## Writing rules - **Document current state, not change history.** Avoid "previously/now/no longer", PRs, commits, and stack positions in durable prose; name the live mechanism. Put change stories in commits, PRs, Agent Notes, or postmortems. @@ -31,7 +39,7 @@ Placement: bugs → postmortems; rationale → Agent Notes; procedures → cookb - **Fenced `ts` blocks must compile** (`doc-typecheck`); a pasted type declaration and its original JSDoc use ` ```ts type-equiv `, while a body-stripped public class declaration uses ` ```ts public-api `; register either in the manifest so neither can drift ([mechanics](development.md#documenting-types-verbatim-ts-type-equiv)). - **The [core-data-structures catalog](core-data-structures/core.md) updates in the same change** that reshapes a documented type. `verify-type-equiv` catches drifted pastes, not never-documented new types ([what counts as core](core-data-structures/core.md#what-counts-as-core)). - **Bilingual pairs update together**: editing either side obligates the counterpart and a re-record in the same change ([i18n contract](i18n/README.md)). -- **Comments and JSDoc state complete contracts, not reasoning transcripts.** Preserve behavior, conditions, timing, modality, exceptions, consequences, and non-obvious orientation; delete implementation narration, test walkthroughs, review analysis, and code restatement. Keep the local contract and link to its owning rationale. Use [dsh-prose-standard](../.agents/skills/dsh-prose-standard/SKILL.md) for required coverage, decision rules, and examples. +- **Comments and JSDoc state complete contracts, not reasoning transcripts.** Preserve behavior, timing, modality, exceptions, consequences, and non-obvious orientation; delete narration, test walkthroughs, review analysis, and code restatement. Keep the local contract and link its rationale. Use [dsh-prose-standard](../.agents/skills/dsh-prose-standard/SKILL.md) for details. - Your audience is professional programmers. Prefer concise and straight-forward English over metaphor. Do not overuse words like "gate", "vocabulary", "surface", "seams". ## Wordcount Budgets @@ -44,20 +52,18 @@ When the gate goes red: 2. **Condense** content that belongs here but can be shorter. 3. **Raise** the ceiling only when the words truly need the space; justify the manifest diff in the PR. A too-low ceiling is a budget bug. -Ceilings are guardrails, not reduction targets. Retain at least 5% headroom; lower a ceiling only when the document's durable contract still has room, and raise it when necessary content would otherwise be deleted. Targets: root `AGENTS.md` ≤ 1,600 words; `architecture.md` ≤ 1,800; each subtree `AGENTS.md` ≤ 600, except `packages/AGENTS.md` ≤ 650 and this file ≤ 1,250; `packages/README.md` ≤ 600. Review and the slop checklist govern unbudgeted tiers. +Ceilings are guardrails, not reduction targets. At or below target, retain at least 5% headroom; above target, freeze the ceiling until relocation or condensation brings the document under target. Lower a ceiling only when the contract still has room, and raise it when content would otherwise be deleted. Targets: root `AGENTS.md` ≤ 1,600 words; `architecture.md` ≤ 1,800; subtree `AGENTS.md` ≤ 600, except `packages/AGENTS.md` ≤ 650 and this file ≤ 1,250; `packages/README.md` ≤ 600. Review governs unbudgeted tiers. ## The slop checklist Hunt these in any doc; the [dsh-doc-standards](../.agents/skills/dsh-doc-standards/SKILL.md) skill runs this list as an audit: - The same rule stated in more than one home. Grep a distinctive phrase; keep one home, convert the rest to links. -- Narrated history: "previously", "now", "no longer", "used to", "renamed", "was moved", references to PRs or commits. State the current fact; the why belongs in an Agent Note, the story in a postmortem or git. -- A war story told inline where a one-line rule plus a postmortem/Agent Note link would do. +- Narrated history or war stories: "previously", "now", "no longer", "used to", "renamed", "was moved", PRs, or commits. State the current fact; link an Agent Note or postmortem when needed. - Implementation-status annotations in prose or diagrams ("implemented!", "future: …"). Status rots; the repo layout and package manifests carry it. -- Hand-restating a generated catalog or JSDoc: event tables, tool arg tables, method signatures. Link instead. -- Hand-maintained inventories of tests, packages, or implementation status when the tree or a generator is authoritative. +- Hand-restated catalogs, JSDoc, or inventories of tests, packages, and status when source or a generator is authoritative. - Reasoning transcripts: step-by-step implementation narration, proof of obvious branches, test walkthroughs, or rejected local alternatives. Keep the resulting contract or durable rationale; delete the path used to derive it. -- The same rationale repeated beside sibling methods. State it once at the owning seam or shared helper. +- Rationale repeated beside sibling methods instead of once at the owning seam or helper. - Paragraph walls: one paragraph carrying several rules and parenthetical asides. Split it, or demote the detail to the linked home. - Emphasis inflation: bold, CAPS, or "critically" everywhere means nothing stands out. Reserve emphasis for the clause that changes behavior. - Spec-speak in `implemented/` Agent Notes: "should", migration plans, acceptance checklists. An implemented Agent Note describes what is, per the [implemented-note instructions](../.agents/notes/implemented/AGENTS.md). diff --git a/scripts/doc-budgets.manifest.json b/scripts/doc-budgets.manifest.json index 9e8d6076ec..a6ad066add 100644 --- a/scripts/doc-budgets.manifest.json +++ b/scripts/doc-budgets.manifest.json @@ -1,6 +1,6 @@ { "AGENTS.md": 1775, - "docs/AGENTS.md": 1150, + "docs/AGENTS.md": 1320, "docs/architecture.md": 2160, "docs/cordis-primer.md": 600, "docs/defensive-patterns.md": 550, From dd8d446286c884620aedac9af2a498a5e60d1aad Mon Sep 17 00:00:00 2001 From: Turtle Date: Tue, 4 Aug 2026 00:54:19 +0800 Subject: [PATCH 2/7] docs: apply hierarchy across the corpus --- apps/cli/reference.md | 82 +++++++++++ apps/cli/reference.zh.md | 82 +++++++++++ docs/architecture.md | 67 ++------- docs/architecture.zh.md | 67 ++------- docs/core-data-structures/llm-streaming.md | 4 +- docs/core-data-structures/llm-streaming.zh.md | 4 +- .../persistence.i18n.yaml | 4 +- docs/core-data-structures/persistence.md | 2 +- docs/core-data-structures/persistence.zh.md | 2 +- docs/development.md | 31 +--- docs/development.zh.md | 31 +--- docs/i18n/README.i18n.yaml | 4 +- docs/i18n/README.md | 2 +- docs/i18n/README.zh.md | 2 +- ...ypert-catalog-integration-design.i18n.yaml | 6 - docs/typert-catalog-integration-design.md | 133 ------------------ docs/typert-catalog-integration-design.zh.md | 133 ------------------ docs/user/develop/basic/index.md | 29 ---- docs/user/develop/practice/index.md | 5 +- docs/user/develop/practice/index.zh.md | 5 +- examples/README.md | 20 +-- examples/acp-agent/README.i18n.yaml | 4 +- examples/acp-agent/README.md | 10 +- examples/acp-agent/README.zh.md | 12 +- examples/headless-agent/README.md | 4 +- examples/headless-agent/README.zh.md | 4 +- examples/jsonrpc-agent/README.i18n.yaml | 4 +- examples/jsonrpc-agent/README.md | 6 +- examples/jsonrpc-agent/README.zh.md | 6 +- examples/web-cordis/README.i18n.yaml | 6 + examples/web-cordis/README.md | 21 +++ examples/web-cordis/README.zh.md | 21 +++ native/README.md | 13 +- packages/acp/README.i18n.yaml | 4 +- packages/acp/README.md | 2 +- packages/acp/README.zh.md | 2 +- packages/bash/bash-local/README.md | 12 +- packages/code-runtime/README.md | 6 +- packages/code-runtime/README.zh.md | 6 +- packages/compact/README.md | 10 +- packages/context/README.i18n.yaml | 4 +- packages/context/README.md | 8 +- packages/context/README.zh.md | 8 +- packages/cordis/README.md | 4 +- packages/core/README.md | 18 ++- packages/core/README.zh.md | 18 ++- packages/credentials/README.i18n.yaml | 4 +- packages/credentials/README.md | 16 +-- packages/credentials/README.zh.md | 16 +-- packages/examples/README.i18n.yaml | 4 +- packages/examples/README.md | 16 +-- packages/examples/README.zh.md | 16 +-- packages/examples/jsonrpc-demo/README.md | 2 +- packages/examples/jsonrpc-demo/README.zh.md | 2 +- packages/fs/README.md | 22 ++- packages/fs/fs/README.md | 17 +-- packages/fs/tool-fs/README.md | 2 +- packages/fs/tool-fs/README.zh.md | 2 +- packages/goal/README.i18n.yaml | 4 +- packages/goal/README.md | 8 +- packages/goal/README.zh.md | 8 +- packages/guard/README.md | 6 +- packages/hooks/README.md | 8 +- packages/hooks/README.zh.md | 8 +- packages/host/README.md | 14 +- packages/llm/README.md | 12 +- packages/lsp/README.md | 10 +- packages/mcp/README.md | 2 +- packages/mcp/README.zh.md | 2 +- packages/plan/README.md | 6 +- packages/plan/plan-mode/README.md | 1 - packages/plan/plan-mode/README.zh.md | 1 - packages/pty/README.md | 12 +- packages/sandbox/README.md | 12 +- packages/sandbox/sandbox-local/README.md | 2 - packages/sandbox/sandbox-local/README.zh.md | 2 - packages/sdk/README.md | 19 ++- packages/sdk/sdk-client/README.md | 4 - packages/sdk/sdk-client/README.zh.md | 4 - packages/session-persistence/README.md | 12 +- packages/session-projection/README.md | 10 +- packages/session-query/README.md | 10 +- packages/session-title/README.i18n.yaml | 4 +- packages/session-title/README.md | 12 +- packages/session-title/README.zh.md | 16 +-- packages/settings/README.i18n.yaml | 4 +- packages/settings/README.md | 8 +- packages/settings/README.zh.md | 12 +- packages/skill/README.md | 12 +- packages/spill/README.md | 14 +- packages/storage/README.md | 12 +- packages/subagent/README.md | 24 ++-- packages/subagent/subagent-acp/README.md | 3 - packages/subagent/subagent-acp/README.zh.md | 3 - packages/subagent/subagent-dsh-sdk/README.md | 2 - .../subagent/subagent-dsh-sdk/README.zh.md | 2 - packages/subprocess/README.md | 10 +- packages/support/README.md | 22 +-- packages/tasks/README.md | 14 +- packages/telemetry/README.md | 10 +- packages/timeout/README.md | 10 +- packages/typert/README.md | 8 +- packages/util/README.md | 22 +-- packages/web/README.md | 20 ++- packages/workflow/README.md | 14 +- packages/workspace/README.md | 8 +- python/README.i18n.yaml | 4 +- python/README.md | 70 +-------- python/README.zh.md | 70 +-------- python/development.i18n.yaml | 6 + python/development.md | 61 ++++++++ python/development.zh.md | 61 ++++++++ 112 files changed, 705 insertions(+), 1057 deletions(-) create mode 100644 apps/cli/reference.md create mode 100644 apps/cli/reference.zh.md delete mode 100644 docs/typert-catalog-integration-design.i18n.yaml delete mode 100644 docs/typert-catalog-integration-design.md delete mode 100644 docs/typert-catalog-integration-design.zh.md create mode 100644 examples/web-cordis/README.i18n.yaml create mode 100644 examples/web-cordis/README.md create mode 100644 examples/web-cordis/README.zh.md create mode 100644 python/development.i18n.yaml create mode 100644 python/development.md create mode 100644 python/development.zh.md diff --git a/apps/cli/reference.md b/apps/cli/reference.md new file mode 100644 index 0000000000..fc86a04b88 --- /dev/null +++ b/apps/cli/reference.md @@ -0,0 +1,82 @@ +# `dsh` CLI behavior reference + +English | [中文](reference.zh.md) + +Argv is parsed once through a [Commander](https://github.com/tj/commander.js) adapter ([`src/args.ts`](src/args.ts)): one program whose default (no subcommand) is the TUI/headless surface (`--config`, `-p`/`--prompt`, `--resume`, `--dump-config`, `--dump-default-config`), whose `meta` subcommand is the same TUI over this checkout, whose `upgrade` subcommand is a guided-session entry, and whose `web` subcommand is the browser UI. `meta` and `upgrade` are experimental: each runs only with its `--experimental` flag or with `DSH_EXPERIMENTAL=1` in the environment, and fails loud (stderr, exit 1) otherwise. `src/bin.ts` switches on the resolved mode and dynamic-imports only that mode's module. `dsh --help` lists every mode and `dsh web --help` renders the web usage, `dsh --version` prints this app's version, and an unknown option or a mistyped `--resume` fails loud (stderr, exit 1) instead of misrouting. Every subcommand that shares no option with the default surface — `upgrade`, `web`, `meta` — rejects a leaked `--config`/`-p`/`--resume`/dump flag rather than running and dropping it. `dsh web`'s `--host`/`--port` are unvalidated pass-through overrides: the `dsh-host-webserver` schema is the single source of both the default (the shipped Web overlay value when a flag is absent) and validity, and rejects a bad value at boot. `--trusted-host` appends named authorities for the /api browser-trust fence; an all-interfaces bind additionally derives the machine's LAN IP literals itself ([`src/app-cli-entry.ts`](src/app-cli-entry.ts)), so the printed LAN URL works without flags. + +The TUI surface: + +- boots `base.cordis.yml` plus `tui.cordis.yml` through [`dsh-app-boot`](../../packages/ui/app-boot/README.md); `--config ` applies a patch-list overlay instead of the personal overlay, while `--config-replace ` boots that file as the complete tree; +- resumes a persisted session with `dsh --resume ` and, when the Node host exposes `process.execve`, supplies the TUI's in-place handoff host: after selector preflight and current-session flush, the host disposes the app and replaces the process with a normalized resume invocation; runtimes without process replacement leave the session running and say so. This CLI owns session identity and the exit line rather than the config: it mints or selects the `main` session id and provides it, plus the exact command that reproduces this invocation, on the boot context ([`MAIN_SESSION_ID_KEY`](../../packages/ui/tui/README.md) and `TUI_GOODBYE_MESSAGE_KEY`). No `cordis.yml` key can drop resume, and a missing or unreadable id fails loud instead of creating a fresh session; +- treats the **invoking directory** as the workspace — sessions, relative paths, and workspace instructions resolve from the cwd (`dsh meta` is the sole exception, below); +- tells the agent where its own source lives: after boot it adds a prompt section naming this harness checkout, resolved from the launcher's real path so it holds under a PATH symlink and an arbitrary cwd, so the self-referential `cordis` toolset can read and modify it; +- applies the personal overlay from `~/.dsh` (see [app-boot's Personal config](../../packages/ui/app-boot/README.md#personal-config)): `config.yaml` patches the booted tree, while `.env` there is the credential provider's own store (never hoisted into the environment, so keys stay rotatable). Environment precedence is ambient > project `.env`. The shipped tree's Cordis HMR keeps `config.yaml` live; an explicit `--config` tree replaces that overlay, and a tree without HMR reads it at startup only. +- presents the [versioned first-run welcome](../../.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md) through the mounted TUI overlay service when its immutable marker is absent under `DSH_HOME`; only Enter creates that version's marker, while Escape, disposal, or process exit leaves it eligible. The official DeepSeek icon, responsive terminal rasters, all-locale Chinese copy, and notice version are static local owners; the overlay never writes a session event or model context. +- registers bare `/compact`: while the agent is idle, it summarizes useful older history even below automatic pressure, rejects arguments, and reports success only after the standalone replacement bracket is durable. A prompt submitted during compaction keeps its queue identity and starts after that checkpoint; injected context remains visible. + +`dsh meta` is that same TUI with this harness checkout as the workspace, so working on dsh itself needs no `cd`. It chdirs to the checkout root — resolved from the launcher's real path, the same root the source-path prompt section names — after the environment is settled, so precedence is unchanged while the session cwd and HMR watch root move together. Meta always starts a fresh session and accepts no default-surface options; use ordinary `dsh --resume ` to resume a persisted session. + +`dsh upgrade` is a guided fresh-session entry over the default TUI surface: it mints a fresh session in the invoking directory and seeds its first turn with the bundled `dsh-upgrade` skill, exactly as if the user typed `/skill:`. The launcher passes the skill name on the boot context ([`INITIAL_SKILL_KEY`](../../packages/ui/tui/README.md)), which the TUI auto-invokes once the chat is live. The command takes no options beyond the experimental gate — `--config`, `-p`, and `--resume` fail loud — and seeds only on this first launch, so a later `dsh --resume ` of the session is an ordinary TUI session with no re-injection. + +`dsh --dump-config` and `dsh web --dump-config` print the composed config tree — the shipped base, the surface overlay, and the `--config` or personal overlay, exactly the layers that surface would boot — as YAML on stdout and exit without booting; `--dump-default-config` stops at the surface overlay, so diffing the two shows precisely what the user layer changes. Each run of rows is preceded by a `# ==` comment naming the file it comes from and the layers that patched it (e.g. `# == base.cordis.yml, patched by tui.cordis.yml`), so the output shows provenance while staying one loadable document. Composition runs through the include's own patch algorithm and YAML dialect (`applyEntryPatches`/`entryListSchema` from `@cordisjs/plugin-include`), so the dump cannot drift from what boots; `!!js` expressions print verbatim and unevaluated, and a patch whose target row is absent is reported on stderr with its layer, mirroring the Loader's boot-time warning. Launcher-owned boot-context values (session identity, CLI-flag patches) are per-invocation facts outside the config tree and do not appear. The dump flags reject boot-only flags (`-p`, `--resume`, `--config-replace`) rather than silently ignoring them, and `--dump-default-config` takes no `--config`. + +The Web and headless surfaces boot `base.cordis.yml` plus `web.cordis.yml`, then apply `$DSH_HOME/config.yaml`; an explicit `--config ` replaces that personal overlay. Both surfaces otherwise share the same composition: both tell the coding agent its resolved model and session working directory, treat the invoking directory as the default project and Workspace root, create named Workspaces beneath that root unless `--workspace-root ` overrides it, load applicable `AGENTS.md`/`CLAUDE.md` instructions into each agent-loop request prefix with a 65,536-byte render budget, opt into first-message model titles, use the same bounded transient model-request retry policy as the TUI, and mount a disposable in-memory SQLite content-index service. Web additionally names the DeepSeek Harness Web GUI as the interaction surface, this checkout as its own source location, and the process's canonical local URL and mode in both the prompt and managed `$DSH_WEB_URL`/`$DSH_WEB_MODE`; references such as “this page” therefore identify the GUI without claiming access to implicit DOM, route, or screenshot state. In production mode the host reads rebuilt frontend dist and client bundles on the next request, so refreshing the existing URL updates that GUI without replacing its process. `dsh web --dev` mounts the client-plugin HMR receiver, but no-refresh updates additionally require `pnpm run dev:web` in the same checkout to watch and rebuild plugin bundles; shell and ordinary package changes still require a rebuild and page refresh. Bare `apps/web` Vite serving fails before listening because it cannot inject `window.__DSH_BOOT__`. The index service is ACTIVE at boot, while its `node:sqlite` module and database handle open only on the first content search. This keeps Node 22 startup output free of SQLite's experimental warning before search is used; the first actual search may still emit the runtime warning. Each service instance owns its database, so parallel invocations neither share unsupported SQLite state nor leave derived index files behind, and the first search lazily reconciles live and persisted logs. Headless differs only in listening on an OS-assigned port (parallel `dsh -p` runs never collide; the stderr-printed URL opens the live session in a browser). Both need the frontend dist and client bundles built (`pnpm run build && pnpm run build:web`). + +The shared composition defaults new TUI, Web, and headless sessions to the `workspace-write` permission preset (`workspace-write` file mode plus `ask` approval policy). Sandbox-enforced bash and filesystem mutations may write only under the session workspace and platform temporary roots; reads, network access, and process visibility are not confined. The browser answers one-shot approval requests and exposes the Access picker; the TUI exposes `/permission`, but has no approval-request answerer, so an automatic wider retry there fails closed until the user deliberately changes the session preset. `DSH_PERMISSION_MODE` changes the process fallback, while a stored General-settings Permission value applies to later sessions without changing an open one. + +All three surfaces consume `$DSH_HOME/config.yaml`; the TUI and Web apply valid edits live, while one-shot headless runs read it at startup. The shipped trees include an empty `repository-plugins` row, so a standalone user can add prepared GitHub Plugins without an SDK project or install command: + +```yaml +- id: repository-plugins + name: '@deepseek-ai/dsh-repository-plugin' + config: + repositories: + - 'github:PolyArch/humanize#' +``` + +The repository must contain a prepared `.dsh-plugin` package; the [repository Plugin contract](../../packages/cordis/repository-plugin/README.md#standalone-app-configuration) documents authoring, nested Plugin paths, the immutable cache, trust boundary, and failure semantics. A failed live edit keeps the last good tree and emits Cordis's `hmr/config-update-failed` event. + +The shipped TUI and Web compositions register the native DeepSeek adapter plus pi-ai OpenAI and Anthropic profiles. Credentials and endpoint overrides come from the provider-standard `DEEPSEEK_API_KEY` / `DEEPSEEK_BASE_URL`, `OPENAI_API_KEY` / `OPENAI_BASE_URL`, and `ANTHROPIC_API_KEY` / `ANTHROPIC_BASE_URL` pairs in the boot's layered environment. + +Every surface also registers `web_search` and only `web_search`. Search uses DeepSeek's Anthropic-compatible Messages endpoint, resolves the same `DEEPSEEK_API_KEY` reference for every call, and accepts the separate `DEEPSEEK_SEARCH_BASE_URL` endpoint override; each search is an auxiliary model request with its own latency and token cost. `web_fetch` remains disabled and the composition mounts no default fetch provider, so deployments that need arbitrary page retrieval must opt in through an overlay. The deployment decision and its security boundary live in the [default Web search Agent Note](../../.agents/notes/implemented/feature/2026-07-31-web-default-search.md). + +`DSH_TOOLS_MODE` selects the tool presentation mode for the whole Web/headless process: `native` (the schema default when unset), `code` (the `run_code`-only Code Mode wire), or `both`; any other value fails loud at boot through the `dsh-tools` config schema. The setting is process-wide because Loader composition is static. The TUI surface ignores it and pins `native`. + +[`core-web.cordis.yml`](config/core-web.cordis.yml) is an opt-in `dsh web --config` overlay that keeps the shipped Web host, browser, Workspace, persistence, and permission composition while reducing the default native model surface to owner-scoped persistent `bash` and `str_replace_editor`. The PTY backend and editor consume the existing Web sandbox and filesystem providers. An open persistent shell prevents changing that session's permission mode until the shell closes, so a shell created under wider access cannot survive a downgrade. `DSH_TOOLS_MODE` still controls native/Code Mode presentation for the resulting two-tool registry. + +From a source checkout, start this minimal Web profile with: + +```sh +pnpm run dsh web --config apps/cli/config/core-web.cordis.yml +``` + +Every `dsh` surface — TUI, Web, and headless — reports session telemetry by default (the row lives in the shared `base.cordis.yml`): every session-log event streams as OTLP/HTTP log records to `https://harness-telemetry.deepseeksvc.com/v1/logs` on a 10-second batch cadence. `DSH_TELEMETRY_OTLP_URL` points the exporter at a different collector; setting `DSH_TELEMETRY_DISABLED` to ANY non-empty value — including `0` or `false` — disables the row before it loads (a privacy switch prefers off-by-mistake over on-by-mistake). No redaction rule is mounted in this composition yet: exported records are the raw captured copy, including message text, tool arguments and results, and the session's working-directory path. The deployment rulings live in the [web-telemetry-default-mount Agent Note](../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md). + +MCP servers are not a shipped default, because a default would have to name one: `@deepseek-ai/dsh-mcp-client` mounts exactly one server per row and spawns it as a child process, outside `ctx.bash` and so outside the sandbox policy. The package is a runtime dependency of this CLI, so an installed `dsh` can mount your own servers from `$DSH_HOME/config.yaml` or a `--config` overlay without a source checkout: + +```yaml +- insert: + - id: mcp-github + name: '@deepseek-ai/dsh-mcp-client' + config: + serverName: github + transport: stdio + command: npx + args: ['-y', '@modelcontextprotocol/server-github'] + env: + GITHUB_TOKEN: !!js process.env.GITHUB_TOKEN +``` + +The model then sees `mcp__github__*`. See the [mcp-client README](../../packages/mcp/mcp-client/README.md) for the Streamable HTTP transport and the full field table. + +## Install (developer machine) + +Symlink the source-running launcher onto your PATH; it resolves the checkout through its own real path, so code changes apply on the next launch with no build step: + +```sh +ln -sf "$(pwd)/bin/dsh" ~/.local/bin/dsh +``` + +Source launches run `apps/cli/src/bin.ts` through tsx's ESM-only hook (`node --import tsx/esm`), which transforms TypeScript and projects the root tsconfig `paths` map into module resolution. Node's native TypeScript modes are not used: Node 26 removed `--experimental-transform-types`, and strip-only mode rejects syntax the source graph relies on (vendored parameter properties, decorators, runtime enums/namespaces). The CJS hook stays off because the source graph is ESM-only and the CJS resolver adds ~0.4s of startup. `bin/dsh` pins `TSX_TSCONFIG_PATH` to the checkout's root tsconfig so resolution is cwd-independent, and the `dsh-source-launch-smoke` node-compat gate runs this exact launch vector on every supported Node line. tsx applies the `paths` map without checking dependency declarations, so declaration completeness rests on the static gates: the TUI configs resolve bare plugins through `examples/package.json`, the Web/headless `cordis.yml` through this package's `dependencies`, and `verify-cordis-config` requires every configured bare plugin to be declared, while allowing unrelated dependencies. + +`pnpm run dsh` runs the same entry from the repo root and forwards arguments directly, for example `pnpm run dsh -p "task"`. The built form (`lib/bin.js`, via `pnpm run build`) boots the same config under plain Node. diff --git a/apps/cli/reference.zh.md b/apps/cli/reference.zh.md new file mode 100644 index 0000000000..49381afe9a --- /dev/null +++ b/apps/cli/reference.zh.md @@ -0,0 +1,82 @@ +# `dsh` CLI(命令行界面)行为参考 + +[English](reference.md) | 中文 + +Argv 只会通过 [Commander](https://github.com/tj/commander.js) 适配器([`src/args.ts`](src/args.ts))解析一次:同一个程序的默认形式(无子命令)是 TUI/无头界面(`--config`、`-p`/`--prompt`、`--resume`、`--dump-config`、`--dump-default-config`),`meta` 子命令是以本 checkout 为 workspace 的同一个 TUI,`upgrade` 子命令是引导会话入口,`web` 子命令则是浏览器 UI。`meta` 与 `upgrade` 是实验性命令:只有带上各自的 `--experimental` 标志或在环境中设置 `DSH_EXPERIMENTAL=1` 才会运行,否则明确报错(stderr,退出码 1)。`src/bin.ts` 按解析后的 mode 分支,仅动态导入该 mode 的模块。`dsh --help` 列出所有 mode,`dsh web --help` 渲染 Web 用法,`dsh --version` 打印此应用的版本;未知选项或拼错的 `--resume` 会明确报错(stderr,退出码 1),而不会被错路由。凡与默认界面不共享任何选项的子命令(`upgrade`、`web`、`meta`)都会拒绝泄漏进来的 `--config`/`-p`/`--resume`/dump 标志,而不会照常运行并丢弃它。`dsh web` 的 `--host`/`--port` 是未验证的直通覆盖:`dsh-host-webserver` schema 是默认值(标志缺失时使用已交付的 Web 覆盖层值)和有效性的唯一真源,并在启动时拒绝错误值。`--trusted-host` 为 /api 浏览器信任栅栏追加具名权威;全接口绑定还会自行推导本机的 LAN IP 字面量([`src/app-cli-entry.ts`](src/app-cli-entry.ts)),因此打印出的 LAN URL 无需任何标志即可使用。 + +TUI 界面: + +- 通过 [`dsh-app-boot`](../../packages/ui/app-boot/README.md) 启动 `base.cordis.yml` 与 `tui.cordis.yml`;`--config ` 应用一个补丁列表覆盖并替代个人覆盖,而 `--config-replace ` 将指定文件作为完整配置树启动; +- 使用 `dsh --resume ` 恢复已持久化会话。当 Node 宿主公开 `process.execve` 时,还会提供 TUI 的原地移交宿主:选择器预检并刷新当前会话后,宿主会对应用执行 dispose(资源释放),并以规范化的恢复调用替换进程;不支持进程替换的运行时会让会话继续运行并给出提示。会话身份与退出行由本 CLI 拥有,而非由配置指定:它创建或选定 `main` 会话 id,并把该 id 以及可复现本次调用的确切命令一起提供到启动上下文([`MAIN_SESSION_ID_KEY`](../../packages/ui/tui/README.md) 与 `TUI_GOODBYE_MESSAGE_KEY`)。任何 `cordis.yml` 键都无法移除恢复能力;缺失或无法读取的 id 会明确报错,而不会创建新会话; +- 将 **调用目录** 视为 workspace:会话、相对路径和 workspace 指令都从 cwd 解析(`dsh meta` 是唯一例外,见下文); +- 告知 agent(智能体)自身源码所在位置:启动后添加一个命名此 harness checkout 的提示词段。该路径从启动器的真实路径解析,因此在 PATH 符号链接和任意 cwd 下仍然有效,使自指的 `cordis` 工具集可以读取并修改它; +- 应用 `~/.dsh` 中的个人覆盖(参见 [app-boot 的个人配置](../../packages/ui/app-boot/README.md#personal-config)):`config.yaml` 修补已启动的树,而那里的 `.env` 是凭据提供方自己的存储(绝不会被提升进环境,因此密钥始终可轮换)。环境优先级为环境中已有的值 > 项目 `.env`。已交付配置树中的 Cordis HMR(热模块替换)会持续应用 `config.yaml` 的变更;显式 `--config` 配置树会替代该个人覆盖,未包含 HMR 的配置树只在启动时读取该文件。 +- 当 `DSH_HOME` 下不存在不可变确认标记时,通过已挂载的 TUI overlay 服务呈现[版本化首次运行欢迎页](../../.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md);只有 Enter 会创建该版本的标记,Escape、资源释放或进程退出仍保留展示资格。官方 DeepSeek 图标、响应式终端栅格图、所有 locale 共用的中文文案和通知版本均由静态本地文件持有;overlay 不会写入会话事件或模型上下文。 +- 注册裸 `/compact`:agent 空闲时,即使未达到自动压力,也会摘要有效的较早历史;该命令拒绝参数,并只在独立替换标记对持久化后报告成功。压缩(compaction)期间提交的提示词保留其队列身份,并在该检查点之后启动;注入的上下文仍保持可见。 + +`dsh meta` 是以本 harness checkout 为 workspace 的同一个 TUI,因此开发 dsh 自身无需 `cd`。它在环境确定之后才 chdir 到 checkout 根目录(从启动器的真实路径解析,与源码路径提示词段所指的根目录相同),因此环境优先级不变,而会话 cwd 与 HMR 监视根目录会一并移动。Meta 始终创建新会话,不接受默认界面的任何选项;恢复已持久化会话应使用普通的 `dsh --resume `。 + +`dsh upgrade` 是默认 TUI 界面之上的引导式全新会话入口:它在调用目录中创建一个全新会话,并以内置 `dsh-upgrade` skill(技能)播种其首轮,效果等同于用户手动键入 `/skill:`。启动器将 skill 名称提供到启动上下文([`INITIAL_SKILL_KEY`](../../packages/ui/tui/README.md)),TUI 在聊天就绪后自动调用它。该命令除实验性门槛外不接受任何选项——`--config`、`-p`、`--resume` 都会明确报错——且仅在首次启动时播种,因此之后 `dsh --resume ` 恢复该会话时是普通 TUI 会话,不会重复注入。 + +`dsh --dump-config` 和 `dsh web --dump-config` 把合成后的配置树——已交付的基础配置、界面覆盖层,以及 `--config` 或个人覆盖层,恰好是该界面启动时组装的那些层——以 YAML 打印到 stdout 后退出,不启动任何东西;`--dump-default-config` 止步于界面覆盖层,因此对两份输出做 diff 就能精确看出用户层改了什么。每段连续的行之前都有一条 `# ==` 注释,标明该段来自哪个文件以及被哪些层修补过(例如 `# == base.cordis.yml, patched by tui.cordis.yml`),因此输出既展示来源,又仍是一份可加载的文档。合成通过 include 自己的补丁算法和 YAML 方言(`@cordisjs/plugin-include` 的 `applyEntryPatches`/`entryListSchema`)完成,因此 dump 不可能与实际启动漂移;`!!js` 表达式原样打印、不求值,目标行不存在的补丁会连同其所在层报到 stderr,与 Loader 启动时的警告一致。由启动器持有的启动上下文值(会话身份、CLI 标志补丁)是每次调用的事实,位于配置树之外,不会出现。dump 标志会拒绝仅用于启动的标志(`-p`、`--resume`、`--config-replace`)而不是静默忽略它们,`--dump-default-config` 不接受 `--config`。 + +Web 和无头界面启动 `base.cordis.yml` 与 `web.cordis.yml`,随后应用 `$DSH_HOME/config.yaml`;显式的 `--config ` 会替代该个人覆盖。除此之外,两者共享同一套组合:两者都会告知编码 agent 所用模型和会话工作目录,将调用目录视为默认项目和 Workspace 根目录,除非通过 `--workspace-root ` 覆盖,否则会在该根目录下创建具名 Workspace;它们会把适用的 `AGENTS.md`/`CLAUDE.md` 指令加载到每个 agent-loop 请求前缀中,渲染预算为 65,536 字节,选用首条消息模型标题,采用与 TUI 相同的有界暂时性模型请求重试策略,并挂载一个可丢弃的内存 SQLite 内容索引服务。Web 还会明确说明交互界面是 DeepSeek Harness Web GUI、当前 checkout 是自身源码位置,并在提示词及受管的 `$DSH_WEB_URL`/`$DSH_WEB_MODE` 中提供该进程的规范本地 URL 和模式;因此,「这个页面」等表述会指向该 GUI,但 agent 不会声称可以访问未显式提供的 DOM、路由或截图状态。在生产模式下,宿主会在下次请求时读取重新构建的前端 dist 和客户端 bundle,因此刷新现有 URL 即可更新该 GUI,无须替换其进程。`dsh web --dev` 会挂载客户端插件的 HMR 接收端,但要实现无刷新更新,还需在同一 checkout 中运行 `pnpm run dev:web`,以监视并重新构建插件 bundle;shell 和普通包(package)的更改仍需重新构建并刷新页面。直接使用裸 `apps/web` Vite 服务会在开始监听前失败,因为它无法注入 `window.__DSH_BOOT__`。索引服务在启动时处于 ACTIVE 状态,但其 `node:sqlite` 模块与数据库句柄分别要到首次内容搜索才会导入和打开。这样可使 Node 22 在尚未使用搜索时的启动输出不出现 SQLite 实验性警告;首次实际搜索仍可能发出运行时警告。每个服务实例独占自己的数据库,因此并行调用既不会共享不受支持的 SQLite 状态,也不会留下派生索引文件,首次搜索还会惰性对账实时日志与持久化日志。无头界面唯一的差异是监听操作系统分配的端口(并行 `dsh -p` 运行绝不冲突;stderr 打印的 URL 会在浏览器中打开实时会话)。两者都需要先构建前端 dist 和客户端 bundle(`pnpm run build && pnpm run build:web`)。 + +共享组合把新建 TUI、Web 和无头会话的权限默认设为 `workspace-write` preset(`workspace-write` 文件模式加 `ask` 审批策略)。由沙箱强制约束的 bash 与文件系统修改只能写入会话工作区和平台临时根目录;读取、网络访问和进程可见性不受该策略约束。浏览器可以应答一次性审批请求,并提供 Access 选择器;TUI 提供 `/permission`,但没有审批请求应答者,因此自动请求更宽权限的重试会以拒绝方式关闭,直到用户主动更改会话 preset。`DSH_PERMISSION_MODE` 会更改进程回退值,而「通用」设置中已存储的「权限」值只适用于之后的会话,不会更改已打开的会话。 + +三个界面都会使用 `$DSH_HOME/config.yaml`;TUI 和 Web 实时应用有效编辑,而一次性无头运行只在启动时读取。已交付的配置树包含一个空的 `repository-plugins` 配置项,因此独立用户无需 SDK 项目或安装命令,只需配置即可添加已准备的 GitHub 插件: + +```yaml +- id: repository-plugins + name: '@deepseek-ai/dsh-repository-plugin' + config: + repositories: + - 'github:PolyArch/humanize#' +``` + +仓库必须包含已准备的 `.dsh-plugin` 包;[仓库插件契约](../../packages/cordis/repository-plugin/README.md#standalone-app-configuration)说明创作方式、嵌套插件路径、不可变缓存、信任边界和失败语义。实时编辑失败时,最后一个可用树保持运行,并发出 Cordis 的 HMR 事件 `hmr/config-update-failed`。 + +已交付的 TUI 和 Web 组合会注册原生 DeepSeek 适配器,以及 pi-ai 的 OpenAI 和 Anthropic 提供方配置。凭据和端点覆盖来自启动分层环境中的提供方标准变量对:`DEEPSEEK_API_KEY` / `DEEPSEEK_BASE_URL`、`OPENAI_API_KEY` / `OPENAI_BASE_URL` 和 `ANTHROPIC_API_KEY` / `ANTHROPIC_BASE_URL`。 + +每个界面也都只注册 `web_search` 这一个 Web 工具。搜索使用 DeepSeek 的 Anthropic 兼容 Messages 端点,每次调用都会解析同一个 `DEEPSEEK_API_KEY` 凭据引用,并接受独立的 `DEEPSEEK_SEARCH_BASE_URL` 端点覆盖;每次搜索都是一次辅助模型请求,会产生独立的延迟与 token 成本。`web_fetch` 仍处于禁用状态,组合也未挂载默认抓取提供方;需要任意页面抓取能力的部署必须通过覆盖层选择启用。部署决策及其安全边界见[默认 Web 搜索 Agent Note(agent 决策记录)](../../.agents/notes/implemented/feature/2026-07-31-web-default-search.md)。 + +`DSH_TOOLS_MODE` 为整个 Web/无头进程选择工具呈现模式:`native`(未设置时的 schema 默认值)、`code`(仅含 `run_code` 的 Code Mode 线路)或 `both`;任何其他值都会经由 `dsh-tools` 配置 schema 在启动时明确报错。由于 Loader 组合是静态的,该设置作用于整个进程。TUI 界面会忽略该变量并固定为 `native`。 + +[`core-web.cordis.yml`](config/core-web.cordis.yml) 是一个可选启用的 `dsh web --config` 覆盖层:它保留已交付的 Web 宿主、浏览器、Workspace、持久化与权限组合,同时将默认的原生模型界面精简为以所有者为作用域的持久 `bash` 以及 `str_replace_editor`。PTY 后端和编辑器分别消费现有的 Web 沙箱与文件系统提供方。持久 shell 处于打开状态时,会阻止所属会话更改权限模式;因此,在较宽权限下创建的 shell 无法在降权后继续存活。`DSH_TOOLS_MODE` 仍控制由此得到的双工具注册表采用原生/Code Mode 呈现。 + +在源码 checkout 中,用以下命令启动这个精简 Web profile: + +```sh +pnpm run dsh web --config apps/cli/config/core-web.cordis.yml +``` + +每个 `dsh` 界面——TUI、Web 与无头——都默认上报会话遥测(该行位于共享的 `base.cordis.yml`):每条会话日志事件以 OTLP/HTTP 日志记录的形式、按 10 秒批处理节奏流向 `https://harness-telemetry.deepseeksvc.com/v1/logs`。`DSH_TELEMETRY_OTLP_URL` 可将 exporter 指向其他 collector;将 `DSH_TELEMETRY_DISABLED` 设为**任意非空值**——包括 `0` 或 `false`——都会在该行加载前将其关停(隐私开关取「宁可误关、不可误开」)。该组合当前未挂载任何脱敏规则:导出记录即原始捕获副本,包含消息正文、工具参数与结果、以及会话工作目录路径。部署口径见 [web-telemetry-default-mount Agent Note](../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md)。 + +MCP 服务器不是交付默认值,因为默认值必须点名一台服务器:`@deepseek-ai/dsh-mcp-client` 每一行只挂载一台服务器,并把它作为子进程 spawn;该进程不经 `ctx.bash`,因此也不受沙箱策略约束。该包是本 CLI 的运行时依赖,所以已安装的 `dsh` 无需源码检出即可从 `$DSH_HOME/config.yaml` 或 `--config` 覆盖层挂载你自己的服务器: + +```yaml +- insert: + - id: mcp-github + name: '@deepseek-ai/dsh-mcp-client' + config: + serverName: github + transport: stdio + command: npx + args: ['-y', '@modelcontextprotocol/server-github'] + env: + GITHUB_TOKEN: !!js process.env.GITHUB_TOKEN +``` + +模型随后会看到 `mcp__github__*`。Streamable HTTP 传输与完整字段表见 [mcp-client README](../../packages/mcp/mcp-client/README.md)。 + +## 安装(开发机) + +将从源码运行的启动器符号链接到 PATH 上;它通过自身真实路径解析 checkout,因此代码更改会在下次启动时生效,无需构建: + +```sh +ln -sf "$(pwd)/bin/dsh" ~/.local/bin/dsh +``` + +源码启动会通过 tsx 的 ESM-only hook(`node --import tsx/esm`)运行 `apps/cli/src/bin.ts`,由它转换 TypeScript 并将根 tsconfig 的 `paths` 映射投射到模块解析中。不使用 Node 原生 TypeScript 模式:Node 26 移除了 `--experimental-transform-types`,而 strip-only 模式无法接受源码图依赖的语法(vendor 中的参数属性、装饰器、运行时 enum/namespace)。CJS hook 保持关闭,因为源码图是纯 ESM,而 CJS 解析器会增加约 0.4s 启动耗时。`bin/dsh` 将 `TSX_TSCONFIG_PATH` 固定到 checkout 的根 tsconfig,使解析与 cwd 无关;node-compat 门禁 `dsh-source-launch-smoke` 会在每条受支持的 Node 版本线上运行这一精确启动向量。tsx 应用 `paths` 映射时不检查依赖声明,声明完整性由静态门禁保障:TUI 配置通过 `examples/package.json` 解析裸插件,Web/无头 `cordis.yml` 通过本包的 `dependencies` 解析;`verify-cordis-config` 要求每个已配置的裸插件均已声明,同时允许存在无关依赖。 + +`pnpm run dsh` 从仓库根目录运行同一入口并直接转发参数,例如 `pnpm run dsh -p "task"`。构建形式(`lib/bin.js`,通过 `pnpm run build`)会在普通 Node 下启动同一配置。 diff --git a/docs/architecture.md b/docs/architecture.md index ee50e249f8..d056b716e9 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -68,80 +68,31 @@ Waterfalls are around-middleware: listeners delegate with `next()`; returning wi ## Default Loop Lifecycle -A **session** is append-only. An ordinary **turn** claims one queued `send()` item; injection claims none. A successor awaits its predecessor's checkpoint but may share its `running` interval ([decision](../.agents/notes/implemented/simplification/2026-07-17-one-send-one-turn.md)). A turn ends when model or plugins stop it; a **step** is one model request plus tools. Quotes in the [sequence below](agent-lifecycle.md) mark durable events. - -Creation without an id mints `-session-`; `sessionId` resumes or creates, while `resumeSessionId` requires history. Resume restores lineage and delegation depth before publication. Setup failures emit `agent-loop/config-start-failed`; teardown is silent. +A **session** is append-only. An ordinary **turn** claims one queued `send()` item; injection claims none. A turn ends when the model or plugins stop it; a **step** is one model request plus its tool calls. Agent and session publication happen only after private setup and resume state are ready. ### Turn Flow ```text -choose declarative identity and fresh/resume path - -> prepare private session + agent.ctx -> await unpublished setup -> invoke optional synchronous setup commit - -> enter session + agent -> session/created -> agent/created - -> enable driving -> agent/session-start(source) -> start driver -forever: - wait for queued occurrence - claim (edit/remove end) -> emit agent/status(running) if starting an interval - open the next-step acceptance window - -> agent/prompt-submit - blocked or failed prompt -> close the window without opening a turn - append a context-only caller batch immediately - keep steering and context staged beside it pending for a later admitted turn - allowed prompt: - 'turn/start' - append prompt + additional contexts as separate 'user/message' events - STEP loop: - agent/step - assemble system prompt and tools - materialize changed runtime context as sourced 'user/message' - drain injected context and provisional steering (steering bypasses prompt-submit) - snapshot the derived messages (the reconstruction boundary) - 'step/start' - admit the drained steering receipts - agent/request (config only) -> prepare adapter defaults/provenance + context capacity under turn signal -> log request/header (+ request/context on route change) -> llm/stream (frozen, registration-bound) - 'assistant/chunk' - 'assistant/message' - schedule tool calls by ctx.tools.executionMode: - exclusive -> barrier - parallel -> rolling pool, <= maxParallelToolCalls; reclassify-at-start; scheduler failure -> stop starts, drain dispatches - start -> 'tool/call' -> ordered tools/pre-execute -> concurrent tools/execute - model-order result -> ordered tools/post-execute -> 'tool/result' - drain accepted tool context after all results; keep steering provisional - 'step/end' - continue for tools or steering unless a result concluded the turn and rejects pending steering - otherwise agent/turn-stopping -> drain context -> continue only for steering - close the next-step acceptance window - 'turn/end' -> agent/settled - start the next waking queued message, or emit agent/status(idle) +queued input -> prompt admission -> turn/start + -> [agent/step -> request -> chunks/message -> tools -> step/end]* + -> turn/end -idle inject: - append 'user/message' - do not open a turn or run the model +idle injection -> user/message ``` -Each step assembles ordered stable system sections, cache-safe dynamic contexts, tool schemas, and variables; unknown references fail the turn. `dsh-system-prompt` owns identity and persona; the loop supplies `provider`, `model`, and `cwd` ([prompt ownership](../.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.md)). - -Admission-time and active-turn `inject()` stage for the next step; tool-time injection and post-tool `additionalContexts` settle after results. Steering shares the outbox but remains provisional until a request admits it. `steer()` returns a message-owned receipt: after `agent/step` and asynchronous prompt assembly succeed, the loop commits the stable batch, snapshots request history, opens `step/start`, then resolves its receipts as admitted with the turn and step; later arrivals wait. A turn-concluding tool result, broad cancellation, disposal, or a claimed idle-steering turn that never opens a step rejects affected receipts, while `cancel(..., { keepInbox: true })` and non-terminal routing preserve pending delivery. Idle `inject()` appends immediately without changing turn numbers; persistence drains eagerly. - -Before driver claim, `updateInbox()` may edit or remove a queued occurrence, or strictly transfer its immutable message into an open next-step window. That transfer ends the queued occurrence and accepts a new steering occurrence; a closed window leaves Queue unchanged. Direct `steer()` remains best-effort for newly submitted input and falls back to a waking follow-up outside the window ([decision](../.agents/notes/implemented/feature/2026-07-30-web-queue-steer-action.md)). - -Pruning precedes summaries; overflow retries require durable progress. `agent/request-error` may authorize one retry turn between failed-step and turn close; cancellation wins. Adapter-owned `retryPolicy` makes normal mode bounded; always mode delegates specialized recovery before retrying until success or cancellation ([compaction](../.agents/notes/implemented/architecture/2026-07-10-after-call-compaction-pressure-and-overflow-recovery.md), [retry foundation](../.agents/notes/implemented/architecture/2026-06-21-bounded-llm-request-recovery.md), [provider policy](../.agents/notes/implemented/feature/2026-07-24-provider-retry-policies.md)). +Each step assembles the prompt, tools, runtime context, adapter settings, and model history before recording its reconstruction boundary. Tool calls then run through the shared execution pipeline. `inject()` adds context without opening an idle turn; `steer()` targets a next-step admission window; queued input remains the source of ordinary turns. The generated [agent lifecycle](agent-lifecycle.md) owns exact event order, and the [agent-loop README](../packages/core/agent-loop/README.md) owns queue, steering, retry, and cancellation mechanics. ### Failure Boundaries -Adapter failures close their step before `agent/request-error` receives the exact `Error`, normalized `LlmFailure`, and signal. A handled failure closes its turn and opens a retry turn from durable history without an idle notification; exhaustion leaves terminal `turn/end`. Failed chunks commit neither messages nor tool calls. - -Other failures use `agent/error`. Cancellation and disposal beat recovery. Before request-header commit, the turn signal cancels asynchronous model-capability preparation; undispatched tools get synthetic `tool/call`/`ABORTED_BEFORE_DISPATCH` pairs. Effective `cancel(cause)` emits its cause before queue clearing and abort; observers cannot veto; idle calls emit nothing. Durability records user or parent cancellation as `aborted`, teardown as `disposed`; teardown awaits quiescence. The cause affects reporting, not late result-context handling ([decision](../.agents/notes/implemented/architecture/2026-07-16-explicit-turn-cancellation.md)). - -Turn and step events are turn-enclosed. Idle `user/message` and standalone `compact/* { turn: null }` consume no turn; their lock-time markers may interleave with injection. Reload synthesizes interrupted turn ends; `session/end-seed` distinguishes stale compaction orphans from live locks. After close, only `agent/error` reports failures. Each turn has one [TurnEndReason](core-data-structures/session.md#why-a-turn-ended-turnendreasonmap). +Adapter failures close their step before `agent/request-error` can authorize recovery from durable history. Other failures use `agent/error`; cancellation and disposal take precedence over recovery. Failed model attempts commit no assistant message or tool side effect. Turn closure is represented by one [TurnEndReason](core-data-structures/session.md#why-a-turn-ended-turnendreasonmap); the exact retry contract belongs to [LLM streaming](core-data-structures/llm-streaming.md). ### Agent Handles -`ctx.agents` owns agents, returning `AgentHandle { agent, dispose() }`. Plugins use `send()` or `followup()`, receipt-bearing `steer()`, and `inject()` presets; [`reserveTurnAdmission()`](../packages/core/agent/README.md#agent-interface-typests) synchronously reserves idle for durable work without changing queued prompt identity. Await a steering receipt when request admission matters; best-effort UI steering may ignore it. `cancel()` and `whenIdle()` control lifecycle. Caller, factory, and consumer co-own teardown through one awaited disposer. +`ctx.agents` owns agents and returns `AgentHandle { agent, dispose() }`. Plugins submit queued work, steering, or injected context through the [agent interface](../packages/core/agent/README.md#agent-interface-typests); cancellation, idleness, and teardown stay behind the same handle. ### Agent Scope -Each agent owns scoped `agent.ctx`; shared storage overlays its tool, prompt, and command entries on globals while preserving domain views ([decision](../.agents/notes/implemented/architecture/2026-07-12-scoped-layers-store.md)). Scoped listeners filter dispatch; contributions unwind with awaited cleanup. `CreateAgentOptions.setup(agentCtx)` composes before publication and may return a synchronous commit that the factory invokes immediately before registry entry, after every setup await. Typed resolvers derive carrier checks from merged `Events` and `scopeTarget` ([semantic gates](../.agents/notes/implemented/process/2026-07-14-typescript-program-backed-semantic-gates.md)). Details: [agent scope](../.agents/notes/implemented/architecture/2026-07-08-agent-scope-contexts.md), [subagent composition](../.agents/notes/implemented/feature/2026-07-12-subagent-persona-tool-filter-and-depth.md). `AgentLoop` runs under `ctx.agents.withInitiator()`; private orchestration derives `agent.session`, but turn, step, signal, cwd, and authority stay explicit ([decision](../.agents/notes/implemented/architecture/2026-07-15-agent-initiator-scope.md)). +Each agent owns scoped `agent.ctx`; shared storage overlays its tools, prompts, and commands on global contributions while scoped listeners filter dispatch. Setup composes before publication and cleanup unwinds contributions. The [agent-scope decision](../.agents/notes/implemented/architecture/2026-07-08-agent-scope-contexts.md) owns the detailed lifecycle. ## State diff --git a/docs/architecture.zh.md b/docs/architecture.zh.md index c2e596cd10..e9e0dc4c20 100644 --- a/docs/architecture.zh.md +++ b/docs/architecture.zh.md @@ -68,80 +68,31 @@ waterfall(瀑布式事件)是环绕中间件:监听器通过 `next()` 委 ## 默认循环生命周期 -**会话**采用仅追加方式。普通**轮次**领取一项已排队的 `send()` 输入;注入不领取输入。后续轮次会等待前一轮次的检查点,但可以与其共用同一个 `running` 区间([决策](../.agents/notes/implemented/simplification/2026-07-17-one-send-one-turn.md))。模型或插件停止轮次时,该轮次结束;一个**步骤**包含一次模型请求及其工具。[下文时序](agent-lifecycle.md)中的引号标记持久事件。 - -创建时若未提供 id,流程会生成 `-session-`;`sessionId` 用于恢复或创建会话,而 `resumeSessionId` 要求已有历史。恢复流程在发布前还原沿袭关系和委托深度。初始化失败会发出 `agent-loop/config-start-failed`;拆卸过程保持静默。 +**会话**仅追加。普通**轮次**认领一个排队的 `send()` 项;注入不会认领。轮次在模型或插件停止时结束;一个**步骤**由一次模型请求及其工具调用组成。只有私有设置和恢复状态准备完毕后,系统才会发布 agent 与会话。 ### 轮次流程 ```text -choose declarative identity and fresh/resume path - -> prepare private session + agent.ctx -> await unpublished setup -> invoke optional synchronous setup commit - -> enter session + agent -> session/created -> agent/created - -> enable driving -> agent/session-start(source) -> start driver -forever: - wait for queued occurrence - claim (edit/remove end) -> emit agent/status(running) if starting an interval - open the next-step acceptance window - -> agent/prompt-submit - blocked or failed prompt -> close the window without opening a turn - append a context-only caller batch immediately - keep steering and context staged beside it pending for a later admitted turn - allowed prompt: - 'turn/start' - append prompt + additional contexts as separate 'user/message' events - STEP loop: - agent/step - assemble system prompt and tools - materialize changed runtime context as sourced 'user/message' - drain injected context and provisional steering (steering bypasses prompt-submit) - snapshot the derived messages (the reconstruction boundary) - 'step/start' - admit the drained steering receipts - agent/request (config only) -> prepare adapter defaults/provenance + context capacity under turn signal -> log request/header (+ request/context on route change) -> llm/stream (frozen, registration-bound) - 'assistant/chunk' - 'assistant/message' - schedule tool calls by ctx.tools.executionMode: - exclusive -> barrier - parallel -> rolling pool, <= maxParallelToolCalls; reclassify-at-start; scheduler failure -> stop starts, drain dispatches - start -> 'tool/call' -> ordered tools/pre-execute -> concurrent tools/execute - model-order result -> ordered tools/post-execute -> 'tool/result' - drain accepted tool context after all results; keep steering provisional - 'step/end' - continue for tools or steering unless a result concluded the turn and rejects pending steering - otherwise agent/turn-stopping -> drain context -> continue only for steering - close the next-step acceptance window - 'turn/end' -> agent/settled - start the next waking queued message, or emit agent/status(idle) +queued input -> prompt admission -> turn/start + -> [agent/step -> request -> chunks/message -> tools -> step/end]* + -> turn/end -idle inject: - append 'user/message' - do not open a turn or run the model +idle injection -> user/message ``` -每个步骤都会组装有序的稳定系统提示词片段、缓存安全的动态上下文、工具 schema 和变量;未知引用会使该轮次失败。`dsh-system-prompt` 负责身份和角色设定;循环提供 `provider`、`model` 和 `cwd`([提示词归属](../.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.md))。 - -接纳期间和活跃轮次内的 `inject()` 会为下一步骤暂存;工具执行期间的注入和工具执行后的 `additionalContexts` 会在结果记录完毕后落定。steering 与其共用 outbox,但在请求接纳前始终处于待准入状态。`steer()` 会返回归属于该消息的回执:`agent/step` 和异步提示词组装成功后,循环提交稳定批次、捕获请求历史并开启 `step/start`,再将其回执解析为已准入并附带轮次与步骤;后续消息继续等待。结束轮次的工具结果、广义取消、dispose(资源释放),以及已领取 idle-steering 消息却从未开启步骤的轮次,都会拒绝受影响的回执;`cancel(..., { keepInbox: true })` 和非终止型路由则保留待处理投递。空闲状态下的 `inject()` 会立即追加,且不改变轮次编号;持久化层会尽快排空。 - -驱动器认领之前,`updateInbox()` 可以编辑或移除 queued 单次入队项,也可以严格地把其不可变消息转移到开放的 next-step 窗口。该转移会结束 queued 单次入队项,并接受一个新的 steering 单次入队项;窗口关闭时 Queue 保持不变。直接调用 `steer()` 时,对新提交的输入仍采用尽力而为的语义,并在窗口之外回退为会唤醒 agent 的后续轮次([决策](../.agents/notes/implemented/feature/2026-07-30-web-queue-steer-action.md))。 - -裁剪先于摘要;溢出重试必须取得持久进展。`agent/request-error` 可以在失败步骤与轮次关闭之间授权一个重试轮次;取消优先。适配器拥有的 `retryPolicy` 使 normal mode 保持有界;always mode 先委托专门恢复,再持续重试直至成功或取消([压缩](../.agents/notes/implemented/architecture/2026-07-10-after-call-compaction-pressure-and-overflow-recovery.md)、[重试基础](../.agents/notes/implemented/architecture/2026-06-21-bounded-llm-request-recovery.md)、[提供方策略](../.agents/notes/implemented/feature/2026-07-24-provider-retry-policies.md))。 +每个步骤都会组装提示词、工具、运行时上下文、适配器设置和模型历史,随后记录其重建边界。之后,工具调用通过共享执行流水线运行。`inject()` 添加上下文但不打开空闲轮次;`steer()` 针对下一步骤的准入窗口;排队输入仍是普通轮次的来源。精确事件顺序由生成的 [agent 生命周期](agent-lifecycle.md)定义;队列、steering、重试与取消机制由 [agent-loop README](../packages/core/agent-loop/README.md)定义。 ### 失败边界 -适配器故障会先关闭自身步骤,再由 `agent/request-error` 接收准确的 `Error`、标准化的 `LlmFailure` 和信号。已处理的失败会关闭所在轮次,并从持久历史开启重试轮次,不发出空闲通知;重试耗尽则留下终态 `turn/end`。失败分片既不提交消息,也不提交工具调用。 - -其他故障使用 `agent/error`。取消和资源释放优先于恢复。在提交请求头之前,轮次信号会取消异步模型能力准备;尚未分派的工具会得到合成的 `tool/call`/`ABORTED_BEFORE_DISPATCH` 对。实际生效的 `cancel(cause)` 在清空队列和中止前发出原因;观察方不能否决;空闲调用不发事件。持久化层将用户或父级取消记录为 `aborted`,拆卸记录为 `disposed`;拆卸会等待完全停稳。原因只影响报告方式,不影响延迟完成的结果上下文处理([决策](../.agents/notes/implemented/architecture/2026-07-16-explicit-turn-cancellation.md))。 - -轮次和步骤事件均位于轮次边界内。空闲 `user/message` 与独立的 `compact/* { turn: null }` 不占用轮次;其锁定时刻标记可以与注入交错。重新加载会为中断的轮次合成结束事件;`session/end-seed` 区分陈旧的压缩遗留项与活跃锁。关闭后仅由 `agent/error` 报告故障。每个轮次有一个 [TurnEndReason](core-data-structures/session.md#why-a-turn-ended-turnendreasonmap)。 +适配器故障会先关闭步骤,再由 `agent/request-error` 授权从持久历史恢复。其他故障使用 `agent/error`;取消和资源释放优先于恢复。失败的模型尝试不会提交 assistant 消息或工具副作用。轮次关闭由一个 [TurnEndReason](core-data-structures/session.md#why-a-turn-ended-turnendreasonmap)表示;准确的重试契约由 [LLM 流式输出](core-data-structures/llm-streaming.md)定义。 ### Agent 句柄 -`ctx.agents` 拥有 agent,返回 `AgentHandle { agent, dispose() }`。插件使用 `send()` 或 `followup()`、带回执的 `steer()` 和 `inject()` 预设;[`reserveTurnAdmission()`](../packages/core/agent/README.md#agent-interface-typests) 为持久工作同步预留空闲状态,同时不改变排队提示词身份。需要确认请求准入时应等待 steering 回执;尽力执行的 UI steering 可以忽略它。`cancel()` 与 `whenIdle()` 控制生命周期。调用方、工厂和消费方通过同一个需等待完成的 disposer 共同拥有拆卸过程。 +`ctx.agents` 管理 agent,并返回 `AgentHandle { agent, dispose() }`。插件通过 [agent 接口](../packages/core/agent/README.md#agent-interface-typests)提交排队工作、steering 或注入上下文;取消、空闲状态和拆卸也都由同一个句柄封装。 ### Agent 作用域 -每个 agent 都拥有作用域化的 `agent.ctx`;共享存储会将其工具、提示词和命令条目叠加到全局条目之上,同时保留各领域视图([决策](../.agents/notes/implemented/architecture/2026-07-12-scoped-layers-store.md))。作用域监听器会过滤分派;贡献都会在撤销时等待清理完成。`CreateAgentOptions.setup(agentCtx)` 在发布前完成组合,并可返回一个同步提交操作;所有 setup 的 await 均完成后,工厂会在进入注册表前立即调用该操作。类型化解析器从合并后的 `Events` 和 `scopeTarget` 推导载体检查([语义门禁](../.agents/notes/implemented/process/2026-07-14-typescript-program-backed-semantic-gates.md))。详情见 [agent 作用域](../.agents/notes/implemented/architecture/2026-07-08-agent-scope-contexts.md)和 [subagent 组合](../.agents/notes/implemented/feature/2026-07-12-subagent-persona-tool-filter-and-depth.md)。`AgentLoop` 在 `ctx.agents.withInitiator()` 内运行;私有编排会派生 `agent.session`,但轮次、步骤、信号、cwd 和权限仍保持显式([决策](../.agents/notes/implemented/architecture/2026-07-15-agent-initiator-scope.md))。 +每个 agent 都拥有作用域化的 `agent.ctx`;共享存储会将其工具、提示词和命令叠加到全局贡献之上,作用域监听器则过滤分派。设置过程在发布前完成组合,清理过程会撤销贡献。详细生命周期由 [agent 作用域决策](../.agents/notes/implemented/architecture/2026-07-08-agent-scope-contexts.md)定义。 ## 状态 diff --git a/docs/core-data-structures/llm-streaming.md b/docs/core-data-structures/llm-streaming.md index 28f775f810..8f5b917ee1 100644 --- a/docs/core-data-structures/llm-streaming.md +++ b/docs/core-data-structures/llm-streaming.md @@ -64,10 +64,10 @@ Every adapter MUST obey these, and every consumer may rely on them: - **Provider stalls are bounded at the transport.** Both shipping remote adapters expose positive finite `streamIdleTimeoutMs` with a five-minute default. The watchdog arms only while iterator `next()` is outstanding, uses one stable signal for the whole request, maps its own expiry to `TIMEOUT`, and keeps an earlier caller abort as `ABORTED`. - **Context overflow has one canonical code.** Both DeepSeek adapters classify explicit provider detail through `isContextWindowExceededError()` and surface `CONTEXT_WINDOW_EXCEEDED`, whether the failure arrives as a thrown HTTP `LlmError` or an in-band finish error. Consumers route on the code, never provider text. - **An empty completion is a retryable error, not a silent success.** Both adapters map a terminal `stop` finish that carried no content blocks to `finish {kind:'error'}` with the canonical `EMPTY_RESPONSE` code, and `dsh-llm-retry` retries it by default; see [empty model responses are retryable](../../.agents/notes/implemented/bug-fix/2026-07-24-empty-model-response-is-retryable.md). -- **Every provider HTTP request carries the app-attribution header.** Adapters send `attributionHeaders()` (below) - the `User-Agent` baseline - and prove it with a wire-level test (mock server asserting the received header, or the library's header hook for a library-backed adapter). +- **Every provider HTTP request carries the app-attribution header.** Adapters send `attributionHeaders()` (below), the `User-Agent` baseline. - **Replay state is adapter-owned.** A successful `finish` may carry lossless-JSON state needed to reconstruct a native provider response. The loop stores it with the assembled assistant message. On a later request, `LlmService` passes the state only when the historical provider and target provider are currently registered to the exact same adapter instance. That adapter validates the state and owns any cross-model or cross-provider conversion; other adapters receive the provider-neutral content and provenance without the private state. -This contract is pinned down by two deliberately independent implementations: `dsh-llm-deepseek` (direct fetch, SSE framing via `eventsource-parser`) and `dsh-llm-pi-ai` (a generic multi-provider adapter through `@earendil-works/pi-ai`). The library-backed adapter exercises the finish-chunk error path, while transport-boundary tests prove each idle watchdog stops its actual request. +Two independent implementations obey this contract: `dsh-llm-deepseek` uses direct fetch with SSE framing through `eventsource-parser`, while `dsh-llm-pi-ai` provides a generic multi-provider adapter through `@earendil-works/pi-ai`. Both carry cancellation and the idle watchdog to the provider request. ## `ResolvedRetryPolicy` diff --git a/docs/core-data-structures/llm-streaming.zh.md b/docs/core-data-structures/llm-streaming.zh.md index db6b28acb8..10e04f0856 100644 --- a/docs/core-data-structures/llm-streaming.zh.md +++ b/docs/core-data-structures/llm-streaming.zh.md @@ -64,10 +64,10 @@ interface LlmFailure { - **提供方停顿在传输层受到时限约束。** 两个已交付的远程适配器都暴露正数且有限的 `streamIdleTimeoutMs`,默认五分钟。watchdog 只在 iterator `next()` 尚未完成时启动,整个请求使用同一个稳定 signal,把自身到期映射为 `TIMEOUT`,并把更早发生的调用方中止保留为 `ABORTED`。 - **上下文溢出只有一个规范 code。** 两个 DeepSeek 适配器都通过 `isContextWindowExceededError()` 对提供方的显式细节分类并暴露 `CONTEXT_WINDOW_EXCEEDED`,无论失败以抛出的 HTTP `LlmError` 还是带内 finish error 到达。消费方按 code 路由,绝不依赖提供方文本。 - **空 completion 是可重试错误,而不是静默的成功结果。** 两个适配器都把没有携带任何内容块的终止性 `stop` 结束映射为携带规范 `EMPTY_RESPONSE` code 的 `finish {kind:'error'}`,`dsh-llm-retry` 默认会重试它;详见[空模型响应可重试](../../.agents/notes/implemented/bug-fix/2026-07-24-empty-model-response-is-retryable.md)。 -- **每个提供方 HTTP 请求都携带应用归属头。** 适配器发送 `attributionHeaders()`(见下文)作为 `User-Agent` 基线,并通过协议级测试加以证明(mock 服务器断言收到的 header,或对基于库的适配器使用库的 header 钩子)。 +- **每个提供方 HTTP 请求都携带应用归属头。** 适配器发送下文的 `attributionHeaders()`,即 `User-Agent` 基线。 - **回放状态归适配器所有。** 成功的 `finish` 可以携带重建提供方原生响应所需的无损 JSON 状态。循环会将其与组装后的 assistant 消息一起存储。后续请求中,仅当历史提供方与目标提供方当前注册到完全相同的适配器实例时,`LlmService` 才会传递该状态。该适配器负责校验状态并拥有所有跨模型或跨提供方转换;其他适配器只会收到提供方无关的内容与 provenance,不会收到私有状态。 -该契约由两个有意保持独立的实现锁定:`dsh-llm-deepseek`(直接 fetch,SSE(Server-Sent Events)分帧经由 `eventsource-parser`)和 `dsh-llm-pi-ai`(通过 `@earendil-works/pi-ai` 实现的通用多提供方适配器)。基于库的适配器覆盖 finish 分片错误路径,而传输边界测试证明每个空闲 watchdog 都会停止其实际请求。 +两个彼此独立的实现遵循该契约:`dsh-llm-deepseek` 使用直接 fetch,并通过 `eventsource-parser` 进行 SSE(Server-Sent Events)分帧;`dsh-llm-pi-ai` 则通过 `@earendil-works/pi-ai` 提供通用多提供方适配器。两者都会把取消与空闲 watchdog 传递至提供方请求。 ## `ResolvedRetryPolicy` diff --git a/docs/core-data-structures/persistence.i18n.yaml b/docs/core-data-structures/persistence.i18n.yaml index 9f42f7dab9..0d14fe7899 100644 --- a/docs/core-data-structures/persistence.i18n.yaml +++ b/docs/core-data-structures/persistence.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/core-data-structures/persistence.md -persistence.md: 237ce268691fa6f4b0546c6c1c14c21eaa462612 -persistence.zh.md: 9cb555f2e157844704896d9b04feb822428540e5 +persistence.md: ed19af4e739c153ce1beec7c1e8de06f0c0bca53 +persistence.zh.md: 83d8ba3c0eabe57b9df661fb86ec05d3db98d8b6 diff --git a/docs/core-data-structures/persistence.md b/docs/core-data-structures/persistence.md index 237ce26869..ed19af4e73 100644 --- a/docs/core-data-structures/persistence.md +++ b/docs/core-data-structures/persistence.md @@ -4,7 +4,7 @@ English | [中文](persistence.zh.md) The **durability seam** for the event log. [session.md](session.md) describes the in-memory `Session` — the append-only `SessionEvent` log that is the source of truth. This page describes how that log is made durable: the abstract `SessionPersistence` service, its backends, the flush checkpoint, crash recovery, and the metadata header that travels alongside the log. The event vocabulary the log carries is enumerated, member by member, in the generated [persistence log event catalog](../persistence-catalog.md). -The seam is a textbook [capability seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md): one abstract service ([dsh-session-persistence](../../packages/session-persistence/session-persistence), `ctx.sessionPersistence`) defining locate/create/append, crash-repairing load, non-mutating inspect, and lightweight list/snapshot observation over the existing `SessionEvent` — **no parallel persisted type** — and two interchangeable backends that pass the same `runPersistenceContract` suite. See the [session-persistence Agent Note](../../.agents/notes/implemented/architecture/2026-06-14-session-persistence.md). +The seam is a textbook [capability seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md): one abstract service ([dsh-session-persistence](../../packages/session-persistence/session-persistence), `ctx.sessionPersistence`) defining locate/create/append, crash-repairing load, non-mutating inspect, and lightweight list/snapshot observation over the existing `SessionEvent` — **no parallel persisted type** — and two interchangeable backends implementing the same contract. See the [session-persistence Agent Note](../../.agents/notes/implemented/architecture/2026-06-14-session-persistence.md). ## The flush checkpoint diff --git a/docs/core-data-structures/persistence.zh.md b/docs/core-data-structures/persistence.zh.md index 9cb555f2e1..83d8ba3c0e 100644 --- a/docs/core-data-structures/persistence.zh.md +++ b/docs/core-data-structures/persistence.zh.md @@ -4,7 +4,7 @@ 事件日志的**持久性 seam**。[session.md](session.md) 描述了内存中的 `Session`:仅追加的 `SessionEvent` 日志即为真源。本页描述如何使该日志持久化:抽象的 `SessionPersistence` 服务、它的后端、flush 检查点、崩溃恢复,以及随日志一同存储的元数据头。日志承载的事件词汇在生成的[持久化日志事件目录](../persistence-catalog.md)中逐项列举。 -该 seam 是典型的[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md):一个抽象服务([dsh-session-persistence](../../packages/session-persistence/session-persistence),`ctx.sessionPersistence`)在现有 `SessionEvent` 上定义 locate/create/append、会执行崩溃修复的 load、不会修改数据的 inspect,以及轻量的 list/snapshot 观察——**没有平行的持久化类型**——以及两个可互换、通过同一套 `runPersistenceContract` 的后端。见 [session-persistence Agent Note(agent 决策记录)](../../.agents/notes/implemented/architecture/2026-06-14-session-persistence.md)。 +该 seam 是典型的[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md):一个抽象服务([dsh-session-persistence](../../packages/session-persistence/session-persistence),`ctx.sessionPersistence`)在现有 `SessionEvent` 上定义 locate/create/append、会执行崩溃修复的 load、不会修改数据的 inspect,以及轻量的 list/snapshot 观察——**没有平行的持久化类型**——以及两个实现同一契约的可互换后端。见 [session-persistence Agent Note(agent 决策记录)](../../.agents/notes/implemented/architecture/2026-06-14-session-persistence.md)。 ## flush 检查点 diff --git a/docs/development.md b/docs/development.md index 77087a95fd..2ebe4e0869 100644 --- a/docs/development.md +++ b/docs/development.md @@ -98,36 +98,7 @@ The keyless [CI workflow](../.github/workflows/ci.yml) groups independent gates ## Daily commands -Use these from the repo root: - -```sh -pnpm run test # unit tests -pnpm run test:coverage # unit tests with per-file coverage gates -pnpm run test:e2e # real-API tests; self-skips without DEEPSEEK_API_KEY -pnpm run check:all # comprehensive opt-in gate set; not wired to Git hooks -pnpm run typecheck # tsc -b over the root solution: emits package/vendor lib/types, checks both aggregates -pnpm run lint # oxlint . -pnpm run lint:fix # formatting-only ESLint, then oxlint . --fix -pnpm run doc-typecheck # compile checked TypeScript snippets in Markdown docs -pnpm run gen-cordis-catalog # regenerate docs/cordis-catalog/events.md + services.md from source -pnpm run verify-cordis-catalog # fail if either cordis catalog is stale -pnpm run verify-export-jsdoc # fail if a module-level package export lacks complete JSDoc -pnpm run gen-doc-graphs # regenerate generated relationship docs from source and curated graph definitions -pnpm run verify-doc-graphs # fail if generated relationship docs are stale -pnpm run verify-md-wrap # fail on hard-wrapped prose paragraphs in docs/README markdown -pnpm run verify-mermaid # fail if a ```mermaid diagram has invalid Mermaid syntax -pnpm run verify-type-equiv # fail if a ```ts type-equiv doc block drifts from its source type -pnpm run verify-doc-budgets # fail if a budgeted standing doc exceeds its word ceiling -pnpm run gen-translation-brief # print the minimal-update briefing for out-of-sync translation pairs (--apply splices code-only edits) -pnpm run doc-sync # all Markdown/doc gates, scheduled concurrently; the doc-sync leaf list in scripts/run-gates.ts is the full list -pnpm run gen-module-graph # regenerate docs/module-graph.md from package peerDeps -pnpm run verify-module-graph # fail if docs/module-graph.md is stale -pnpm run build # emit lib/types intermediates, then bundle lib/index.* runtime files -pnpm run verify-node-next-types # fail if built declarations are not NodeNext-consumable -pnpm run hygiene # knip, publint, workspace constraints, and NodeNext declaration check -``` - -When changing package public behavior, update the relevant README or JSDoc in the same change. `pnpm run doc-sync` catches checked TypeScript snippets, generated doc freshness, markdown wrap/link drift, type equivalence, translation pairing, Mermaid syntax, and doc budgets, but broader prose/API sync still needs review. +The root [contributor instructions](../AGENTS.md#commands) summarize common commands, while [`package.json`](../package.json) and [scripts/run-gates.ts](../scripts/run-gates.ts) own the current script and gate inventories. Select the smallest checks that cover the changed surface. Documentation changes use `pnpm run doc-sync`; package-public behavior changes also update the owning README or JSDoc, and built-artifact checks require `pnpm run build` first. ## Demos diff --git a/docs/development.zh.md b/docs/development.zh.md index 18e130e2f3..995b479ea6 100644 --- a/docs/development.zh.md +++ b/docs/development.zh.md @@ -98,36 +98,7 @@ keyless [CI 工作流](../.github/workflows/ci.yml) 将独立门禁分组到若 ## 日常命令 -在仓库根目录使用: - -```sh -pnpm run test # unit tests -pnpm run test:coverage # unit tests with per-file coverage gates -pnpm run test:e2e # real-API tests; self-skips without DEEPSEEK_API_KEY -pnpm run check:all # comprehensive opt-in gate set; not wired to Git hooks -pnpm run typecheck # tsc -b over the root solution: emits package/vendor lib/types, checks both aggregates -pnpm run lint # oxlint . -pnpm run lint:fix # formatting-only ESLint, then oxlint . --fix -pnpm run doc-typecheck # compile checked TypeScript snippets in Markdown docs -pnpm run gen-cordis-catalog # regenerate docs/cordis-catalog/events.md + services.md from source -pnpm run verify-cordis-catalog # fail if either cordis catalog is stale -pnpm run verify-export-jsdoc # fail if a module-level package export lacks complete JSDoc -pnpm run gen-doc-graphs # regenerate generated relationship docs from source and curated graph definitions -pnpm run verify-doc-graphs # fail if generated relationship docs are stale -pnpm run verify-md-wrap # fail on hard-wrapped prose paragraphs in docs/README markdown -pnpm run verify-mermaid # fail if a ```mermaid diagram has invalid Mermaid syntax -pnpm run verify-type-equiv # fail if a ```ts type-equiv doc block drifts from its source type -pnpm run verify-doc-budgets # fail if a budgeted standing doc exceeds its word ceiling -pnpm run gen-translation-brief # print the minimal-update briefing for out-of-sync translation pairs (--apply splices code-only edits) -pnpm run doc-sync # all Markdown/doc gates, scheduled concurrently; the doc-sync leaf list in scripts/run-gates.ts is the full list -pnpm run gen-module-graph # regenerate docs/module-graph.md from package peerDeps -pnpm run verify-module-graph # fail if docs/module-graph.md is stale -pnpm run build # emit lib/types intermediates, then bundle lib/index.* runtime files -pnpm run verify-node-next-types # fail if built declarations are not NodeNext-consumable -pnpm run hygiene # knip, publint, workspace constraints, and NodeNext declaration check -``` - -修改 package 的公开行为时,请在同一个变更中更新相关 README 或 JSDoc。`pnpm run doc-sync` 能检测到被检查的 TypeScript 片段、生成文档的新鲜度、Markdown 换行/链接漂移、type-equiv、翻译配对、Mermaid 语法和文档预算,但更广泛的行文/API 同步仍需评审把关。 +根目录的[贡献者说明](../AGENTS.md#commands)概述常用命令,[`package.json`](../package.json) 与 [scripts/run-gates.ts](../scripts/run-gates.ts) 则负责当前脚本和门禁清单。请选择覆盖变更表面的最小检查集。文档变更使用 `pnpm run doc-sync`;package 公开行为变更还需更新所属 README 或 JSDoc,而基于构建产物的检查需要先运行 `pnpm run build`。 ## 演示 diff --git a/docs/i18n/README.i18n.yaml b/docs/i18n/README.i18n.yaml index 5ad0a7b7dd..464a3c8df0 100644 --- a/docs/i18n/README.i18n.yaml +++ b/docs/i18n/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/i18n/README.md -README.md: d1fff61a1d3db7fbe93d3c95b144598c2ca41c8b -README.zh.md: 9f1d61f1b854ead0ef5851157cce2e9a6de83d89 +README.md: 2bac578034441bbe786ce54c051cc622bf9275c0 +README.zh.md: b7566bf6f88e03eb06227b63011f78252b4dcd07 diff --git a/docs/i18n/README.md b/docs/i18n/README.md index d1fff61a1d..2bac578034 100644 --- a/docs/i18n/README.md +++ b/docs/i18n/README.md @@ -43,7 +43,7 @@ The gate's limit, stated plainly: **a green gate means the pair was confirmed co **Excluded** (never paired, and the gate rejects a `.zh.md` or `.i18n.yaml` for them): -- `docs/cordis-catalog/`, `docs/tool-catalog/`, `docs/config-catalog.md`, `docs/persistence-catalog.md`, `docs/module-graph.md`, `docs/agent-lifecycle.md`, `docs/capability-seams.md`, `docs/event-producer-consumer.md`, `docs/graph-atlas.md`, and `docs/tool-execution-pipeline.md` — generated files; their generators emit English only today, so a hand-written translation would go stale on every regeneration. The planned follow-up is to teach the generators to emit Chinese alongside English, at which point these leave the exclusion list. +- `docs/cordis-catalog/`, `docs/tool-catalog/`, `docs/config-catalog.md`, `docs/persistence-catalog.md`, `docs/module-graph.md`, `docs/agent-lifecycle.md`, `docs/capability-seams.md`, `docs/event-producer-consumer.md`, `docs/graph-atlas.md`, and `docs/tool-execution-pipeline.md` — generated files whose generators emit English only; a hand-written translation would go stale on regeneration. - `docs/AGENTS.md`, `.agents/notes/**/AGENTS.md`, and their `CLAUDE.md` instruction symlinks — agent instructions, maintained in English only like the root `AGENTS.md`. - `docs/i18n/terminology.md` and [style-samples.md](style-samples.md) — both are bilingual by construction. - [translation-prompt.md](translation-prompt.md) — the automated pipeline's prompt template; its body is machine-consumed verbatim, so a paired translation would change pipeline behavior. diff --git a/docs/i18n/README.zh.md b/docs/i18n/README.zh.md index 9f1d61f1b8..b7566bf6f8 100644 --- a/docs/i18n/README.zh.md +++ b/docs/i18n/README.zh.md @@ -43,7 +43,7 @@ **排除**(永不配对,门禁拒绝为它们建 `.zh.md` 或 `.i18n.yaml`): -- `docs/cordis-catalog/`、`docs/tool-catalog/`、`docs/config-catalog.md`、`docs/persistence-catalog.md`、`docs/module-graph.md`、`docs/agent-lifecycle.md`、`docs/capability-seams.md`、`docs/event-producer-consumer.md`、`docs/graph-atlas.md` 与 `docs/tool-execution-pipeline.md`:生成文件;生成器目前只输出英文,手写译文在每次重新生成时必然陈旧。计划中的后续工作是让生成器同时输出中文,届时这些文件移出排除清单。 +- `docs/cordis-catalog/`、`docs/tool-catalog/`、`docs/config-catalog.md`、`docs/persistence-catalog.md`、`docs/module-graph.md`、`docs/agent-lifecycle.md`、`docs/capability-seams.md`、`docs/event-producer-consumer.md`、`docs/graph-atlas.md` 与 `docs/tool-execution-pipeline.md`:生成文件,其生成器只输出英文;手写译文会在重新生成时变得陈旧。 - `docs/AGENTS.md`、`.agents/notes/**/AGENTS.md` 以及指向它们的 `CLAUDE.md` 指令符号链接:agent 指令,与根 `AGENTS.md` 一样只以英文维护。 - `docs/i18n/terminology.md` 与 [style-samples.md](style-samples.md):二者本身即为中英对照文档。 - [translation-prompt.md](translation-prompt.md):自动翻译流水线的提示词模板;正文逐字进入模型请求,配对翻译会改变流水线行为。 diff --git a/docs/typert-catalog-integration-design.i18n.yaml b/docs/typert-catalog-integration-design.i18n.yaml deleted file mode 100644 index a7290fae68..0000000000 --- a/docs/typert-catalog-integration-design.i18n.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each -# side as of the last confirmed-consistent state. Both languages carry equal authority; -# after editing either side, bring the other along and re-record with: -# pnpm run verify-translation-pairing --write docs/typert-catalog-integration-design.md -typert-catalog-integration-design.md: c7d601730655f61f3b875ad5ad6997d3c888bfea -typert-catalog-integration-design.zh.md: abaddfe1f740d4bd7cff5b2db8fd91e34626aab8 diff --git a/docs/typert-catalog-integration-design.md b/docs/typert-catalog-integration-design.md deleted file mode 100644 index c7d6017306..0000000000 --- a/docs/typert-catalog-integration-design.md +++ /dev/null @@ -1,133 +0,0 @@ -# Typert Catalog Integration Design - -English | [中文](typert-catalog-integration-design.zh.md) - -## Current State and Problem - -Typert already provides separate host/client `FaceModel` instances, a `TypeGraph` with explicit cross-face references, and analysis support for services, events, `@typert object`, generics, inheritance, and External types. The TypeScript compiler API should only translate source code into this standard model; downstream consumers should not traverse the TypeScript AST again. - -The repository currently has two catalog pipelines that analyze TypeScript source directly: the static API catalog consumed by `tool-cordis`, and the generation and freshness gate for `docs/cordis-catalog/events.md` and `docs/cordis-catalog/services.md`. They analyze the same services, events, and related types, but maintain separate collection and rendering logic, so they cannot prove that the Typert model is sufficient to represent the existing domain semantics. - -The first phase makes both pipelines consume the Typert model while keeping the three committed artifacts character-for-character identical to their pre-migration versions: - -- `docs/cordis-catalog/events.md` -- `docs/cordis-catalog/services.md` -- `packages/cordis/tool-cordis/src/api-catalog.ts` - -This phase does not require product plugins to publish Typert subpaths, example applications to load Typert, or changes to the runtime dependencies of `tool-cordis`. - -## Options - -### Drive `tool-cordis` from the Runtime Registry - -Each plugin publishes and loads Typert artifacts, then `tool-cordis` reads the current runtime model from `ctx.typert`. This path reflects the set of plugins actually loaded, but it requires every product package represented in the catalog to add package exports, generated artifacts, registry contributions, and application assembly. That integration surface is much larger than the analysis capability being validated now. - -### Publish Typert Artifacts Repository-Wide, Then Aggregate Them Statically - -All product packages generate host/client JS and DTS during the normal build/typecheck process, then the catalog generator aggregates those artifacts. This path establishes the complete publication protocol up front, but it also changes many package manifests and the build topology at once, coupling catalog migration to repository-wide Typert publication. - -### Analyze at Build Time, Then Project the Catalog - -`WorkspaceAnalyzer` builds a `WorkspaceModel` and `TypeGraph` from the host TypeScript project. The repository-specific `CordisCatalogProjector` consumes only that model and generates the three texts. `tool-cordis` continues to import the committed static `api-catalog.ts`, so the runtime does not need the Typert service. - -This phase uses build-time projection. It directly verifies that the standard Typert model can replace the existing AST collector while leaving runtime publication and automatic loading to separate follow-up decisions. - -## Phase-One Architecture - -```text -tsconfig.host.json - │ - ▼ -WorkspaceAnalyzer ── TypeScript compiler API 的唯一边界 - │ - ▼ -WorkspaceModel + TypeGraph - │ - ▼ -CordisCatalogProjector ── 不依赖 TypeScript AST - ├── docs/cordis-catalog/events.md - ├── docs/cordis-catalog/services.md - └── packages/cordis/tool-cordis/src/api-catalog.ts -``` - -The objects have the following responsibilities: - -- `WorkspaceAnalyzer` analyzes packages, exports, services, events, type declarations, and reference relationships, and produces a compiler-independent model. -- `WorkspaceModel` and `TypeGraph` are the standard data structures shared by all generation and scanning analyses. They preserve developer-authored generics, inheritance, and type trees without retaining the TypeScript AST. -- The root entry point of `@deepseek-ai/dsh-typert-generator` exports `CordisCatalogProjector`, which performs model-driven selection, sorting, summary extraction, source location handling, JSDoc completeness checks, type-link closure, and rendering in three text formats. Its implementation remains in a dedicated Cordis catalog file, but it does not create another package subpath or embed a list of repository type names. -- `scripts/gen-cordis-catalog.ts` provides `LINK_MAP`, `FOUNDATION_TYPE_NAMES`, `TYPE_LINK_EXEMPTIONS`, and the inherited Cordis list, injects them explicitly into the projector through `CordisCatalogPolicy`, and owns the write/check CLI behavior. The vendor Cordis core pages continue to be generated by a separate pinned-source projector. -- `tool-cordis` imports only the static `api-catalog.ts` and does not depend on `typert-registry` or `typert-loader`. - -`CordisCatalogProjector` is a repository-specific downstream consumer and is not part of Typert's general-purpose model. When adding another category, first extend the standard model, then add the corresponding projector. The Typert analyzer must not absorb Cordis documentation formats or `tool-cordis` presentation logic. - -## Model Additions - -In addition to type structure, the catalog's character-for-character projection needs the declaration forms written by developers and exact source locations. The standard model therefore retains event/service locations, body-free text for events and members, parameter initializers, and the export status and canonical text of type declarations. `SourceDeclarationModel` also indexes top-level exported declarations for ambiguity checks and static type closure, without promoting them to domain graph roots. - -```ts -interface SourceLocation { - readonly file: string - readonly line: number - readonly column: number -} - -interface EventModel { - readonly location: SourceLocation - readonly text: string -} -``` - -Repository-wide analysis supports building bounded `ts.Program` instances in package batches, then merging them through source-location-stable graph ids into a face model equivalent to monolithic analysis. This capability changes only the memory boundary of the compiler program; it does not change package, declaration, or type graph semantics. - -All information required by the projector must come from `WorkspaceModel` or `TypeGraph`. If a fact required for character-for-character compatibility cannot be expressed by the model, extend the standard model; do not reintroduce `ts.Node`, `ts.Symbol`, or `ts.TypeChecker` in the projector or script. - -## Character-for-Character Migration Oracle - -Before migration, retain the three texts produced by the old generator against the same source state. After migration, run the new analyzer and projector and require the three outputs to be byte-for-byte identical. Newlines, spaces, ordering, JSDoc, source pointers, and generated headers are all part of the comparison. - -`pnpm run verify-cordis-catalog` retains its `--check` mode, which reads the three committed artifacts and compares them directly with the newly computed results. A missing file or any differing character makes the artifact stale, and the error points to the single `pnpm run gen-cordis-catalog` repair command. - -Tests pin both of the following layers: - -- Typert fixture snapshots pin the `WorkspaceModel`, `TypeGraph`, JS, DTS, and Zod outputs, proving the behavior of the standard model and general-purpose emitters. -- Cordis catalog tests or snapshots pin the projector's three complete texts, proving that the repository-specific product projection does not bypass the standard model and providing directly reviewable textual evidence. - -The three committed artifacts are the migration oracle between the old and new implementations and the continuing freshness oracle after migration. The old `gen-cordis-api` AST collector is removed. The scripts and commands with that name remain only as compatibility entry points for the unified projector because the generated file header itself contains the command; retaining the entry point preserves the character-for-character oracle without creating a second source of truth. - -## Exact Change List - -### Typert Generator - -- Add the locations, authored declaration text, parameter initializers, export status, and top-level source declaration index needed for character-for-character projection, with coverage in analyzer and model snapshots. -- Support bounded package-batch analysis and prove that direct and batched models are equivalent. -- Confirm that the catalog's required service declarations, public instance members, JSDoc, generics, inheritance, and referenced types are all available from the model. -- Keep the TypeScript compiler API encapsulated within the analyzer; the public model and projector inputs do not expose compiler objects. - -### Cordis Catalog Projector - -- Select the complete set of Cordis services and events from the host `WorkspaceModel`. -- Preserve the old generator's JSDoc rules: events must have `@mode` and payload `@param` tags; service methods must have a matching `@param` for every parameter; non-void returns must have `@returns`. -- Compute the type links used by signatures and the transitive public type closure required by `tool-cordis` from the type graph. -- Receive caller-maintained type classifications and the inherited surface through an explicit `CordisCatalogPolicy`; do not maintain the repository documentation taxonomy inside the generator package. -- Preserve the existing output rules for source pointers, signatures, summaries, ordering, declaration truncation, and the inherited context catalog. -- Project once and render the events Markdown, services Markdown, and TypeScript API catalog, preventing drift between documentation and tool data. - -### Commands and Consumers - -- `scripts/gen-cordis-catalog.ts` maintains repository policy data, assembles the analyzer and projector, and writes/checks all three artifacts together. Parsing, validation, and rendering logic lives in the generator's dedicated Cordis source file and is exported uniformly from the package root entry point. -- Narrow `scripts/gen-cordis-api.ts` to a logic-free compatibility entry point for the unified CLI; the root `gen-cordis-api` and `verify-cordis-api` aliases point to that entry point. -- Restore the static catalog default in `tool-cordis` and remove its dependencies on `ctx.typert`, `typert-registry`, and runtime package-model completeness. -- `gen-doc-graphs` obtains the projector's model-level result once and reuses its services and events; it must not continue to import the AST collector or analyze the repository again. - -### Narrow the Scope of Phase-One Changes - -- Remove the newly added `./typert` and `./client/typert` exports and `lib/typert.*` files from product plugin package.json files. -- Remove `typert-registry` and `typert-loader` assembly from examples. -- Normal build/typecheck does not run repository-wide `gen-typert` or require product-package Typert artifacts to exist before it runs on a clean tree. -- Retain `packages/typert/generator`, `packages/typert/registry`, and `packages/typert/loader`, along with their independent fixture, emitter, and runtime registration tests. - -## Future Extensions - -The runtime registry remains the receiving and query layer for generated JS/Zod, and the loader remains the automatic loading mechanism; neither supplies data to the first-phase static catalog. When product packages need runtime reflection, they can opt in by publishing `package/typert` and `package/client/typert`, which the loader then registers with `ctx.typert`. - -Future integration does not change the phase-one layering: only the analyzer handles TypeScript, the standard model serves both static generation and scan analysis, and the emitter produces runtime artifacts from that same model. Whether to extend publication to more packages, enable the loader by default, or extend the runtime registry's query capabilities are separate review decisions and remain decoupled from the Cordis catalog migration. diff --git a/docs/typert-catalog-integration-design.zh.md b/docs/typert-catalog-integration-design.zh.md deleted file mode 100644 index abaddfe1f7..0000000000 --- a/docs/typert-catalog-integration-design.zh.md +++ /dev/null @@ -1,133 +0,0 @@ -# Typert catalog 接入设计 - -[English](typert-catalog-integration-design.md) | 中文 - -## 现状与问题 - -Typert 已经具备独立的 host/client `FaceModel`、可显式跨 face 引用的 `TypeGraph`,以及 service、event、`@typert object`、泛型、继承和 External 类型的分析能力。TypeScript compiler API 只应负责把源码转换成这套标准模型;后续消费者不应再次遍历 TypeScript AST。 - -仓库目前有两条直接分析 TypeScript 源码的 catalog 链路:`tool-cordis` 使用的静态 API catalog,以及 `docs/cordis-catalog/events.md`、`docs/cordis-catalog/services.md` 的生成与 freshness gate。它们分析的是同一批 service、event 和相关类型,却分别维护收集与渲染逻辑,不能证明 Typert 模型足以承载现有业务语义。 - -第一阶段的目标是让这两条链路共同消费 Typert 模型,并保持三份已提交产物与迁移前字符级一致: - -- `docs/cordis-catalog/events.md` -- `docs/cordis-catalog/services.md` -- `packages/cordis/tool-cordis/src/api-catalog.ts` - -本阶段不要求业务插件发布 Typert 子路径,不要求示例应用加载 Typert,也不改变 `tool-cordis` 的运行时依赖关系。 - -## 可选路径 - -### 运行时 registry 驱动 `tool-cordis` - -每个插件发布并加载 Typert 产物,`tool-cordis` 再从 `ctx.typert` 读取当前运行时模型。这条路径可以反映实际加载的插件集合,但会要求所有参与 catalog 的业务包增加 package exports、生成产物、registry contribution 和应用装配,接入面远大于当前要验证的分析能力。 - -### 全仓发布 Typert 产物后静态汇总 - -所有业务包在普通 build/typecheck 中生成 host/client JS 与 DTS,再由 catalog 生成器汇总这些产物。这条路径能够提前建立完整的发布协议,但会同时修改大量 package manifest 和构建拓扑,使 catalog 迁移与 Typert 的全仓发布绑定。 - -### 构建期分析后投影 catalog - -`WorkspaceAnalyzer` 从 host TypeScript project 构建 `WorkspaceModel` 与 `TypeGraph`,仓库专用的 `CordisCatalogProjector` 只消费该模型并生成三份文本。`tool-cordis` 继续导入已提交的静态 `api-catalog.ts`,运行时不需要 Typert service。 - -本阶段采用构建期投影。它直接验证 Typert 标准模型能否替代现有 AST collector,同时把运行时 publication 和自动加载留在独立的后续决策中。 - -## 第一阶段架构 - -```text -tsconfig.host.json - │ - ▼ -WorkspaceAnalyzer ── TypeScript compiler API 的唯一边界 - │ - ▼ -WorkspaceModel + TypeGraph - │ - ▼ -CordisCatalogProjector ── 不依赖 TypeScript AST - ├── docs/cordis-catalog/events.md - ├── docs/cordis-catalog/services.md - └── packages/cordis/tool-cordis/src/api-catalog.ts -``` - -各对象的职责如下: - -- `WorkspaceAnalyzer` 负责 package、export、service、event、类型声明和引用关系的分析,并产生 compiler-independent model。 -- `WorkspaceModel` 与 `TypeGraph` 是所有生成和扫描分析共用的标准数据结构,保留开发者写出的泛型、继承和类型树,不保存 TypeScript AST。 -- `@deepseek-ai/dsh-typert-generator` 根入口导出的 `CordisCatalogProjector` 负责模型驱动的选择、排序、摘要、源位置、JSDoc 完整性、类型链接闭包和三种文本格式;实现仍单独放在 Cordis catalog 专用文件中,但不形成额外的 package subpath,也不内置仓库类型名单。 -- `scripts/gen-cordis-catalog.ts` 提供 `LINK_MAP`、`FOUNDATION_TYPE_NAMES`、`TYPE_LINK_EXEMPTIONS` 和 inherited Cordis 清单,通过 `CordisCatalogPolicy` 显式注入 projector,并负责 write/check 的命令行行为;vendor Cordis core 页面仍由独立的 pinned-source projector 生成。 -- `tool-cordis` 只导入静态 `api-catalog.ts`,不依赖 `typert-registry` 或 `typert-loader`。 - -`CordisCatalogProjector` 是仓库业务消费者,不进入 Typert 通用模型。新增其他类别时,先扩展标准模型,再增加对应 projector;Typert analyzer 不吸收 Cordis 文档格式或 `tool-cordis` 展示逻辑。 - -## 模型补充 - -Catalog 的字符级投影除了类型结构,还需要开发者写下的声明形式和精确源码位置。标准模型因此保留 event/service location、event/member 的 body-free text、parameter initializer,以及 type declaration 的 export 状态和 canonical text;`SourceDeclarationModel` 另外索引顶层导出声明,供歧义检查和静态类型闭包使用,但不把它们提升为业务 graph root。 - -```ts -interface SourceLocation { - readonly file: string - readonly line: number - readonly column: number -} - -interface EventModel { - readonly location: SourceLocation - readonly text: string -} -``` - -全仓分析支持按 package 分批构建有界 `ts.Program`,再依靠源码位置稳定的 graph id 合并为与一次性分析等价的 face model。该能力只改变 compiler program 的内存边界,不改变 package、declaration 或 type graph 语义。 - -projector 所需信息必须来自 `WorkspaceModel` 或 `TypeGraph`。如果字符级兼容需要的事实无法从模型表达,应补充标准模型;不得在 projector 或脚本中重新引入 `ts.Node`、`ts.Symbol` 或 `ts.TypeChecker`。 - -## 字符级迁移 oracle - -迁移前,在同一份源码状态下保留旧生成器产生的三份文本。迁移后运行新的 analyzer 与 projector,要求三份输出逐字节相等;换行、空格、排序、JSDoc、source pointer 和生成头都属于比较内容。 - -`pnpm run verify-cordis-catalog` 的 `--check` 模式继续读取三份 committed artifact,并与本次计算结果直接比较。任一文件缺失或任一字符不同都视为 stale,错误信息指向统一的 `pnpm run gen-cordis-catalog` 修复命令。 - -测试同时固定以下两层: - -- Typert fixture snapshots 固定 `WorkspaceModel`、`TypeGraph`、JS、DTS 与 Zod 输出,证明标准模型和通用 emitter 的行为。 -- Cordis catalog 测试或 snapshot 固定 projector 的三份完整文本,证明仓库业务投影没有绕过标准模型,并给出可直接评审的文本证据。 - -三份 committed artifact 是旧实现与新实现的迁移 oracle,也是迁移完成后的持续 freshness oracle。旧 `gen-cordis-api` AST collector 被删除;同名脚本和命令只作为统一 projector 的兼容入口保留,因为生成文件头本身包含该命令,保留入口可以维持字符级 oracle 而不产生第二套真源。 - -## 精确改造清单 - -### Typert generator - -- 补齐字符级投影所需的 location、authored declaration text、parameter initializer、export 状态和顶层 source declaration index,并在 analyzer 与 model snapshots 中覆盖。 -- 支持有界 package batch 分析,并证明 direct 与 batched model 等价。 -- 确认 catalog 所需的 service 声明、public instance member、JSDoc、泛型、继承和引用类型均可从 model 读取。 -- 保持 TypeScript compiler API 封装在 analyzer 内;公共 model 和 projector 输入不暴露 compiler 对象。 - -### Cordis catalog projector - -- 从 host `WorkspaceModel` 选择完整的 Cordis service/event 集合。 -- 保留旧生成器的 JSDoc 规则:event 必须有 `@mode` 和 payload `@param`,service method 必须有参数对应的 `@param`,非 void 返回必须有 `@returns`。 -- 从 type graph 计算签名涉及的类型链接和 `tool-cordis` 所需的传递 public type closure。 -- 通过显式 `CordisCatalogPolicy` 接收调用方维护的类型分类和 inherited surface,不在 generator 包内维护仓库文档 taxonomy。 -- 保留 source pointer、签名、摘要、排序、声明截断和 inherited context catalog 的既有输出规则。 -- 一次投影并渲染 events Markdown、services Markdown 与 TypeScript API catalog,避免文档和工具数据漂移。 - -### 命令与消费方 - -- `scripts/gen-cordis-catalog.ts` 维护仓库 policy 数据、组装 analyzer/projector,并同时 write/check 三份产物;解析、校验和渲染逻辑位于 generator 的 Cordis 专用源文件,并统一从 package 根入口导出。 -- 将 `scripts/gen-cordis-api.ts` 收窄为统一 CLI 的无逻辑兼容入口;根目录的 `gen-cordis-api`、`verify-cordis-api` aliases 指向该入口。 -- `tool-cordis` 恢复静态 catalog 默认值,移除对 `ctx.typert`、`typert-registry` 和运行时 package model 完整性的依赖。 -- `gen-doc-graphs` 一次取得 projector 的 model-level 结果并复用 services/events,不能继续导入 AST collector 或重复分析全仓。 - -### 收窄本阶段改动面 - -- 撤销业务插件 package.json 中新增的 `./typert`、`./client/typert` exports 和 `lib/typert.*` files。 -- 撤销 examples 中的 `typert-registry`、`typert-loader` 装配。 -- 普通 build/typecheck 不运行全仓 `gen-typert`,也不要求 clean tree 预先存在业务包 Typert artifact。 -- 保留 `packages/typert/generator`、`packages/typert/registry`、`packages/typert/loader` 及其独立 fixture、emitter 和 runtime registration 测试。 - -## 后续扩展 - -Runtime registry 继续作为生成 JS/Zod 后的接收与查询层,loader 继续作为自动装载机制;两者不承担第一阶段静态 catalog 的数据来源。业务包需要运行时反射时,可以按 package opt-in 发布 `package/typert` 与 `package/client/typert`,再由 loader 注册到 `ctx.typert`。 - -后续接入不改变本阶段的分层:TypeScript 只进入 analyzer,标准模型同时服务静态生成与扫描分析,runtime artifact 由 emitter 从同一模型产生。是否把更多 package 接入 publication、是否默认启用 loader,以及 runtime registry 最终提供哪些查询能力,分别评审,不与 Cordis catalog 迁移捆绑。 diff --git a/docs/user/develop/basic/index.md b/docs/user/develop/basic/index.md index 5a9f8dfb8f..95efe5c232 100644 --- a/docs/user/develop/basic/index.md +++ b/docs/user/develop/basic/index.md @@ -120,35 +120,6 @@ export default class MyService extends Service { Function form is sufficient in most cases. Use class form when the plugin provides a service to other plugins; see [services and dependencies](../framework/service.md). -## Complete example - -A minimal tool plugin registers its definition on `ctx.tools`: - -```ts -import type { Context } from 'cordis' -import { defineTool } from '@deepseek-ai/dsh-tools' - -export const name = 'greet-tool' -export const inject = ['tools'] - -export function apply(ctx: Context) { - ctx.tools.register(defineTool({ - name: 'greet', - description: 'Greet the named person.', - parameters: { - name: { type: 'string', required: true }, - }, - output: { - schema: { type: 'string' }, - render: (_args, value) => [{ type: 'text', text: value }], - }, - async execute(args) { - return `Hello, ${args.name}!` - }, - })) -} -``` - ## Next steps - [Build a tool](./tool.md) — learn the tool definition DSL diff --git a/docs/user/develop/practice/index.md b/docs/user/develop/practice/index.md index e197d499d7..ea517aa925 100644 --- a/docs/user/develop/practice/index.md +++ b/docs/user/develop/practice/index.md @@ -32,10 +32,7 @@ One interface can have multiple implementations selected through `cordis.yml`: # Local execution - name: '@deepseek-ai/dsh-bash-local' -# Or a future remote sandbox implementation -# - name: '@deepseek-ai/dsh-bash-remote' -# config: -# endpoint: 'https://sandbox.example.com' +# Replace this row with another package that implements the same service. ``` The interface and tool remain unchanged while the implementation changes. diff --git a/docs/user/develop/practice/index.zh.md b/docs/user/develop/practice/index.zh.md index cacf13e8b2..7aaa5abb5f 100644 --- a/docs/user/develop/practice/index.zh.md +++ b/docs/user/develop/practice/index.zh.md @@ -32,10 +32,7 @@ # Local execution - name: '@deepseek-ai/dsh-bash-local' -# Or a future remote sandbox implementation -# - name: '@deepseek-ai/dsh-bash-remote' -# config: -# endpoint: 'https://sandbox.example.com' +# Replace this row with another package that implements the same service. ``` 更换实现时,接口和工具均保持不变。 diff --git a/examples/README.md b/examples/README.md index 64e9804eb6..5d021d9d9c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,32 +2,24 @@ English | [中文](README.zh.md) -Runnable demos (not workspaces) that showcase how the harness is wired. Each example is a **thin leaf**: either a `cordis.yml` tree that picks swappable backends and loads one app package, or an **overlay** — a patch list `dsh --config` applies over the shipped composition ([`apps/cli/config/base.cordis.yml`](../apps/cli/config/base.cordis.yml) plus a surface overlay). Bundled compositions live in [`@deepseek-ai/dsh-cli-demo`](../packages/examples/cli-demo), [`@deepseek-ai/dsh-acp-demo`](../packages/examples/acp-demo), and their shared [`@deepseek-ai/dsh-agent-spine-demo`](../packages/examples/agent-spine-demo) bundle; the `dsh` surfaces use flat config trees instead. There is no `start.ts`; the terminal `demo:*` scripts boot through the [`dsh`](../apps/cli/README.md) CLI, and the headless/ACP scripts invoke the `cli-demo`/`acp-demo` bins. +Runnable demonstrations of the main DeepSeek Harness interfaces and extension points. Each child directory owns its configuration, prerequisites, commands, and detailed behavior. ## mcp-memory -Three default-off reference overlays connect a memory MCP server through the generic MCP client. Pick one file and pass it to `dsh --config`; DSH does not install or configure the upstream memory system. See [mcp-memory/README.md](mcp-memory/README.md) for pinned prerequisites, identity mapping, the shared optional prompt, and the write → fresh-session recall → use verification recipe. +Optional overlays that connect supported third-party memory servers through the generic MCP client. See the [memory example reference](mcp-memory/README.md). ## headless-agent -A non-interactive agent demo that accepts one positional task, runs one complete model/tool turn on the `@deepseek-ai/dsh-cli-demo` app, persists a fresh session, prints `text`, `json`, or `stream-json`, and exits. - -Run with: `pnpm run demo:headless "task"` (needs `DEEPSEEK_API_KEY`). See [headless-agent/README.md](headless-agent/README.md) for the output contract, safety boundaries, and snapshot suite. +A non-interactive agent that accepts one task, runs it, and emits a selected machine-readable or human-readable output format. See the [headless example reference](headless-agent/README.md). ## jsonrpc-agent -An unattended coding agent driven through the Python SDK: JSON-RPC stdio, foreground-only `bash`, `read` / `write` / `edit`, one foreground `subagent`, `todo_write`, JSONL persistence, and compaction. It excludes terminal UI, stdout logging, approvals, skills, and background task controls. See [jsonrpc-agent/README.md](jsonrpc-agent/README.md). +An unattended coding agent driven through the Python SDK and JSON-RPC. See the [JSON-RPC example reference](jsonrpc-agent/README.md). ## web-cordis -The **self-referential** demo: the coding spine plus [`@deepseek-ai/dsh-tool-cordis`](../packages/cordis/tool-cordis), whose three tools (`cordis_inspect` / `cordis_mount` / `cordis_unmount`) let the agent inspect the current DSH process, mount model-written temporary Plugins (an event listener, a brand-new tool, or a service another temporary Plugin injects), and unmount them again. These Plugins exist only in memory and share one internal `cordis-dynamic` fiber subtree; `ctx.fs`/`ctx.web` ride along provider-only as capabilities they can use. - -Run the browser UI at `http://127.0.0.1:3081` with `pnpm run demo:cordis`, or the ACP server with `pnpm run demo:cordis acp` (both need `DEEPSEEK_API_KEY`). See [the toolset Agent Note](../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md) for the design and sandbox caveats. +A self-referential agent that can inspect and change its in-memory Cordis plugin tree. See the [web-cordis example reference](web-cordis/README.md). ## acp-agent -An agent exposed as an **Agent Client Protocol (ACP)** automation server over JSON-RPC stdio, via [`@deepseek-ai/dsh-acp-demo`](../packages/examples/acp-demo). Programmatic clients create fresh sessions, send text prompts, consume committed assistant text, answer one-shot permission requests, and cancel work. It owns the ACP keyless snapshot suite. - -Run with: `pnpm run demo:acp` (needs `DEEPSEEK_API_KEY`); `pnpm run demo:code-mode` boots the same server in Code Mode via the `code-mode.cordis.yml` overlay. See [acp-agent/README.md](acp-agent/README.md) for the protocol and snapshot-test contracts. - -The default `cordis.yml` composes [`@deepseek-ai/dsh-sandbox-local`](../packages/sandbox/sandbox-local), [`@deepseek-ai/dsh-bash-sandbox`](../packages/bash/bash-sandbox), and [`@deepseek-ai/dsh-user-approval`](../packages/ui/user-approval). `workspace-write` confines bash and filesystem mutations to each session workspace; a wider retry becomes a one-shot machine permission request over ACP. +An Agent Client Protocol automation server for programmatic clients, with session, permission, and cancellation support. See the [ACP example reference](acp-agent/README.md). diff --git a/examples/acp-agent/README.i18n.yaml b/examples/acp-agent/README.i18n.yaml index dc603691e1..5c7abee2a4 100644 --- a/examples/acp-agent/README.i18n.yaml +++ b/examples/acp-agent/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write examples/acp-agent/README.md -README.md: a37954d4d52900413a506f227759a0a7dd2a25d0 -README.zh.md: a38431ea2f01fe16ff39b5dd00ec23e881713f5d +README.md: 61c6efafe9dde4f91385beebdfd426c57006187b +README.zh.md: 0b0dec487dda0e7915f8e6eed82ed338dc23cf81 diff --git a/examples/acp-agent/README.md b/examples/acp-agent/README.md index a37954d4d5..61c6efafe9 100644 --- a/examples/acp-agent/README.md +++ b/examples/acp-agent/README.md @@ -9,7 +9,7 @@ pnpm run demo:acp # needs DEEPSEEK_API_KEY (repo-root .env or env) pnpm run demo:code-mode # same protocol with the Code Mode tool transport ``` -The leaf loads the ACP app, DeepSeek adapter, sandboxed bash and filesystem stacks, one-shot approval policy, compaction, subagents, workflows, hooks, a derived session-query index, and repeat guard. The app creates one fresh agent per `session/new`, persists sessions to JSONL, and keeps stdout protocol-pure. [`session-query.cordis.yml`](session-query.cordis.yml) explicitly opts into the workspace-authorized query tools and generic timeout/spill policies for their dedicated snapshot; [`fs.cordis.yml`](fs.cordis.yml) adds spill storage for filesystem scenarios, [`code-mode.cordis.yml`](code-mode.cordis.yml) adds `run_code` and its generated TypeScript SDK, and [`web.cordis.yml`](web.cordis.yml) adds the web seam, the local fetch provider, `web_fetch`, and a loopback HTML fixture server for the web-fetch snapshot. +The leaf loads the ACP app, DeepSeek adapter, sandboxed bash and filesystem stacks, one-shot approval policy, compaction, subagents, workflows, hooks, a derived session-query index, and repeat guard. The app creates one fresh agent per `session/new`, persists sessions to JSONL, and keeps stdout protocol-pure. Optional overlays add session queries, filesystem spill storage, Code Mode, or web fetching. ## Protocol channel @@ -19,12 +19,6 @@ The automation contract — supported methods, baseline prompt content, committe ## Session workspaces and permissions -Each `session/new` supplies an absolute `cwd`. Sandboxed bash and filesystem mutations resolve `workspace-write` against that session cwd, so concurrent sessions can use separate project roots; platform temporary roots remain shared writable scratch space ([sandbox contract](../../packages/sandbox/sandbox/README.md)). `DSH_PERMISSION_MODE` selects `workspace-write` or `danger-full-access` for deployment and tests. +Each `session/new` supplies an absolute `cwd`. Sandboxed bash and filesystem mutations resolve `workspace-write` against that session cwd, so concurrent sessions can use separate project roots; platform temporary roots remain shared writable scratch space ([sandbox contract](../../packages/sandbox/sandbox/README.md)). `DSH_PERMISSION_MODE` selects `workspace-write` or `danger-full-access` for the deployment. Under `workspace-write`, a model retry requesting wider sandbox access triggers `session/request_permission` with `allow_once` and `reject_once`. The client decides programmatically; dismissal or an unavailable answer fails closed. The selected outcome applies only to that retry and is recorded through the normal tool-result/audit path. The server never exposes a permission picker or persists client policy. - -## Snapshot tests - -This example owns the ACP snapshot suite. It boots the real automation server, replays committed model streams through `dsh-llm-replay`, and compares both normalized protocol output and re-persisted session logs. Recording uses the real model; refresh reuses committed replay input. Overrides cover throw/hang behavior, and optional `workspace/` fixtures seed world-state checks. - -Most scenarios pin backend behavior rather than ACP-specific behavior; the [automation-only ACP decision](../../.agents/notes/implemented/simplification/2026-07-23-acp-automation-only-protocol.md#snapshot-boundary) owns why that coverage remains transport-coupled. diff --git a/examples/acp-agent/README.zh.md b/examples/acp-agent/README.zh.md index a38431ea2f..0b0dec487d 100644 --- a/examples/acp-agent/README.zh.md +++ b/examples/acp-agent/README.zh.md @@ -2,14 +2,14 @@ [English](README.md) | 中文 -通过 JSON-RPC stdio 提供的面向自动化的 [Agent Client Protocol(ACP)](https://agentclientprotocol.com) 服务器。它面向 parent agent(父智能体)、subagent 提供方和其他程序化客户端,而非产品 UI。 +通过 JSON-RPC stdio 提供的面向自动化的 [ACP(Agent Client Protocol)](https://agentclientprotocol.com)服务器。它面向 parent agent(父智能体)、subagent 提供方和其他程序化客户端,而非产品 UI。 ```sh pnpm run demo:acp # needs DEEPSEEK_API_KEY (repo-root .env or env) pnpm run demo:code-mode # same protocol with the Code Mode tool transport ``` -该叶节点加载 ACP 应用、DeepSeek 适配器、受沙箱限制的 bash 与文件系统栈、一次性批准策略、压缩(compaction)、subagent、工作流、钩子、派生会话查询索引和重复守卫。应用为每次 `session/new` 创建一个新 agent,将会话持久化到 JSONL,并保持 stdout 只含协议内容。[`session-query.cordis.yml`](session-query.cordis.yml) 为其专用快照显式选用 workspace 授权的查询工具和通用超时/溢出策略;[`fs.cordis.yml`](fs.cordis.yml) 为文件系统场景添加溢出存储,[`code-mode.cordis.yml`](code-mode.cordis.yml) 添加 `run_code` 及其生成的 TypeScript SDK,[`web.cordis.yml`](web.cordis.yml) 则为 web-fetch 快照添加 web seam、本地抓取提供方、`web_fetch` 与一个回环 HTML fixture(测试前置数据)服务器。 +该叶节点加载 ACP 应用、DeepSeek 适配器、受沙箱限制的 bash 与文件系统栈、一次性批准策略、压缩(compaction)、subagent、工作流、钩子、派生会话查询索引和重复守卫。应用为每次 `session/new` 创建一个新 agent,将会话持久化到 JSONL,并保持 stdout 只含协议内容。可选 overlay 可添加会话查询、文件系统溢出存储、Code Mode 或 Web 抓取。 ## 协议通道 @@ -19,12 +19,6 @@ Stdout 只携带以换行分隔的 ACP JSON-RPC。`@deepseek-ai/dsh-acp-demo` ## 会话 workspace 与权限 -每次 `session/new` 都提供一个绝对 `cwd`。受沙箱限制的 bash 和文件系统修改会以该会话 cwd 为基准应用 `workspace-write`,因此并发会话可以使用不同的项目根目录;平台临时根目录仍是共享可写暂存空间(参见[沙箱契约](../../packages/sandbox/sandbox/README.md))。`DSH_PERMISSION_MODE` 在部署和测试中选择 `workspace-write` 或 `danger-full-access`。 +每次 `session/new` 都提供一个绝对 `cwd`。受沙箱限制的 bash 和文件系统修改会以该会话 cwd 为基准应用 `workspace-write`,因此并发会话可以使用不同的项目根目录;平台临时根目录仍是共享可写暂存空间(参见[沙箱契约](../../packages/sandbox/sandbox/README.md))。`DSH_PERMISSION_MODE` 为部署选择 `workspace-write` 或 `danger-full-access`。 在 `workspace-write` 下,如果模型重试请求更广泛的沙箱访问权限,就会触发 `session/request_permission`,选项为 `allow_once` 和 `reject_once`。客户端以程序方式决策;客户端放弃选择或无法给出答复时,系统会按拒绝处理。选定结果仅适用于该次重试,并通过常规工具结果/审计路径记录。服务器绝不公开权限选择器,也不持久化客户端策略。 - -## 快照测试 - -此示例拥有 ACP 快照套件。它会启动真实自动化服务器,通过 `dsh-llm-replay` 回放已提交的模型流,并比较规范化后的协议输出与重新持久化的会话日志。录制使用真实模型;刷新会复用已提交的回放输入。覆盖配置涵盖抛错/挂起行为,可选的 `workspace/` fixture 则为环境状态检查预置状态。 - -大多数场景锁定后端行为,而非 ACP 专用行为;[仅面向自动化的 ACP 决策](../../.agents/notes/implemented/simplification/2026-07-23-acp-automation-only-protocol.md#snapshot-boundary)说明了为何该覆盖仍与传输层耦合。 diff --git a/examples/headless-agent/README.md b/examples/headless-agent/README.md index 445804a261..670a91ab40 100644 --- a/examples/headless-agent/README.md +++ b/examples/headless-agent/README.md @@ -19,8 +19,8 @@ Exactly one nonblank positional task is required; quote tasks containing spaces. Each invocation creates and persists a fresh session, runs all model and tool steps in one turn, flushes, disposes, and exits. This is non-interactive automation: there is no prompt, approval, resume, second turn, or stdin context. The configured tools can mutate the launch workspace, run commands, spawn child agents, and consume provider tokens. -## Advanced and snapshot wiring +## Advanced configuration -[`advanced.cordis.yml`](advanced.cordis.yml) adds Code Mode and the Cordis tools to the shipped leaf. [`advanced.cordis.snapshot.yml`](advanced.cordis.snapshot.yml) replaces only the live LLM with replay. The tests under [`tests/`](tests/) own the keyless real-Loader smoke, key-gated world-verified smoke, and the `stream-json` replay snapshot with its parent and child session fixtures. +[`advanced.cordis.yml`](advanced.cordis.yml) adds Code Mode and the Cordis tools to the shipped leaf. The package-level [CLI contract](../../packages/examples/cli-demo/README.md) documents output records, exit status, cancellation, persistence, and model/token effects. diff --git a/examples/headless-agent/README.zh.md b/examples/headless-agent/README.zh.md index f1cf1964bb..6c8b3b5694 100644 --- a/examples/headless-agent/README.zh.md +++ b/examples/headless-agent/README.zh.md @@ -19,8 +19,8 @@ pnpm run demo:headless --output-format stream-json -- "run the focused tests" 每次调用都会创建并持久化新会话,在一个轮次中运行所有模型和工具步骤,然后刷写持久化数据、执行 dispose(资源释放),再退出。这是非交互式自动化:没有提示符、批准、恢复、第二轮次或 stdin 上下文。已配置工具可以修改启动时所在的工作区、运行命令、spawn 子 agent,并消耗提供方 token。 -## 高级与快照接线 +## 高级配置 -[`advanced.cordis.yml`](advanced.cordis.yml) 在已交付叶节点上添加 Code Mode 和 Cordis 工具。[`advanced.cordis.snapshot.yml`](advanced.cordis.snapshot.yml) 只将实时 LLM(大语言模型)替换为回放。[`tests/`](tests/) 下涵盖无密钥真实 Loader 冒烟测试、密钥门控的外部状态验证冒烟测试,以及带父子会话 fixture(测试前置数据)的 `stream-json` 回放快照。 +[`advanced.cordis.yml`](advanced.cordis.yml) 在已交付叶节点上添加 Code Mode 和 Cordis 工具。 这份包级 [CLI(命令行界面)契约](../../packages/examples/cli-demo/README.md) 说明输出记录、退出状态、取消、持久化以及模型/token 影响。 diff --git a/examples/jsonrpc-agent/README.i18n.yaml b/examples/jsonrpc-agent/README.i18n.yaml index 75a6ed038e..ce35a749a0 100644 --- a/examples/jsonrpc-agent/README.i18n.yaml +++ b/examples/jsonrpc-agent/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write examples/jsonrpc-agent/README.md -README.md: 5e7b7d79415a4af0b0d16c61b0a4590dce57e545 -README.zh.md: 00450838e7d10ae9e95624b5f89992dd747d5440 +README.md: bcc1027d2edb30ab374dfa2ed13ad8e6360d923b +README.zh.md: 197c25d7b4f5645aeb7e92d8c36ef4a423beb2e8 diff --git a/examples/jsonrpc-agent/README.md b/examples/jsonrpc-agent/README.md index 5e7b7d7941..bcc1027d2e 100644 --- a/examples/jsonrpc-agent/README.md +++ b/examples/jsonrpc-agent/README.md @@ -33,8 +33,4 @@ Pass the config path through the Python SDK's `cordis` option or `DSH_CORDIS_CON - owner-scoped persistent `bash` - `str_replace_editor` with `view`, `create`, `str_replace`, and `insert` -It composes the real local PTY, filesystem intent policy, and session sandbox policy. The keyless SDK snapshot drives the shipped JSON-RPC runtime through both tools, proves that shell cwd/environment survive across calls, and pins the notification stream, turn result, and persisted JSONL: - -```bash -pnpm exec vitest run --config vitest.snapshot.config.ts -t persistent-tools -``` +It composes the local PTY, filesystem intent policy, and session sandbox policy. diff --git a/examples/jsonrpc-agent/README.zh.md b/examples/jsonrpc-agent/README.zh.md index 00450838e7..197c25d7b4 100644 --- a/examples/jsonrpc-agent/README.zh.md +++ b/examples/jsonrpc-agent/README.zh.md @@ -33,8 +33,4 @@ - agent 独占、状态持久的 `bash` - 提供 `view`、`create`、`str_replace` 与 `insert` 的 `str_replace_editor` -它组合真实本地 PTY、文件系统 intent 策略与 session 沙箱策略。无密钥 SDK 快照会通过正式 JSON-RPC runtime 驱动这两个工具,验证 shell 的 cwd 与环境变量能跨调用保留,并锁定通知流、轮次结果与已持久化的 JSONL: - -```bash -pnpm exec vitest run --config vitest.snapshot.config.ts -t persistent-tools -``` +它组合本地 PTY、文件系统 intent 策略与会话沙箱策略。 diff --git a/examples/web-cordis/README.i18n.yaml b/examples/web-cordis/README.i18n.yaml new file mode 100644 index 0000000000..0564a4e0fc --- /dev/null +++ b/examples/web-cordis/README.i18n.yaml @@ -0,0 +1,6 @@ +# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each +# side as of the last confirmed-consistent state. Both languages carry equal authority; +# after editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write examples/web-cordis/README.md +README.md: 75c7610fecc20777607c7c8cbb6ed2fc7f590b73 +README.zh.md: 5c25347ee17037bbfc7d10a5507424ce52e3faff diff --git a/examples/web-cordis/README.md b/examples/web-cordis/README.md new file mode 100644 index 0000000000..75c7610fec --- /dev/null +++ b/examples/web-cordis/README.md @@ -0,0 +1,21 @@ +# web-cordis + +English | [中文](README.zh.md) + +Self-referential demonstration of [`@deepseek-ai/dsh-tool-cordis`](../../packages/cordis/tool-cordis/README.md). The agent can inspect its current Cordis process and mount or unmount model-authored plugins in memory. Temporary plugins disappear when they are unmounted or the process exits and may affect other sessions in the same process. + +## Run it + +Start the browser interface: + +```sh +pnpm run demo:cordis +``` + +Start the ACP automation server instead: + +```sh +pnpm run demo:cordis acp +``` + +Both commands require `DEEPSEEK_API_KEY`. The [Cordis tool reference](../../packages/cordis/tool-cordis/README.md) owns the tool, lifecycle, and safety contracts. diff --git a/examples/web-cordis/README.zh.md b/examples/web-cordis/README.zh.md new file mode 100644 index 0000000000..5c25347ee1 --- /dev/null +++ b/examples/web-cordis/README.zh.md @@ -0,0 +1,21 @@ +# web-cordis + +[English](README.md) | 中文 + +[`@deepseek-ai/dsh-tool-cordis`](../../packages/cordis/tool-cordis/README.md) 的自指示例。agent(智能体)可以检查当前 Cordis 进程,并在内存中挂载或卸载模型编写的插件。临时插件会在卸载或进程退出时消失,并可能影响同一进程中的其他会话。 + +## 运行 + +启动浏览器界面: + +```sh +pnpm run demo:cordis +``` + +改为启动 ACP(Agent Client Protocol)自动化服务器: + +```sh +pnpm run demo:cordis acp +``` + +这两条命令都需要 `DEEPSEEK_API_KEY`。工具、生命周期和安全契约由 [Cordis 工具参考](../../packages/cordis/tool-cordis/README.md)定义。 diff --git a/native/README.md b/native/README.md index 84808b2ee9..a79d9ca574 100644 --- a/native/README.md +++ b/native/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -Source of record for `node-addon-landlock-run`, the Landlock self-restrict-then-exec launcher the harness consumes from npm (`packages/sandbox/sandbox-local`, `packages/bash/bash-sandbox`). Launcher development happens HERE, next to the consumers; the standalone repository is the release mirror that packs and publishes the npm package family. +Source of record for `node-addon-landlock-run`, the Landlock self-restrict-then-exec launcher consumed by the harness. The [`landlock-run/` workspace](landlock-run/README.md) owns its architecture, package family, platform support, development workflow, and release procedure. The standalone repository is a release mirror. ## Release mirror @@ -10,13 +10,4 @@ Source of record for `node-addon-landlock-run`, the Landlock self-restrict-then- |---|---|---|---| | `landlock-run/` | https://github.com/deepseek-harness/node-addon-landlock-run | `v0.0.1` | `614f7fd7dc11e6eaceefba9e7ff1fbe28b51ba22` | -The subtree is a self-contained pnpm workspace with its own `AGENTS.md`, docs, gates, and lockfile; it is NOT part of the harness workspace (`pnpm-workspace.yaml` does not include it), so harness installs, builds, and CI gates never touch it. The mirror's `.github/` stays out of the subtree — [.github/workflows/landlock-run.yml](../.github/workflows/landlock-run.yml) (manual dispatch) runs the subtree's CI legs here, and a change to those legs is mirrored into the mirror's `ci.yml` at the next export. - -## Export procedure (cutting a release) - -1. Land the launcher change here through a normal harness PR; dispatch the `Landlock Run` workflow and get its legs green. -2. In the mirror checkout, replace everything except `.github/`: `git -C rm -rq -- . ':!.github'`, then `git -C archive HEAD:native/landlock-run | tar -x -C `, then `git -C add -A` and commit. -3. In the mirror, follow its release checklist (`docs/release.md`): `pnpm release:commit ` → merge → tag `vX.Y.Z` → two-phase `Release` workflow (`publish=false` rehearsal, then `publish=true` from the tag). -4. Update the manifest table above with the released tag/commit, and bump the harness consumers' dependency range in the same change. - -The mirror must not diverge: a change committed there directly (hotfix during a release) is ported back here before the next export. +The subtree is a self-contained pnpm workspace and is not part of the harness workspace. The [launcher release reference](landlock-run/docs/release.md) owns the export and publication workflow. The mirror must not diverge: port any direct mirror hotfix back here before the next export. diff --git a/packages/acp/README.i18n.yaml b/packages/acp/README.i18n.yaml index 08051fea69..ca4ec66f67 100644 --- a/packages/acp/README.i18n.yaml +++ b/packages/acp/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/acp/README.md -README.md: 326615210e5cfc39004fc5ab7462623089ac4126 -README.zh.md: 8679f2428a9e82a81de69d7d1413132a946fcafa +README.md: 3ba247598f29f2244456061fe7f9a3282086148f +README.zh.md: c13bc05b12fff2ef97b4d547936aa14a6556430a diff --git a/packages/acp/README.md b/packages/acp/README.md index 326615210e..3ba247598f 100644 --- a/packages/acp/README.md +++ b/packages/acp/README.md @@ -6,6 +6,6 @@ The ACP group exposes harness agents to programmatic clients. It is an interoper | Package | Role | |---|---| -| [`acp/`](acp/README.md) | Automation-only ACP server: fresh text sessions, committed assistant output, machine permission policy, cancellation, and connection-owned teardown. | +| [`acp/`](acp/README.md) | Automation-only ACP server. | The matching out-of-process subagent client remains in [`subagent/subagent-acp`](../subagent/subagent-acp/README.md) because it implements the subagent provider interface; arbitrary ACP clients may drive the same server contract. diff --git a/packages/acp/README.zh.md b/packages/acp/README.zh.md index 8679f2428a..c13bc05b12 100644 --- a/packages/acp/README.zh.md +++ b/packages/acp/README.zh.md @@ -6,6 +6,6 @@ ACP(Agent Client Protocol)组将 harness 中的 agent(智能体)公开 | 包 | 职责 | |---|---| -| [`acp/`](acp/README.md) | 仅面向自动化的 ACP 服务器:新文本会话、已提交的 assistant 输出、机器权限策略、取消和由连接负责的清理。 | +| [`acp/`](acp/README.md) | 仅面向自动化的 ACP 服务器。 | 与之匹配的进程外 subagent 客户端仍位于 [`subagent/subagent-acp`](../subagent/subagent-acp/README.md),因为它实现 subagent 提供方接口;任意 ACP 客户端都可以按照同一服务器契约驱动该服务器。 diff --git a/packages/bash/bash-local/README.md b/packages/bash/bash-local/README.md index 694b7a7686..bb87ad6fe0 100644 --- a/packages/bash/bash-local/README.md +++ b/packages/bash/bash-local/README.md @@ -20,15 +20,13 @@ The package root exports the default and named `LocalBashExecutor` plugin plus i graceMs: 3000 # kill escalation and post-exit pipe-drain grace ``` -## Behavior (and where it came from) +## Behavior -Design surveyed against the bash tools of Claude Code, OpenCode, Codex, and pi; the notable choices: - -- **Spawn per call, no shell state** — every call is a fresh non-login `bash -c` (deterministic; no rc files). All four surveyed tools spawn per call. `XXX(stateful-shell)` in `src/index.ts` records the two proven stateful designs (Claude Code's cwd-only persistence; Codex's PTY exec sessions) for when real workflows demand them. -- **Configured budgets over managed groups** — `resolve()` fills `workdir`/`timeoutMs`/`stdoutMaxBytes` from config, and every spawn hands the service explicit byte caps, spill cap, and `graceMs` (default 3s — OpenCode's escalation). Process-group kills, the post-exit pipe-drain grace, tail-keep truncation, and bounded spill files are [`dsh-subprocess-local`](../../subprocess/subprocess-local/README.md) mechanics. A foreground `BashExecRequest.stdoutMaxBytes` can raise stdout's capture budget for one trusted caller; stderr and background runs still use `maxOutputBytes`. +- **Spawn per call, no shell state** — every call is a fresh non-login `bash -c` with no rc files. +- **Configured budgets over managed groups** — `resolve()` fills `workdir`/`timeoutMs`/`stdoutMaxBytes` from config, and every spawn hands the service explicit byte caps, spill cap, and `graceMs`. Process-group kills, post-exit pipe draining, tail retention, and bounded spill files are [`dsh-subprocess-local`](../../subprocess/subprocess-local/README.md) mechanics. A foreground `BashExecRequest.stdoutMaxBytes` can raise stdout's capture budget for one trusted caller; stderr and background runs still use `maxOutputBytes`. - **Timeout and cancel classification** — `run()` fuses its config-clamped timeout with the caller's signal through one deadline; only the executor's own timeout reports `timedOut`, an upstream cancel reports `aborted`, and a self-signaled command reports neither ([timeout-library Agent Note](../../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md)). -- **Model-friendly terminal env** — `NO_COLOR=1 TERM=dumb PAGER=cat GIT_PAGER=cat` (Codex's hardcoded set) so pagers and ANSI color don't garble results, merged as ordinary env under the service's credential scrub and `DSH_*` channel rules; an explicit caller entry still wins. See the [stdin/env Agent Note](../../../.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.md) and [managed environment Agent Note](../../../.agents/notes/implemented/feature/2026-07-10-agent-session-identity-and-log-location.md). -- **Background processes** — `start()` returns a live `BashProcess` handle immediately, no timeout applies (Claude Code detaches timeouts when backgrounding), and the handle's `readOutput()` merges the service's offset-based stdout/stderr reads into one marked-section delta with a consuming cursor. A still-running process belongs to the subprocess service, so it survives executor reloads and dies (killed and joined) with the service's disposal. Everything task-shaped (ids, ownership, polling, notices) lives in the generic [`ctx.tasks` runtime](../../tasks/tasks/README.md), which the tool layer registers the handle with — this executor never sees a session or a registry. +- **Model-friendly terminal env** — `NO_COLOR=1 TERM=dumb PAGER=cat GIT_PAGER=cat` prevents pagers and ANSI color from garbling results. These values merge as ordinary env under the service's credential scrub and `DSH_*` channel rules; an explicit caller entry still wins. See the [stdin/env Agent Note](../../../.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.md) and [managed environment Agent Note](../../../.agents/notes/implemented/feature/2026-07-10-agent-session-identity-and-log-location.md). +- **Background processes** — `start()` returns a live `BashProcess` handle immediately with no timeout, and `readOutput()` merges offset-based stdout/stderr reads into one consuming delta, placing stderr under a `[stderr]` marker when present. A running process belongs to the subprocess service, survives executor reloads, and is killed and joined on service disposal. Task ids, ownership, polling, and notices belong to the generic [`ctx.tasks` runtime](../../tasks/tasks/README.md), which the tool layer registers the handle with. ## Model Experience diff --git a/packages/code-runtime/README.md b/packages/code-runtime/README.md index dbe6b37ffa..2d32a05071 100644 --- a/packages/code-runtime/README.md +++ b/packages/code-runtime/README.md @@ -6,7 +6,7 @@ The code-execution capability seam (see [capability seams](../../.agents/notes/i | Package | Role | ctx key | |---|---|---| -| `code-runtime/` | Abstract code-execution seam (interface + vocabulary) | `ctx.codeRuntime` | -| [`code-runtime-worker/`](code-runtime-worker/README.md) | Worker-thread backend: fresh worker per run, TypeScript via host-side type-strip (annotations advisory, never type-checked), port-bridged bindings, budget/heap containment | registers `ctx.codeRuntime` | +| [`code-runtime/`](code-runtime/README.md) | Code-execution seam and shared vocabulary | `ctx.codeRuntime` | +| [`code-runtime-worker/`](code-runtime-worker/README.md) | Worker-thread backend | registers `ctx.codeRuntime` | -The interface lives at `code-runtime/code-runtime/`; the shipped backend at `code-runtime/code-runtime-worker/`. Backends differ by execution substrate (worker thread, process, container) and by source language — both readonly descriptors on the service — and register `ctx.codeRuntime` without touching the interface or its consumer; that split is what makes a hardened backend a drop-in later. +Backends register the seam without changing its consumer. The child READMEs own language, isolation, and execution-budget details. diff --git a/packages/code-runtime/README.zh.md b/packages/code-runtime/README.zh.md index a5acbad3cc..f3dfa3e7e9 100644 --- a/packages/code-runtime/README.zh.md +++ b/packages/code-runtime/README.zh.md @@ -6,7 +6,7 @@ | 包 | 职责 | ctx 键 | |---|---|---| -| `code-runtime/` | 抽象代码执行 seam(接口 + 词汇) | `ctx.codeRuntime` | -| [`code-runtime-worker/`](code-runtime-worker/README.md) | worker 线程后端:每次运行使用全新 worker,由宿主侧剥离 TypeScript 类型(类型注解仅供参考,绝不执行类型检查)、端口桥接绑定、预算/堆限制 | 注册 `ctx.codeRuntime` | +| [`code-runtime/`](code-runtime/README.md) | 代码执行 seam 与共享词汇 | `ctx.codeRuntime` | +| [`code-runtime-worker/`](code-runtime-worker/README.md) | worker 线程后端 | 注册 `ctx.codeRuntime` | -接口位于 `code-runtime/code-runtime/`,随附的后端位于 `code-runtime/code-runtime-worker/`。不同后端可以采用不同执行基底(worker 线程、进程、容器)与源语言;二者都是服务上的只读描述符。后端注册 `ctx.codeRuntime`,无需修改接口或消费方;正是这种拆分,使未来可以直接换入加固后端。 +后端注册该 seam,无需改动消费方。语言、隔离与执行预算细节由子级 README 负责。 diff --git a/packages/compact/README.md b/packages/compact/README.md index aa9fa6d941..509ea27642 100644 --- a/packages/compact/README.md +++ b/packages/compact/README.md @@ -6,9 +6,9 @@ A compaction capability family (see [capability seams](../../.agents/notes/imple | Package | Role | ctx key | |---|---|---| -| `compact/` | Abstract compaction seam (interface + `compact/*` events + `CompactionResult`) | `ctx.compact` | -| `compact-basic/` | A backend: `ctx.tokenMeter` pressure + token-budget retention + `llm.stream()` summarization | (registers `ctx.compact`) | -| `compact-tool-result-prune/` | Optional model-free head/middle/tail rewriting before summary compaction | `ctx.toolResultPrune` | -| `command-compact/` | Human `/compact` command over the backend-independent `compactNow()` seam | (registers on `ctx.commands`) | +| [`compact/`](compact/README.md) | Compaction seam and event vocabulary | `ctx.compact` | +| [`compact-basic/`](compact-basic/README.md) | Token-pressure and summarization backend | registers `ctx.compact` | +| [`compact-tool-result-prune/`](compact-tool-result-prune/README.md) | Optional model-free tool-result pruning | `ctx.toolResultPrune` | +| [`command-compact/`](command-compact/README.md) | Human compaction command | registers on `ctx.commands` | -The interface lives at `compact/compact/`, the backend at `compact/compact-basic/`, deterministic pruning at `compact/compact-tool-result-prune/`, and the command at `compact/command-compact/`. Unlike the bash seam, the interface depends on `dsh-session` and `dsh-llm` because its verbs are defined over a `Session` and its output uses `ContentBlock`. That deviation is recorded in the [compaction capability-seam Agent Note](../../.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md). Token measurement remains a reusable LLM-family service; a template- or model-backed compactor can replace `compact-basic` without changing the meter, pruner, command, or automatic callers. +The backend, optional pruner, and human command compose through the seam; token measurement remains a separate LLM-family service. The [compaction capability-seam Agent Note](../../.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md) owns the dependency rationale. diff --git a/packages/context/README.i18n.yaml b/packages/context/README.i18n.yaml index 7836a2a03c..77176bd6ab 100644 --- a/packages/context/README.i18n.yaml +++ b/packages/context/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/context/README.md -README.md: fce6e21816d261171aaeaa217171580adb7c43f9 -README.zh.md: b8a4d68ca6892b51ed52479a7296513f5edcc292 +README.md: b4adb18c912617588711d7a0641426ce2cf28d89 +README.zh.md: d129c63371682ae6fff3ca0c56f5906bdf6cfab3 diff --git a/packages/context/README.md b/packages/context/README.md index fce6e21816..b4adb18c91 100644 --- a/packages/context/README.md +++ b/packages/context/README.md @@ -6,9 +6,9 @@ Product plugins that add model-visible request context without defining a tool. | Package | Role | ctx key | |---|---|---| -| `session-reference/` | Bounded current-surface snapshots of other sessions | `ctx.sessionReferences` | -| `time-context/` | Durable per-step current time and elapsed-time context | (none) | -| `tmux-context/` | Durable per-turn context with this agent's tmux pane/window location | (listens on `agent/step`, reads `ctx.bash`) | -| `workspace-context/` | `AGENTS.md`/`CLAUDE.md` workspace context loader | (listens on `agent/step` + `tools/post-execute`) | +| [`session-reference/`](session-reference/README.md) | Bounded snapshots of other sessions | `ctx.sessionReferences` | +| [`time-context/`](time-context/README.md) | Current-time and elapsed-time context | — | +| [`tmux-context/`](tmux-context/README.md) | tmux location context | — | +| [`workspace-context/`](workspace-context/README.md) | Workspace-instruction context | — | The [`workspace-context` decision record](../../.agents/notes/implemented/feature/2026-06-24-workspace-context.md) explains its per-agent/session isolation and lifecycle split. diff --git a/packages/context/README.zh.md b/packages/context/README.zh.md index b8a4d68ca6..d129c63371 100644 --- a/packages/context/README.zh.md +++ b/packages/context/README.zh.md @@ -6,9 +6,9 @@ | 包 | 职责 | ctx key | |---|---|---| -| `session-reference/` | 其他会话当前表层的有界快照 | `ctx.sessionReferences` | -| `time-context/` | 持久化的逐步骤当前时间与已用时上下文 | (无) | -| `tmux-context/` | 持久化的逐轮次上下文,记录本 agent 所在的 tmux pane/window 位置 | (监听 `agent/step`,读取 `ctx.bash`) | -| `workspace-context/` | `AGENTS.md`/`CLAUDE.md` 工作区上下文 loader | (监听 `agent/step` + `tools/post-execute`) | +| [`session-reference/`](session-reference/README.md) | 其他会话的有界快照 | `ctx.sessionReferences` | +| [`time-context/`](time-context/README.md) | 当前时间与已用时上下文 | — | +| [`tmux-context/`](tmux-context/README.md) | tmux 位置上下文 | — | +| [`workspace-context/`](workspace-context/README.md) | 工作区指令上下文 | — | [`workspace-context` 决策记录](../../.agents/notes/implemented/feature/2026-06-24-workspace-context.md)解释了每个 agent(智能体)和会话各自隔离的方式,以及相应的生命周期拆分。 diff --git a/packages/cordis/README.md b/packages/cordis/README.md index 485a6ce785..e8b0790f91 100644 --- a/packages/cordis/README.md +++ b/packages/cordis/README.md @@ -6,5 +6,5 @@ Plugins that integrate Harness-owned formats with the Cordis runtime: the self-r | Package | Role | ctx key | |---|---|---| -| [`tool-cordis/`](tool-cordis/README.md) | The `cordis_inspect` / `cordis_mount` / `cordis_unmount` tools: read the current-process runtime and manage in-memory temporary Plugins under one owned group fiber | registers on `ctx.tools` | -| [`repository-plugin/`](repository-plugin/README.md) | Prepare and mount static repository skills plus common `.mcp.json` servers through DSH-owned child Plugins | registers a Loader builtin | +| [`tool-cordis/`](tool-cordis/README.md) | Model-facing runtime inspection and temporary-plugin tools | registers on `ctx.tools` | +| [`repository-plugin/`](repository-plugin/README.md) | Repository skill and MCP composition | registers a Loader builtin | diff --git a/packages/core/README.md b/packages/core/README.md index 63ca0f7711..d51ef73f7f 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -6,15 +6,13 @@ The session log, system-prompt assembly, tool registry, agent vocabulary, and co | Package | Role | ctx key | |---|---|---| -| `scope/` | Scoped-context registration primitive (scope tags, scope-filtered dispatch) | (library — no ctx key) | -| `session/` | Event-sourced session log + in-memory store | `ctx.sessions` | -| `system-prompt/` | Prompt-section + tool-schema assembly registry | `ctx.systemPrompt` | -| `tools/` | Scoped tool registry + pre-policy, guards, around-dispatch, post-policy, and final-result observation | `ctx.tools` | -| `agent/` | Agent interface, live registry, process-local initiator scope, `agent/*` event vocabulary | `ctx.agents` | -| `agent-loop/` | Concrete plugin implementing the public `Agent` contract and owning the loop driver | `ctx.agentLoop` | +| [`scope/`](scope/README.md) | Scoped-context registration primitive | library — no ctx key | +| [`session/`](session/README.md) | Event-sourced session log and in-memory store | `ctx.sessions` | +| [`system-prompt/`](system-prompt/README.md) | Prompt and tool-schema assembly registry | `ctx.systemPrompt` | +| [`tools/`](tools/README.md) | Scoped tool registry and execution pipeline | `ctx.tools` | +| [`agent/`](agent/README.md) | Agent interface, registry, and event vocabulary | `ctx.agents` | +| [`agent-loop/`](agent-loop/README.md) | Default concrete agent driver | `ctx.agentLoop` | -`scope/` is the one non-service package here: a dependency-free library (`createScope`/`scopeOf`/`scopeTarget`) the registries and the loop build per-agent scoping on — it sits below `session/` and `system-prompt/` in the module graph precisely so they can consume it without a cycle. +`scope` supplies the shared scoping primitive. `agent` owns the public seam, while `agent-loop` is its default implementation; extension plugins depend on the seam so the driver remains swappable. -`agent-loop` is the one concrete implementation of the `agent` seam and lives here because it is the harness's default product loop. It runs each driver inside `ctx.agents.withInitiator()`. Extension plugins depend on `agent`, including when they need the initiating Agent, and never on `agent-loop` directly, so the loop stays swappable. - -The default composition that wires this spine into a runnable agent lives in [`examples/agent-spine-demo`](../examples/agent-spine-demo/README.md): one bundle plugin that loads the control spine plus selected default capabilities (`timer` + `llm` + sessions + fallback session titles + system-prompt + tools + agents + invariants + the local [skill family](../skill/README.md) + `tool-bash` + workspace-context + `agent-loop`) and forwards `agent-loop`'s `agents` list as its own config. It sits in `examples/` — ready-to-run demo/reference bundles — not in `core/`: `core/` ships the swappable spine pieces, while a demo bundle picks one concrete composition of them and adds a front door. +Runnable compositions belong to [`examples/agent-spine-demo`](../examples/agent-spine-demo/README.md); this group owns only the swappable spine pieces. diff --git a/packages/core/README.zh.md b/packages/core/README.zh.md index d4c1a85ef7..d0f16fb033 100644 --- a/packages/core/README.zh.md +++ b/packages/core/README.zh.md @@ -6,15 +6,13 @@ | 包 | 角色 | ctx 键 | |---|---|---| -| `scope/` | 带作用域的上下文注册原语(作用域标签、按作用域筛选的分发) | (库,没有 ctx 键) | -| `session/` | 事件溯源会话日志与内存存储 | `ctx.sessions` | -| `system-prompt/` | 提示词段与工具 schema 组装注册表 | `ctx.systemPrompt` | -| `tools/` | 带作用域的工具注册表,以及前置策略、守卫、环绕分发、后置策略与最终结果观测 | `ctx.tools` | -| `agent/` | Agent 接口、实时注册表、进程本地发起方作用域、`agent/*` 事件词汇 | `ctx.agents` | -| `agent-loop/` | 实现公开 `Agent` 契约并拥有循环驱动器的具体插件 | `ctx.agentLoop` | +| [`scope/`](scope/README.md) | 带作用域的上下文注册原语 | 库,无 ctx 键 | +| [`session/`](session/README.md) | 事件溯源会话日志与内存存储 | `ctx.sessions` | +| [`system-prompt/`](system-prompt/README.md) | 提示词与工具 schema 组装注册表 | `ctx.systemPrompt` | +| [`tools/`](tools/README.md) | 带作用域的工具注册表与执行流水线 | `ctx.tools` | +| [`agent/`](agent/README.md) | Agent 接口、注册表与事件词汇 | `ctx.agents` | +| [`agent-loop/`](agent-loop/README.md) | 默认的具体 agent 驱动器 | `ctx.agentLoop` | -`scope/` 是此处唯一的非服务包:它是不含依赖的库(`createScope`/`scopeOf`/`scopeTarget`),注册表和循环基于它实现按 agent 分域。它在模块图中位于 `session/` 和 `system-prompt/` 之下,正是为了让二者可以消费它而不形成环。 +`scope` 提供共享的作用域原语。`agent` 拥有公开 seam,`agent-loop` 则是其默认实现;扩展插件依赖 seam,使驱动器保持可替换。 -`agent-loop` 是 `agent` seam 的唯一具体实现,位于此处是因为它就是 harness 的默认产品循环。它在 `ctx.agents.withInitiator()` 中运行每个驱动器。扩展插件依赖 `agent`,即使需要发起调用的 Agent 也是如此;它们绝不直接依赖 `agent-loop`,因此循环保持可替换。 - -将这条主干接成可运行 agent 的默认组合位于 [`examples/agent-spine-demo`](../examples/agent-spine-demo/README.md):一个 bundle(组合包)插件,加载控制主干及所选默认能力(`timer` + `llm` + 会话 + 后备会话标题 + 系统提示词 + 工具 + agent + 不变式 + 本地 [skill(技能)系列](../skill/README.md) + `tool-bash` + 工作区上下文 + `agent-loop`),并将 `agent-loop` 的 `agents` 列表作为自身配置转发。它位于 `examples/`,即开箱可运行的演示/参考组合包,而不是 `core/`:`core/` 交付可替换的主干组件,演示组合包则选定其中一种具体组合并添加一个对外交互入口。 +可运行组合属于 [`examples/agent-spine-demo`](../examples/agent-spine-demo/README.md);本组只负责可替换的主干组件。 diff --git a/packages/credentials/README.i18n.yaml b/packages/credentials/README.i18n.yaml index e8b35ba48e..04f14e8e75 100644 --- a/packages/credentials/README.i18n.yaml +++ b/packages/credentials/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/credentials/README.md -README.md: 1d450cbeef84750fa57ca0151563c496aed0ce12 -README.zh.md: 843230c3cebf35f234d3ad812165b16ea734678b +README.md: c08831c90333f8515bf86a5af717c38c50b50817 +README.zh.md: c9756c010af5d2db0fc41f3108eabdc7f1161293 diff --git a/packages/credentials/README.md b/packages/credentials/README.md index 1d450cbeef..c08831c903 100644 --- a/packages/credentials/README.md +++ b/packages/credentials/README.md @@ -1,14 +1,12 @@ -# credentials/ +# credentials/ — credential references English | [中文](README.zh.md) -The credential capability seam, as three-package shape dictates (interface / implementation / consumers): +The credential capability family separates reference resolution from its provider: -| Package | Role | -|---|---| -| [`credentials/`](credentials/README.md) | Abstract `ctx.credentials`: branded `CredentialRef` references, per-operation `resolve`, UI-safe `describe`, fail-loud `set`/`unset`, the `credentials/updated` commit event | -| [`credentials-local/`](credentials-local/README.md) | File/environment provider: the live process environment (read-only, wins) layered over `$DSH_HOME/.env` (writable, byte-preserving line edits, hot-reloaded) | +| Package | Role | ctx key | +|---|---|---| +| [`credentials/`](credentials/README.md) | Credential-reference seam | `ctx.credentials` | +| [`credentials-local/`](credentials-local/README.md) | Environment and local-file provider | registers `ctx.credentials` | -Configuration files carry *references* to secrets (`apiKeyEnv: DEEPSEEK_API_KEY`), never the secrets: the settings document stays safe to sync and render, and rotating a value touches no configuration. The LLM adapters are the first consumers — they resolve their reference once per model request, which is what makes a key stored moments ago reach the very next request without restarting anything. - -The seam shape leaves room for keyring-, helper-command-, and KMS-backed providers. +Configuration carries references, not secret values. Consumers resolve those references at their operation boundary; the child READMEs own mutation, precedence, and storage semantics. diff --git a/packages/credentials/README.zh.md b/packages/credentials/README.zh.md index 843230c3ce..c9756c010a 100644 --- a/packages/credentials/README.zh.md +++ b/packages/credentials/README.zh.md @@ -1,14 +1,12 @@ -# credentials/ +# credentials/:凭据引用 [English](README.md) | 中文 -凭据能力 seam,按三包形态的要求组织(接口/实现/消费方): +凭据能力家族将引用解析与提供方分离: -| 包 | 角色 | -|---|---| -| [`credentials/`](credentials/README.md) | 抽象 `ctx.credentials`:品牌化 `CredentialRef` 引用、按操作 `resolve`、对 UI 安全的 `describe`、响亮失败的 `set`/`unset`,以及 `credentials/updated` 提交事件 | -| [`credentials-local/`](credentials-local/README.md) | 文件/环境 provider:活跃进程环境(只读、优先)叠加在 `$DSH_HOME/.env`(可写、保字节行级编辑、热重载)之上 | +| 包 | 角色 | ctx 键 | +|---|---|---| +| [`credentials/`](credentials/README.md) | 凭据引用 seam | `ctx.credentials` | +| [`credentials-local/`](credentials-local/README.md) | 环境与本地文件提供方 | 注册 `ctx.credentials` | -配置文件携带的是对机密的*引用*(`apiKeyEnv: DEEPSEEK_API_KEY`),绝不携带机密本身:设置文档可以放心同步与渲染,轮换值不触碰任何配置。LLM 适配器是第一批消费方——它们每次模型请求解析一次引用,正因如此,片刻前存入的密钥无需重启任何组件即可作用于紧随其后的下一次请求。 - -seam 形状为 keyring、辅助命令与 KMS 后端的 provider 留有余地。 +配置携带引用而非机密值。消费方在其操作边界解析这些引用;变更、优先级与存储语义由子级 README 负责。 diff --git a/packages/examples/README.i18n.yaml b/packages/examples/README.i18n.yaml index 0b0ee3db7c..7b75fa452d 100644 --- a/packages/examples/README.i18n.yaml +++ b/packages/examples/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/examples/README.md -README.md: d3ad432e71036db0d21f059e52f5d32e58010c42 -README.zh.md: 0218e60df2511974b8eb222e23331c5a70c9df60 +README.md: 64fff8cb3f53386d48a9a831c1cbdd946ad483cc +README.zh.md: c346a41d297a545991a2441df625286e1b830998 diff --git a/packages/examples/README.md b/packages/examples/README.md index d3ad432e71..64fff8cb3f 100644 --- a/packages/examples/README.md +++ b/packages/examples/README.md @@ -6,17 +6,13 @@ Pre-composed plugin bundles a thin leaf `cordis.yml` loads instead of assembling | Package | npm name | Role | |---|---|---| -| `agent-spine-demo/` | `@deepseek-ai/dsh-agent-spine-demo` | The executor-less/UI-less agent spine as one bundle plugin, with fallback session titles and an opt-in persisted-goal stack | -| `cli-demo/` | `@deepseek-ai/dsh-cli-demo` | Headless one-shot app: the spine + JSONL persistence + a pre-created `main` agent, with text and DSH-native JSON output | -| `acp-demo/` | `@deepseek-ai/dsh-acp-demo` | ACP automation server app: the spine + persisted goals + JSONL persistence + the [`acp`](../acp/acp/README.md) bridge (no stdout logger), with a boot `bin` | -| `jsonrpc-demo/` | `@deepseek-ai/dsh-jsonrpc-demo` | Bin-only runtime that boots an external `cordis.yml` for the stdio JSON-RPC SDK client | +| [`agent-spine-demo/`](agent-spine-demo/README.md) | `@deepseek-ai/dsh-agent-spine-demo` | Reusable agent-spine bundle | +| [`cli-demo/`](cli-demo/README.md) | `@deepseek-ai/dsh-cli-demo` | Headless one-shot application bundle | +| [`acp-demo/`](acp-demo/README.md) | `@deepseek-ai/dsh-acp-demo` | ACP automation application bundle | +| [`jsonrpc-demo/`](jsonrpc-demo/README.md) | `@deepseek-ai/dsh-jsonrpc-demo` | External-config JSON-RPC runtime | -`agent-spine-demo` is the shared bundle; `cli-demo` and `acp-demo` compose it with headless one-shot and ACP automation front doors, and own their boot bins. The product [`dsh`](../../apps/cli/README.md) CLI uses no bundle: its TUI and web surfaces are a shared `base.cordis.yml` plus one overlay each. `jsonrpc-demo` mounts no composition of its own — it boots whatever tree the deployment's `cordis.yml` names, and is what the Python SDK runtime launches. +`agent-spine-demo` is the shared bundle; `cli-demo` and `acp-demo` add their front doors, while `jsonrpc-demo` boots a deployment-owned plugin tree. -These are **not** product API. The spine pieces they bundle live in [`core/`](../core/README.md), human/SDK channels and boot glue in [`ui/`](../ui/README.md), the automation transport in [`acp/`](../acp/README.md), and swappable backends in their capability groups; a demo bundle just picks one concrete composition of them. Swap or fork one freely. +These packages are not product API. Product seams and front doors remain in their owning groups; demo bundles select concrete compositions. Do not confuse this group with the repo-root [`examples/`](../../examples/AGENTS.md): that directory holds the runnable `cordis.yml` **leaves**; this group holds the **bundles** those leaves load. - -## The jsonrpc bin/exe names are legacy - -`jsonrpc-demo` renamed like its siblings, but its bin is still `dsh-jsonrpc-agent` and the single-file executable is still `dsh-jsonrpc-agent-pkg` (referenced across the [Python distribution](../../python/sdk-runtime/README.md)). Those names are the SDK's runtime-startup surface; they are reconciled when the SDK unifies that startup flow, not by this move. diff --git a/packages/examples/README.zh.md b/packages/examples/README.zh.md index 0218e60df2..c346a41d29 100644 --- a/packages/examples/README.zh.md +++ b/packages/examples/README.zh.md @@ -6,17 +6,13 @@ | 包 | npm 名称 | 角色 | |---|---|---| -| `agent-spine-demo/` | `@deepseek-ai/dsh-agent-spine-demo` | 不含执行器和 UI 的 agent 主干,打包为一个组合包插件,带后备会话标题和选用的持久目标栈 | -| `cli-demo/` | `@deepseek-ai/dsh-cli-demo` | 无头单次应用:主干 + JSONL 持久化 + 预创建的 `main` agent,提供文本和 DSH 原生 JSON 输出 | -| `acp-demo/` | `@deepseek-ai/dsh-acp-demo` | ACP 自动化服务器应用:主干 + 持久目标 + JSONL 持久化 + [`acp`](../acp/acp/README.md) 桥接层(无 stdout logger),带启动 `bin` | -| `jsonrpc-demo/` | `@deepseek-ai/dsh-jsonrpc-demo` | 只有 bin 的 runtime,用于启动外部 `cordis.yml`,供 stdio JSON-RPC SDK 客户端使用 | +| [`agent-spine-demo/`](agent-spine-demo/README.md) | `@deepseek-ai/dsh-agent-spine-demo` | 可复用的 agent 主干组合包 | +| [`cli-demo/`](cli-demo/README.md) | `@deepseek-ai/dsh-cli-demo` | 无头单次应用组合包 | +| [`acp-demo/`](acp-demo/README.md) | `@deepseek-ai/dsh-acp-demo` | ACP 自动化应用组合包 | +| [`jsonrpc-demo/`](jsonrpc-demo/README.md) | `@deepseek-ai/dsh-jsonrpc-demo` | 外部配置 JSON-RPC 运行时 | -`agent-spine-demo` 是共享组合包;`cli-demo` 和 `acp-demo` 分别将它与无头单次和 ACP 自动化前端入口组合,并拥有各自的启动 bin。产品 [`dsh`](../../apps/cli/README.md) CLI 不使用组合包:其 TUI 与 web surface 都是一份共享的 `base.cordis.yml` 加各自一份 overlay。`jsonrpc-demo` 自身不挂载任何组合,而是启动部署的 `cordis.yml` 所指名的任意插件树;Python SDK runtime 会启动它。 +`agent-spine-demo` 是共享组合包;`cli-demo` 与 `acp-demo` 添加各自的前端入口,`jsonrpc-demo` 则启动由部署方拥有的插件树。 -这些 **不是** 产品 API。它们打包的主干组件位于 [`core/`](../core/README.md),人类/SDK 通道和启动粘合代码位于 [`ui/`](../ui/README.md),自动化传输位于 [`acp/`](../acp/README.md),可替换后端位于各自能力组;演示组合包只选定其中一种具体组合。可以自由替换或 fork。 +这些包不是产品 API。产品 seam 与前端入口仍位于各自的归属组;演示组合包只选择具体组合。 不要将此组与仓库根目录的 [`examples/`](../../examples/AGENTS.md) 混淆:该目录存放可运行的 `cordis.yml` **叶节点**;此组存放这些叶节点加载的 **组合包**。 - -## jsonrpc bin/exe 名称是历史遗留 - -`jsonrpc-demo` 已像同级包一样重命名,但其 bin 仍为 `dsh-jsonrpc-agent`,单文件可执行程序仍为 `dsh-jsonrpc-agent-pkg`(在 [Python 分发](../../python/sdk-runtime/README.md)各处被引用)。这些名称属于 SDK 的 runtime 启动表层;只有 SDK 统一该启动流程时才会协调它们,而不会在此次移动中处理。 diff --git a/packages/examples/jsonrpc-demo/README.md b/packages/examples/jsonrpc-demo/README.md index 75e9e39439..2cc496da3e 100644 --- a/packages/examples/jsonrpc-demo/README.md +++ b/packages/examples/jsonrpc-demo/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -Bin-only app that boots an external `cordis.yml`; its [`jsonrpc`](../../ui/jsonrpc/README.md) entry serves SDK clients over newline-delimited stdio. The config composes the spine, backends, and serving plugin. `lib/bin.js` is also the [single-executable runtime](../../../.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md) entry. +Bin-only app that boots an external `cordis.yml`; its [`jsonrpc`](../../ui/jsonrpc/README.md) entry serves SDK clients over newline-delimited stdio. The config composes the spine, backends, and serving plugin. The published bin is `dsh-jsonrpc-agent`, and `lib/bin.js` also ships as the `dsh-jsonrpc-agent-pkg` [single-executable runtime](../../../.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md) used by the Python SDK. ## Config discovery diff --git a/packages/examples/jsonrpc-demo/README.zh.md b/packages/examples/jsonrpc-demo/README.zh.md index d7732582e0..98d397b3c3 100644 --- a/packages/examples/jsonrpc-demo/README.zh.md +++ b/packages/examples/jsonrpc-demo/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -只包含 bin 的应用,启动外部 `cordis.yml`;其 [`jsonrpc`](../../ui/jsonrpc/README.md) 入口通过按换行分隔的 stdio 为 SDK 客户端提供服务。配置负责组合主干、后端和服务插件。`lib/bin.js` 也是[单文件可执行运行时](../../../.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md) 的入口。 +只包含 bin 的应用,启动外部 `cordis.yml`;其 [`jsonrpc`](../../ui/jsonrpc/README.md) 入口通过按换行分隔的 stdio 为 SDK 客户端提供服务。配置负责组合主干、后端和服务插件。发布的 bin 名为 `dsh-jsonrpc-agent`,`lib/bin.js` 还会作为 Python SDK 使用的 `dsh-jsonrpc-agent-pkg` [单文件可执行运行时](../../../.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md)交付。 ## 配置发现 diff --git a/packages/fs/README.md b/packages/fs/README.md index b5e0ac9d1c..108d7d862a 100644 --- a/packages/fs/README.md +++ b/packages/fs/README.md @@ -2,20 +2,16 @@ English | [中文](README.zh.md) -The filesystem stack: a provider seam (text IO + atomic mutation with an optional version guard), a local implementation, a policy gate plugin (observed-state + read-before-edit + version-guarded write/edit), the model-facing file tools + executor, and the bash-backed discovery tools. All **product** packages. +The filesystem capability family: provider seam, interchangeable backends, policy, and model-facing tools. All **product** packages. | Package | Role | ctx key | |---|---|---| -| `fs/` | Provider seam: text IO + atomic mutation primitives (optional version guard); owns the `fs/*` policy events | `ctx.fs` | -| `fs-local/` | Local-filesystem `FileSystem` implementation | (registers `ctx.fs`) | -| `fs-sandbox/` | Sandbox-enforcing `FileSystem`: extends `fs-local` and fences write/edit by the per-call mode + workspace root policy (read-only denies, workspace-write contains to the session workspace + temp roots), reads pass through | (registers `ctx.fs`) | -| `fs-policy/` | Policy gate plugin: observed-state + read-before-edit + version-guarded write/edit, via the `fs/*` event gate | (no service — `fs/*` listeners) | -| `tool-fs/` | Model-facing `read`/`write`/`edit` tools AND the executor (reads via `ctx.fs`, owns read windowing, dispatches `fs/*`); preserves filesystem semantics for session-cwd-relative paths and advertises sandbox escalation fields when the mounted `ctx.fs` confines | (registers on `ctx.tools`) | -| `tool-fs-search/` | Model-facing `glob`/`grep` discovery tools when `rg` is available on the bash executor `PATH`, backed by fixed ripgrep commands through `ctx.bash`, NOT by `ctx.fs` provider methods | (registers on `ctx.tools`) | -| `tool-str-replace-editor/` | Model-facing `str_replace_editor` with view/create/unique literal replace/line insert operations over `ctx.fs` | (registers on `ctx.tools`) | +| [`fs/`](fs/README.md) | Filesystem provider seam and policy-event vocabulary | `ctx.fs` | +| [`fs-local/`](fs-local/README.md) | Local-filesystem backend | registers `ctx.fs` | +| [`fs-sandbox/`](fs-sandbox/README.md) | Sandbox-enforcing backend | registers `ctx.fs` | +| [`fs-policy/`](fs-policy/README.md) | Observed-state and mutation policy | `fs/*` listeners | +| [`tool-fs/`](tool-fs/README.md) | Model-facing file tools | registers on `ctx.tools` | +| [`tool-fs-search/`](tool-fs-search/README.md) | Process-backed discovery tools | registers on `ctx.tools` | +| [`tool-str-replace-editor/`](tool-str-replace-editor/README.md) | Model-facing string-replacement editor | registers on `ctx.tools` | -The interface lives at `fs/fs/`. A sandboxed, remote, or project-scoped filesystem backend can replace `fs-local` without touching the seam, the policy gate, or the model-facing tool schemas — `fs-sandbox` is the first such replacement (an in-process path fence over the shared sandbox mode; see [the cross-family fs sandbox Agent Note](../../.agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.md)). The policy (`fs-policy/`) is a plugin that participates only through the `fs/*` event gate, not a service the tool injects — so dropping it gracefully loses the policy and leaves the unconstrained bare provider rather than breaking the tool. A deployment that loads `tool-fs/` is expected to also load it. The mode fence and the read-before-edit gate are orthogonal and compose. Discovery (`tool-fs-search/`) deliberately does NOT extend the provider seam: search is a process-backed `rg` workflow on the bash executor, so filesystem backends stay free of a universal search contract; its tools register only when that executor can find `rg`, and its results are follow-up-readable when the bash workdir and the `read` root are the same workspace (the co-located deployment its README documents). - -## No timeouts on file IO - -`read`/`write`/`edit` take **no** `timeoutMs`, and the provider seam arms no deadline — unlike bash and web (which consume [`@deepseek-ai/dsh-timeout`](../util/timeout/README.md)) and the bash-backed `glob`/`grep` (whose declared `timeoutMs` is enforced by `@deepseek-ai/dsh-timeout-policy`): those are process-backed, where a deadline can really kill the work. A local syscall is best-effort-abortable at most: a timeout could not force an in-progress `fsync`/`rename` to stop, so a deadline here would be a knob that cannot deliver on its promise. Adding one would also be an implicit default in the exact place explicit-over-implicit forbids. Both reference agents (Claude Code, Codex) leave file IO untimed for the same reason; cancellation still propagates through the tool-execution signal for best-effort abort at syscall boundaries. +Backends replace one another behind `ctx.fs`; policy and tools consume the seam independently. Discovery remains process-backed instead of expanding the provider contract. Child READMEs own containment, mutation, schema, and timeout details. diff --git a/packages/fs/fs/README.md b/packages/fs/fs/README.md index 9e6c954aba..6c80cc22f6 100644 --- a/packages/fs/fs/README.md +++ b/packages/fs/fs/README.md @@ -4,16 +4,7 @@ English | [中文](README.zh.md) The **filesystem provider seam**: an abstract `FileSystem` service (`ctx.fs`) defining the storage primitives a backend provides — resolve a path, stat metadata, no-follow path metadata, read/stream text, list directories, write atomically, and apply a literal edit — without saying HOW. Both mutations take their version guard **optionally**, so `ctx.fs` on its own is a complete, unconstrained text-storage seam. This package also owns the `fs/*` policy event vocabulary the tool dispatches and the policy plugin listens for. -This package is the provider-seam layer of the four-layer filesystem stack, split so each concern can evolve (and be swapped) independently (see [the capability-seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md), [the filesystem capability-seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-17-filesystem-capability-seam.md), [the split-the-filesystem-seam Agent Note](../../../.agents/notes/implemented/simplification/2026-06-26-fsspec-style-fs-seam.md), and [the file-context event-gate Agent Note](../../../.agents/notes/implemented/architecture/2026-06-26-file-context-as-event-gate.md)): - -| Layer | Package | Role | -|---|---|---| -| tool / executor | `@deepseek-ai/dsh-tool-fs` | model-facing `read`/`write`/`edit` schemas + read windowing + text rendering; reads/writes/edits via `ctx.fs`, dispatches the `fs/*` events | -| policy | `@deepseek-ai/dsh-fs-policy` | observed-state + read-before-edit + version-guarded write/edit, contributed through the `fs/*` event gate (no service) | -| provider seam | `@deepseek-ai/dsh-fs` (this) | `ctx.fs`: text IO + atomic mutation primitives (optional version guard); owns the `fs/*` event vocabulary | -| provider | `@deepseek-ai/dsh-fs-local` | the host-filesystem implementation | - -A future sandboxed, virtual, or remote backend implements this interface and the policy/tool layers don't change. +This package is the provider-seam layer of the [filesystem family](../README.md). The [tool](../tool-fs/README.md), [policy](../fs-policy/README.md), and [local](../fs-local/README.md) and [sandboxed](../fs-sandbox/README.md) backends remain separate consumers and implementations; the capability-seam decisions own the split ([foundation](../../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md), [filesystem seam](../../../.agents/notes/implemented/architecture/2026-06-17-filesystem-capability-seam.md), [provider split](../../../.agents/notes/implemented/simplification/2026-06-26-fsspec-style-fs-seam.md), [event gate](../../../.agents/notes/implemented/architecture/2026-06-26-file-context-as-event-gate.md)). ## Service API (`ctx.fs`) @@ -46,6 +37,10 @@ This package declares three events (see the generated [events catalog](../../../ `FsTargetKey` / `FsVersion` are branded opaque ids ([the branded-ids Agent Note](../../../.agents/notes/implemented/architecture/2026-06-20-branded-ids.md)) — consumers must not parse `targetKey` or interpret `version`; only `displayPath` is for model/UI output. `FsWriteIntent` is the explicit GUARDED write intent (`createIfAbsent` creates a missing target and rejects an existing one with `FS_NOT_OBSERVED`; `replaceIfVersion` replaces only at the observed version, else `FS_STALE_VERSION`); omitting it from `writeText` is the third, unconditional state. `FsPathInfo` is the no-follow metadata shape that can report `symlink`, unlike target-level `FsInfo`. Failures throw `FsError` (extends `HarnessError`, [the structured error taxonomy Agent Note](../../../.agents/notes/implemented/architecture/2026-06-11-structured-error-taxonomy.md)) carrying a stable `FsErrorCode` (`FS_NOT_FOUND`, `FS_NOT_DIRECTORY`, `FS_NOT_TEXT`, `FS_NOT_REGULAR_FILE`, `FS_PERMISSION_DENIED`, `FS_IO_ERROR`, `FS_STALE_VERSION`, `FS_NOT_OBSERVED`, `FS_AMBIGUOUS_EDIT`, `FS_EDIT_NOT_FOUND`, `FS_ABORTED`); the tool registry surfaces `{ name, code }` on `isError` results. See `src/types.ts` for the full contracts. +## No IO deadline + +Filesystem primitives accept an optional `AbortSignal` but arm no deadline. Local IO is only best-effort abortable: a timeout cannot force an in-progress `fsync` or `rename` to stop, so a fixed deadline would promise control the backend cannot provide. Process-backed discovery owns its separate timeout contract. + ## Model Experience Indirectly, through `dsh-tool-fs`, which renders provider text and errors as bounded, retained filesystem tool results. @@ -58,5 +53,5 @@ No direct invalidation; the named consumer owns any request-prefix changes. - **Text-only by contract** — backends reject binary/non-UTF-8 content with `FS_NOT_TEXT`; binary-safe operations are a deliberate deferral of [the tool-schemas Agent Note](../../../.agents/notes/implemented/feature/2026-06-17-filesystem-tool-schemas.md). - **Eight primitives only** — no delete, rename/move, copy, or watch; `listDir` is single-level, with recursion, globbing, pagination, and search out of scope per [the directory-listing Agent Note](../../../.agents/notes/archived/architecture/2026-07-03-filesystem-directory-listing-seam.md). -- **No IO deadline** — the seam arms no timeout; cancellation is a best-effort optional `AbortSignal` per primitive (the deliberate [fs-family stance](../README.md)). +- **No IO deadline** — cancellation is best-effort at primitive boundaries. - **Resolve-then-operate costs a remote backend two round-trips per tool call** — folding or caching resolution is left to such a backend. diff --git a/packages/fs/tool-fs/README.md b/packages/fs/tool-fs/README.md index a695d0ba8f..1ecfa0d013 100644 --- a/packages/fs/tool-fs/README.md +++ b/packages/fs/tool-fs/README.md @@ -150,4 +150,4 @@ Append-only; newly visible content follows the reusable request prefix and does - **No model-facing directory listing ships** — `ctx.fs.listDir` serves provider code such as skill discovery, while the sibling [`dsh-tool-fs-search`](../tool-fs-search/) package supplies bash-backed `glob` and `grep` rather than extending the filesystem seam. - **`read` handles UTF-8 text files only** — binary-safe reads and PDF/image/multimodal content are deferred; a directory target is `FS_NOT_REGULAR_FILE`. -- **No timeout surface** — `read`/`write`/`edit` take no timeout argument and declare no `timeout-policy` budget; cancellation rides `exec.signal` only (the deliberate [fs-family stance](../README.md)). +- **No timeout surface** — `read`/`write`/`edit` take no timeout argument and declare no `timeout-policy` budget; cancellation rides `exec.signal` only ([provider rationale](../fs/README.md#no-io-deadline)). diff --git a/packages/fs/tool-fs/README.zh.md b/packages/fs/tool-fs/README.zh.md index 7b7dc26f51..8cd48bdc29 100644 --- a/packages/fs/tool-fs/README.zh.md +++ b/packages/fs/tool-fs/README.zh.md @@ -150,4 +150,4 @@ Use the edit tool for targeted changes to existing UTF-8 text files. It replaces - **未交付面向模型的目录列表工具**:`ctx.fs.listDir` 服务于 skill(技能)发现等提供方代码,同级 [`dsh-tool-fs-search`](../tool-fs-search/) 包则提供基于 bash 的 `glob` 与 `grep`,而不是扩展文件系统 seam。 - **`read` 只处理 UTF-8 文本文件**:二进制安全读取和 PDF/图像/多模态内容均延期处理;目录目标为 `FS_NOT_REGULAR_FILE`。 -- **没有超时接口**:`read`/`write`/`edit` 不接受超时参数,也不声明 `timeout-policy` 预算;取消只通过 `exec.signal` 传递(见有意采用的 [fs 能力族立场](../README.md))。 +- **没有超时接口**:`read`/`write`/`edit` 不接受超时参数,也不声明 `timeout-policy` 预算;取消只通过 `exec.signal` 传递(见[提供方理由](../fs/README.md#no-io-deadline))。 diff --git a/packages/goal/README.i18n.yaml b/packages/goal/README.i18n.yaml index 1a4406e288..4238bac3c4 100644 --- a/packages/goal/README.i18n.yaml +++ b/packages/goal/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/goal/README.md -README.md: f43dfd8258eabe8342207c0b1b9d6acc9e215e9f -README.zh.md: 70b38caac723d757f44109e4ec75e3c31e7c34b8 +README.md: 9fc6b0c18b1862a8be08275785ea3b6185e9bdbc +README.zh.md: 08f15bcc4e405e25d4dfd5981bbc99408833403c diff --git a/packages/goal/README.md b/packages/goal/README.md index f43dfd8258..9fc6b0c18b 100644 --- a/packages/goal/README.md +++ b/packages/goal/README.md @@ -6,9 +6,9 @@ The goal family owns durable objective state independently of the model-facing t | Package | Role | ctx key | |---|---|---| -| `goal/` | Event-sourced goal lifecycle, replay fold, compare-and-set mutations, and process-local activation | `ctx.goals` | -| `goal-session/` | Same-session goal-round admission, outcome mapping, and lifecycle race fencing | — | -| `tool-goal/` | Model-facing read/create/update tools with execution-time authority checks | — | -| `command-goal/` | Human-facing `/goal` status and lifecycle control over the command plane | — | +| [`goal/`](goal/README.md) | Goal state and lifecycle | `ctx.goals` | +| [`goal-session/`](goal-session/README.md) | Same-session goal continuation | — | +| [`tool-goal/`](tool-goal/README.md) | Model-facing goal tools | — | +| [`command-goal/`](command-goal/README.md) | Human-facing goal command | — | Goal state is part of the owning session log. Consumers depend on `dsh-goal`, not on the concrete agent loop; continuation behavior belongs in a separate plugin on the public agent seams. diff --git a/packages/goal/README.zh.md b/packages/goal/README.zh.md index 70b38caac7..08f15bcc4e 100644 --- a/packages/goal/README.zh.md +++ b/packages/goal/README.zh.md @@ -6,9 +6,9 @@ goal 家族负责持久目标状态,与消费该状态的面向模型工具和 | 包 | 职责 | ctx 键 | |---|---|---| -| `goal/` | 事件溯源的目标生命周期、回放折叠、比较并设置变更,以及进程本地激活 | `ctx.goals` | -| `goal-session/` | 同会话 Goal Round 的准入、结果映射与生命周期竞态隔离 | 无 | -| `tool-goal/` | 面向模型的读取/创建/更新工具,并在执行时检查权限 | 无 | -| `command-goal/` | 面向用户的 `/goal` 状态,以及通过命令平面执行的生命周期控制 | 无 | +| [`goal/`](goal/README.md) | 目标状态与生命周期 | `ctx.goals` | +| [`goal-session/`](goal-session/README.md) | 同会话目标续行 | 无 | +| [`tool-goal/`](tool-goal/README.md) | 面向模型的目标工具 | 无 | +| [`command-goal/`](command-goal/README.md) | 面向用户的目标命令 | 无 | 目标状态是其所属会话日志的一部分。消费方依赖 `dsh-goal`,而不是具体的 agent loop(智能体循环);续行行为由基于公开 agent seam 的独立插件负责。 diff --git a/packages/guard/README.md b/packages/guard/README.md index b7375fd2bb..8da2f2ddcc 100644 --- a/packages/guard/README.md +++ b/packages/guard/README.md @@ -2,10 +2,8 @@ English | [中文](README.zh.md) -Behavioral guard plugins that watch the agent loop for unproductive patterns and nudge the model back on course. A single **product** package — there is no interface/implementation seam here, because a guard is a self-contained consumer of existing core seams (`tools/post-execute`, `agent/prompt-submit`, `agent/status`), not a swappable capability. +Behavioral guard plugins watch the agent loop for unproductive patterns and nudge the model back on course. A guard is a self-contained consumer of core seams, not a swappable capability. | Package | Role | ctx key | |---|---|---| -| `repeat-tool-guard/` | Advisory reminders when an agent loops on identical tool calls | (listens on `ctx.tools`' waterfalls) | - -Reminders travel as `additionalContexts` on the `tools/post-execute` decision; the agent loop appends them as logged plugin-sourced `user/message` events after the step's tool results (see [the tools package](../core/tools)), so everything a guard says to the model is reconstructable from the session log. +| [`repeat-tool-guard/`](repeat-tool-guard/README.md) | Advisory reminders for repeated tool calls | listens on tool and agent events | diff --git a/packages/hooks/README.md b/packages/hooks/README.md index 23478fb5e9..3598929231 100644 --- a/packages/hooks/README.md +++ b/packages/hooks/README.md @@ -6,8 +6,8 @@ The hooks subsystem lets users extend the agent at lifecycle points the way Clau | Package | Role | Shape | |---|---|---| -| `hook-protocol/` | Shared wire-protocol core: matcher primitive, exit-code/stdout codec, `runHook` (via `ctx.bash`), most-restrictive merge, `hook/*` session events, detached-run quiescence | library (no plugin) | -| `hooks-claude/` | Bridge for a Claude Code `hooks.json` / settings | plugin | -| `hooks-codex/` | Bridge for a Codex `hooks.json` | plugin | +| [`hook-protocol/`](hook-protocol/README.md) | Shared shell-hook protocol library | library | +| [`hooks-claude/`](hooks-claude/README.md) | Claude Code hook bridge | plugin | +| [`hooks-codex/`](hooks-codex/README.md) | Codex hook bridge | plugin | -Codex deliberately reimplements a *subset* of the Claude Code protocol (same `hooks.json` shape, 5 events vs CC's many, command-only, regex-only matcher, no env/substitution), so `hook-protocol` owns the genuinely-identical primitives and each bridge owns only what differs (its per-event stdin payload, env, and the mapping of a hook's neutral outcome onto the harness's typed Decisions). See [hook-protocol/README.md](hook-protocol/README.md). +The shared library owns common protocol behavior; each bridge owns its dialect-specific event mapping. The child READMEs document those contracts. diff --git a/packages/hooks/README.zh.md b/packages/hooks/README.zh.md index 778d5f334e..0ab2202921 100644 --- a/packages/hooks/README.zh.md +++ b/packages/hooks/README.zh.md @@ -6,8 +6,8 @@ hooks 子系统让用户可以像使用 Claude Code 和 Codex 一样,在 agent | 包 | 职责 | 形态 | |---|---|---| -| `hook-protocol/` | 共享协议格式核心:matcher 原语、退出码/stdout codec、`runHook`(通过 `ctx.bash`)、最严格合并、`hook/*` 会话事件、分离运行完全停稳 | 库(非插件) | -| `hooks-claude/` | Claude Code `hooks.json`/settings 的桥接 | 插件 | -| `hooks-codex/` | Codex `hooks.json` 的桥接 | 插件 | +| [`hook-protocol/`](hook-protocol/README.md) | 共享 shell hook 协议库 | 库 | +| [`hooks-claude/`](hooks-claude/README.md) | Claude Code hook 桥接 | 插件 | +| [`hooks-codex/`](hooks-codex/README.md) | Codex hook 桥接 | 插件 | -Codex 有意重新实现 Claude Code 协议的一个*子集*(`hooks.json` 结构相同、5 个事件而非 CC 的众多事件、仅命令、仅使用正则的 matcher、没有 env/替换),因此 `hook-protocol` 负责真正相同的原语,每个桥接只负责不同部分(逐事件 stdin 载荷、env,以及把 hook 的中性结果映射到 harness 类型化 Decision 的方式)。参见 [hook-protocol/README.md](hook-protocol/README.md)。 +共享库负责通用协议行为;每个桥接负责其方言特有的事件映射。相应契约由子级 README 记录。 diff --git a/packages/host/README.md b/packages/host/README.md index 391adb7009..7cd331f113 100644 --- a/packages/host/README.md +++ b/packages/host/README.md @@ -6,11 +6,11 @@ The host side of the dsh web GUI: the API gateway every client shape shares, and | Package | Role | ctx key | |---|---|---| -| `apiproxy/` | The shared API gateway: the zero-Node TS wire contract (`src/api/`), the fetch carrier pair (`toFetchHandler` host-side, `AbstractApiClient` client-side), and the host implementation over `ctx.agents`/`ctx.workspace` | `ctx.apiProxy` | -| `webserver/` | Plain HTTP route-registration carrier: `node:http` server listening on activation; routes register as named `exact`/`prefix` handlers | `ctx.httpServer` | -| `directory-picker/` | Workspace-directory picking seam: discriminated `native`/`browse` capability the gateway's picker RPCs delegate to | `ctx.directoryPicker` | -| `directory-picker-native/` | Dual-face native interaction: OS-chooser backend (osascript / PowerShell / Zenity+KDialog, host-display only) + the browser half filling ui-workspace's directory-flow slots | (registers `ctx.directoryPicker`) | -| `directory-picker-browse/` | Dual-face browse interaction: listing/creation primitives over Node stdlib (remote-capable) + the browser half rendering the in-app Select Workspace Directory dialog | (registers `ctx.directoryPicker`) | -| `directory-picker-auto/` | Adaptive chooser: resolves the host's situation once at boot (bind host, SSH, display) and mounts the matching dual-face backend as an in-memory Loader entry | (mounts a backend row) | +| [`apiproxy/`](apiproxy/README.md) | Shared host API gateway and wire contract | `ctx.apiProxy` | +| [`webserver/`](webserver/README.md) | HTTP route carrier | `ctx.httpServer` | +| [`directory-picker/`](directory-picker/README.md) | Workspace-directory picking seam | `ctx.directoryPicker` | +| [`directory-picker-native/`](directory-picker-native/README.md) | Native directory-picker backend and browser interaction | registers `ctx.directoryPicker` | +| [`directory-picker-browse/`](directory-picker-browse/README.md) | In-app directory-browser backend and interaction | registers `ctx.directoryPicker` | +| [`directory-picker-auto/`](directory-picker-auto/README.md) | Host-adaptive picker composition | mounts a backend | -`apiproxy` is transport-agnostic by design — it registers no routes; carriers wrap `ctx.apiProxy` themselves. The HTTP carrier route (with its `/api` browser-trust fence) is mounted by [`client/connection`](../client/connection/README.md)'s node half, which is why that package lives in the client group: it owns both ends of the wire. +`apiproxy` remains transport-independent; [`client/connection`](../client/connection/README.md) supplies the browser/HTTP carrier. Picker implementations replace one another behind the shared seam. diff --git a/packages/llm/README.md b/packages/llm/README.md index 66b7beabd7..92d9fbfa2b 100644 --- a/packages/llm/README.md +++ b/packages/llm/README.md @@ -6,10 +6,10 @@ The LLM seam and its provider adapters. The interface package (`llm`) owns the a | Package | Role | ctx key | |---|---|---| -| `llm/` | Abstract LLM service + content-block vocabulary + chunk assembler | `ctx.llm` | -| `token-meter/` | Replay-aware request and surface token measurement | `ctx.tokenMeter` | -| `llm-retry/` | Exact-provider normal or unbounded request retry policy | (listens to `agent/request-error`) | -| `llm-deepseek/` | DeepSeek API adapter (direct fetch + eventsource-parser SSE) | (registers on `ctx.llm`) | -| `llm-pi-ai/` | Multi-provider adapter via `@earendil-works/pi-ai` | (registers on `ctx.llm`) | +| [`llm/`](llm/README.md) | LLM service and shared streaming vocabulary | `ctx.llm` | +| [`token-meter/`](token-meter/README.md) | Replay-aware token measurement | `ctx.tokenMeter` | +| [`llm-retry/`](llm-retry/README.md) | Provider-scoped retry policy | listens to `agent/request-error` | +| [`llm-deepseek/`](llm-deepseek/README.md) | Direct DeepSeek adapter | registers on `ctx.llm` | +| [`llm-pi-ai/`](llm-pi-ai/README.md) | Multi-provider pi-ai adapter | registers on `ctx.llm` | -The interface lives at `llm/llm/`; adapters, retry policy, and the reusable token meter are flat siblings under the group. Requests route by `provider`, while `model` is passed through to the selected adapter. The route-owning adapter supplies retry policy and resolves available exact-model identity, context capacity, and reasoning metadata; the retry executor and token meter remain provider-agnostic. A new provider adapter registers one or more provider routes on `ctx.llm` without touching the consumers. See [twin LLM adapters](../../.agents/notes/implemented/architecture/2026-06-13-twin-llm-adapters.md) for the two shipping implementations, the [replay token meter Agent Note](../../.agents/notes/implemented/architecture/2026-07-15-replay-token-meter-service.md) for measurement ownership, and the [routed model context Agent Note](../../.agents/notes/implemented/architecture/2026-07-20-routed-model-context-and-compaction-policy.md) for capacity and compaction-policy ownership. +Adapters register provider routes on the seam; retry and token measurement remain separate consumers. The child READMEs own routing, metadata, replay, and provider-wire details; the [LLM architecture decisions](../../.agents/notes/implemented/architecture/2026-06-13-twin-llm-adapters.md) own the rationale. diff --git a/packages/lsp/README.md b/packages/lsp/README.md index 7b5d9e0f50..4964d78f10 100644 --- a/packages/lsp/README.md +++ b/packages/lsp/README.md @@ -6,10 +6,8 @@ The language-server capability seam: an abstract LSP interface, a generic stdio | Package | Role | ctx key | |---|---|---| -| `lsp/` | Abstract LSP seam (provider registry by branded id + extension mapping, per-query selection, vocabulary, `LspError`) | `ctx.lsp` | -| `lsp-local/` | Generic multi-server local backend (spawn, JSON-RPC, transient-open queries) | (registers providers on `ctx.lsp`) | -| `tool-lsp/` | Model-facing `lsp` tool (four operations, one-based UTF-16 cursor coordinates) | (registers on `ctx.tools`) | +| [`lsp/`](lsp/README.md) | LSP provider seam and shared vocabulary | `ctx.lsp` | +| [`lsp-local/`](lsp-local/README.md) | Local stdio language-server backend | registers providers on `ctx.lsp` | +| [`tool-lsp/`](tool-lsp/README.md) | Model-facing semantic-navigation tool | registers on `ctx.tools` | -The interface lives at `lsp/lsp/`. The seam exposes exactly four semantic operations — `goToDefinition`, `findReferences`, `goToImplementation`, `hover` — and no generic JSON-RPC escape hatch, so a provider swap does not change how the model asks for navigation and no protocol payload or unreviewed mutation reaches the model contract. Providers register **capabilities**, not tools; `tool-lsp` is the only owner of the model-facing name, schema, prompt guidance, and presentation. - -See the [LSP capability seam Agent Note](../../.agents/notes/implemented/architecture/2026-07-15-lsp-capability-seam.md) for the design rationale, including why documents open transiently per query, why the local host reads through Node APIs rather than `ctx.fs`, and why extension ownership is exclusive within one runtime. +Providers register semantic capabilities; the tool owns the model-facing contract. The child READMEs document operation, protocol, and presentation details, while the [LSP capability-seam Agent Note](../../.agents/notes/implemented/architecture/2026-07-15-lsp-capability-seam.md) owns the rationale. diff --git a/packages/mcp/README.md b/packages/mcp/README.md index 3bde902336..cc3440f6cb 100644 --- a/packages/mcp/README.md +++ b/packages/mcp/README.md @@ -6,4 +6,4 @@ Packages bridging the harness to the MCP ecosystem. | Package | Role | |---|---| -| `mcp-client/` | MCP client bridge: connects to external MCP servers and registers their tools on `ctx.tools` | +| [`mcp-client/`](mcp-client/README.md) | MCP client bridge that registers external server tools on `ctx.tools` | diff --git a/packages/mcp/README.zh.md b/packages/mcp/README.zh.md index 46a11c9461..38a5c8b1c1 100644 --- a/packages/mcp/README.zh.md +++ b/packages/mcp/README.zh.md @@ -6,4 +6,4 @@ | 包 | 角色 | |---|---| -| `mcp-client/` | MCP 客户端桥接:连接外部 MCP 服务器,并将其工具注册到 `ctx.tools` | +| [`mcp-client/`](mcp-client/README.md) | MCP 客户端桥接:将外部服务器工具注册到 `ctx.tools` | diff --git a/packages/plan/README.md b/packages/plan/README.md index eeb58703c3..598974e105 100644 --- a/packages/plan/README.md +++ b/packages/plan/README.md @@ -2,10 +2,10 @@ English | [中文](README.zh.md) -Plan mode is one logged, per-agent collaboration state. It is a single **product** package, not a generic mode registry or a capability-seam trio. +Plan mode is logged, per-agent collaboration state rather than a generic mode registry or capability seam. | Package | Role | ctx key | |---|---|---| -| `plan-mode/` | `plan/mode` vocabulary + fold, boundary-applied state, the `plan:policy` guidance section, `/plan [message]` entry and `/plan off` exit, and the model-facing `exit_plan_mode` review tool | `ctx.planMode` | +| [`plan-mode/`](plan-mode/README.md) | Owns plan-mode state, guidance, commands, and review flow | `ctx.planMode` | -The active state is a pure function of the session log, so resume and fork restore it without extra machinery. The deployment supplies plan instructions through Cordis config, while `exit_plan_mode` stays registered when planning is inactive to keep the request tool catalog stable. Interactive adapters use the plugin-owned `/plan` command; sandbox mode and approval policy remain independent enforcement settings. Design: [plan-specific collaboration state](../../.agents/notes/implemented/simplification/2026-07-22-plan-specific-collaboration-state.md). +The [plan-specific collaboration state](../../.agents/notes/implemented/simplification/2026-07-22-plan-specific-collaboration-state.md) decision records the family design. diff --git a/packages/plan/plan-mode/README.md b/packages/plan/plan-mode/README.md index 6f0a9ac477..dfa7b7c9a7 100644 --- a/packages/plan/plan-mode/README.md +++ b/packages/plan/plan-mode/README.md @@ -94,5 +94,4 @@ Mode transitions do not change the tool catalog; plan arguments and review resul - Plan mode guides rather than enforces; deployments needing a hard boundary must combine independent sandbox and approval controls. - A pending selection made while idle is lost if the process exits before the next boundary, so the UI must reapply it. - Forked agents inherit logged plan state, while newly spawned agents begin inactive; there is no creation-time plan option. -- The `exit_plan_mode` review arc has one assembled-application snapshot, the Web `plan-review` e2e lane (submit → decision card → approved flip). The rejected-feedback and dismissed branches are covered by package tests only, and the TUI keyless scenarios exercise only `/plan` entry and `/plan off` exit. - Only the Web UI renders the `plan-review` intent; the TUI presents the review through its generic question flow, which is answerable but does not read as a plan gate. diff --git a/packages/plan/plan-mode/README.zh.md b/packages/plan/plan-mode/README.zh.md index 9d3d98ef38..80876b16d0 100644 --- a/packages/plan/plan-mode/README.zh.md +++ b/packages/plan/plan-mode/README.zh.md @@ -94,5 +94,4 @@ mode 转换不改变工具目录;plan 参数与评审结果按常规方式扩 - Plan mode 只进行引导,而不强制执行;需要硬边界的部署必须组合独立的沙箱与批准控制。 - 如果进程在下一个边界之前退出,空闲时作出的待生效选择会丢失,因此 UI 必须重新应用它。 - Fork 的 agent 会继承已记录的 plan 状态,新 spawn 的 agent 则从未激活状态开始;不存在创建时 plan 选项。 -- `exit_plan_mode` 评审弧有一个组装应用快照,即 Web `plan-review` e2e 通道(提交 → 决定卡片 → 已批准切换)。已拒绝反馈与放弃审阅两个分支仅由包测试覆盖,TUI 无密钥场景只演练 `/plan` 进入和 `/plan off` 退出。 - 只有 Web UI 渲染 `plan-review` 意图;TUI 通过其通用问题流程呈现该评审,可以回答,但读起来不像一个计划关口。 diff --git a/packages/pty/README.md b/packages/pty/README.md index e54dcf64db..9c8206464d 100644 --- a/packages/pty/README.md +++ b/packages/pty/README.md @@ -2,13 +2,13 @@ English | [中文](README.zh.md) -`PTY` stands for **Pseudo-Terminal**(伪终端). This capability provides persistent, owner-scoped terminal sessions for workflows that require state across tool calls or interactive stdin. PTY complements the one-shot bash and filesystem tools; it does not replace their stronger per-operation contracts. +This family provides persistent, owner-scoped pseudo-terminal sessions for interactive or stateful terminal work. It complements one-shot bash execution. | Package | Role | ctx key | |---|---|---| -| [`pty`](pty/README.md) (`@deepseek-ai/dsh-pty`) | Backend registry, branded ids, exact-Agent ownership, session operations, and awaited cleanup | `ctx.pty` | -| `pty-local` (`@deepseek-ai/dsh-pty-local`) | Local `node-pty` backend, readiness detection, bounded terminal state, sandboxing, and process-session supervision | registers on `ctx.pty` | -| `tool-pty` (`@deepseek-ai/dsh-tool-pty`) | Six model-facing tools and generic task integration for background sends | registers on `ctx.tools` | -| `tool-bash-persistent` (`@deepseek-ai/dsh-tool-bash-persistent`) | One model-facing `bash` backed by an owner-scoped reusable PTY shell | consumes `ctx.pty`, registers on `ctx.tools` | +| [`pty/`](pty/README.md) | Defines the PTY service and session lifecycle | `ctx.pty` | +| [`pty-local/`](pty-local/README.md) | Provides local persistent terminal sessions | registers on `ctx.pty` | +| [`tool-pty/`](tool-pty/README.md) | Exposes PTY session operations to the model | registers on `ctx.tools` | +| [`tool-bash-persistent/`](tool-bash-persistent/README.md) | Exposes a reusable PTY-backed bash tool | registers on `ctx.tools` | -The design and deferred boundaries live in the [persistent PTY Agent Note](../../.agents/notes/implemented/feature/2026-07-16-persistent-pty-sessions.md). +The [persistent PTY decision](../../.agents/notes/implemented/feature/2026-07-16-persistent-pty-sessions.md) records the family boundary. diff --git a/packages/sandbox/README.md b/packages/sandbox/README.md index 4a651f262e..1ba56d2233 100644 --- a/packages/sandbox/README.md +++ b/packages/sandbox/README.md @@ -2,14 +2,12 @@ English | [中文](README.zh.md) -The confinement half of the [capability-seam split](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md): an abstract provider interface, platform backends, and the shared policy home. Consumers hand `ctx.sandbox` the exact argv they are about to spawn and spawn the returned (wrapped) argv instead; a complete `SandboxExecutionPolicy` (mode + workspace root) rides each capability call, and its confined subset becomes the provider's `SandboxPolicy`. Different sessions and consumers can therefore confine under different policies at the same instant. All **product** packages. +This family applies per-session confinement policy to process execution. It covers same-world subprocesses; isolated environments replace complete capability implementations instead of registering here. | Package | Role | ctx key | |---|---|---| -| `sandbox/` | Abstract process-sandbox seam (the `SandboxProvider` contract + the mode/enforcement/policy vocabulary) plus the shared ESCALATION kit (`approveEscalation`, the strictly-wider ladder, the denial/hint markers) and the `writableRoots` derivation every enforcement dialect shares | `ctx.sandbox` | -| `sandbox-local/` | Local backends by platform chain: Linux `bwrap` else the `landlock-run` launcher (the npm-distributed [`node-addon-landlock-run`](https://www.npmjs.com/package/node-addon-landlock-run) family, built and released from its own repository), darwin `sandbox-exec`/Seatbelt — multi-candidate chains functionally probed, sole candidates selected directly, verdict cached, fail-closed | (registers `ctx.sandbox`) | -| `sandbox-policy/` | The policy resolver: deployment fallbacks plus each session's durable mode and immutable cwd root. Both enforcing families consume its complete per-call result, so bash and fs cannot confine to different roots | `ctx.sandboxPolicy` | +| [`sandbox/`](sandbox/README.md) | Defines the process-sandbox service and shared escalation vocabulary | `ctx.sandbox` | +| [`sandbox-local/`](sandbox-local/README.md) | Provides local platform confinement backends | registers on `ctx.sandbox` | +| [`sandbox-policy/`](sandbox-policy/README.md) | Resolves durable per-session sandbox policy | `ctx.sandboxPolicy` | -The seam confines SAME-WORLD subprocesses only (shared filesystem and kernel). Containers, microVMs, and remote executors are NOT backends here — they replace whole capability implementations (`ctx.bash`, `ctx.fs`) as environment-coherent groups; the boundary is recorded in [the sandbox Agent Note](../../.agents/notes/implemented/feature/2026-07-06-sandbox.md). - -Consumers today: [`bash/bash-sandbox`](../bash/bash-sandbox/) (wraps `['bash', '-c', command]` through `ctx.sandbox`) and [`fs/fs-sandbox`](../fs/fs-sandbox/) (an in-process path fence, not an argv wrapper — reads `ctx.sandboxPolicy` and enforces the shared mode on write/edit). The cross-family boundary is the sandbox Agent Note's [cross-family fs sandbox](../../.agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.md) phase; the shared vocabulary lets both families teach the model one denial marker and one escalation flow. +See the [sandbox decision](../../.agents/notes/implemented/feature/2026-07-06-sandbox.md) for the capability boundary and the [filesystem integration decision](../../.agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.md) for cross-family policy use. diff --git a/packages/sandbox/sandbox-local/README.md b/packages/sandbox/sandbox-local/README.md index f7dd5619fa..17ae8c98a1 100644 --- a/packages/sandbox/sandbox-local/README.md +++ b/packages/sandbox/sandbox-local/README.md @@ -14,8 +14,6 @@ The Seatbelt profile is allow-default with `(deny file-write*)` plus write allow [`node-addon-landlock-run`](https://www.npmjs.com/package/node-addon-landlock-run) supplies the platform launcher, functional probe, and CLI argument vocabulary. This provider owns only mode-to-grant mapping and runner selection. Keeping path resolution and probe parsing with the versioned binary prevents contract drift. -Each rung has a self-skipping keyless world-effect test; CI runs platform legs against real kernels and rejects a silent all-skip. The packed-install test exercises the registry launcher and executable mode through a plain-Node consumer. - ```yaml - id: sandbox name: '@deepseek-ai/dsh-sandbox-local' diff --git a/packages/sandbox/sandbox-local/README.zh.md b/packages/sandbox/sandbox-local/README.zh.md index c8abe05408..9c1fd304a4 100644 --- a/packages/sandbox/sandbox-local/README.zh.md +++ b/packages/sandbox/sandbox-local/README.zh.md @@ -14,8 +14,6 @@ Seatbelt profile 默认允许,但带 `(deny file-write*)` 和写入 allow-list [`node-addon-landlock-run`](https://www.npmjs.com/package/node-addon-landlock-run)提供平台 launcher、功能探测和 CLI 参数词汇。该提供方只负责模式到授权的映射与 runner 选择。把路径解析和探测解析保留在带版本的 binary 中,可防止契约漂移。 -每个权限层级都有会自行跳过的无密钥 world-effect 测试;CI 在真实内核上运行平台 job,并拒绝所有测试静默跳过。打包安装测试通过纯 Node 消费方运行注册表 launcher 与可执行模式。 - ```yaml - id: sandbox name: '@deepseek-ai/dsh-sandbox-local' diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 0dcf3655fc..3f99d6d45d 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -2,18 +2,15 @@ English | [中文](README.zh.md) -Developer tooling for creating, editing, building, and running DeepSeek Harness projects, plus the client SDK stack for driving a harness runtime from another process. - -The [feature Agent Note](../../.agents/notes/proposed/feature/2026-07-14-sdk-developer-projects.md) owns the developer workflow; the [architecture Agent Note](../../.agents/notes/proposed/architecture/2026-07-15-sdk-project-editing-architecture.md) owns the package and project-editing boundaries; the [TypeScript SDK Agent Note](../../.agents/notes/implemented/feature/2026-07-27-typescript-sdk-and-sdk-subagent-backend.md) owns the client SDK stack. +This group contains developer tooling for Harness projects and the client stack for driving a Harness runtime from another process. | Package | Role | |---|---| -| [`helper`](helper/README.md) | Project aggregate, edit session, builtin features, project documents, templates, package managers, and prompt abstraction | -| [`scripts`](scripts/README.md) | The `dsh-sdk` launcher: `start`, `dev`, `build`, and interactive `config` | -| [`create-sdk`](create-sdk/README.md) | The `npm create @deepseek-ai/sdk` initializer | -| [`sdk-protocol`](sdk-protocol/README.md) | Shared SDK runtime wire protocol: the newline-delimited JSON-RPC transport + named request/notification types | -| [`sdk-client`](sdk-client/README.md) | TypeScript client SDK: drive a harness runtime subprocess over stdio JSON-RPC (the Python SDK's design twin) | +| [`helper/`](helper/README.md) | Provides the shared project-editing domain | +| [`scripts/`](scripts/README.md) | Provides the `dsh-sdk` project commands | +| [`create-sdk/`](create-sdk/README.md) | Creates new SDK projects | +| [`sdk-protocol/`](sdk-protocol/README.md) | Defines the SDK runtime wire protocol | +| [`sdk-client/`](sdk-client/README.md) | Drives a Harness runtime through the TypeScript client API | +| [`telemetry/`](telemetry/README.md) | Provides launcher telemetry, consent, and redaction primitives | -`@deepseek-ai/create-sdk` is the one package-name exception to the repository's `@deepseek-ai/dsh-*` rule: npm's scoped initializer convention requires that name for `npm create @deepseek-ai/sdk`. - -Generated projects keep `cordis.yml` as the only runtime plugin tree. `dsh-sdk dev` adds TypeScript and local-workspace resolution around that same file; it does not create a development-only config. +`@deepseek-ai/create-sdk` follows npm's scoped initializer naming convention; the other packages follow the repository's `@deepseek-ai/dsh-*` convention. See the [developer-project workflow](../../.agents/notes/proposed/feature/2026-07-14-sdk-developer-projects.md), [project-editing architecture](../../.agents/notes/proposed/architecture/2026-07-15-sdk-project-editing-architecture.md), and [TypeScript SDK design](../../.agents/notes/implemented/feature/2026-07-27-typescript-sdk-and-sdk-subagent-backend.md). diff --git a/packages/sdk/sdk-client/README.md b/packages/sdk/sdk-client/README.md index eb0387292f..5bce396aa3 100644 --- a/packages/sdk/sdk-client/README.md +++ b/packages/sdk/sdk-client/README.md @@ -31,10 +31,6 @@ The protocol client under the turns API: explicit `start()`/`initialize()`/`prom `HarnessClientOptions.env` replaces the child environment entirely when given (`undefined` inherits the parent's); callers own credential policy — `scrubbedParentEnv` from `dsh-subprocess` is the shared scrub base for isolation-minded launches. -## Testing - -Keyless unit tests drive a scripted fake runtime subprocess (`tests/fake-runtime.ts`, protocol-only, env-scripted) over real stdio: turn loop, session-tree scoping, timeout/death/malformed-response surfaces, and the dispose ladder. The [SDK snapshot suite](../../../examples/jsonrpc-agent/tests/sdk.snapshot.ts) drives the real `dsh-jsonrpc-agent` runtime through this client keylessly via `llm-replay`, pinning the notification stream, the turn result, and the persisted logs; `DSH_SNAPSHOT=record` re-records against the live API. - ## Model Experience None, as this is a client-process library; the model runs in the spawned runtime, whose experience is owned by the plugins its `cordis.yml` composes. diff --git a/packages/sdk/sdk-client/README.zh.md b/packages/sdk/sdk-client/README.zh.md index c264557984..cf72caa669 100644 --- a/packages/sdk/sdk-client/README.zh.md +++ b/packages/sdk/sdk-client/README.zh.md @@ -31,10 +31,6 @@ console.log(result.status, result.finalResponse) `HarnessClientOptions.env` 给定时整体替换子进程环境(`undefined` 原样继承父进程环境);凭据策略归调用方——`dsh-subprocess` 的 `scrubbedParentEnv` 是面向隔离启动的共享擦除基底。 -## 测试 - -免密钥单元测试通过真实 stdio 驱动一个脚本化伪运行时子进程(`tests/fake-runtime.ts`,纯协议、环境变量脚本化):轮次循环、会话树范围限定、超时、进程死亡和响应畸形场景,以及 dispose(资源释放)阶梯。[SDK 快照套件](../../../examples/jsonrpc-agent/tests/sdk.snapshot.ts) 经由 `llm-replay` 免密钥地通过本客户端驱动真实 `dsh-jsonrpc-agent` 运行时,固定通知流、轮次结果与持久化日志;`DSH_SNAPSHOT=record` 对真实 API 重录。 - ## 模型体验 无,因为这是一个客户端进程库;模型运行在 spawn 出的运行时中,其体验由该运行时的 `cordis.yml` 所组合的插件决定。 diff --git a/packages/session-persistence/README.md b/packages/session-persistence/README.md index ac4e0a8310..060f757b35 100644 --- a/packages/session-persistence/README.md +++ b/packages/session-persistence/README.md @@ -2,13 +2,13 @@ English | [中文](README.zh.md) -The durable session-persistence seam and its storage backends. The interface package owns the abstract `SessionPersistence` service and the shared write coordinator; the backends are concrete implementations that register on `ctx.sessionPersistence`. All **product** packages. +This family defines durable session persistence, semantic checkpoint policy, and the shipped storage backends. | Package | Role | ctx key | |---|---|---| -| `session-persistence/` | Persistence seam + shared write coordinator | `ctx.sessionPersistence` | -| `session-checkpoint-policy/` | Semantic durability barriers for agent requests and tool execution | (wraps `ctx.llm` / `ctx.tools`, listens on agent events) | -| `session-persistence-jsonl/` | JSONL-sidecar persistence backend | (registers `ctx.sessionPersistence`) | -| `session-persistence-sqlite/` | SQLite persistence backend | (registers `ctx.sessionPersistence`) | +| [`session-persistence/`](session-persistence/README.md) | Defines the persistence service and shared write coordination | `ctx.sessionPersistence` | +| [`session-checkpoint-policy/`](session-checkpoint-policy/README.md) | Applies semantic durability checkpoints | wraps `ctx.llm` and `ctx.tools` | +| [`session-persistence-jsonl/`](session-persistence-jsonl/README.md) | Persists sessions in JSONL files | registers on `ctx.sessionPersistence` | +| [`session-persistence-sqlite/`](session-persistence-sqlite/README.md) | Persists sessions in SQLite | registers on `ctx.sessionPersistence` | -The interface lives at `session-persistence/session-persistence/`; backends are flat siblings. A new storage backend joins here and registers on `ctx.sessionPersistence`. See [session persistence](../../.agents/notes/implemented/architecture/2026-06-14-session-persistence.md). +The [session-persistence decision](../../.agents/notes/implemented/architecture/2026-06-14-session-persistence.md) records the family design. diff --git a/packages/session-projection/README.md b/packages/session-projection/README.md index ae80a90570..f1fc8337bb 100644 --- a/packages/session-projection/README.md +++ b/packages/session-projection/README.md @@ -1,10 +1,10 @@ -# session-projection/ +# session-projection/ — session projection capability family English | [中文](README.zh.md) -Session-projection capability family: the seam through which domain host plugins serve whole current values of log-derived per-session state to client carriers. +This family serves current, log-derived per-session state to client carriers. -| Package | ctx key | Role | +| Package | Role | ctx key | |---|---|---| -| [`session-projection`](session-projection/README.md) | `sessionProjections` | The interface package: the merge-extensible `SessionProjectionMap` type table, the `ProjectionDefinition` unit contract, and the eagerly driven registry carriers read synchronously | -| [`session-projection-cache`](session-projection-cache/README.md) | `sessionProjectionCache` | Persisted projection cache: durable per-session unit checkpoints over the domain data form, throttled write-behind with mandatory turn/end + detach points, and the cold-read ladder (cache row + persistence tail replay) | +| [`session-projection/`](session-projection/README.md) | Defines and drives session projection units | `ctx.sessionProjections` | +| [`session-projection-cache/`](session-projection-cache/README.md) | Persists and restores projection checkpoints | `ctx.sessionProjectionCache` | diff --git a/packages/session-query/README.md b/packages/session-query/README.md index 38edbf6b03..a9c9f33e2d 100644 --- a/packages/session-query/README.md +++ b/packages/session-query/README.md @@ -2,12 +2,10 @@ English | [中文](README.zh.md) -Trusted exact reads, relationship traces, provider-independent semantic filtering, and SQLite full-text search over live and durable session logs. +This family provides authorized retrieval over live and durable session logs, independently of compaction. | Package | Role | ctx key | |---|---|---| -| [`session-query/`](session-query/README.md) | Combined service contract with concrete logical-corpus reads, traces, and semantic filters plus abstract full-text methods | `ctx.sessionQuery` | -| [`session-query-sqlite/`](session-query-sqlite/README.md) | Concrete service backend with SQLite FTS5 persistent bases and live overlays | `ctx.sessionQuery` | -| [`tool-session-query/`](tool-session-query/README.md) | Workspace-authorized model-facing search, lineage, relationship, and exact event tools | — | - -The query service is independent of compaction: it reads canonical lineage, surface operations, logged provenance, and semantic event text but does not participate in compaction policy or execution. One abstract service combines every query operation, one concrete backend owns the full-text lifecycle without a provider registry or coordinator, and the consumer leaves oversized plain-text results to the generic post-execute spill policy. +| [`session-query/`](session-query/README.md) | Defines trusted reads, relationship queries, and search operations | `ctx.sessionQuery` | +| [`session-query-sqlite/`](session-query-sqlite/README.md) | Implements session queries with SQLite full-text search | `ctx.sessionQuery` | +| [`tool-session-query/`](tool-session-query/README.md) | Exposes workspace-authorized session queries to the model | registers on `ctx.tools` | diff --git a/packages/session-title/README.i18n.yaml b/packages/session-title/README.i18n.yaml index 9cdb34dc49..2c5d1f2788 100644 --- a/packages/session-title/README.i18n.yaml +++ b/packages/session-title/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/session-title/README.md -README.md: 64bca8153e566e1590776af511662a873198106e -README.zh.md: 5ff52211ac975e31845285f99618503cd775eded +README.md: c2ccc1cd6d329a58f673a9ea5d47ce2f35c9dc41 +README.zh.md: b8df266a861da5cb20a76d0738bba4993218c3d5 diff --git a/packages/session-title/README.md b/packages/session-title/README.md index 64bca8153e..c2ccc1cd6d 100644 --- a/packages/session-title/README.md +++ b/packages/session-title/README.md @@ -2,13 +2,13 @@ English | [中文](README.zh.md) -Durable session-title state, one optional asynchronous provider seam, and two opt-in model-backed implementations. The built-in first-message fallback is part of the service, so every composition can title a session without an auxiliary model call. +This family derives durable session titles from the session log, with an optional model-backed provider. | Package | Role | ctx key | |---|---|---| -| [`session-title/`](session-title/README.md) | Log fold, deterministic fallback, provider registry, and refresh API | `ctx.sessionTitle` | -| [`session-title-llm/`](session-title-llm/README.md) | Shared route, request logging, prompt, timeout, stream, and validation helper | — | -| [`session-title-first-message-llm/`](session-title-first-message-llm/README.md) | Optional provider using the first eligible human message | registers on `ctx.sessionTitle` | -| [`session-title-all-messages-llm/`](session-title-all-messages-llm/README.md) | Optional provider using every eligible human message | registers on `ctx.sessionTitle` | +| [`session-title/`](session-title/README.md) | Owns title state, fallback behavior, provider registration, and refresh | `ctx.sessionTitle` | +| [`session-title-llm/`](session-title-llm/README.md) | Provides shared model-backed title generation | — | +| [`session-title-first-message-llm/`](session-title-first-message-llm/README.md) | Titles a session from its first eligible human message | registers on `ctx.sessionTitle` | +| [`session-title-all-messages-llm/`](session-title-all-messages-llm/README.md) | Titles a session from all eligible human messages | registers on `ctx.sessionTitle` | -Only one provider may register at a time. The shared demo spine mounts the fallback service but leaves both model providers outside default composition, so deployments choose auxiliary cost and retitling cadence explicitly. +Deployments may register one model-backed provider; the service retains a deterministic fallback when none is present. diff --git a/packages/session-title/README.zh.md b/packages/session-title/README.zh.md index 5ff52211ac..b8df266a86 100644 --- a/packages/session-title/README.zh.md +++ b/packages/session-title/README.zh.md @@ -1,14 +1,14 @@ -# session-title/:日志支持的会话标题能力家族 +# session-title/:日志支持的会话标题能力族 [English](README.md) | 中文 -持久化的会话标题状态、一个可选异步提供方 seam,以及两个由模型支持、可选启用的实现。内置首消息回退属于服务本身,因此任何组合都能在不调用辅助模型的情况下为会话生成标题。 +该包族从会话日志派生持久会话标题,并支持可选的模型后端 provider。 -| 包 | 职责 | ctx 键 | +| 包 | 职责 | ctx key | |---|---|---| -| [`session-title/`](session-title/README.md) | 日志折叠、确定性回退、提供方注册表与刷新 API | `ctx.sessionTitle` | -| [`session-title-llm/`](session-title-llm/README.md) | 共享路由、请求日志记录、提示词、超时、流与验证辅助模块 | 无 | -| [`session-title-first-message-llm/`](session-title-first-message-llm/README.md) | 使用第一条符合条件的用户消息的可选提供方 | 注册到 `ctx.sessionTitle` | -| [`session-title-all-messages-llm/`](session-title-all-messages-llm/README.md) | 使用所有符合条件的用户消息的可选提供方 | 注册到 `ctx.sessionTitle` | +| [`session-title/`](session-title/README.md) | 负责标题状态、回退行为、provider 注册与刷新 | `ctx.sessionTitle` | +| [`session-title-llm/`](session-title-llm/README.md) | 提供共享的模型标题生成能力 | — | +| [`session-title-first-message-llm/`](session-title-first-message-llm/README.md) | 根据第一条合格的人类消息生成会话标题 | 注册到 `ctx.sessionTitle` | +| [`session-title-all-messages-llm/`](session-title-all-messages-llm/README.md) | 根据所有合格的人类消息生成会话标题 | 注册到 `ctx.sessionTitle` | -同一时间只能注册一个提供方。共享 demo 主干会挂载回退服务,但默认组合不包含两个模型提供方,因此部署会显式选择辅助成本和重新生成标题的节奏。 +部署可注册一个模型后端 provider;未注册时,服务仍提供确定性回退。 diff --git a/packages/settings/README.i18n.yaml b/packages/settings/README.i18n.yaml index 7c78505615..ec2d2124a3 100644 --- a/packages/settings/README.i18n.yaml +++ b/packages/settings/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/settings/README.md -README.md: 7a91355dd01805938944f0abce77765021288e6d -README.zh.md: 2df40b67eb8ce6cfc693ed6bf3574815c0219ec0 +README.md: 3f43647f0558abf53c373ef7d97af16c0e17d2fe +README.zh.md: b5779bfe5da4cae148bcc6515ef13790f78bca7a diff --git a/packages/settings/README.md b/packages/settings/README.md index 7a91355dd0..3f43647f05 100644 --- a/packages/settings/README.md +++ b/packages/settings/README.md @@ -2,11 +2,9 @@ English | [中文](README.zh.md) -The user-settings seam and its providers. The interface package owns the abstract `Settings` service — namespace registration, layered resolution, and change commits; providers implement raw-document storage and push external edits through the seam. All **product** packages. +This family resolves user-editable configuration through registered namespaces and swappable storage providers. | Package | Role | ctx key | |---|---|---| -| `settings/` | Settings seam: namespace registry, layered resolution, commit events | `ctx.settings` | -| `settings-local/` | File-backed provider (`settings.yaml`/`.json`) with hot reload and comment-preserving write-back | (registers `ctx.settings`) | - -The interface lives at `settings/settings/`; providers are flat siblings. A network configuration-center provider (for example a nacos-style backend) joins here and registers on `ctx.settings`. Composition config stays in `cordis.yml`: a settings namespace carries only the user-editable subset, resolved as schema defaults, then the registrant's composition `base`, then the user document. +| [`settings/`](settings/README.md) | Defines namespace registration, layered resolution, and commits | `ctx.settings` | +| [`settings-local/`](settings-local/README.md) | Stores settings in a local file and observes external edits | registers on `ctx.settings` | diff --git a/packages/settings/README.zh.md b/packages/settings/README.zh.md index 2df40b67eb..b5779bfe5d 100644 --- a/packages/settings/README.zh.md +++ b/packages/settings/README.zh.md @@ -1,12 +1,10 @@ -# settings/ — 用户设置能力族 +# settings/:用户设置能力族 [English](README.md) | 中文 -用户设置 seam 及其 provider。接口包拥有抽象 `Settings` 服务——namespace 注册、分层解析与变更提交;provider 实现原始文档存储并把外部修改推入 seam。全部为**产品**包。 +该包族通过注册的命名空间与可替换存储 provider 解析用户可编辑配置。 -| 包 | 角色 | ctx key | +| 包 | 职责 | ctx key | |---|---|---| -| `settings/` | 设置 seam:namespace 注册表、分层解析、提交事件 | `ctx.settings` | -| `settings-local/` | 文件 provider(`settings.yaml`/`.json`),热重载与保留注释的写回 | (注册 `ctx.settings`) | - -接口位于 `settings/settings/`;provider 平级并列。网络配置中心 provider(例如 nacos 类后端)加入本组并注册到 `ctx.settings`。组合配置仍留在 `cordis.yml`:settings namespace 只承载用户可编辑子集,解析顺序为 schema 默认值、注册方的组合 `base`、用户文档。 +| [`settings/`](settings/README.md) | 定义命名空间注册、分层解析与提交 | `ctx.settings` | +| [`settings-local/`](settings-local/README.md) | 在本地文件中存储设置并观察外部编辑 | 注册到 `ctx.settings` | diff --git a/packages/skill/README.md b/packages/skill/README.md index 4fb41dda5d..d10049ac3e 100644 --- a/packages/skill/README.md +++ b/packages/skill/README.md @@ -1,13 +1,13 @@ -# skill/ - skill capability family +# skill/ — skill capability family English | [中文](README.zh.md) -The canonical three-package capability seam for reusable agent instructions: a provider registry, a local implementation, and the model-facing catalog/loader consumer. All are **product** packages. +This family discovers reusable agent instructions and exposes them to the model through a provider-neutral catalog and loader. | Package | Role | ctx key | |---|---|---| -| `skill/` | Provider registry, precedence resolution, complete/incomplete catalog snapshots, and full-definition lookup | `ctx.skills` | -| `skill-local/` | Project/custom/user filesystem provider with membership watching | (registers on `ctx.skills`) | -| `tool-skill/` | Initial and replacement catalogs plus the model-facing `skill` loader | (registers on `ctx.tools`) | +| [`skill/`](skill/README.md) | Defines skill provider registration and lookup | `ctx.skills` | +| [`skill-local/`](skill-local/README.md) | Discovers skills from local filesystems | registers on `ctx.skills` | +| [`tool-skill/`](tool-skill/README.md) | Publishes the skill catalog and model-facing loader | registers on `ctx.tools` | -The interface lives at `skill/skill/`. Providers register synchronously and perform asynchronous discovery through `ctx.skills`; `tool-skill` consumes only that interface, so an embedded or remote provider can replace or complement `skill-local` without changing the model-facing contract. `agent-core` loads this family by default, but it remains a capability outside the core control spine, parallel to [`bash/`](../bash/README.md), [`fs/`](../fs/README.md), [`web/`](../web/README.md), and [`subagent/`](../subagent/README.md). +This capability remains outside the core control spine and can use local, embedded, or remote providers without changing the model-facing contract. diff --git a/packages/spill/README.md b/packages/spill/README.md index cb6f1e9af9..b96375b1a3 100644 --- a/packages/spill/README.md +++ b/packages/spill/README.md @@ -1,15 +1,13 @@ -# spill/ - spill storage capability family +# spill/ — tool-output spill capability family English | [中文](README.zh.md) -The tool-output spill capability seam: an abstract storage interface, a local filesystem implementation, and the tool-result policy that uses it. All **product** packages. +This family persists oversized tool output and replaces the inline result with a bounded preview and retrieval locator. | Package | Role | ctx key | |---|---|---| -| `spill/` | Abstract spill storage seam (`saveText` — persist oversized tool text and return a locator + retrieval hint) | `ctx.spillStore` | -| `spill-local/` | Local-filesystem backend: private, session-scoped files with traversal-safe names | (registers on `ctx.spillStore`) | -| `spill-policy/` | `tools/post-execute` policy: replaces oversized plain-text results with a preview + spill locator | (no service surface) | +| [`spill/`](spill/README.md) | Defines spill storage | `ctx.spillStore` | +| [`spill-local/`](spill-local/README.md) | Stores spilled text in session-scoped local files | registers on `ctx.spillStore` | +| [`spill-policy/`](spill-policy/README.md) | Applies the post-execution spill policy | listens on `ctx.tools` | -The interface lives at `spill/spill/`. The split mirrors bash/fs: the seam owns storage only, `spill-local` owns the filesystem mechanics, and `spill-policy` owns WHEN to spill and the model-facing notice. Preview mechanics stay in [`util/retention`](../util/README.md) — the policy composes the two without either owning the other's job. - -See the [tool output spill Agent Note](../../.agents/notes/implemented/architecture/2026-07-08-tool-output-spill-files.md) for the design rationale, including why final-result spill is separate from tool-owned early spill (bash streams, subagent rollouts) and why creation belongs to the runtime spill seam rather than the model-facing `write` tool. +See the [tool-output spill decision](../../.agents/notes/implemented/architecture/2026-07-08-tool-output-spill-files.md) for the boundary between storage, retention, and tool-owned output handling. diff --git a/packages/storage/README.md b/packages/storage/README.md index c9eca7355f..a5f9d0204b 100644 --- a/packages/storage/README.md +++ b/packages/storage/README.md @@ -2,13 +2,13 @@ English | [中文](README.zh.md) -The storage family persists everything that is not a session event log: a hub where named backends and typed data forms meet. Design record: [domain KV storage Agent Note](../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.zh.md). +This family persists application data other than session event logs through named backends and typed data forms. | Package | Role | ctx key | |---|---|---| -| `storage/` | The hub: named backend registry + merge-extensible data-form mounts, backend facet vocabulary, shared conformance suite | `ctx.storage` | -| `storage-json/` | JSON backend: one human-readable file per unit, atomic whole-file rewrite | registers backend `json` | -| `storage-sqlite/` | SQLite backend: one database hosting all routed units, document-per-row | registers backend `sqlite` | -| `domain/` | Domain data form: zod-validated records, per-domain write chain, `domain/changed` events, backend routing by configuration | `ctx.storageDomain` + `ctx.storage.domain` | +| [`storage/`](storage/README.md) | Connects registered backends with typed data forms | `ctx.storage` | +| [`storage-json/`](storage-json/README.md) | Stores data in JSON files | registers backend `json` | +| [`storage-sqlite/`](storage-sqlite/README.md) | Stores data in SQLite | registers backend `sqlite` | +| [`storage-domain/`](storage-domain/README.md) | Provides validated domain-record storage | `ctx.storageDomain` | -Backends own one medium each and expose data-shape **facets** (`kv` today; an append-log facet is reserved for the future session-backend migration). Each backend plugin publishes an internal lifecycle service after registration; the domain plugin injects every configured backend key before exposing its own service, so config-tree row order carries no startup semantics. Consumers never touch backends directly — they inject `storageDomain` and open declared domains through it. +Consumers use a data form rather than accessing a backend directly. The [domain storage decision](../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.md) records the family design. diff --git a/packages/subagent/README.md b/packages/subagent/README.md index f9b04b4aa8..6aeb7fb1ea 100644 --- a/packages/subagent/README.md +++ b/packages/subagent/README.md @@ -2,20 +2,18 @@ English | [中文](README.zh.md) -The subagent seam: an agent delegating work to a child agent. Like the [bash](../bash/README.md) and [llm](../llm/README.md) families this is a capability seam (see [capability seams](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)) — but with one defining difference: **multiple provider implementations coexist in one context**, registered by name, rather than the single-implementation bash shape. The registry mirrors the LLM adapter registry. +This family lets an agent delegate work to child agents. Multiple named providers may coexist in one context. | Package | Role | ctx key | |---|---|---| -| `subagent/` | Subagent service: named-provider registry, vocabulary, durable descriptor, and continuable-child orchestration | `ctx.subagents` | -| `subagent-inprocess/` | Shared in-process run driver (no provider; one cleanup effect per run) | — | -| `subagent-spawn/` | In-process backend: a fresh child agent, with cold resume | (registers on `ctx.subagents`) | -| `subagent-fork/` | In-process backend: a child seeded with the parent's completed-turn prefix, with cold resume | (registers on `ctx.subagents`) | -| `subagent-acp/` | Out-of-process backend: a child agent in a spawned subprocess, driven over ACP (one-shot) | (registers on `ctx.subagents`) | -| `subagent-dsh-sdk/` | Out-of-process backend: a child harness runtime in a spawned subprocess, driven over stdio JSON-RPC through the TypeScript SDK client | (registers on `ctx.subagents`) | -| `tool-subagent/` | Model-facing `subagent` delegation tool over `ctx.subagents` | (registers on `ctx.tools`) | -| `tool-subagent-control/` | The optional, globally named `send_message` and `list_agents` tools over `ctx.subagents` | (registers on `ctx.tools`) | -| `tool-subagent-report/` | Child-scoped `report` return channel for continuable in-process children | (registers in each child scope) | +| [`subagent/`](subagent/README.md) | Defines provider registration, delegation, and continuation | `ctx.subagents` | +| [`subagent-inprocess/`](subagent-inprocess/README.md) | Provides the shared in-process run driver | — | +| [`subagent-spawn/`](subagent-spawn/README.md) | Starts a fresh in-process child | registers on `ctx.subagents` | +| [`subagent-fork/`](subagent-fork/README.md) | Starts an in-process child from the parent's completed history | registers on `ctx.subagents` | +| [`subagent-acp/`](subagent-acp/README.md) | Starts an out-of-process child over ACP | registers on `ctx.subagents` | +| [`subagent-dsh-sdk/`](subagent-dsh-sdk/README.md) | Starts an out-of-process Harness child through the TypeScript SDK | registers on `ctx.subagents` | +| [`tool-subagent/`](tool-subagent/README.md) | Exposes delegation to the model | registers on `ctx.tools` | +| [`tool-subagent-control/`](tool-subagent-control/README.md) | Exposes child messaging and listing to the model | registers on `ctx.tools` | +| [`tool-subagent-report/`](tool-subagent-report/README.md) | Provides the child-to-parent report channel | registers in child scopes | -The interface and continuation orchestration live at `subagent/subagent/`. One-shot provider `start` dispatch stays independent of persistence; an internal continuation manager owns each durable continuable child as one Session plus at most one process-local Activation, binding no Task, and exists only while the Agent service is present, resolving persistence per continuation operation. The in-process `subagent-spawn` / `subagent-fork` backends share the `subagent-inprocess` driver (a library with no provider of its own — both depend on it, neither on the other), and the out-of-process `subagent-acp` / `subagent-dsh-sdk` backends spawn their children through the [`subprocess/`](../subprocess/README.md) seam (the shared credential scrub, tree-scoped teardown, and dispose ladder). Tests replace only the child boundary with package-local fixtures. - -The design rationale: [.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md](../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md), [.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md](../../.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md), and [.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md](../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md). +See the decisions for the [capability family](../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md), [continuable children](../../.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md), and [control tools](../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md). diff --git a/packages/subagent/subagent-acp/README.md b/packages/subagent/subagent-acp/README.md index efcc77c442..83a5f60414 100644 --- a/packages/subagent/subagent-acp/README.md +++ b/packages/subagent/subagent-acp/README.md @@ -61,8 +61,6 @@ The child spawns through the [`dsh-subprocess`](../../subprocess/subprocess/READ The package has no default export. Cordis loader unwrapping would otherwise hide the named `inject` metadata; see [postmortem 0001](../../../docs/postmortem/0001-acp-default-export-drops-inject.md). -Keyless tests drive a scripted ACP subprocess over real stdio, including a Loader-composed stdio app proving parent-session cwd inheritance end to end. The with-key e2e drives the repository's real ACP agent and self-skips without `DEEPSEEK_API_KEY`. - ## Model Experience ### Child-agent request @@ -100,4 +98,3 @@ Append-only; newly visible content follows the reusable request prefix and does - **No optional start-time capabilities** — this provider cannot apply the local harness's `outputSchema`, depth cap, tool filter, or persona inside the remote process, so it advertises none and the service rejects requests that require them. - **Only committed `agent_message_chunk` text is collected** — the automation server keeps reasoning, tool activity, plans, and other trace data in the child session log rather than emitting them on ACP. - **Permission prompts are auto-answered** (`permission: allow | reject`) — no human is surfaced a child's `session/request_permission` in this cut. -- **No snapshot-tier replay coverage** (`TODO(acp-subagent-replay)`) — an ACP child is its own process with its own replay shape, deferred. diff --git a/packages/subagent/subagent-acp/README.zh.md b/packages/subagent/subagent-acp/README.zh.md index baeecb623d..4ea8daef93 100644 --- a/packages/subagent/subagent-acp/README.zh.md +++ b/packages/subagent/subagent-acp/README.zh.md @@ -61,8 +61,6 @@ ACP 不声明任何启动时能力,因为当前进程无法强制执行远程 本包没有默认导出。否则 Cordis loader 的解包会隐藏具名 `inject` 元数据;见[事故复盘(postmortem)0001](../../../docs/postmortem/0001-acp-default-export-drops-inject.md)。 -无密钥测试通过真实 stdio 驱动脚本化 ACP 子进程,其中包括一个由 Loader 组合的 stdio 应用,用于端到端证明父会话 cwd 继承。带密钥 e2e 会驱动仓库中的真实 ACP agent;没有 `DEEPSEEK_API_KEY` 时自行跳过。 - ## 模型体验 ### 子 agent 请求 @@ -100,4 +98,3 @@ ACP 不声明任何启动时能力,因为当前进程无法强制执行远程 - **不支持可选启动时能力**:该提供方无法在远程进程内应用本地 harness 的 `outputSchema`、深度上限、工具过滤器或 persona,因此不会声明这些能力;服务会拒绝需要它们的请求。 - **只收集已提交的 `agent_message_chunk` 文本**:自动化服务器把推理(reasoning)、工具活动、计划和其他 trace 数据保留在子 agent 会话日志中,不通过 ACP 发出。 - **权限提示自动回答**(`permission: allow | reject`):当前版本不会把子 agent 的 `session/request_permission` 呈现给人。 -- **没有快照层回放覆盖率**(`TODO(acp-subagent-replay)`):ACP 子 agent 拥有独立进程和独立回放形态,该工作延期处理。 diff --git a/packages/subagent/subagent-dsh-sdk/README.md b/packages/subagent/subagent-dsh-sdk/README.md index a0c0811b58..266825daaa 100644 --- a/packages/subagent/subagent-dsh-sdk/README.md +++ b/packages/subagent/subagent-dsh-sdk/README.md @@ -59,8 +59,6 @@ The child environment is the [`dsh-subprocess`](../../subprocess/README.md) seam The package has no default export. Cordis loader unwrapping would otherwise hide the named `inject` metadata; see [postmortem 0001](../../../docs/postmortem/0001-acp-default-export-drops-inject.md). -Keyless tests drive the SDK client package's scripted fake runtime over real stdio, including a Loader-composed e2e where the child is a real second harness runtime proving parent-session cwd inheritance end to end (`tests/loader-composition.e2e.ts`). - ## Model Experience ### Child-agent request diff --git a/packages/subagent/subagent-dsh-sdk/README.zh.md b/packages/subagent/subagent-dsh-sdk/README.zh.md index 2d699dec90..d68d3ea7a9 100644 --- a/packages/subagent/subagent-dsh-sdk/README.zh.md +++ b/packages/subagent/subagent-dsh-sdk/README.zh.md @@ -59,8 +59,6 @@ Provider 不宣告任何启动期能力(`outputSchema`/`depthLimit`/`toolFilte 本包没有默认导出。否则 Cordis loader 解包会隐藏具名 `inject` 元数据;见[事故复盘(postmortem)0001](../../../docs/postmortem/0001-acp-default-export-drops-inject.md)。 -免密钥测试通过真实 stdio 驱动 SDK 客户端包的脚本化伪运行时,还包括一个 Loader 组合 e2e:子进程是真实的第二个 harness 运行时,端到端证明父会话 cwd 继承(`tests/loader-composition.e2e.ts`)。 - ## 模型体验 ### 子 agent 请求 diff --git a/packages/subprocess/README.md b/packages/subprocess/README.md index 64e4740c7a..187ea5b778 100644 --- a/packages/subprocess/README.md +++ b/packages/subprocess/README.md @@ -2,11 +2,11 @@ English | [中文](README.zh.md) -The shared home for spawning managed child-process trees: fully-specified spawn specs with Node-shaped per-stream stdio dispositions (raw pipes, inherit, bounded tail-keep collection with spill files), the one credential scrub every harness spawner uses, offset-based incremental reads, tree-scoped signalling with SIGTERM→grace→SIGKILL escalation, and the cooperative dispose ladder. Command defaulting, shell semantics, deadlines, protocol framing, and presentation stay with consumers — the [bash executors](../bash/README.md), the [LSP host](../lsp/README.md), and the [ACP subagent backend](../subagent/README.md). See the [subprocess seam Agent Note](../../.agents/notes/implemented/architecture/2026-07-26-subprocess-seam.md). +This family runs host subprocesses behind an explicit process-lifecycle service. -| Package | ctx key | Role | +| Package | Role | ctx key | |---|---|---| -| [`subprocess`](subprocess/README.md) (`@deepseek-ai/dsh-subprocess`) | `ctx.subprocess` | The seam: abstract `SubprocessService.spawn(spec)`, the fully-explicit `SubprocessSpawnSpec` with per-stream stdio dispositions, `SubprocessHandle` (streams, offset-based readers, terminate/waitForExit/dispose), and the shared scrub + `DSH_*`/`CollectedOutput` vocabulary | -| [`subprocess-local`](subprocess-local/README.md) (`@deepseek-ai/dsh-subprocess-local`) | — | The local implementation: detached process trees, per-disposition stream wiring, tail-keep truncation with bounded private spill files, the `DSH_*` merge order, tree signalling with escalation, the dispose ladder, and terminate-and-join disposal | +| [`subprocess/`](subprocess/README.md) | Defines subprocess launch, stream, termination, and disposal contracts | `ctx.subprocess` | +| [`subprocess-local/`](subprocess-local/README.md) | Implements local process-tree execution | registers on `ctx.subprocess` | -The service owns process lifetime across consumer reloads; consumers own what a process means (a bash command, a future non-shell runner) and every default that shapes one. +The service owns process lifetime; each consumer owns what the process does and which defaults apply. diff --git a/packages/support/README.md b/packages/support/README.md index b9550fd54f..15cb82d3d7 100644 --- a/packages/support/README.md +++ b/packages/support/README.md @@ -1,16 +1,16 @@ -# support/ — dev/test/example infrastructure +# support/ — development and test infrastructure English | [中文](README.zh.md) -Packages that exist to serve development, testing, and the examples rather than to ship as product API. They are real workspace packages (typed, tested, under the coverage gate), but they carry **lower compatibility expectations**: they may change or be removed when the development need behind them does, without the deprecation care a product package would warrant. +These packages support repository development, tests, and examples rather than product APIs. Their compatibility follows the development need they serve. -| Package | Role | ctx key | -|---|---|---| -| `acp-snapshot/` | ACP test kit: shared subprocess/client launcher + snapshot harness, normalizers, and suite factory | (library — imported by ACP e2e and `*.snapshot.ts` suites) | -| `agent-loop-testkit/` | Shared prerequisite mounting for tests that exercise the concrete agent loop | (library — imported by AgentLoop integration tests) | -| `invariants/` | Runtime event-contract assertions for development diagnostics | (listens on `session/*`, `agent/*`) | -| `loader-smoke/` | Shared real-Loader subprocess harness for keyless example smokes | (library — imported by example e2e suites) | -| `llm-mock-server/` | Scriptable OpenAI-compatible HTTP/SSE fault server + CLI for LLM recovery tests | (standalone server and test library) | -| `llm-replay/` | Record/replay adapter: short-circuits `llm/stream` from a recorded session JSONL (keyless snapshot tests) | (listens on `llm/stream`) | +| Package | Role | +|---|---| +| [`acp-snapshot/`](acp-snapshot/README.md) | Provides the ACP snapshot-test toolkit | +| [`agent-loop-testkit/`](agent-loop-testkit/README.md) | Mounts shared prerequisites for AgentLoop tests | +| [`invariants/`](invariants/README.md) | Runs development-time runtime-contract assertions | +| [`loader-smoke/`](loader-smoke/README.md) | Launches Loader-composed applications for smoke tests | +| [`llm-mock-server/`](llm-mock-server/README.md) | Provides a deterministic OpenAI-compatible fault server | +| [`llm-replay/`](llm-replay/README.md) | Replays recorded model responses for keyless tests and demos | -`invariants` is development support but has no environment guard: it runs wherever registered, and the default `dsh-agent-spine-demo` bundle mounts it unconditionally. `agent-loop-testkit` centralizes the mandatory service spine for hand-built AgentLoop tests without owning their loop or scenario. `llm-replay` backs the demos and the snapshot test tier under the per-file coverage gate, while `llm-mock-server` drives real provider adapters through deterministic HTTP/SSE faults. `acp-snapshot` carries the ACP subprocess/client boundary plus the snapshot harness, normalizers, and suite machinery, while `loader-smoke` owns the parallel real-Loader launch boundary used by keyless example e2e suites. A package graduates OUT of `support/` into a product group only when it gains documented product consumers. +A package moves out of `support/` when it gains a product contract and product consumers. diff --git a/packages/tasks/README.md b/packages/tasks/README.md index 9bafe5633b..05ed9c4396 100644 --- a/packages/tasks/README.md +++ b/packages/tasks/README.md @@ -1,13 +1,13 @@ -# tasks/ — background task capability family +# tasks/ — background-task capability family English | [中文](README.zh.md) -The shared home for background-task ids, owner isolation, reads, cancellation, waiting, and completion notices. Bash, subagents, and future long-running tools use one model-facing protocol. See the [background-task runtime Agent Note](../../.agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.md) and the [task-registry seam Agent Note](../../.agents/notes/implemented/architecture/2026-07-26-task-registry-seam.md). +This family gives long-running tools one owner-isolated background-task protocol for observation, cancellation, waiting, and completion notices. -| Package | ctx key | Role | +| Package | Role | ctx key | |---|---|---| -| [`tasks`](tasks/README.md) (`@deepseek-ai/dsh-tasks`) | `ctx.tasks` | The registry seam: branded `-N` ids, the owner-fenced read/kill/wait/list contract, snapshot vocabulary, the `attachSurface` misconfiguration fence, and the snapshot invariant companion | -| [`tasks-local`](tasks-local/README.md) (`@deepseek-ai/dsh-tasks-local`) | — | The process-local registry implementation: in-memory records, first-wins settlement bookkeeping, and the awaited owner-cleanup and teardown paths | -| [`tool-tasks`](tool-tasks/README.md) (`@deepseek-ai/dsh-tool-tasks`) | — | The model-facing control surface: `task_output`, `task_list`, `task_kill`, the completion-notice injection, and the background-habit prompt section | +| [`tasks/`](tasks/README.md) | Defines the task registry and lifecycle contract | `ctx.tasks` | +| [`tasks-local/`](tasks-local/README.md) | Implements the process-local task registry | registers on `ctx.tasks` | +| [`tool-tasks/`](tool-tasks/README.md) | Exposes task control and completion notices to the model | registers on `ctx.tools` | -The registry owns state across producer or surface reloads; the tool package owns presentation. Producers register execution hooks through `ctx.tasks.start` and own whether their config exposes `run_in_background`. +See the [background-task runtime](../../.agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.md) and [task-registry](../../.agents/notes/implemented/architecture/2026-07-26-task-registry-seam.md) decisions. diff --git a/packages/telemetry/README.md b/packages/telemetry/README.md index 944cb3f9ba..c390493d40 100644 --- a/packages/telemetry/README.md +++ b/packages/telemetry/README.md @@ -1,10 +1,12 @@ -# telemetry/ +# telemetry/ — session telemetry capability family English | [中文](README.zh.md) -Outbound session reporting: the telemetry seam plus its OpenTelemetry backend. The design — the boundary axiom (the harness's aspect ends at `emit()`; delivery is the reporting SDK's), the `telemetry/record` waterfall (deployment-mounted redaction rules; the seam ships none), the fixed chunk projection, the handoff cursor, and the operational-record channel — is pinned in [the revival Agent Note](../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.md). +This family projects session activity into outbound telemetry and delegates delivery to a configured reporting backend. | Package | Role | |---|---| -| [`@deepseek-ai/dsh-session-telemetry`](session-telemetry/) | The seam: capture points, projection, redaction, handoff cursor, ops signals, and the minimal backend contract (`emit`/`flush?`/`shutdown`). | -| [`@deepseek-ai/dsh-session-telemetry-otel`](session-telemetry-otel/) | The backend a deployment loads: the OTel JS SDK's log pipeline (`LoggerProvider` + `BatchLogRecordProcessor` + OTLP/HTTP exporter), configured verbatim through passthroughs. | +| [`session-telemetry/`](session-telemetry/README.md) | Defines capture, redaction, projection, and backend delivery | +| [`session-telemetry-otel/`](session-telemetry-otel/README.md) | Delivers telemetry through OpenTelemetry logs | + +The [telemetry decision](../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.md) records the reporting boundary. diff --git a/packages/timeout/README.md b/packages/timeout/README.md index 2a75e4d54a..d9d8e8a829 100644 --- a/packages/timeout/README.md +++ b/packages/timeout/README.md @@ -2,10 +2,10 @@ English | [中文](README.zh.md) -The tool-call timeout policy plugin. A single **product** package: it is a deployment-policy consumer of the `tools/execute` around-dispatch seam (owned by [`dsh-tools`](../core/tools)) and the pure [`dsh-timeout`](../util/timeout) library — not a swappable capability with an interface/implementation split, so it needs no seam trio. +This group applies deployment-configured deadlines to model-facing tool calls. Capabilities remain responsible for terminating their own work. -| Package | Role | ctx key | -|---|---|---| -| `timeout-policy/` | A `tools/execute` wrapper: for each configured tool it arms a per-call deadline on `exec.signal` and returns a structured `TOOL_TIMEOUT` result when that deadline wins | (registers a `tools/execute` listener; injects nothing) | +| Package | Role | +|---|---| +| [`timeout-policy/`](timeout-policy/README.md) | Enforces configured per-tool call deadlines | -Timeout is split across three layers: [`dsh-timeout`](../util/timeout) owns the pure timing/classification primitive (`deadline`/`timeoutOf`), each capability owns termination (bash kills its process group, the fetch provider tears down its socket), and this package owns the *model-facing tool-call budget as deployment policy* — no model-facing timeout argument, no global default. It is the middleware the [timeout-library Agent Note](../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md) foresaw. `bash` and hook command execution keep their own `BASH_TIMEOUT` backend timeout and do not route through this policy. +The pure timing primitives live in [`util/timeout`](../util/timeout/README.md). See the [timeout-library decision](../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md). diff --git a/packages/typert/README.md b/packages/typert/README.md index d11fd8f572..ad9f843e48 100644 --- a/packages/typert/README.md +++ b/packages/typert/README.md @@ -2,10 +2,10 @@ English | [中文](README.zh.md) -Typert separates source analysis, runtime storage, and Loader discovery into independent packages. +Typert separates source analysis, runtime storage, and Loader discovery. | Package | Role | Cordis key | |---|---|---| -| [`registry/`](registry/README.md) | Runtime package reflection and live Zod schema registry | `ctx.typert` | -| [`loader/`](loader/README.md) | Loader-entry discovery and generated host-artifact registration | consumes `ctx.loader`, `ctx.typert` | -| [`generator/`](generator/README.md) | Compiler-independent type analysis and artifact generation | build-time library | +| [`registry/`](registry/README.md) | Stores runtime package reflection and schemas | `ctx.typert` | +| [`loader/`](loader/README.md) | Discovers Loader entries and registers generated host artifacts | consumes `ctx.loader` and `ctx.typert` | +| [`generator/`](generator/README.md) | Generates runtime artifacts from source types | build-time library | diff --git a/packages/util/README.md b/packages/util/README.md index 46904aba70..b84f2cbb88 100644 --- a/packages/util/README.md +++ b/packages/util/README.md @@ -2,21 +2,13 @@ English | [中文](README.zh.md) -Zero-dependency primitives shared across the other groups. A package lands here when it owns a tiny, foundational type or helper that several capability families need but that belongs to none of them — keeping it out of any one group avoids a capability package depending on an unrelated one just to reach a shared primitive. These are **support** packages: small, stable, and free of harness dependencies. +These zero-dependency packages provide small primitives shared by multiple capability families. Business semantics remain with each consuming capability. | Package | Role | |---|---| -| `brand/` | The type-only `Branded` nominal-typing primitive (no runtime code, no harness deps) | -| `paths/` | Canonical single-root `DSH_HOME` resolution plus shared filesystem path constants and helpers for harness user data (no harness deps) | -| `timeout/` | The timing/classification half of a timeout — `clampTimeout`/`deadline`/`timeoutOf`/`TimeoutReason` (pure functions, no harness deps); termination stays in each capability | -| `retention/` | Bounded model-facing output — `ItemRetainer`/`TextRetainer` + neutral notice helpers (pure, no harness deps); business semantics stay in each tool | -| `atomic-write/` | Atomic file replacement — `writeFileAtomic` (exclusive-create temp + rename carrying the caller-stated mode); shared by the settings and credentials stores | -| `native-command/` | No-shell `execFile` runner for host-native OS integrations — utf8 capture, abort propagation, Windows hide (no harness deps); command choice stays in each caller | - -`dsh-brand` is the canonical case: it owns ONLY the `Branded` helper, so a capability package can brand the ids it owns (`dsh-tasks`'s `TaskId`, `dsh-session`'s `SessionId`, …) by depending on `dsh-brand` alone, without pulling in an unrelated package just to reach `Branded`. - -`dsh-paths` gives every package the same configurable Harness home without assigning that cross-cutting fact to bash, skills, telemetry, or a composition bundle. It resolves an explicit value before `$DSH_HOME`, falls back to `~/.dsh`, and returns an absolute path without caching, creating, or mutating anything. The harness keeps all user data under one root. - -`dsh-timeout` follows the same shape for the timeout family: `dsh-bash` and `dsh-web-fetch-local` each fuse a caller's cancellation with a deadline and later classify "timed out" vs "cancelled" by depending on `dsh-timeout` alone. It deliberately owns only the timing/classification half — the *termination* (SIGKILL a process group, tear down a fetch socket) stays in each capability, because no shared layer can own every capability's kill (see [the timeout-library Agent Note](../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md)). - -`dsh-retention` is the same split for bounded tool output: a tool (`glob`/`grep`/`bash`/`web_fetch`/`web_search`) feeds items or text into a retainer and gets back what it kept and exactly what it omitted — while grouping, exit codes, provider errors, and recovery prose stay tool-owned. It deliberately owns only the retention mechanic; `truncated` is a budget fact, never an "incomplete inspection" state (see [the retention-library Agent Note](../../.agents/notes/implemented/architecture/2026-07-06-tool-result-retention-library.md)). +| [`brand/`](brand/README.md) | Provides nominally branded types | +| [`paths/`](paths/README.md) | Resolves the Harness data root and shared paths | +| [`timeout/`](timeout/README.md) | Provides deadline and timeout classification primitives | +| [`retention/`](retention/README.md) | Bounds retained text and item collections | +| [`atomic-write/`](atomic-write/README.md) | Replaces files atomically | +| [`native-command/`](native-command/README.md) | Runs host-native commands without a shell | diff --git a/packages/web/README.md b/packages/web/README.md index 8cd173b922..65b3dde1e2 100644 --- a/packages/web/README.md +++ b/packages/web/README.md @@ -1,18 +1,16 @@ -# web/ - web capability family +# web/ — web capability family English | [中文](README.zh.md) -The web access capability seam: an abstract web interface, search/fetch provider implementations, and the model-facing web tools. All **product** packages. +This family provides provider-neutral web search and fetch operations plus the model-facing tools that consume them. | Package | Role | ctx key | |---|---|---| -| `web/` | Abstract web seam (search/fetch provider registries + selection + vocabulary + `WebError`) | `ctx.web` | -| `web-search-exa/` | Exa-backed `WebSearchProvider` | (registers on `ctx.web`) | -| `web-search-perplexity/` | Perplexity-backed `WebSearchProvider` | (registers on `ctx.web`) | -| `web-search-deepseek/` | DeepSeek-backed `WebSearchProvider` using native `web_search` through the Anthropic-compatible API | (registers on `ctx.web`) | -| `web-fetch-local/` | Anonymous public HTTP(S) `WebFetchProvider` | (registers on `ctx.web`) | -| `tool-web/` | Model-facing `web_search`/`web_fetch` tool schemas | (registers on `ctx.tools`) | +| [`web/`](web/README.md) | Defines web provider registration, selection, and shared errors | `ctx.web` | +| [`web-search-exa/`](web-search-exa/README.md) | Provides web search through Exa | registers on `ctx.web` | +| [`web-search-perplexity/`](web-search-perplexity/README.md) | Provides web search through Perplexity | registers on `ctx.web` | +| [`web-search-deepseek/`](web-search-deepseek/README.md) | Provides native DeepSeek web search | registers on `ctx.web` | +| [`web-fetch-local/`](web-fetch-local/README.md) | Fetches public HTTP and HTTPS resources | registers on `ctx.web` | +| [`tool-web/`](tool-web/README.md) | Exposes web search and fetch to the model | registers on `ctx.tools` | -The interface lives at `web/web/`. Unlike bash/fs, the seam spans **two capabilities** (search and fetch) with potentially multiple providers each: `ctx.web` is one web-access middle layer with one provider-selection policy, one abort/error vocabulary, and one product-facing "how this harness reaches the web" config surface. Providers register **capabilities**, not tools; `tool-web` is the only owner of model-facing names, schemas, prompt guidance, and presentation. A search provider swap does not change how the model asks for a query, and a fetch implementation swap does not change how the model asks for a URL. - -See the [web capability seam Agent Note](../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md) for the design rationale, including why search and fetch are deliberately one seam and why `web_fetch`'s SSRF protection is deferred. +The [web capability decision](../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md) records why search and fetch share one provider-selection service. diff --git a/packages/workflow/README.md b/packages/workflow/README.md index 17da3397e4..2416e26e73 100644 --- a/packages/workflow/README.md +++ b/packages/workflow/README.md @@ -2,15 +2,13 @@ English | [中文](README.zh.md) -The workflow seam: a model-written JavaScript orchestration script that fans out subagents at scale (phases, structured per-agent results, concurrency caps), modeled on Claude Code's dynamic workflows. A capability seam (see [capability seams](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)) in the bash shape: ONE engine implementation per context registers as `ctx.workflows`; the model-facing tool consumes it. +This family runs model-authored orchestration workflows over subagents and exposes general and fixed-policy tools to the model. | Package | Role | ctx key | |---|---|---| -| `workflow/` | Abstract workflow seam: service base class + run vocabulary + `workflow/*` events | `ctx.workflows` | -| `workflow-workerthread/` | `node:worker_threads` engine: one worker per run; the script's vm context lives inside the worker, `agent()` bridges to `ctx.subagents` over the message port | (provides `ctx.workflows`) | -| `tool-workflow/` | Model-facing `workflow` tool over `ctx.workflows` | (registers on `ctx.tools`) | -| `tool-ralph/` | Fixed fresh-agent Ralph policy over `ctx.workflows` and a fresh structured-output subagent provider | (registers on `ctx.tools`) | +| [`workflow/`](workflow/README.md) | Defines workflow execution and lifecycle events | `ctx.workflows` | +| [`workflow-workerthread/`](workflow-workerthread/README.md) | Runs workflow scripts in worker threads | registers on `ctx.workflows` | +| [`tool-workflow/`](tool-workflow/README.md) | Exposes general workflow execution to the model | registers on `ctx.tools` | +| [`tool-ralph/`](tool-ralph/README.md) | Exposes the fixed fresh-agent Ralph workflow | registers on `ctx.tools` | -The interface lives at `workflow/workflow/`. The engine's `agent()` hook rides the [subagent seam](../subagent/README.md) (any registered provider; the shipped examples use `spawn`), and `agent({ schema })` rides the structured-output support the in-process backends implement. The worker thread isolates the SCRIPT — the host never blocks on it, and a cancelled run's post-grace termination is real — but it is NOT a security boundary; an isolated-vm/separate-process engine (actual sandboxing) swaps in behind the same interface if that ever matters. - -The general script engine's decisions and deferred work live in the [dynamic-workflows Agent Note](../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md). The separate [Ralph consumer](../../.agents/notes/implemented/feature/2026-07-19-fresh-agent-ralph-workflow-tool.md) fixes the script and fresh-provider policy rather than adding another engine or an agent-loop mode. +Worker threads isolate workflow execution from the host event loop but are not a security boundary. See the [dynamic-workflow](../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md) and [Ralph tool](../../.agents/notes/implemented/feature/2026-07-19-fresh-agent-ralph-workflow-tool.md) decisions. diff --git a/packages/workspace/README.md b/packages/workspace/README.md index ba92e95d3c..4b3b19b282 100644 --- a/packages/workspace/README.md +++ b/packages/workspace/README.md @@ -1,11 +1,11 @@ -# workspace/ — the workspace entity +# workspace/ — workspace entity family English | [中文](README.zh.md) -The workspace family owns the persistent workspace concept: a directory the user works in, with a title and the ordered list of sessions that belong to it. Design record: [domain KV storage Agent Note](../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.md). +This family owns persistent workspaces: user directories with titles and ordered session membership. | Package | Role | ctx key | |---|---|---| -| `workspace/` | `WorkspaceRegistry` service over the storage domain form: realpath-unique paths, session-ownership accounting, entity cache | `ctx.workspace` | +| [`workspace/`](workspace/README.md) | Registers workspaces and accounts for their sessions | `ctx.workspace` | -Ownership truth lives in the workspace record's `sessionIds` (ordered), never derived from session cwd; `attachSession` verifies the session header's cwd resolves to the workspace path, so one session structurally belongs to at most one workspace. Deleting a Workspace removes only this registry record and account: directories, user files, and session logs remain, and the Sessions become Ungrouped ([decision](../../.agents/notes/implemented/feature/2026-07-27-workspace-registration-deletion.md)). +Workspace deletion removes the registry record, not user files or session logs. See the [workspace lifecycle decision](../../.agents/notes/implemented/feature/2026-07-27-workspace-registration-deletion.md) and [storage design](../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.md). diff --git a/python/README.i18n.yaml b/python/README.i18n.yaml index d0b46a7167..29806c7c41 100644 --- a/python/README.i18n.yaml +++ b/python/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write python/README.md -README.md: dfd9d909122f9245a19fe91d8a394156795b13ae -README.zh.md: b4b2b472e3164b5bf1fd2cfd77512de5be354f61 +README.md: 010d922b52eae0be184baec063e307b5c8e55819 +README.zh.md: ebf773d943e3ad3248fb41c58ca7e44950bb9d7f diff --git a/python/README.md b/python/README.md index dfd9d90912..010d922b52 100644 --- a/python/README.md +++ b/python/README.md @@ -2,75 +2,19 @@ English | [中文](README.zh.md) -Python packages for driving DeepSeek Harness as a subprocess: a client SDK that spawns the `dsh-jsonrpc-agent` binary and talks newline-delimited JSON-RPC over stdio. The runtime carrier is the single-file executable produced by this repo; design, build, and acceptance details live in [.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md](../.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md). +Python packages for driving DeepSeek Harness as a subprocess. The client SDK communicates with the bundled runtime over newline-delimited JSON-RPC on stdio. ## Packages | Directory | Dist / module | Role | |---|---|---| -| [sdk](sdk/) | `deepseek-harness` / `deepseek_harness` | Client SDK: the `DeepSeekHarness` high-level turns API and the lower-level `HarnessClient` JSON-RPC client | -| [sdk-runtime](sdk-runtime/) | `deepseek-harness-runtime-bin` / `deepseek_harness_runtime` | Runtime carrier: locates the bundled runtime binaries and ships the default agent configuration | +| [sdk](sdk/README.md) | `deepseek-harness` / `deepseek_harness` | High-level turns API and lower-level JSON-RPC client | +| [sdk-runtime](sdk-runtime/README.md) | `deepseek-harness-runtime-bin` / `deepseek_harness_runtime` | Bundled runtime binaries and default agent configuration | -## Building the runtime executable +## Behavior -The platform executables are build artifacts, not checked into git. From the repo root: +The SDK starts the matching bundled runtime unless the caller selects an explicit channel. The client owns channel selection and default-configuration injection; the runtime itself always requires an explicit configuration. The [SDK reference](sdk/README.md) and [runtime carrier reference](sdk-runtime/README.md) own the complete resolution and configuration contracts. -```sh -pnpm install -pnpm exec tsx scripts/build-exe-for-python-sdk.ts # host platform, ~2 min -pnpm exec tsx scripts/build-exe-for-python-sdk.ts --skip-build # lib/ artifacts already built -pnpm exec tsx scripts/build-exe-for-python-sdk.ts --targets=node24-linux-x64,node24-linux-arm64,node24-macos-arm64 -``` +## Contributor workflows -Products land in `dist-exe/` and are synced into this package as `sdk-runtime/src/deepseek_harness_runtime/runtime/dsh-jsonrpc-agent-pkg--` (platform: `linux`/`macos`; arch: `x64`/`arm64`); macOS builds also sync the matching `-spawn-helper` required by `node-pty`. After a local build the SDK finds the runtime with no further setup. The `build-exe-for-python-sdk` CI workflow (manual dispatch, or the `build-exe` PR label) exercises the same products. A full three-target run retains four release wheels; a subset dispatch retains the SDK wheel and selected platform wheels. Which plugins the exe bundles and how the carriers are organized: [sdk-runtime README](sdk-runtime/README.md); the build also refreshes the dev-only node carrier (see "against the Node source" below). - -## Validating the SDK against the executable - -```sh -export UV_PROJECT_ENVIRONMENT="$PWD/tmp/py-sdk-venv" # keep the venv out of python/ -uv sync --project python/sdk --group test -uv run --project python/sdk pytest python/sdk/tests/test_bundled_runtime.py # boots the real carriers -uv run --project python/sdk pytest # full suite; keyless tests included -``` - -For an interactive check (needs `DEEPSEEK_API_KEY` in the environment or the repo-root `.env`): - -```python -from deepseek_harness import DeepSeekHarness -with DeepSeekHarness() as harness: - print(harness.run("say hi").final_response) # auto-resolution picks the bundled exe -``` - -## Running the SDK against the Node source (no executable) - -Two flavors, both for repo members: - -- **Built node carrier** — set `DSH_RUNTIME_MODE=node` and the SDK runs `runtime/node/node_modules/@deepseek-ai/dsh-jsonrpc-demo/lib/bin.js` on the system Node (>= 22.19). The tree is refreshed on every build-script run and is the same dependency closure the exe snapshots, so plugin semantics are identical. Never auto-selected, never distributed. -- **Unbuilt source (tsx)** — point the client straight at the bin's TypeScript source for edit-run loops and debugging: `launch_args_override=("./node_modules/.bin/tsx", "packages/examples/jsonrpc-demo/src/bin.ts")` with `cwd` at the repo root, plus a config via `cordis=...` (or rely on the default-config injection). [sdk/tests/manual_sdk_agent_smoke.py](sdk/tests/manual_sdk_agent_smoke.py) is the worked example. - -## Distributing the Python packages - -The root [`package.json`](../package.json) version is authoritative for both Python distributions. The common staging script reads that version, injects it into both wheels, and pins the SDK metadata to the same `deepseek-harness-runtime-bin==X.Y.Z`; an optional `python-vX.Y.Z` release tag is accepted only when it matches the repository version. Build the pure SDK wheel once and one runtime wheel on each native platform: - -```sh -version="$(node -p "require('./package.json').version")" -python scripts/build-python-release.py --package sdk --output-dir dist-python -python scripts/build-python-release.py --package runtime --platform macos-arm64 --runtime-exe dist-exe/dsh-jsonrpc-agent-pkg-macos-arm64 --output-dir dist-python -pip install --find-links dist-python deepseek-harness=="$version" -``` - -The runtime distribution is wheel-only and rejects sdist builds, missing executables, and mixed-platform payloads. Its three wheel tags are `py3-none-manylinux_2_28_x86_64`, `py3-none-manylinux_2_28_aarch64`, and `py3-none-macosx_11_0_arm64`; the SDK remains `py3-none-any`. A matching `python-vX.Y.Z` tag pipeline builds these four non-conflicting files and publishes them together, so a normal `pip install deepseek-harness==X.Y.Z` selects the matching runtime wheel and `import deepseek_harness` needs no `runtime_bin`. - -## Zero-config semantics - -The runtime binary itself always requires an explicit config (`$DSH_CORDIS_CONFIG`, or a config path as the first argv argument), has no built-in fallback, and boots only what the config lists. Zero-config is SDK wrapper behavior: when the caller uses no explicit channel, the client injects the runtime package's checked-in default configuration ([runtime/cordis.yml](sdk-runtime/src/deepseek_harness_runtime/runtime/cordis.yml)) via `DSH_CORDIS_CONFIG`; any explicit channel wins and disables the injection. The full injection conditions live in the [sdk README](sdk/README.md); the default config's contents and the hard semantic in the [sdk-runtime README](sdk-runtime/README.md). - -The executable is also a supported direct interface; keep stdin open for the NDJSON JSON-RPC exchange and supply a config explicitly: - -```sh -DSH_CORDIS_CONFIG=/absolute/path/cordis.yml ./dsh-jsonrpc-agent-pkg-macos-arm64 -``` - -## Test layout - -`test_client.py` is fully keyless (a Python fake runtime is the peer). `test_bundled_runtime.py` boots each bundled carrier and skips per carrier when its artifact is missing. `test_runtime_resolution.py` covers the carrier-resolution rules without spawning anything. +The [Python contributor reference](development.md) covers building runtime artifacts, validating the packages, source-mode development, and distribution. diff --git a/python/README.zh.md b/python/README.zh.md index b4b2b472e3..ebf773d943 100644 --- a/python/README.zh.md +++ b/python/README.zh.md @@ -2,75 +2,19 @@ [English](README.md) | 中文 -以子进程方式驱动 DeepSeek Harness 的 Python 包:客户端 SDK 启动 `dsh-jsonrpc-agent` 二进制,并通过 stdio 上按行分隔的 JSON-RPC 与之通信。运行时载体是本仓库产出的单文件可执行文件;设计、构建与验收细节见 [.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md](../.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md)。 +用于以子进程方式驱动 DeepSeek Harness 的 Python 包。客户端 SDK 通过 stdio 上按行分隔的 JSON-RPC 与内置运行时通信。 ## 包 | 目录 | 分发名 / 模块 | 职责 | |---|---|---| -| [sdk](sdk/) | `deepseek-harness` / `deepseek_harness` | 客户端 SDK:高层轮次 API `DeepSeekHarness` 与低层 JSON-RPC 客户端 `HarnessClient` | -| [sdk-runtime](sdk-runtime/) | `deepseek-harness-runtime-bin` / `deepseek_harness_runtime` | 运行时载体:定位内置的运行时二进制,并携带默认的 agent(智能体)配置 | +| [sdk](sdk/README.md) | `deepseek-harness` / `deepseek_harness` | 高层轮次 API 与低层 JSON-RPC 客户端 | +| [sdk-runtime](sdk-runtime/README.md) | `deepseek-harness-runtime-bin` / `deepseek_harness_runtime` | 内置运行时二进制与默认 agent(智能体)配置 | -## 构建运行时可执行文件 +## 行为 -各平台可执行文件是构建产物,不检入 git。在仓库根目录执行: +除非调用方选择显式通道,否则 SDK 会启动匹配的内置运行时。客户端负责选择通道和注入默认配置;运行时本身始终要求显式配置。完整的解析与配置契约分别见 [SDK 参考](sdk/README.md)和[运行时载体参考](sdk-runtime/README.md)。 -```sh -pnpm install -pnpm exec tsx scripts/build-exe-for-python-sdk.ts # host platform, ~2 min -pnpm exec tsx scripts/build-exe-for-python-sdk.ts --skip-build # lib/ artifacts already built -pnpm exec tsx scripts/build-exe-for-python-sdk.ts --targets=node24-linux-x64,node24-linux-arm64,node24-macos-arm64 -``` +## 贡献者工作流 -产物落入 `dist-exe/`,并同步进本包的 `sdk-runtime/src/deepseek_harness_runtime/runtime/dsh-jsonrpc-agent-pkg--`(platform:`linux`/`macos`;arch:`x64`/`arm64`);macOS 构建还会同步 `node-pty` 所需的同名 `-spawn-helper` 伴随文件。本地构建完成后 SDK 不需要额外设置就能找到运行时。`build-exe-for-python-sdk` CI 工作流(手动触发,或给 PR 打 `build-exe` 标签)会测试同样的产物。完整构建三个目标时保留 4 个发布用 wheel 包;手动选择部分目标时保留 SDK wheel 与所选平台的 wheel。exe 内置哪些插件、载体如何组织,见 [sdk-runtime README](sdk-runtime/README.md);构建还会顺带刷新仅供开发使用的 `node` 载体(见下文「对着 Node 源码运行」)。 - -## 用可执行文件验证 SDK - -```sh -export UV_PROJECT_ENVIRONMENT="$PWD/tmp/py-sdk-venv" # keep the venv out of python/ -uv sync --project python/sdk --group test -uv run --project python/sdk pytest python/sdk/tests/test_bundled_runtime.py # boots the real carriers -uv run --project python/sdk pytest # full suite; keyless tests included -``` - -交互式验证(需要环境变量或仓库根 `.env` 中的 `DEEPSEEK_API_KEY`): - -```python -from deepseek_harness import DeepSeekHarness -with DeepSeekHarness() as harness: - print(harness.run("say hi").final_response) # auto-resolution picks the bundled exe -``` - -## 对着 Node 源码运行 SDK(不用可执行文件) - -两种方式,均面向仓库成员: - -- **已构建的 `node` 载体**——设置 `DSH_RUNTIME_MODE=node`,SDK 会用系统 Node(>= 22.19)运行 `runtime/node/node_modules/@deepseek-ai/dsh-jsonrpc-demo/lib/bin.js`。这棵树每次运行构建脚本都会刷新,与 exe 打入 pkg 虚拟文件系统(VFS)的是同一份依赖闭包,因此插件语义一致。它不会被自动选中,也不进入分发物。 -- **未构建的源码(tsx)**——把客户端直接指向 `bin` 的 TypeScript 源码,用于编辑、运行和调试:`launch_args_override=("./node_modules/.bin/tsx", "packages/examples/jsonrpc-demo/src/bin.ts")`,`cwd` 设为仓库根,再通过 `cordis=...` 传入配置(或使用默认配置注入)。[sdk/tests/manual_sdk_agent_smoke.py](sdk/tests/manual_sdk_agent_smoke.py) 是现成范例。 - -## 分发 Python 包 - -根目录 [`package.json`](../package.json) 的版本是两个 Python 分发物的权威版本。统一暂存脚本读取这个版本并注入两个 wheel 包,同时在 SDK 元数据中钉死相同版本的 `deepseek-harness-runtime-bin==X.Y.Z`;可选的 `python-vX.Y.Z` 发布标签只有与仓库版本匹配时才会被接受。纯 SDK wheel 包只构建一次,运行时 wheel 包则在每个原生平台各构建一个: - -```sh -version="$(node -p "require('./package.json').version")" -python scripts/build-python-release.py --package sdk --output-dir dist-python -python scripts/build-python-release.py --package runtime --platform macos-arm64 --runtime-exe dist-exe/dsh-jsonrpc-agent-pkg-macos-arm64 --output-dir dist-python -pip install --find-links dist-python deepseek-harness=="$version" -``` - -运行时分发物只提供 wheel 包,并拒绝 sdist 构建、缺失可执行文件以及混合平台载荷。三个 wheel 包标签分别是 `py3-none-manylinux_2_28_x86_64`、`py3-none-manylinux_2_28_aarch64` 与 `py3-none-macosx_11_0_arm64`;SDK 保持 `py3-none-any`。匹配的 `python-vX.Y.Z` 标签流水线统一构建并发布这 4 个互不冲突的文件,因此常规的 `pip install deepseek-harness==X.Y.Z` 会选中匹配平台的运行时 wheel 包,`import deepseek_harness` 不需要 `runtime_bin`。 - -## 零配置语义 - -运行时二进制本身始终要求显式配置(`$DSH_CORDIS_CONFIG`,或作为首个 argv 参数的配置路径),没有内置兜底,也只启动配置里列出的内容。零配置是 SDK 包装层的行为:调用方没有使用任何显式通道时,客户端把运行时包中检入的默认配置([runtime/cordis.yml](sdk-runtime/src/deepseek_harness_runtime/runtime/cordis.yml))注入 `DSH_CORDIS_CONFIG`;任一显式通道存在即优先采用,并禁用注入。注入条件的完整定义见 [sdk README](sdk/README.md),默认配置的内容与强制语义见 [sdk-runtime README](sdk-runtime/README.md)。 - -可执行文件也支持直接调用;在 NDJSON JSON-RPC 交互期间保持 stdin 打开,并显式提供配置: - -```sh -DSH_CORDIS_CONFIG=/absolute/path/cordis.yml ./dsh-jsonrpc-agent-pkg-macos-arm64 -``` - -## 测试布局 - -`test_client.py` 完全无需密钥(对端是 Python 假运行时)。`test_bundled_runtime.py` 逐个启动内置载体,某个载体产物缺失时跳过对应用例。`test_runtime_resolution.py` 覆盖载体解析规则,不启动任何进程。 +[Python 贡献者参考](development.md)介绍运行时产物构建、包验证、源码模式开发和分发。 diff --git a/python/development.i18n.yaml b/python/development.i18n.yaml new file mode 100644 index 0000000000..ec6e970c2c --- /dev/null +++ b/python/development.i18n.yaml @@ -0,0 +1,6 @@ +# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each +# side as of the last confirmed-consistent state. Both languages carry equal authority; +# after editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write python/development.md +development.md: f7cf69f798c08f8e2b558bb68431bc77ab220378 +development.zh.md: 53e31bc1455367f95260dccf5a2bcc7d2ed6e55d diff --git a/python/development.md b/python/development.md new file mode 100644 index 0000000000..f7cf69f798 --- /dev/null +++ b/python/development.md @@ -0,0 +1,61 @@ +# Python contributor workflows + +English | [中文](development.zh.md) + +Reference for building, validating, and distributing the Python SDK and bundled runtime. Package behavior belongs in the [SDK reference](sdk/README.md) and [runtime carrier reference](sdk-runtime/README.md). + +## Build runtime artifacts + +Platform executables are build artifacts and are not checked into git. Run the build from the repository root: + +```sh +pnpm install +pnpm exec tsx scripts/build-exe-for-python-sdk.ts +``` + +Use `--skip-build` when the required `lib/` artifacts already exist, or `--targets=node24-linux-x64,node24-linux-arm64,node24-macos-arm64` to select platforms. Products land in `dist-exe/` and the script syncs the selected carriers into `python/sdk-runtime/`. macOS builds also sync the matching spawn helper required by `node-pty`. + +## Validate the SDK + +Keep the virtual environment outside `python/`, install the test group, and run the Python suite: + +```sh +export UV_PROJECT_ENVIRONMENT="$PWD/tmp/py-sdk-venv" +uv sync --project python/sdk --group test +uv run --project python/sdk pytest +``` + +`python/sdk/tests/test_bundled_runtime.py` exercises available bundled carriers and skips a carrier when its artifact has not been built. For repository-wide test policy, see [Testing](../docs/testing.md). + +An interactive smoke test needs `DEEPSEEK_API_KEY` in the environment or repository-root `.env`: + +```python +from deepseek_harness import DeepSeekHarness + +with DeepSeekHarness() as harness: + print(harness.run("say hi").final_response) +``` + +## Run against Node source + +Repository contributors can select either development carrier: + +- Set `DSH_RUNTIME_MODE=node` to use the built Node carrier on system Node `>=22.19`. The build script refreshes this carrier, but distributions never include or auto-select it. +- Set `launch_args_override=("./node_modules/.bin/tsx", "packages/examples/jsonrpc-demo/src/bin.ts")` with the repository root as `cwd` to run unbuilt TypeScript source. Supply `cordis=...` when the default configuration is not suitable. + +See `python/sdk/tests/manual_sdk_agent_smoke.py` for a complete source-mode invocation. + +## Build distributions + +The root `package.json` version is authoritative for both Python distributions. The staging script injects that version into both wheels and pins the SDK to the same `deepseek-harness-runtime-bin` version. + +Build the pure SDK wheel once and one runtime wheel on each native platform: + +```sh +version="$(node -p "require('./package.json').version")" +python scripts/build-python-release.py --package sdk --output-dir dist-python +python scripts/build-python-release.py --package runtime --platform macos-arm64 --runtime-exe dist-exe/dsh-jsonrpc-agent-pkg-macos-arm64 --output-dir dist-python +pip install --find-links dist-python deepseek-harness=="$version" +``` + +The runtime distribution is wheel-only. The release pipeline publishes three platform wheels with the pure SDK wheel: Linux x64, Linux arm64, and macOS arm64. A `python-vX.Y.Z` tag is accepted only when it matches the repository version. diff --git a/python/development.zh.md b/python/development.zh.md new file mode 100644 index 0000000000..53e31bc145 --- /dev/null +++ b/python/development.zh.md @@ -0,0 +1,61 @@ +# Python 贡献者工作流 + +[English](development.md) | 中文 + +构建、验证和分发 Python SDK 与内置运行时的参考。包行为分别见 [SDK 参考](sdk/README.md)和[运行时载体参考](sdk-runtime/README.md)。 + +## 构建运行时产物 + +各平台可执行文件是构建产物,不检入 git。请在仓库根目录运行构建: + +```sh +pnpm install +pnpm exec tsx scripts/build-exe-for-python-sdk.ts +``` + +所需 `lib/` 产物已存在时使用 `--skip-build`;如需选择平台,请使用 `--targets=node24-linux-x64,node24-linux-arm64,node24-macos-arm64`。产物写入 `dist-exe/`,脚本会将所选载体同步到 `python/sdk-runtime/`。macOS 构建还会同步 `node-pty` 所需的配套 spawn helper。 + +## 验证 SDK + +请将虚拟环境放在 `python/` 之外,安装测试组,然后运行 Python 测试套件: + +```sh +export UV_PROJECT_ENVIRONMENT="$PWD/tmp/py-sdk-venv" +uv sync --project python/sdk --group test +uv run --project python/sdk pytest +``` + +`python/sdk/tests/test_bundled_runtime.py` 会运行可用的内置载体;某个载体的产物尚未构建时,会跳过该载体。仓库级测试政策见[测试](../docs/testing.md)。 + +交互式冒烟测试需要环境变量或仓库根目录 `.env` 中存在 `DEEPSEEK_API_KEY`: + +```python +from deepseek_harness import DeepSeekHarness + +with DeepSeekHarness() as harness: + print(harness.run("say hi").final_response) +``` + +## 针对 Node 源码运行 + +仓库贡献者可以选择以下任一开发载体: + +- 设置 `DSH_RUNTIME_MODE=node`,在系统 Node `>=22.19` 上使用已构建的 Node 载体。构建脚本会刷新该载体,但分发物绝不会包含或自动选择它。 +- 将仓库根目录设为 `cwd`,并设置 `launch_args_override=("./node_modules/.bin/tsx", "packages/examples/jsonrpc-demo/src/bin.ts")`,以运行未构建的 TypeScript 源码。默认配置不合适时,请提供 `cordis=...`。 + +完整的源码模式调用见 `python/sdk/tests/manual_sdk_agent_smoke.py`。 + +## 构建分发物 + +根目录 `package.json` 的版本是两个 Python 分发物的权威版本。暂存脚本会将该版本注入两个 wheel 包,并将 SDK 固定到同版本的 `deepseek-harness-runtime-bin`。 + +纯 SDK wheel 包只需构建一次;每个原生平台分别构建一个运行时 wheel 包: + +```sh +version="$(node -p "require('./package.json').version")" +python scripts/build-python-release.py --package sdk --output-dir dist-python +python scripts/build-python-release.py --package runtime --platform macos-arm64 --runtime-exe dist-exe/dsh-jsonrpc-agent-pkg-macos-arm64 --output-dir dist-python +pip install --find-links dist-python deepseek-harness=="$version" +``` + +运行时分发物仅提供 wheel 包。发布流水线会连同纯 SDK wheel 包一起发布三个平台 wheel 包:Linux x64、Linux arm64 和 macOS arm64。只有与仓库版本匹配时,才接受 `python-vX.Y.Z` 标签。 From 5634c08088d0e2fe5b0bc3ed885d1dc3bb9f0ee2 Mon Sep 17 00:00:00 2001 From: Turtle Date: Tue, 4 Aug 2026 10:06:45 +0800 Subject: [PATCH 3/7] docs: restore architecture turn flow --- docs/architecture.md | 45 +++++++++++++++++++++++++++++++++++++---- docs/architecture.zh.md | 45 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index d056b716e9..9c3b0a2f13 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -73,11 +73,48 @@ A **session** is append-only. An ordinary **turn** claims one queued `send()` it ### Turn Flow ```text -queued input -> prompt admission -> turn/start - -> [agent/step -> request -> chunks/message -> tools -> step/end]* - -> turn/end +choose declarative identity and fresh/resume path + -> prepare private session + agent.ctx -> await unpublished setup -> invoke optional synchronous setup commit + -> enter session + agent -> session/created -> agent/created + -> enable driving -> agent/session-start(source) -> start driver +forever: + wait for queued occurrence + claim (edit/remove end) -> emit agent/status(running) if starting an interval + open the next-step acceptance window + -> agent/prompt-submit + blocked or failed prompt -> close the window without opening a turn + append a context-only caller batch immediately + keep steering and context staged beside it pending for a later admitted turn + allowed prompt: + 'turn/start' + append prompt + additional contexts as separate 'user/message' events + STEP loop: + agent/step + assemble system prompt and tools + materialize changed runtime context as sourced 'user/message' + drain injected context and provisional steering (steering bypasses prompt-submit) + snapshot the derived messages (the reconstruction boundary) + 'step/start' + admit the drained steering receipts + agent/request (config only) -> prepare adapter defaults/provenance + context capacity under turn signal -> log request/header (+ request/context on route change) -> llm/stream (frozen, registration-bound) + 'assistant/chunk' + 'assistant/message' + schedule tool calls by ctx.tools.executionMode: + exclusive -> barrier + parallel -> rolling pool, <= maxParallelToolCalls; reclassify-at-start; scheduler failure -> stop starts, drain dispatches + start -> 'tool/call' -> ordered tools/pre-execute -> concurrent tools/execute + model-order result -> ordered tools/post-execute -> 'tool/result' + drain accepted tool context after all results; keep steering provisional + 'step/end' + continue for tools or steering unless a result concluded the turn and rejects pending steering + otherwise agent/turn-stopping -> drain context -> continue only for steering + close the next-step acceptance window + 'turn/end' -> agent/settled + start the next waking queued message, or emit agent/status(idle) -idle injection -> user/message +idle inject: + append 'user/message' + do not open a turn or run the model ``` Each step assembles the prompt, tools, runtime context, adapter settings, and model history before recording its reconstruction boundary. Tool calls then run through the shared execution pipeline. `inject()` adds context without opening an idle turn; `steer()` targets a next-step admission window; queued input remains the source of ordinary turns. The generated [agent lifecycle](agent-lifecycle.md) owns exact event order, and the [agent-loop README](../packages/core/agent-loop/README.md) owns queue, steering, retry, and cancellation mechanics. diff --git a/docs/architecture.zh.md b/docs/architecture.zh.md index e9e0dc4c20..b4ff801156 100644 --- a/docs/architecture.zh.md +++ b/docs/architecture.zh.md @@ -73,11 +73,48 @@ waterfall(瀑布式事件)是环绕中间件:监听器通过 `next()` 委 ### 轮次流程 ```text -queued input -> prompt admission -> turn/start - -> [agent/step -> request -> chunks/message -> tools -> step/end]* - -> turn/end +choose declarative identity and fresh/resume path + -> prepare private session + agent.ctx -> await unpublished setup -> invoke optional synchronous setup commit + -> enter session + agent -> session/created -> agent/created + -> enable driving -> agent/session-start(source) -> start driver +forever: + wait for queued occurrence + claim (edit/remove end) -> emit agent/status(running) if starting an interval + open the next-step acceptance window + -> agent/prompt-submit + blocked or failed prompt -> close the window without opening a turn + append a context-only caller batch immediately + keep steering and context staged beside it pending for a later admitted turn + allowed prompt: + 'turn/start' + append prompt + additional contexts as separate 'user/message' events + STEP loop: + agent/step + assemble system prompt and tools + materialize changed runtime context as sourced 'user/message' + drain injected context and provisional steering (steering bypasses prompt-submit) + snapshot the derived messages (the reconstruction boundary) + 'step/start' + admit the drained steering receipts + agent/request (config only) -> prepare adapter defaults/provenance + context capacity under turn signal -> log request/header (+ request/context on route change) -> llm/stream (frozen, registration-bound) + 'assistant/chunk' + 'assistant/message' + schedule tool calls by ctx.tools.executionMode: + exclusive -> barrier + parallel -> rolling pool, <= maxParallelToolCalls; reclassify-at-start; scheduler failure -> stop starts, drain dispatches + start -> 'tool/call' -> ordered tools/pre-execute -> concurrent tools/execute + model-order result -> ordered tools/post-execute -> 'tool/result' + drain accepted tool context after all results; keep steering provisional + 'step/end' + continue for tools or steering unless a result concluded the turn and rejects pending steering + otherwise agent/turn-stopping -> drain context -> continue only for steering + close the next-step acceptance window + 'turn/end' -> agent/settled + start the next waking queued message, or emit agent/status(idle) -idle injection -> user/message +idle inject: + append 'user/message' + do not open a turn or run the model ``` 每个步骤都会组装提示词、工具、运行时上下文、适配器设置和模型历史,随后记录其重建边界。之后,工具调用通过共享执行流水线运行。`inject()` 添加上下文但不打开空闲轮次;`steer()` 针对下一步骤的准入窗口;排队输入仍是普通轮次的来源。精确事件顺序由生成的 [agent 生命周期](agent-lifecycle.md)定义;队列、steering、重试与取消机制由 [agent-loop README](../packages/core/agent-loop/README.md)定义。 From 012bb0a54977f1c0c1167fdc58cab634b3f7a88c Mon Sep 17 00:00:00 2001 From: Turtle Date: Tue, 4 Aug 2026 10:48:36 +0800 Subject: [PATCH 4/7] docs: address review feedback --- apps/cli/reference/README.i18n.yaml | 6 +++++ .../cli/{reference.md => reference/README.md} | 24 +++++++++---------- .../README.zh.md} | 24 +++++++++---------- docs/architecture.md | 2 +- docs/architecture.zh.md | 2 +- 5 files changed, 32 insertions(+), 26 deletions(-) create mode 100644 apps/cli/reference/README.i18n.yaml rename apps/cli/{reference.md => reference/README.md} (72%) rename apps/cli/{reference.zh.md => reference/README.zh.md} (68%) diff --git a/apps/cli/reference/README.i18n.yaml b/apps/cli/reference/README.i18n.yaml new file mode 100644 index 0000000000..ff9cab7b06 --- /dev/null +++ b/apps/cli/reference/README.i18n.yaml @@ -0,0 +1,6 @@ +# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each +# side as of the last confirmed-consistent state. Both languages carry equal authority; +# after editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write apps/cli/reference/README.md +README.md: 6c83dd8312cf797614b03e8632b72fd1133577d3 +README.zh.md: 00016ebc2f2cffa489072695cdc280ed2d875145 diff --git a/apps/cli/reference.md b/apps/cli/reference/README.md similarity index 72% rename from apps/cli/reference.md rename to apps/cli/reference/README.md index fc86a04b88..6c83dd8312 100644 --- a/apps/cli/reference.md +++ b/apps/cli/reference/README.md @@ -1,22 +1,22 @@ # `dsh` CLI behavior reference -English | [中文](reference.zh.md) +English | [中文](README.zh.md) -Argv is parsed once through a [Commander](https://github.com/tj/commander.js) adapter ([`src/args.ts`](src/args.ts)): one program whose default (no subcommand) is the TUI/headless surface (`--config`, `-p`/`--prompt`, `--resume`, `--dump-config`, `--dump-default-config`), whose `meta` subcommand is the same TUI over this checkout, whose `upgrade` subcommand is a guided-session entry, and whose `web` subcommand is the browser UI. `meta` and `upgrade` are experimental: each runs only with its `--experimental` flag or with `DSH_EXPERIMENTAL=1` in the environment, and fails loud (stderr, exit 1) otherwise. `src/bin.ts` switches on the resolved mode and dynamic-imports only that mode's module. `dsh --help` lists every mode and `dsh web --help` renders the web usage, `dsh --version` prints this app's version, and an unknown option or a mistyped `--resume` fails loud (stderr, exit 1) instead of misrouting. Every subcommand that shares no option with the default surface — `upgrade`, `web`, `meta` — rejects a leaked `--config`/`-p`/`--resume`/dump flag rather than running and dropping it. `dsh web`'s `--host`/`--port` are unvalidated pass-through overrides: the `dsh-host-webserver` schema is the single source of both the default (the shipped Web overlay value when a flag is absent) and validity, and rejects a bad value at boot. `--trusted-host` appends named authorities for the /api browser-trust fence; an all-interfaces bind additionally derives the machine's LAN IP literals itself ([`src/app-cli-entry.ts`](src/app-cli-entry.ts)), so the printed LAN URL works without flags. +Argv is parsed once through a [Commander](https://github.com/tj/commander.js) adapter ([`src/args.ts`](../src/args.ts)): one program whose default (no subcommand) is the TUI/headless surface (`--config`, `-p`/`--prompt`, `--resume`, `--dump-config`, `--dump-default-config`), whose `meta` subcommand is the same TUI over this checkout, whose `upgrade` subcommand is a guided-session entry, and whose `web` subcommand is the browser UI. `meta` and `upgrade` are experimental: each runs only with its `--experimental` flag or with `DSH_EXPERIMENTAL=1` in the environment, and fails loud (stderr, exit 1) otherwise. `src/bin.ts` switches on the resolved mode and dynamic-imports only that mode's module. `dsh --help` lists every mode and `dsh web --help` renders the web usage, `dsh --version` prints this app's version, and an unknown option or a mistyped `--resume` fails loud (stderr, exit 1) instead of misrouting. Every subcommand that shares no option with the default surface — `upgrade`, `web`, `meta` — rejects a leaked `--config`/`-p`/`--resume`/dump flag rather than running and dropping it. `dsh web`'s `--host`/`--port` are unvalidated pass-through overrides: the `dsh-host-webserver` schema is the single source of both the default (the shipped Web overlay value when a flag is absent) and validity, and rejects a bad value at boot. `--trusted-host` appends named authorities for the /api browser-trust fence; an all-interfaces bind additionally derives the machine's LAN IP literals itself ([`src/app-cli-entry.ts`](../src/app-cli-entry.ts)), so the printed LAN URL works without flags. The TUI surface: -- boots `base.cordis.yml` plus `tui.cordis.yml` through [`dsh-app-boot`](../../packages/ui/app-boot/README.md); `--config ` applies a patch-list overlay instead of the personal overlay, while `--config-replace ` boots that file as the complete tree; -- resumes a persisted session with `dsh --resume ` and, when the Node host exposes `process.execve`, supplies the TUI's in-place handoff host: after selector preflight and current-session flush, the host disposes the app and replaces the process with a normalized resume invocation; runtimes without process replacement leave the session running and say so. This CLI owns session identity and the exit line rather than the config: it mints or selects the `main` session id and provides it, plus the exact command that reproduces this invocation, on the boot context ([`MAIN_SESSION_ID_KEY`](../../packages/ui/tui/README.md) and `TUI_GOODBYE_MESSAGE_KEY`). No `cordis.yml` key can drop resume, and a missing or unreadable id fails loud instead of creating a fresh session; +- boots `base.cordis.yml` plus `tui.cordis.yml` through [`dsh-app-boot`](../../../packages/ui/app-boot/README.md); `--config ` applies a patch-list overlay instead of the personal overlay, while `--config-replace ` boots that file as the complete tree; +- resumes a persisted session with `dsh --resume ` and, when the Node host exposes `process.execve`, supplies the TUI's in-place handoff host: after selector preflight and current-session flush, the host disposes the app and replaces the process with a normalized resume invocation; runtimes without process replacement leave the session running and say so. This CLI owns session identity and the exit line rather than the config: it mints or selects the `main` session id and provides it, plus the exact command that reproduces this invocation, on the boot context ([`MAIN_SESSION_ID_KEY`](../../../packages/ui/tui/README.md) and `TUI_GOODBYE_MESSAGE_KEY`). No `cordis.yml` key can drop resume, and a missing or unreadable id fails loud instead of creating a fresh session; - treats the **invoking directory** as the workspace — sessions, relative paths, and workspace instructions resolve from the cwd (`dsh meta` is the sole exception, below); - tells the agent where its own source lives: after boot it adds a prompt section naming this harness checkout, resolved from the launcher's real path so it holds under a PATH symlink and an arbitrary cwd, so the self-referential `cordis` toolset can read and modify it; -- applies the personal overlay from `~/.dsh` (see [app-boot's Personal config](../../packages/ui/app-boot/README.md#personal-config)): `config.yaml` patches the booted tree, while `.env` there is the credential provider's own store (never hoisted into the environment, so keys stay rotatable). Environment precedence is ambient > project `.env`. The shipped tree's Cordis HMR keeps `config.yaml` live; an explicit `--config` tree replaces that overlay, and a tree without HMR reads it at startup only. -- presents the [versioned first-run welcome](../../.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md) through the mounted TUI overlay service when its immutable marker is absent under `DSH_HOME`; only Enter creates that version's marker, while Escape, disposal, or process exit leaves it eligible. The official DeepSeek icon, responsive terminal rasters, all-locale Chinese copy, and notice version are static local owners; the overlay never writes a session event or model context. +- applies the personal overlay from `~/.dsh` (see [app-boot's Personal config](../../../packages/ui/app-boot/README.md#personal-config)): `config.yaml` patches the booted tree, while `.env` there is the credential provider's own store (never hoisted into the environment, so keys stay rotatable). Environment precedence is ambient > project `.env`. The shipped tree's Cordis HMR keeps `config.yaml` live; an explicit `--config` tree replaces that overlay, and a tree without HMR reads it at startup only. +- presents the [versioned first-run welcome](../../../.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md) through the mounted TUI overlay service when its immutable marker is absent under `DSH_HOME`; only Enter creates that version's marker, while Escape, disposal, or process exit leaves it eligible. The official DeepSeek icon, responsive terminal rasters, all-locale Chinese copy, and notice version are static local owners; the overlay never writes a session event or model context. - registers bare `/compact`: while the agent is idle, it summarizes useful older history even below automatic pressure, rejects arguments, and reports success only after the standalone replacement bracket is durable. A prompt submitted during compaction keeps its queue identity and starts after that checkpoint; injected context remains visible. `dsh meta` is that same TUI with this harness checkout as the workspace, so working on dsh itself needs no `cd`. It chdirs to the checkout root — resolved from the launcher's real path, the same root the source-path prompt section names — after the environment is settled, so precedence is unchanged while the session cwd and HMR watch root move together. Meta always starts a fresh session and accepts no default-surface options; use ordinary `dsh --resume ` to resume a persisted session. -`dsh upgrade` is a guided fresh-session entry over the default TUI surface: it mints a fresh session in the invoking directory and seeds its first turn with the bundled `dsh-upgrade` skill, exactly as if the user typed `/skill:`. The launcher passes the skill name on the boot context ([`INITIAL_SKILL_KEY`](../../packages/ui/tui/README.md)), which the TUI auto-invokes once the chat is live. The command takes no options beyond the experimental gate — `--config`, `-p`, and `--resume` fail loud — and seeds only on this first launch, so a later `dsh --resume ` of the session is an ordinary TUI session with no re-injection. +`dsh upgrade` is a guided fresh-session entry over the default TUI surface: it mints a fresh session in the invoking directory and seeds its first turn with the bundled `dsh-upgrade` skill, exactly as if the user typed `/skill:`. The launcher passes the skill name on the boot context ([`INITIAL_SKILL_KEY`](../../../packages/ui/tui/README.md)), which the TUI auto-invokes once the chat is live. The command takes no options beyond the experimental gate — `--config`, `-p`, and `--resume` fail loud — and seeds only on this first launch, so a later `dsh --resume ` of the session is an ordinary TUI session with no re-injection. `dsh --dump-config` and `dsh web --dump-config` print the composed config tree — the shipped base, the surface overlay, and the `--config` or personal overlay, exactly the layers that surface would boot — as YAML on stdout and exit without booting; `--dump-default-config` stops at the surface overlay, so diffing the two shows precisely what the user layer changes. Each run of rows is preceded by a `# ==` comment naming the file it comes from and the layers that patched it (e.g. `# == base.cordis.yml, patched by tui.cordis.yml`), so the output shows provenance while staying one loadable document. Composition runs through the include's own patch algorithm and YAML dialect (`applyEntryPatches`/`entryListSchema` from `@cordisjs/plugin-include`), so the dump cannot drift from what boots; `!!js` expressions print verbatim and unevaluated, and a patch whose target row is absent is reported on stderr with its layer, mirroring the Loader's boot-time warning. Launcher-owned boot-context values (session identity, CLI-flag patches) are per-invocation facts outside the config tree and do not appear. The dump flags reject boot-only flags (`-p`, `--resume`, `--config-replace`) rather than silently ignoring them, and `--dump-default-config` takes no `--config`. @@ -34,15 +34,15 @@ All three surfaces consume `$DSH_HOME/config.yaml`; the TUI and Web apply valid - 'github:PolyArch/humanize#' ``` -The repository must contain a prepared `.dsh-plugin` package; the [repository Plugin contract](../../packages/cordis/repository-plugin/README.md#standalone-app-configuration) documents authoring, nested Plugin paths, the immutable cache, trust boundary, and failure semantics. A failed live edit keeps the last good tree and emits Cordis's `hmr/config-update-failed` event. +The repository must contain a prepared `.dsh-plugin` package; the [repository Plugin contract](../../../packages/cordis/repository-plugin/README.md#standalone-app-configuration) documents authoring, nested Plugin paths, the immutable cache, trust boundary, and failure semantics. A failed live edit keeps the last good tree and emits Cordis's `hmr/config-update-failed` event. The shipped TUI and Web compositions register the native DeepSeek adapter plus pi-ai OpenAI and Anthropic profiles. Credentials and endpoint overrides come from the provider-standard `DEEPSEEK_API_KEY` / `DEEPSEEK_BASE_URL`, `OPENAI_API_KEY` / `OPENAI_BASE_URL`, and `ANTHROPIC_API_KEY` / `ANTHROPIC_BASE_URL` pairs in the boot's layered environment. -Every surface also registers `web_search` and only `web_search`. Search uses DeepSeek's Anthropic-compatible Messages endpoint, resolves the same `DEEPSEEK_API_KEY` reference for every call, and accepts the separate `DEEPSEEK_SEARCH_BASE_URL` endpoint override; each search is an auxiliary model request with its own latency and token cost. `web_fetch` remains disabled and the composition mounts no default fetch provider, so deployments that need arbitrary page retrieval must opt in through an overlay. The deployment decision and its security boundary live in the [default Web search Agent Note](../../.agents/notes/implemented/feature/2026-07-31-web-default-search.md). +Every surface also registers `web_search` and only `web_search`. Search uses DeepSeek's Anthropic-compatible Messages endpoint, resolves the same `DEEPSEEK_API_KEY` reference for every call, and accepts the separate `DEEPSEEK_SEARCH_BASE_URL` endpoint override; each search is an auxiliary model request with its own latency and token cost. `web_fetch` remains disabled and the composition mounts no default fetch provider, so deployments that need arbitrary page retrieval must opt in through an overlay. The deployment decision and its security boundary live in the [default Web search Agent Note](../../../.agents/notes/implemented/feature/2026-07-31-web-default-search.md). `DSH_TOOLS_MODE` selects the tool presentation mode for the whole Web/headless process: `native` (the schema default when unset), `code` (the `run_code`-only Code Mode wire), or `both`; any other value fails loud at boot through the `dsh-tools` config schema. The setting is process-wide because Loader composition is static. The TUI surface ignores it and pins `native`. -[`core-web.cordis.yml`](config/core-web.cordis.yml) is an opt-in `dsh web --config` overlay that keeps the shipped Web host, browser, Workspace, persistence, and permission composition while reducing the default native model surface to owner-scoped persistent `bash` and `str_replace_editor`. The PTY backend and editor consume the existing Web sandbox and filesystem providers. An open persistent shell prevents changing that session's permission mode until the shell closes, so a shell created under wider access cannot survive a downgrade. `DSH_TOOLS_MODE` still controls native/Code Mode presentation for the resulting two-tool registry. +[`core-web.cordis.yml`](../config/core-web.cordis.yml) is an opt-in `dsh web --config` overlay that keeps the shipped Web host, browser, Workspace, persistence, and permission composition while reducing the default native model surface to owner-scoped persistent `bash` and `str_replace_editor`. The PTY backend and editor consume the existing Web sandbox and filesystem providers. An open persistent shell prevents changing that session's permission mode until the shell closes, so a shell created under wider access cannot survive a downgrade. `DSH_TOOLS_MODE` still controls native/Code Mode presentation for the resulting two-tool registry. From a source checkout, start this minimal Web profile with: @@ -50,7 +50,7 @@ From a source checkout, start this minimal Web profile with: pnpm run dsh web --config apps/cli/config/core-web.cordis.yml ``` -Every `dsh` surface — TUI, Web, and headless — reports session telemetry by default (the row lives in the shared `base.cordis.yml`): every session-log event streams as OTLP/HTTP log records to `https://harness-telemetry.deepseeksvc.com/v1/logs` on a 10-second batch cadence. `DSH_TELEMETRY_OTLP_URL` points the exporter at a different collector; setting `DSH_TELEMETRY_DISABLED` to ANY non-empty value — including `0` or `false` — disables the row before it loads (a privacy switch prefers off-by-mistake over on-by-mistake). No redaction rule is mounted in this composition yet: exported records are the raw captured copy, including message text, tool arguments and results, and the session's working-directory path. The deployment rulings live in the [web-telemetry-default-mount Agent Note](../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md). +Every `dsh` surface — TUI, Web, and headless — reports session telemetry by default (the row lives in the shared `base.cordis.yml`): every session-log event streams as OTLP/HTTP log records to `https://harness-telemetry.deepseeksvc.com/v1/logs` on a 10-second batch cadence. `DSH_TELEMETRY_OTLP_URL` points the exporter at a different collector; setting `DSH_TELEMETRY_DISABLED` to ANY non-empty value — including `0` or `false` — disables the row before it loads (a privacy switch prefers off-by-mistake over on-by-mistake). No redaction rule is mounted in this composition yet: exported records are the raw captured copy, including message text, tool arguments and results, and the session's working-directory path. The deployment rulings live in the [web-telemetry-default-mount Agent Note](../../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md). MCP servers are not a shipped default, because a default would have to name one: `@deepseek-ai/dsh-mcp-client` mounts exactly one server per row and spawns it as a child process, outside `ctx.bash` and so outside the sandbox policy. The package is a runtime dependency of this CLI, so an installed `dsh` can mount your own servers from `$DSH_HOME/config.yaml` or a `--config` overlay without a source checkout: @@ -67,7 +67,7 @@ MCP servers are not a shipped default, because a default would have to name one: GITHUB_TOKEN: !!js process.env.GITHUB_TOKEN ``` -The model then sees `mcp__github__*`. See the [mcp-client README](../../packages/mcp/mcp-client/README.md) for the Streamable HTTP transport and the full field table. +The model then sees `mcp__github__*`. See the [mcp-client README](../../../packages/mcp/mcp-client/README.md) for the Streamable HTTP transport and the full field table. ## Install (developer machine) diff --git a/apps/cli/reference.zh.md b/apps/cli/reference/README.zh.md similarity index 68% rename from apps/cli/reference.zh.md rename to apps/cli/reference/README.zh.md index 49381afe9a..00016ebc2f 100644 --- a/apps/cli/reference.zh.md +++ b/apps/cli/reference/README.zh.md @@ -1,22 +1,22 @@ # `dsh` CLI(命令行界面)行为参考 -[English](reference.md) | 中文 +[English](README.md) | 中文 -Argv 只会通过 [Commander](https://github.com/tj/commander.js) 适配器([`src/args.ts`](src/args.ts))解析一次:同一个程序的默认形式(无子命令)是 TUI/无头界面(`--config`、`-p`/`--prompt`、`--resume`、`--dump-config`、`--dump-default-config`),`meta` 子命令是以本 checkout 为 workspace 的同一个 TUI,`upgrade` 子命令是引导会话入口,`web` 子命令则是浏览器 UI。`meta` 与 `upgrade` 是实验性命令:只有带上各自的 `--experimental` 标志或在环境中设置 `DSH_EXPERIMENTAL=1` 才会运行,否则明确报错(stderr,退出码 1)。`src/bin.ts` 按解析后的 mode 分支,仅动态导入该 mode 的模块。`dsh --help` 列出所有 mode,`dsh web --help` 渲染 Web 用法,`dsh --version` 打印此应用的版本;未知选项或拼错的 `--resume` 会明确报错(stderr,退出码 1),而不会被错路由。凡与默认界面不共享任何选项的子命令(`upgrade`、`web`、`meta`)都会拒绝泄漏进来的 `--config`/`-p`/`--resume`/dump 标志,而不会照常运行并丢弃它。`dsh web` 的 `--host`/`--port` 是未验证的直通覆盖:`dsh-host-webserver` schema 是默认值(标志缺失时使用已交付的 Web 覆盖层值)和有效性的唯一真源,并在启动时拒绝错误值。`--trusted-host` 为 /api 浏览器信任栅栏追加具名权威;全接口绑定还会自行推导本机的 LAN IP 字面量([`src/app-cli-entry.ts`](src/app-cli-entry.ts)),因此打印出的 LAN URL 无需任何标志即可使用。 +Argv 只会通过 [Commander](https://github.com/tj/commander.js) 适配器([`src/args.ts`](../src/args.ts))解析一次:同一个程序的默认形式(无子命令)是 TUI/无头界面(`--config`、`-p`/`--prompt`、`--resume`、`--dump-config`、`--dump-default-config`),`meta` 子命令是以本 checkout 为 workspace 的同一个 TUI,`upgrade` 子命令是引导会话入口,`web` 子命令则是浏览器 UI。`meta` 与 `upgrade` 是实验性命令:只有带上各自的 `--experimental` 标志或在环境中设置 `DSH_EXPERIMENTAL=1` 才会运行,否则明确报错(stderr,退出码 1)。`src/bin.ts` 按解析后的 mode 分支,仅动态导入该 mode 的模块。`dsh --help` 列出所有 mode,`dsh web --help` 渲染 Web 用法,`dsh --version` 打印此应用的版本;未知选项或拼错的 `--resume` 会明确报错(stderr,退出码 1),而不会被错路由。凡与默认界面不共享任何选项的子命令(`upgrade`、`web`、`meta`)都会拒绝泄漏进来的 `--config`/`-p`/`--resume`/dump 标志,而不会照常运行并丢弃它。`dsh web` 的 `--host`/`--port` 是未验证的直通覆盖:`dsh-host-webserver` schema 是默认值(标志缺失时使用已交付的 Web 覆盖层值)和有效性的唯一真源,并在启动时拒绝错误值。`--trusted-host` 为 /api 浏览器信任栅栏追加具名权威;全接口绑定还会自行推导本机的 LAN IP 字面量([`src/app-cli-entry.ts`](../src/app-cli-entry.ts)),因此打印出的 LAN URL 无需任何标志即可使用。 TUI 界面: -- 通过 [`dsh-app-boot`](../../packages/ui/app-boot/README.md) 启动 `base.cordis.yml` 与 `tui.cordis.yml`;`--config ` 应用一个补丁列表覆盖并替代个人覆盖,而 `--config-replace ` 将指定文件作为完整配置树启动; -- 使用 `dsh --resume ` 恢复已持久化会话。当 Node 宿主公开 `process.execve` 时,还会提供 TUI 的原地移交宿主:选择器预检并刷新当前会话后,宿主会对应用执行 dispose(资源释放),并以规范化的恢复调用替换进程;不支持进程替换的运行时会让会话继续运行并给出提示。会话身份与退出行由本 CLI 拥有,而非由配置指定:它创建或选定 `main` 会话 id,并把该 id 以及可复现本次调用的确切命令一起提供到启动上下文([`MAIN_SESSION_ID_KEY`](../../packages/ui/tui/README.md) 与 `TUI_GOODBYE_MESSAGE_KEY`)。任何 `cordis.yml` 键都无法移除恢复能力;缺失或无法读取的 id 会明确报错,而不会创建新会话; +- 通过 [`dsh-app-boot`](../../../packages/ui/app-boot/README.md) 启动 `base.cordis.yml` 与 `tui.cordis.yml`;`--config ` 应用一个补丁列表覆盖并替代个人覆盖,而 `--config-replace ` 将指定文件作为完整配置树启动; +- 使用 `dsh --resume ` 恢复已持久化会话。当 Node 宿主公开 `process.execve` 时,还会提供 TUI 的原地移交宿主:选择器预检并刷新当前会话后,宿主会对应用执行 dispose(资源释放),并以规范化的恢复调用替换进程;不支持进程替换的运行时会让会话继续运行并给出提示。会话身份与退出行由本 CLI 拥有,而非由配置指定:它创建或选定 `main` 会话 id,并把该 id 以及可复现本次调用的确切命令一起提供到启动上下文([`MAIN_SESSION_ID_KEY`](../../../packages/ui/tui/README.md) 与 `TUI_GOODBYE_MESSAGE_KEY`)。任何 `cordis.yml` 键都无法移除恢复能力;缺失或无法读取的 id 会明确报错,而不会创建新会话; - 将 **调用目录** 视为 workspace:会话、相对路径和 workspace 指令都从 cwd 解析(`dsh meta` 是唯一例外,见下文); - 告知 agent(智能体)自身源码所在位置:启动后添加一个命名此 harness checkout 的提示词段。该路径从启动器的真实路径解析,因此在 PATH 符号链接和任意 cwd 下仍然有效,使自指的 `cordis` 工具集可以读取并修改它; -- 应用 `~/.dsh` 中的个人覆盖(参见 [app-boot 的个人配置](../../packages/ui/app-boot/README.md#personal-config)):`config.yaml` 修补已启动的树,而那里的 `.env` 是凭据提供方自己的存储(绝不会被提升进环境,因此密钥始终可轮换)。环境优先级为环境中已有的值 > 项目 `.env`。已交付配置树中的 Cordis HMR(热模块替换)会持续应用 `config.yaml` 的变更;显式 `--config` 配置树会替代该个人覆盖,未包含 HMR 的配置树只在启动时读取该文件。 -- 当 `DSH_HOME` 下不存在不可变确认标记时,通过已挂载的 TUI overlay 服务呈现[版本化首次运行欢迎页](../../.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md);只有 Enter 会创建该版本的标记,Escape、资源释放或进程退出仍保留展示资格。官方 DeepSeek 图标、响应式终端栅格图、所有 locale 共用的中文文案和通知版本均由静态本地文件持有;overlay 不会写入会话事件或模型上下文。 +- 应用 `~/.dsh` 中的个人覆盖(参见 [app-boot 的个人配置](../../../packages/ui/app-boot/README.md#personal-config)):`config.yaml` 修补已启动的树,而那里的 `.env` 是凭据提供方自己的存储(绝不会被提升进环境,因此密钥始终可轮换)。环境优先级为环境中已有的值 > 项目 `.env`。已交付配置树中的 Cordis HMR(热模块替换)会持续应用 `config.yaml` 的变更;显式 `--config` 配置树会替代该个人覆盖,未包含 HMR 的配置树只在启动时读取该文件。 +- 当 `DSH_HOME` 下不存在不可变确认标记时,通过已挂载的 TUI overlay 服务呈现[版本化首次运行欢迎页](../../../.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md);只有 Enter 会创建该版本的标记,Escape、资源释放或进程退出仍保留展示资格。官方 DeepSeek 图标、响应式终端栅格图、所有 locale 共用的中文文案和通知版本均由静态本地文件持有;overlay 不会写入会话事件或模型上下文。 - 注册裸 `/compact`:agent 空闲时,即使未达到自动压力,也会摘要有效的较早历史;该命令拒绝参数,并只在独立替换标记对持久化后报告成功。压缩(compaction)期间提交的提示词保留其队列身份,并在该检查点之后启动;注入的上下文仍保持可见。 `dsh meta` 是以本 harness checkout 为 workspace 的同一个 TUI,因此开发 dsh 自身无需 `cd`。它在环境确定之后才 chdir 到 checkout 根目录(从启动器的真实路径解析,与源码路径提示词段所指的根目录相同),因此环境优先级不变,而会话 cwd 与 HMR 监视根目录会一并移动。Meta 始终创建新会话,不接受默认界面的任何选项;恢复已持久化会话应使用普通的 `dsh --resume `。 -`dsh upgrade` 是默认 TUI 界面之上的引导式全新会话入口:它在调用目录中创建一个全新会话,并以内置 `dsh-upgrade` skill(技能)播种其首轮,效果等同于用户手动键入 `/skill:`。启动器将 skill 名称提供到启动上下文([`INITIAL_SKILL_KEY`](../../packages/ui/tui/README.md)),TUI 在聊天就绪后自动调用它。该命令除实验性门槛外不接受任何选项——`--config`、`-p`、`--resume` 都会明确报错——且仅在首次启动时播种,因此之后 `dsh --resume ` 恢复该会话时是普通 TUI 会话,不会重复注入。 +`dsh upgrade` 是默认 TUI 界面之上的引导式全新会话入口:它在调用目录中创建一个全新会话,并以内置 `dsh-upgrade` skill(技能)播种其首轮,效果等同于用户手动键入 `/skill:`。启动器将 skill 名称提供到启动上下文([`INITIAL_SKILL_KEY`](../../../packages/ui/tui/README.md)),TUI 在聊天就绪后自动调用它。该命令除实验性门槛外不接受任何选项——`--config`、`-p`、`--resume` 都会明确报错——且仅在首次启动时播种,因此之后 `dsh --resume ` 恢复该会话时是普通 TUI 会话,不会重复注入。 `dsh --dump-config` 和 `dsh web --dump-config` 把合成后的配置树——已交付的基础配置、界面覆盖层,以及 `--config` 或个人覆盖层,恰好是该界面启动时组装的那些层——以 YAML 打印到 stdout 后退出,不启动任何东西;`--dump-default-config` 止步于界面覆盖层,因此对两份输出做 diff 就能精确看出用户层改了什么。每段连续的行之前都有一条 `# ==` 注释,标明该段来自哪个文件以及被哪些层修补过(例如 `# == base.cordis.yml, patched by tui.cordis.yml`),因此输出既展示来源,又仍是一份可加载的文档。合成通过 include 自己的补丁算法和 YAML 方言(`@cordisjs/plugin-include` 的 `applyEntryPatches`/`entryListSchema`)完成,因此 dump 不可能与实际启动漂移;`!!js` 表达式原样打印、不求值,目标行不存在的补丁会连同其所在层报到 stderr,与 Loader 启动时的警告一致。由启动器持有的启动上下文值(会话身份、CLI 标志补丁)是每次调用的事实,位于配置树之外,不会出现。dump 标志会拒绝仅用于启动的标志(`-p`、`--resume`、`--config-replace`)而不是静默忽略它们,`--dump-default-config` 不接受 `--config`。 @@ -34,15 +34,15 @@ Web 和无头界面启动 `base.cordis.yml` 与 `web.cordis.yml`,随后应用 - 'github:PolyArch/humanize#' ``` -仓库必须包含已准备的 `.dsh-plugin` 包;[仓库插件契约](../../packages/cordis/repository-plugin/README.md#standalone-app-configuration)说明创作方式、嵌套插件路径、不可变缓存、信任边界和失败语义。实时编辑失败时,最后一个可用树保持运行,并发出 Cordis 的 HMR 事件 `hmr/config-update-failed`。 +仓库必须包含已准备的 `.dsh-plugin` 包;[仓库插件契约](../../../packages/cordis/repository-plugin/README.md#standalone-app-configuration)说明创作方式、嵌套插件路径、不可变缓存、信任边界和失败语义。实时编辑失败时,最后一个可用树保持运行,并发出 Cordis 的 HMR 事件 `hmr/config-update-failed`。 已交付的 TUI 和 Web 组合会注册原生 DeepSeek 适配器,以及 pi-ai 的 OpenAI 和 Anthropic 提供方配置。凭据和端点覆盖来自启动分层环境中的提供方标准变量对:`DEEPSEEK_API_KEY` / `DEEPSEEK_BASE_URL`、`OPENAI_API_KEY` / `OPENAI_BASE_URL` 和 `ANTHROPIC_API_KEY` / `ANTHROPIC_BASE_URL`。 -每个界面也都只注册 `web_search` 这一个 Web 工具。搜索使用 DeepSeek 的 Anthropic 兼容 Messages 端点,每次调用都会解析同一个 `DEEPSEEK_API_KEY` 凭据引用,并接受独立的 `DEEPSEEK_SEARCH_BASE_URL` 端点覆盖;每次搜索都是一次辅助模型请求,会产生独立的延迟与 token 成本。`web_fetch` 仍处于禁用状态,组合也未挂载默认抓取提供方;需要任意页面抓取能力的部署必须通过覆盖层选择启用。部署决策及其安全边界见[默认 Web 搜索 Agent Note(agent 决策记录)](../../.agents/notes/implemented/feature/2026-07-31-web-default-search.md)。 +每个界面也都会注册 `web_search`,且只注册 `web_search`。搜索使用 DeepSeek 的 Anthropic 兼容 Messages 端点,每次调用都会解析同一个 `DEEPSEEK_API_KEY` 凭据引用,并接受独立的 `DEEPSEEK_SEARCH_BASE_URL` 端点覆盖;每次搜索都是一次辅助模型请求,会产生独立的延迟与 token 成本。`web_fetch` 仍处于禁用状态,组合也未挂载默认抓取提供方;需要任意页面抓取能力的部署必须通过覆盖层选择启用。部署决策及其安全边界见[默认 Web 搜索 Agent Note(agent 决策记录)](../../../.agents/notes/implemented/feature/2026-07-31-web-default-search.md)。 `DSH_TOOLS_MODE` 为整个 Web/无头进程选择工具呈现模式:`native`(未设置时的 schema 默认值)、`code`(仅含 `run_code` 的 Code Mode 线路)或 `both`;任何其他值都会经由 `dsh-tools` 配置 schema 在启动时明确报错。由于 Loader 组合是静态的,该设置作用于整个进程。TUI 界面会忽略该变量并固定为 `native`。 -[`core-web.cordis.yml`](config/core-web.cordis.yml) 是一个可选启用的 `dsh web --config` 覆盖层:它保留已交付的 Web 宿主、浏览器、Workspace、持久化与权限组合,同时将默认的原生模型界面精简为以所有者为作用域的持久 `bash` 以及 `str_replace_editor`。PTY 后端和编辑器分别消费现有的 Web 沙箱与文件系统提供方。持久 shell 处于打开状态时,会阻止所属会话更改权限模式;因此,在较宽权限下创建的 shell 无法在降权后继续存活。`DSH_TOOLS_MODE` 仍控制由此得到的双工具注册表采用原生/Code Mode 呈现。 +[`core-web.cordis.yml`](../config/core-web.cordis.yml) 是一个可选启用的 `dsh web --config` 覆盖层:它保留已交付的 Web 宿主、浏览器、Workspace、持久化与权限组合,同时将默认的原生模型界面精简为以所有者为作用域的持久 `bash` 以及 `str_replace_editor`。PTY 后端和编辑器分别消费现有的 Web 沙箱与文件系统提供方。持久 shell 处于打开状态时,会阻止所属会话更改权限模式;因此,在较宽权限下创建的 shell 无法在降权后继续存活。`DSH_TOOLS_MODE` 仍控制由此得到的双工具注册表采用原生/Code Mode 呈现。 在源码 checkout 中,用以下命令启动这个精简 Web profile: @@ -50,7 +50,7 @@ Web 和无头界面启动 `base.cordis.yml` 与 `web.cordis.yml`,随后应用 pnpm run dsh web --config apps/cli/config/core-web.cordis.yml ``` -每个 `dsh` 界面——TUI、Web 与无头——都默认上报会话遥测(该行位于共享的 `base.cordis.yml`):每条会话日志事件以 OTLP/HTTP 日志记录的形式、按 10 秒批处理节奏流向 `https://harness-telemetry.deepseeksvc.com/v1/logs`。`DSH_TELEMETRY_OTLP_URL` 可将 exporter 指向其他 collector;将 `DSH_TELEMETRY_DISABLED` 设为**任意非空值**——包括 `0` 或 `false`——都会在该行加载前将其关停(隐私开关取「宁可误关、不可误开」)。该组合当前未挂载任何脱敏规则:导出记录即原始捕获副本,包含消息正文、工具参数与结果、以及会话工作目录路径。部署口径见 [web-telemetry-default-mount Agent Note](../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md)。 +每个 `dsh` 界面——TUI、Web 与无头——都默认上报会话遥测(该行位于共享的 `base.cordis.yml`):每条会话日志事件以 OTLP/HTTP 日志记录的形式、按 10 秒批处理节奏流向 `https://harness-telemetry.deepseeksvc.com/v1/logs`。`DSH_TELEMETRY_OTLP_URL` 可将 exporter 指向其他 collector;将 `DSH_TELEMETRY_DISABLED` 设为**任意非空值**——包括 `0` 或 `false`——都会在该行加载前将其关停(隐私开关取「宁可误关、不可误开」)。该组合当前未挂载任何脱敏规则:导出记录即原始捕获副本,包含消息正文、工具参数与结果、以及会话工作目录路径。部署口径见 [web-telemetry-default-mount Agent Note](../../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md)。 MCP 服务器不是交付默认值,因为默认值必须点名一台服务器:`@deepseek-ai/dsh-mcp-client` 每一行只挂载一台服务器,并把它作为子进程 spawn;该进程不经 `ctx.bash`,因此也不受沙箱策略约束。该包是本 CLI 的运行时依赖,所以已安装的 `dsh` 无需源码检出即可从 `$DSH_HOME/config.yaml` 或 `--config` 覆盖层挂载你自己的服务器: @@ -67,7 +67,7 @@ MCP 服务器不是交付默认值,因为默认值必须点名一台服务器 GITHUB_TOKEN: !!js process.env.GITHUB_TOKEN ``` -模型随后会看到 `mcp__github__*`。Streamable HTTP 传输与完整字段表见 [mcp-client README](../../packages/mcp/mcp-client/README.md)。 +模型随后会看到 `mcp__github__*`。Streamable HTTP 传输与完整字段表见 [mcp-client README](../../../packages/mcp/mcp-client/README.md)。 ## 安装(开发机) diff --git a/docs/architecture.md b/docs/architecture.md index 9c3b0a2f13..c4665bce6e 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -68,7 +68,7 @@ Waterfalls are around-middleware: listeners delegate with `next()`; returning wi ## Default Loop Lifecycle -A **session** is append-only. An ordinary **turn** claims one queued `send()` item; injection claims none. A turn ends when the model or plugins stop it; a **step** is one model request plus its tool calls. Agent and session publication happen only after private setup and resume state are ready. +A **session** is append-only. An ordinary **turn** claims one queued `send()` item; injection claims none. A turn ends when the model or plugins stop it; a **step** is one model request plus its tool calls. Agent and session publication happen only after private setup and resume state are ready. Quotes in the [sequence below](agent-lifecycle.md) mark durable events. ### Turn Flow diff --git a/docs/architecture.zh.md b/docs/architecture.zh.md index b4ff801156..41a5a9de7c 100644 --- a/docs/architecture.zh.md +++ b/docs/architecture.zh.md @@ -68,7 +68,7 @@ waterfall(瀑布式事件)是环绕中间件:监听器通过 `next()` 委 ## 默认循环生命周期 -**会话**仅追加。普通**轮次**认领一个排队的 `send()` 项;注入不会认领。轮次在模型或插件停止时结束;一个**步骤**由一次模型请求及其工具调用组成。只有私有设置和恢复状态准备完毕后,系统才会发布 agent 与会话。 +**会话**仅追加。普通**轮次**认领一个排队的 `send()` 项;注入不会认领。轮次在模型或插件停止时结束;一个**步骤**由一次模型请求及其工具调用组成。只有私有设置和恢复状态准备完毕后,系统才会发布 agent 与会话。[下文时序](agent-lifecycle.md)中的引号标记持久事件。 ### 轮次流程 From 8af3babaea4ef50b5e19a81caebf56dfb1ed268e Mon Sep 17 00:00:00 2001 From: Turtle Date: Wed, 5 Aug 2026 12:46:38 +0800 Subject: [PATCH 5/7] docs: rescan rebased documentation hierarchy --- ...2026-07-04-doc-tiers-and-budgets.i18n.yaml | 2 +- README.i18n.yaml | 4 +- README.md | 8 +- README.zh.md | 8 +- THIRD_PARTY_NOTICES.md | 2 +- apps/cli/README.i18n.yaml | 4 +- apps/cli/README.md | 81 ++----- apps/cli/README.zh.md | 81 ++----- apps/cli/reference/README.i18n.yaml | 4 +- apps/cli/reference/README.md | 114 +++++---- apps/cli/reference/README.zh.md | 114 +++++---- apps/web/tests/scaffold.ts | 8 +- docs/architecture.i18n.yaml | 4 +- docs/architecture.md | 2 +- docs/architecture.zh.md | 2 +- docs/cookbook/adding-a-tool.i18n.yaml | 4 +- docs/cookbook/adding-a-tool.md | 10 +- docs/cookbook/adding-a-tool.zh.md | 10 +- docs/cookbook/extension-cookbook.i18n.yaml | 4 +- docs/cookbook/extension-cookbook.md | 39 +-- docs/cookbook/extension-cookbook.zh.md | 39 +-- .../llm-streaming.i18n.yaml | 4 +- docs/development.i18n.yaml | 4 +- docs/development.md | 42 ++-- docs/development.zh.md | 42 ++-- docs/testing.i18n.yaml | 4 +- docs/testing.md | 2 +- docs/testing.zh.md | 2 +- docs/user/develop/basic/config.i18n.yaml | 4 +- docs/user/develop/basic/config.md | 29 +-- docs/user/develop/basic/config.zh.md | 29 +-- docs/user/develop/basic/index.i18n.yaml | 4 +- docs/user/develop/basic/index.md | 27 ++- docs/user/develop/basic/index.zh.md | 56 ++--- docs/user/develop/basic/tool.i18n.yaml | 4 +- docs/user/develop/basic/tool.md | 221 ++--------------- docs/user/develop/basic/tool.zh.md | 223 ++---------------- docs/user/develop/practice/index.i18n.yaml | 4 +- docs/user/develop/practice/index.md | 16 +- docs/user/develop/practice/index.zh.md | 16 +- docs/user/guide/config.i18n.yaml | 4 +- docs/user/guide/config.md | 2 +- docs/user/guide/config.zh.md | 4 +- docs/web-styling.i18n.yaml | 4 +- docs/web-styling.md | 114 ++------- docs/web-styling.zh.md | 114 ++------- examples/README.i18n.yaml | 4 +- examples/README.zh.md | 20 +- examples/acp-agent/README.i18n.yaml | 2 +- examples/acp-agent/README.zh.md | 2 +- examples/headless-agent/README.i18n.yaml | 4 +- native/README.i18n.yaml | 4 +- native/README.zh.md | 15 +- packages/acp/acp/README.i18n.yaml | 4 +- packages/acp/acp/README.md | 2 +- packages/acp/acp/README.zh.md | 2 +- packages/bash/README.i18n.yaml | 4 +- packages/bash/README.md | 18 +- packages/bash/README.zh.md | 20 +- packages/bash/bash-env/README.i18n.yaml | 2 +- packages/bash/bash-env/README.zh.md | 2 +- packages/bash/bash-local/README.i18n.yaml | 4 +- packages/bash/bash-local/README.zh.md | 12 +- packages/bash/pwsh-local/README.i18n.yaml | 4 +- packages/bash/pwsh-local/README.md | 2 +- packages/bash/pwsh-local/README.zh.md | 2 +- packages/bash/tool-bash/README.i18n.yaml | 2 +- packages/bash/tool-bash/README.zh.md | 2 +- packages/bash/tool-pwsh/README.i18n.yaml | 2 +- packages/bash/tool-pwsh/README.zh.md | 4 +- packages/client/AGENTS.md | 10 +- packages/client/README.i18n.yaml | 4 +- packages/client/README.md | 61 ++--- packages/client/README.zh.md | 61 ++--- packages/client/connection/README.i18n.yaml | 4 +- packages/client/connection/README.md | 5 +- packages/client/connection/README.zh.md | 5 +- packages/client/hmr/README.i18n.yaml | 4 +- packages/client/hmr/README.md | 4 +- packages/client/hmr/README.zh.md | 4 +- packages/client/locale/README.i18n.yaml | 4 +- packages/client/locale/README.md | 2 +- packages/client/locale/README.zh.md | 2 +- packages/client/runtime/README.i18n.yaml | 4 +- packages/client/runtime/README.md | 2 +- packages/client/runtime/README.zh.md | 2 +- packages/client/schema-form/README.i18n.yaml | 4 +- packages/client/schema-form/README.md | 6 +- packages/client/schema-form/README.zh.md | 6 +- packages/client/test-runtime/README.i18n.yaml | 4 +- packages/client/test-runtime/README.md | 2 +- packages/client/test-runtime/README.zh.md | 2 +- packages/client/ui-command/README.i18n.yaml | 4 +- packages/client/ui-command/README.md | 2 +- packages/client/ui-command/README.zh.md | 2 +- .../client/ui-conversation/README.i18n.yaml | 4 +- packages/client/ui-conversation/README.md | 12 +- packages/client/ui-conversation/README.zh.md | 6 +- .../src/client/contract/diff-card-model.ts | 7 +- packages/client/ui-goal/README.i18n.yaml | 4 +- packages/client/ui-goal/README.md | 2 +- packages/client/ui-goal/README.zh.md | 2 +- packages/client/ui-layout/README.i18n.yaml | 4 +- packages/client/ui-layout/README.md | 2 +- packages/client/ui-layout/README.zh.md | 2 +- .../client/ui-primitives/README.i18n.yaml | 4 +- packages/client/ui-primitives/README.md | 6 +- packages/client/ui-primitives/README.zh.md | 4 +- .../client/ui-primitives/src/head-tail-cap.ts | 3 +- packages/client/ui-sidebar/README.i18n.yaml | 4 +- packages/client/ui-sidebar/README.md | 4 +- packages/client/ui-sidebar/README.zh.md | 4 +- packages/client/ui-slash/README.i18n.yaml | 4 +- packages/client/ui-slash/README.md | 2 +- packages/client/ui-slash/README.zh.md | 2 +- packages/client/ui-theme/README.i18n.yaml | 2 +- packages/client/ui-theme/README.zh.md | 2 +- .../client/ui-trajectory/README.i18n.yaml | 4 +- packages/client/ui-trajectory/README.md | 2 +- packages/client/ui-trajectory/README.zh.md | 2 +- packages/client/ui-workspace/README.i18n.yaml | 4 +- packages/client/ui-workspace/README.md | 2 +- packages/client/ui-workspace/README.zh.md | 2 +- packages/client/web-react/README.i18n.yaml | 4 +- packages/client/web-react/README.md | 2 +- packages/client/web-react/README.zh.md | 2 +- packages/client/web/README.i18n.yaml | 4 +- packages/client/web/README.md | 2 +- packages/client/web/README.zh.md | 2 +- packages/code-runtime/README.i18n.yaml | 4 +- packages/code-runtime/README.zh.md | 10 +- packages/compact/README.i18n.yaml | 4 +- packages/compact/README.zh.md | 14 +- .../compact/command-compact/README.i18n.yaml | 4 +- packages/compact/command-compact/README.md | 4 +- packages/compact/command-compact/README.zh.md | 4 +- packages/context/README.i18n.yaml | 4 +- packages/context/README.md | 2 +- packages/context/README.zh.md | 10 +- .../session-reference/README.i18n.yaml | 4 +- packages/context/session-reference/README.md | 4 +- .../context/session-reference/README.zh.md | 4 +- .../context/tmux-context/README.i18n.yaml | 4 +- packages/context/tmux-context/README.md | 2 +- packages/context/tmux-context/README.zh.md | 2 +- packages/cordis/README.i18n.yaml | 4 +- packages/cordis/README.zh.md | 10 +- .../cordis/repository-plugin/README.i18n.yaml | 4 +- packages/cordis/repository-plugin/README.md | 4 +- .../cordis/repository-plugin/README.zh.md | 4 +- packages/core/README.i18n.yaml | 4 +- packages/core/README.zh.md | 22 +- .../agent-spine-demo/README.i18n.yaml | 4 +- packages/examples/agent-spine-demo/README.md | 4 +- .../examples/agent-spine-demo/README.zh.md | 4 +- .../examples/jsonrpc-demo/README.i18n.yaml | 4 +- packages/fs/README.i18n.yaml | 4 +- packages/fs/README.zh.md | 26 +- packages/fs/fs-sandbox/README.i18n.yaml | 2 +- packages/fs/fs-sandbox/README.zh.md | 2 +- packages/fs/fs/README.i18n.yaml | 4 +- packages/fs/fs/README.zh.md | 17 +- packages/fs/tool-fs/README.i18n.yaml | 4 +- packages/fs/tool-fs/README.zh.md | 2 +- packages/goal/command-goal/README.i18n.yaml | 4 +- packages/goal/command-goal/README.md | 6 +- packages/goal/command-goal/README.zh.md | 6 +- packages/goal/goal-session/README.i18n.yaml | 2 +- packages/goal/goal-session/README.zh.md | 2 +- packages/guard/README.i18n.yaml | 4 +- packages/guard/README.zh.md | 10 +- packages/hooks/README.i18n.yaml | 4 +- packages/hooks/README.zh.md | 12 +- packages/host/README.i18n.yaml | 4 +- packages/host/README.zh.md | 20 +- packages/host/apiproxy/README.i18n.yaml | 4 +- packages/host/apiproxy/README.md | 8 +- packages/host/apiproxy/README.zh.md | 8 +- .../directory-picker-browse/README.i18n.yaml | 4 +- .../host/directory-picker-browse/README.md | 2 +- .../host/directory-picker-browse/README.zh.md | 2 +- .../directory-picker-native/README.i18n.yaml | 4 +- .../host/directory-picker-native/README.md | 2 +- .../host/directory-picker-native/README.zh.md | 2 +- packages/llm/README.i18n.yaml | 4 +- packages/llm/README.zh.md | 16 +- packages/llm/token-meter/README.i18n.yaml | 4 +- packages/llm/token-meter/README.md | 5 +- packages/llm/token-meter/README.zh.md | 5 +- packages/llm/token-meter/src/index.ts | 4 +- packages/llm/token-meter/src/projection.ts | 3 +- packages/lsp/README.i18n.yaml | 4 +- packages/lsp/README.zh.md | 12 +- packages/mcp/README.i18n.yaml | 4 +- packages/mcp/README.zh.md | 8 +- packages/plan/README.i18n.yaml | 4 +- packages/plan/README.zh.md | 6 +- packages/plan/plan-mode/README.i18n.yaml | 4 +- packages/plan/plan-mode/README.md | 4 +- packages/plan/plan-mode/README.zh.md | 4 +- packages/pty/README.i18n.yaml | 4 +- packages/pty/README.zh.md | 12 +- packages/sandbox/README.i18n.yaml | 4 +- packages/sandbox/README.zh.md | 12 +- .../sandbox/sandbox-local/README.i18n.yaml | 4 +- packages/sdk/README.i18n.yaml | 4 +- packages/sdk/README.zh.md | 19 +- .../src/project/project-edit-session.ts | 4 +- .../sdk/scripts/src/config/config-workflow.ts | 2 +- packages/sdk/sdk-client/README.i18n.yaml | 4 +- packages/session-persistence/README.i18n.yaml | 4 +- packages/session-persistence/README.zh.md | 12 +- packages/session-projection/README.i18n.yaml | 4 +- packages/session-projection/README.zh.md | 10 +- .../session-projection/README.i18n.yaml | 4 +- .../session-projection/README.md | 2 +- .../session-projection/README.zh.md | 2 +- .../session-projection/src/index.ts | 4 +- packages/session-query/README.i18n.yaml | 4 +- packages/session-query/README.zh.md | 10 +- .../settings/settings-local/README.i18n.yaml | 2 +- packages/settings/settings-local/README.zh.md | 6 +- packages/settings/settings/README.i18n.yaml | 2 +- packages/settings/settings/README.zh.md | 8 +- packages/skill/README.i18n.yaml | 4 +- packages/skill/README.zh.md | 12 +- packages/spill/README.i18n.yaml | 4 +- packages/spill/README.zh.md | 14 +- packages/storage/README.i18n.yaml | 4 +- packages/storage/README.zh.md | 12 +- packages/storage/storage/README.i18n.yaml | 4 +- packages/storage/storage/README.md | 15 +- packages/storage/storage/README.zh.md | 15 +- packages/subagent/README.i18n.yaml | 4 +- packages/subagent/README.zh.md | 26 +- .../subagent/subagent-acp/README.i18n.yaml | 4 +- .../subagent-dsh-sdk/README.i18n.yaml | 4 +- packages/subagent/subagent/README.i18n.yaml | 4 +- packages/subagent/subagent/README.md | 16 +- packages/subagent/subagent/README.zh.md | 18 +- packages/subprocess/README.i18n.yaml | 4 +- packages/subprocess/README.zh.md | 10 +- packages/support/README.i18n.yaml | 4 +- packages/support/README.zh.md | 22 +- .../support/acp-snapshot/README.i18n.yaml | 4 +- packages/support/acp-snapshot/README.md | 2 +- packages/support/acp-snapshot/README.zh.md | 2 +- packages/support/llm-replay/README.i18n.yaml | 4 +- packages/support/llm-replay/README.md | 2 +- packages/support/llm-replay/README.zh.md | 2 +- packages/tasks/README.i18n.yaml | 4 +- packages/tasks/README.zh.md | 12 +- packages/telemetry/README.i18n.yaml | 4 +- packages/telemetry/README.zh.md | 10 +- packages/timeout/README.i18n.yaml | 4 +- packages/timeout/README.zh.md | 10 +- packages/todo/README.i18n.yaml | 4 +- packages/todo/README.md | 6 +- packages/todo/README.zh.md | 6 +- packages/typert/README.i18n.yaml | 4 +- packages/typert/README.zh.md | 8 +- packages/ui/README.i18n.yaml | 4 +- packages/ui/README.md | 20 +- packages/ui/README.zh.md | 22 +- packages/ui/app-boot/src/index.ts | 17 +- packages/ui/app-boot/tests/app-boot.spec.ts | 9 +- .../ui/app-boot/tests/config-reload.spec.ts | 5 +- .../ui/app-boot/tests/personal-config.spec.ts | 10 +- packages/ui/commands/README.i18n.yaml | 4 +- packages/ui/commands/README.md | 2 +- packages/ui/commands/README.zh.md | 2 +- packages/ui/user-interaction/README.i18n.yaml | 4 +- packages/ui/user-interaction/README.md | 2 +- packages/ui/user-interaction/README.zh.md | 2 +- packages/util/README.i18n.yaml | 4 +- packages/util/README.zh.md | 22 +- packages/util/atomic-write/README.i18n.yaml | 2 +- packages/util/atomic-write/README.zh.md | 6 +- packages/util/native-command/README.i18n.yaml | 2 +- packages/util/native-command/README.zh.md | 2 +- packages/util/retention/README.i18n.yaml | 4 +- packages/util/retention/README.md | 4 +- packages/util/retention/README.zh.md | 4 +- packages/util/timeout/README.i18n.yaml | 4 +- packages/util/timeout/README.md | 2 +- packages/util/timeout/README.zh.md | 2 +- packages/web/README.i18n.yaml | 4 +- packages/web/README.zh.md | 20 +- packages/workflow/README.i18n.yaml | 4 +- packages/workflow/README.zh.md | 18 +- packages/workspace/README.i18n.yaml | 4 +- packages/workspace/README.md | 2 +- packages/workspace/README.zh.md | 8 +- python/README.i18n.yaml | 4 +- python/README.md | 2 +- python/README.zh.md | 2 +- python/development.i18n.yaml | 4 +- python/development.md | 2 +- python/development.zh.md | 2 +- python/sdk/README.i18n.yaml | 2 +- python/sdk/README.zh.md | 2 +- scripts/gen-third-party-notices.ts | 2 +- skills/dsh-customize/SKILL.md | 2 +- skills/dsh-upstream-customization/SKILL.md | 2 +- 304 files changed, 1091 insertions(+), 1983 deletions(-) diff --git a/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.i18n.yaml b/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.i18n.yaml index 0d2231b449..ff01e8a69a 100644 --- a/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.i18n.yaml +++ b/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.md 2026-07-04-doc-tiers-and-budgets.md: e7b3421d09a1ae5ab9a9373e8040832c1b0d4b97 -2026-07-04-doc-tiers-and-budgets.zh.md: 3e372f975943e8e4868aa6a386361ba324ef7109 +2026-07-04-doc-tiers-and-budgets.zh.md: 18bf98777c87512e6161a4529db66030ced47bfd diff --git a/README.i18n.yaml b/README.i18n.yaml index f5eb3be571..8b5cea2324 100644 --- a/README.i18n.yaml +++ b/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write README.md -README.md: 7777c35a2785856e553cb963920288ff8021dc27 -README.zh.md: 85ee977063e4d822d71e1e780ab4a461260aaf53 +README.md: 7f393077201a974335f9f72f5924d8b1fec585c3 +README.zh.md: ecdb2057fafc4678220b8ccedfe39cb7f387b36a diff --git a/README.md b/README.md index 7777c35a27..7f39307720 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,9 @@ It uses an architecture where **everything is a plugin**. ## Internal testing notice -感谢您愿意拨冗试用 DeepSeek Harness。当前版本仍处于内部测试阶段,功能仍待完善,体验难免有些粗糙。 +DeepSeek Harness is under internal testing. Features and interfaces may change. -“如切如磋,如琢如磨。” 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中发现的问题,也可能促使我们重新审视,甚至推翻已有的设计。 - -为了帮助我们更准确地还原您真实使用中的问题,内测版本默认会上传所有 Session Log;如需关闭,可以设置环境变量 `DSH_TELEMETRY_DISABLED=1`。另外,如果您有任何反馈与建议,请在企业微信群中留言告诉我们。每一条反馈,都会帮助我们把它打磨得更好。 +The internal build uploads all Session Logs by default to help diagnose reported problems. Set `DSH_TELEMETRY_DISABLED=1` to disable telemetry. Send feedback through the internal WeChat group. ## Install @@ -26,7 +24,7 @@ scripts/install.sh The installer requires `git` and Node `^22.19 || >=24`, offers to install `pnpm` when it is missing, prompts for a DeepSeek API key, builds the required repository artifacts, and launches the Web UI. -The installer keeps every checkout under `~/.dsh/source`: the master clone at `~/.dsh/source/master` and each install's staging checkout as a git worktree `~/.dsh/source/staging-`. The stable symlink `~/.dsh/source/current` points at the active staging worktree, and `dsh` in `~/.local/bin` links to `current/bin/dsh`, so an upgrade repoints one symlink and the `dsh` on PATH never moves. Re-running the command adds a fresh staging worktree from an updated master and repoints `current` at it. See [`scripts/install.sh`](scripts/install.sh) for alternate install locations and other options. +The default active checkout is `~/.dsh/source/current`, and the launcher is linked into `~/.local/bin`. Re-run the installer to update. [`scripts/install.sh`](scripts/install.sh) owns alternate locations, update mechanics, and recovery options. ## Use DeepSeek Harness diff --git a/README.zh.md b/README.zh.md index 85ee977063..ecdb2057fa 100644 --- a/README.zh.md +++ b/README.zh.md @@ -8,11 +8,9 @@ DeepSeek Harness(`dsh`)是一款基于 DeepSeek Harness SDK 构建的开源 ## 内测声明 -感谢您愿意拨冗试用 DeepSeek Harness。当前版本仍处于内部测试阶段,功能仍待完善,体验难免有些粗糙。 +DeepSeek Harness 正处于内部测试阶段,功能和接口可能发生变化。 -“如切如磋,如琢如磨。” 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中发现的问题,也可能促使我们重新审视,甚至推翻已有的设计。 - -为了帮助我们更准确地还原您真实使用中的问题,内测版本默认会上传所有 Session Log;如需关闭,可以设置环境变量 `DSH_TELEMETRY_DISABLED=1`。另外,如果您有任何反馈与建议,请在企业微信群中留言告诉我们。每一条反馈,都会帮助我们把它打磨得更好。 +为帮助诊断上报的问题,内测版本默认上传所有会话日志。设置 `DSH_TELEMETRY_DISABLED=1` 可关闭遥测。请通过内部企业微信群反馈问题和建议。 ## 安装 @@ -26,7 +24,7 @@ scripts/install.sh 安装器要求系统已安装 `git` 和 Node `^22.19 || >=24`,缺少 `pnpm` 时可代为安装,并会提示输入 DeepSeek API 密钥,然后构建所需的仓库产物并启动 Web UI。 -安装器会把所有检出都放在 `~/.dsh/source` 下:master 克隆位于 `~/.dsh/source/master`,每次安装的 staging 检出是一个 git worktree `~/.dsh/source/staging-<时间戳>`。稳定符号链接 `~/.dsh/source/current` 指向当前生效的 staging worktree,`~/.local/bin` 中的 `dsh` 链接到 `current/bin/dsh`,因此升级只需重指一个符号链接,PATH 上的 `dsh` 从不移动。再次运行该命令会基于更新后的 master 新增一个 staging worktree,并把 `current` 重指到它。其他安装位置和选项见 [`scripts/install.sh`](scripts/install.sh)。 +默认生效的检出位于 `~/.dsh/source/current`,启动器链接到 `~/.local/bin`。再次运行安装器即可更新。其他位置、更新机制和恢复选项由 [`scripts/install.sh`](scripts/install.sh) 负责。 ## 使用 DeepSeek Harness diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 8b04f25504..be03addc50 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -27,7 +27,7 @@ The Cordis framework and its foundation libraries are source-vendored into this ## Runtime npm dependencies -External packages that a workspace package resolves at runtime. `scripts/install.sh` installs this repository itself, so the tier covers every plugin a user can mount from `cordis.yml` — not only what the `dsh` CLI/TUI, the Web UI, and the Python SDK runtime load by default. +External packages that a workspace package resolves at runtime. `scripts/install.sh` installs this repository itself, so the tier covers every plugin a user can mount from `cordis.yml` — not only what the `dsh` CLI, Web UI, and Python SDK runtime load by default. | Package | License | | --- | --- | diff --git a/apps/cli/README.i18n.yaml b/apps/cli/README.i18n.yaml index fd317e355d..e115e43e64 100644 --- a/apps/cli/README.i18n.yaml +++ b/apps/cli/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write apps/cli/README.md -README.md: 6fdca68eed11dffe46bf2fbde9a7899359690dca -README.zh.md: d8d7122729df1dd8aaed8207ddfb0a0470778b01 +README.md: ce7af5a299e45d6f107686aff043246914dce8ed +README.zh.md: e97fec9d6bb726cb1e419a1ca2fa1871d4d203ca diff --git a/apps/cli/README.md b/apps/cli/README.md index 6fdca68eed..ce7af5a299 100644 --- a/apps/cli/README.md +++ b/apps/cli/README.md @@ -2,75 +2,24 @@ English | [中文](README.zh.md) -The `dsh` command has three entry modes: a required raw config overlay, a one-shot headless prompt, and the Web UI. [`src/args.ts`](src/args.ts) owns the Commander grammar, and [`src/bin.ts`](src/bin.ts) dynamically imports only the selected runner. Unknown commands and leaked options fail with a nonzero exit code. +The `dsh` command is the product launcher for raw Cordis configurations, the Web UI, and one-shot headless tasks. [`src/args.ts`](src/args.ts) owns the command grammar, and [`src/bin.ts`](src/bin.ts) loads only the selected runner. Invalid commands, options from another mode, configuration errors, and boot failures exit nonzero. + +## Entry modes + +| Command | Purpose | +|---|---| +| `dsh --config ./app.cordis.yml` | Run an explicit patch-list configuration over the shipped base. | +| `dsh web` | Start the browser UI with the shipped Web composition and optional personal configuration. | +| `dsh -p "task"` | Run one fresh persisted session, print the final answer, and exit. | + +The invoking directory is the default workspace root. Web and headless share the shipped provider, persistence, policy, tool, repository Plugin, and telemetry composition; raw config selects its own deployment-specific front door. ## Raw config -Raw `dsh` requires an explicit patch-list config: +Raw `dsh` requires `--config`. The named patch list is applied directly over [`config/base.cordis.yml`](config/base.cordis.yml); it is not a complete replacement tree and does not add a surface overlay or personal `$DSH_HOME/config.yaml`. Use `--dump-default-config` and `--dump-config` to inspect the resulting tree without booting it. -```sh -dsh --config ./app.cordis.yml -``` +The [CLI behavior reference](reference/README.md) owns exact overlay precedence, flags, shutdown behavior, deployment defaults, and the source launcher. -The named file is applied directly over [`config/base.cordis.yml`](config/base.cordis.yml) through the Include plugin's patch algorithm. It is not a complete replacement tree, and neither the personal `$DSH_HOME/config.yaml` nor another surface overlay is added. The base deliberately contains no startup agent or interaction front door; the required overlay selects those deployment details. Relative config paths resolve from the invoking directory. A parse, schema, resolution, or plugin boot failure is reported and exits nonzero. SIGINT and SIGTERM dispose the mounted root before exit. +## Development -A patch targets a base row by `id` and replaces that row's complete `config` value rather than deep-merging keys. Patch lists may also insert new rows whose plugin modules the shipped Loader can resolve: - -```yaml -- id: agent-loop - config: - agents: - - id: main - provider: deepseek-official - model: deepseek-v4-flash -``` - -Inspect the effective tree without booting it: - -```sh -dsh --dump-default-config -dsh --config ./app.cordis.yml --dump-config -``` - -`--dump-default-config` prints only the shipped base. `--dump-config` requires `--config` and prints base plus overlay with provenance comments. Composition uses `applyEntryPatches` and `entryListSchema` from `@cordisjs/plugin-include`; `!!js` expressions remain unevaluated, and unmatched patch targets are reported on stderr. - -## Web and headless - -`dsh web` boots `base.cordis.yml` plus [`config/web.cordis.yml`](config/web.cordis.yml), followed by `$DSH_HOME/config.yaml` when present. `dsh web --config ` replaces that personal layer with the explicit patch list. `--host`, `--port`, `--workspace-root`, and repeatable `--trusted-host` values become Web host patches; their owning plugin schemas validate them at boot. `--dev` mounts the client-plugin HMR receiver and expects a separate `pnpm run dev:web` watcher for no-refresh client bundle updates. - -```sh -dsh web -dsh web --config ./web-profile.cordis.yml -dsh web --dump-default-config -dsh web --dump-config -``` - -The production Web runner needs built package and frontend artifacts (`pnpm run build`). It serves `http://127.0.0.1:3080` by default. Binding all interfaces also trusts the machine's discovered LAN IP literals; `--trusted-host` adds named authorities accepted by the `/api` browser-trust fence. - -`dsh -p "task"` uses the same base and Web composition with the startup personal config, starts its Web host on an OS-assigned port, runs one fresh persisted session, prints the final answer, and exits. It accepts neither `--config` nor raw config-dump flags. - -Web and headless process shutdown gives the plugin tree up to five seconds to dispose. The first `SIGINT`/`SIGTERM` starts that graceful drain; a second signal forces immediate exit. If headless normal completion is already stuck in disposal, the first `Ctrl+C` is the escalation and exits immediately instead of being swallowed. - -Both modes treat the invoking directory as the default workspace root, load applicable `AGENTS.md` or `CLAUDE.md` instructions with a 65,536-byte render budget, and use an in-memory SQLite session content index. Web watches valid personal config edits; headless reads the file once at startup. The [app-boot personal-config contract](../../packages/ui/app-boot/README.md#personal-config) owns layer precedence, credential storage, live-update failure behavior, and `$DSH_HOME` resolution. - -New sessions default to the `workspace-write` permission preset. Bash and filesystem mutations are restricted to the session workspace and platform temporary roots; reads, network access, and process visibility are not confined. `DSH_PERMISSION_MODE` changes the process fallback. Stored General-settings permissions affect later Web sessions, not an already-open one. - -`DSH_TOOLS_MODE` selects `native`, `code`, or `both` for the Web/headless process; another value fails at boot. [`config/core-web.cordis.yml`](config/core-web.cordis.yml) is an optional Web overlay that reduces the native model surface to persistent `bash` and `str_replace_editor` while retaining the shipped host, browser, workspace, persistence, and permission composition. - -## Shared deployment behavior - -The base mounts the native DeepSeek adapter, settings and credential providers, stable `web_search`, repository Plugin support, and session telemetry. Provider credentials live in `$DSH_HOME/.env` or the ambient environment and remain rotatable because the launcher never hoists the credential file into `process.env`. Search uses `DEEPSEEK_API_KEY` and accepts `DEEPSEEK_SEARCH_BASE_URL`; `web_fetch` is disabled unless an overlay inserts a provider and enables it. - -Session events stream as OTLP/HTTP logs by default. `DSH_TELEMETRY_OTLP_URL` selects another collector. Any non-empty `DSH_TELEMETRY_DISABLED` disables the telemetry row before boot. The shipped base has no telemetry redaction rule, so exported records can contain message text, tool arguments and results, and workspace paths; the [telemetry Agent Note](../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md) owns that deployment decision. - -The empty `repository-plugins` row lets Web/headless personal config and raw overlays mount prepared immutable repository Plugin generations. See the [repository Plugin contract](../../packages/cordis/repository-plugin/README.md#standalone-app-configuration). The CLI also ships `@deepseek-ai/dsh-mcp-client` as a dependency for overlays, but no MCP server is enabled by default because each server command is trusted executable code outside the agent sandbox. - -## Source launcher - -Link the source-running launcher onto PATH: - -```sh -ln -sf "$(pwd)/bin/dsh" ~/.local/bin/dsh -``` - -It resolves the checkout through its real path and launches `apps/cli/src/bin.ts` with `node --import tsx/esm`. `TSX_TSCONFIG_PATH` is pinned to the checkout root, so workspace package resolution is independent of the invoking directory. `pnpm run dsh` uses the same entry and forwards arguments. The built form is `apps/cli/lib/bin.js` after `pnpm run build`. +Production Web and headless runs require built package and frontend artifacts. From a checkout, `pnpm run dsh` runs the TypeScript entry and forwards arguments; the [source-launcher reference](reference/README.md#source-launcher) describes the PATH symlink and module-resolution contract. diff --git a/apps/cli/README.zh.md b/apps/cli/README.zh.md index d8d7122729..e97fec9d6b 100644 --- a/apps/cli/README.zh.md +++ b/apps/cli/README.zh.md @@ -2,75 +2,24 @@ [English](README.md) | 中文 -`dsh` 命令有三种入口模式:必需的原始配置 overlay、一次性 headless 提示词,以及 Web UI。[`src/args.ts`](src/args.ts) 拥有 Commander 命令行语法,[`src/bin.ts`](src/bin.ts) 只会动态导入选中模式的运行器。未知命令和误传入其他模式的选项都会以非零代码退出。 +`dsh` 命令是原始 Cordis 配置、Web UI 和一次性无头任务的产品启动器。[`src/args.ts`](src/args.ts) 负责命令语法,[`src/bin.ts`](src/bin.ts) 只加载选中的运行器。无效命令、来自其他模式的选项、配置错误和启动失败都会以非零状态退出。 + +## 入口模式 + +| 命令 | 用途 | +|---|---| +| `dsh --config ./app.cordis.yml` | 在随附基础配置之上运行显式 patch 列表配置。 | +| `dsh web` | 使用随附 Web 组合和可选个人配置启动浏览器 UI。 | +| `dsh -p "task"` | 运行一个新的持久化会话,打印最终答案并退出。 | + +调用目录是默认 workspace 根目录。Web 与无头模式共享随附的提供方、持久化、策略、工具、repository Plugin 和遥测组合;原始配置自行选择部署专用前端入口。 ## 原始配置 -原始 `dsh` 要求显式传入一份 patch 列表配置: +原始 `dsh` 必须提供 `--config`。指定的 patch 列表直接应用到 [`config/base.cordis.yml`](config/base.cordis.yml) 之上;它不是完整替代树,也不会添加 surface overlay 或个人 `$DSH_HOME/config.yaml`。使用 `--dump-default-config` 和 `--dump-config` 可在不启动的情况下检查生成的配置树。 -```sh -dsh --config ./app.cordis.yml -``` +[CLI(命令行界面)行为参考](reference/README.md)负责确切的 overlay 优先级、flag、关闭行为、部署默认值和源码启动器。 -指定文件会通过 Include 插件的 patch 算法,直接应用在 [`config/base.cordis.yml`](config/base.cordis.yml) 之上。它不是完整替换树,系统也不会添加个人 `$DSH_HOME/config.yaml` 或其他 surface overlay。base 有意不包含启动 agent(智能体)或交互入口;必需的 overlay 负责选择这些部署细节。相对配置路径以调用目录为基准解析。配置解析、schema 校验、模块解析或插件启动失败都会被报告,并以非零代码退出。SIGINT 和 SIGTERM 会在退出前 dispose(资源释放)已挂载的根上下文。 +## 开发 -patch 通过 `id` 定位 base 配置项,并替换该配置项的完整 `config` 值,而不是深度合并各个键。它也可以插入新配置项: - -```yaml -- id: agent-loop - config: - agents: - - id: main - provider: deepseek-official - model: deepseek-v4-flash -``` - -可以在不启动应用的情况下检查有效配置树: - -```sh -dsh --dump-default-config -dsh --config ./app.cordis.yml --dump-config -``` - -`--dump-default-config` 只打印随附 base。`--dump-config` 要求提供 `--config`,并打印带来源注释的 base 与 overlay。组合过程使用 `@cordisjs/plugin-include` 的 `applyEntryPatches` 和 `entryListSchema`;`!!js` 表达式保持未求值状态,未匹配的 patch 目标会报告到 stderr。 - -## Web 与 headless - -`dsh web` 会启动 `base.cordis.yml` 加 [`config/web.cordis.yml`](config/web.cordis.yml),并在 `$DSH_HOME/config.yaml` 存在时继续应用该文件。`dsh web --config ` 会以显式 patch 列表替换个人层。`--host`、`--port`、`--workspace-root` 和可重复的 `--trusted-host` 值会转为 Web 宿主 patch;各自所属插件的 schema 会在启动时校验它们。`--dev` 会挂载客户端插件 HMR(热模块替换)接收器,要实现无需刷新的客户端 bundle 更新,还需单独运行 `pnpm run dev:web` watcher。 - -```sh -dsh web -dsh web --config ./web-profile.cordis.yml -dsh web --dump-default-config -dsh web --dump-config -``` - -生产 Web 运行器需要已构建的包(package)与前端产物(`pnpm run build`)。它默认通过 `http://127.0.0.1:3080` 提供服务。绑定所有网络接口时,系统也会信任本机探测到的 LAN IP 字面量;`--trusted-host` 可添加 `/api` 浏览器信任边界所接受的具名权威。 - -`dsh -p "task"` 使用相同的 base 与 Web 组合及启动时个人配置,在由操作系统分配的端口上启动 Web 宿主,运行一个全新的持久会话,打印最终答案后退出。它不接受 `--config` 或原始配置输出标志。 - -Web 与 headless 的进程关闭流程最多给插件树 5 秒执行 dispose。第一次 `SIGINT`/`SIGTERM` 会启动这次优雅排空;第二次信号会立即强制退出。如果 headless 的正常完成流程已经卡在 dispose 中,第一次 `Ctrl+C` 就会触发强制退出:进程立即结束,该信号不再被吞掉。 - -两种模式都以调用目录作为默认 workspace 根目录,加载适用的 `AGENTS.md` 或 `CLAUDE.md` 指令,渲染预算为 65,536 字节,并使用内存 SQLite 会话内容索引。Web 会持续应用有效的个人配置编辑;headless 只在启动时读取该文件一次。层次优先级、凭据存储、实时更新失败行为与 `$DSH_HOME` 解析均由 [app-boot 个人配置契约](../../packages/ui/app-boot/README.md#personal-config) 统一定义。 - -新会话默认使用 `workspace-write` 权限 preset。Bash 和文件系统写操作受限于会话 workspace 与平台临时根目录;读取、网络访问与进程可见性不受限制。`DSH_PERMISSION_MODE` 会改变进程回退值。已存储的常规设置权限会影响之后的 Web 会话,不会更改已打开的会话。 - -`DSH_TOOLS_MODE` 为 Web/headless 进程选择 `native`、`code` 或 `both`;其他值会在启动时失败。[`config/core-web.cordis.yml`](config/core-web.cordis.yml) 是可选的 Web overlay,它在保留随附宿主、浏览器、workspace、持久化与权限组合的同时,将面向原生模型的工具缩减为持久 `bash` 和 `str_replace_editor`。 - -## 共享部署行为 - -base 会挂载原生 DeepSeek 适配器、设置与凭据提供方、稳定的 `web_search`、仓库插件支持与会话遥测。提供方凭据位于 `$DSH_HOME/.env` 或环境中,且仍可轮换,因为启动器绝不会把凭据文件提升进 `process.env`。搜索使用 `DEEPSEEK_API_KEY` 并接受 `DEEPSEEK_SEARCH_BASE_URL`;除非 overlay 插入提供方并启用 `web_fetch`,否则后者处于禁用状态。 - -会话事件默认以 OTLP/HTTP 日志的形式流式发送。`DSH_TELEMETRY_OTLP_URL` 用于选择其他 collector。`DSH_TELEMETRY_DISABLED` 的任何非空值都会在启动前禁用遥测配置项。随附 base 没有遥测脱敏规则,因此导出记录可能包含消息文本、工具参数与结果,以及 workspace 路径;该部署决策由[遥测 Agent Note](../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md) 统一定义。 - -空的 `repository-plugins` 配置项允许 Web/headless 个人配置与原始 overlay 挂载已准备的不可变仓库插件 generation。详见[仓库插件契约](../../packages/cordis/repository-plugin/README.md#standalone-app-configuration)。CLI(命令行界面)还将 `@deepseek-ai/dsh-mcp-client` 作为 overlay 依赖发布,但默认不启用任何 MCP 服务器,因为每条服务器命令都是 agent 沙箱之外的受信任可执行代码。 - -## 源码启动器 - -将以源码运行的启动器链接到 PATH: - -```sh -ln -sf "$(pwd)/bin/dsh" ~/.local/bin/dsh -``` - -它会通过自身实际路径解析该检出,并使用 `node --import tsx/esm` 启动 `apps/cli/src/bin.ts`。`TSX_TSCONFIG_PATH` 固定指向检出根目录,因此 workspace 包解析不受调用目录影响。`pnpm run dsh` 使用同一入口并转发参数。构建后的形式是执行 `pnpm run build` 后的 `apps/cli/lib/bin.js`。 +生产环境的 Web 和无头运行需要已构建的包与前端产物。在 checkout 中,`pnpm run dsh` 会运行 TypeScript 入口并转发参数;[源码启动器参考](reference/README.md#source-launcher)说明 PATH 符号链接和模块解析契约。 diff --git a/apps/cli/reference/README.i18n.yaml b/apps/cli/reference/README.i18n.yaml index ff9cab7b06..7e5b8e5c58 100644 --- a/apps/cli/reference/README.i18n.yaml +++ b/apps/cli/reference/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write apps/cli/reference/README.md -README.md: 6c83dd8312cf797614b03e8632b72fd1133577d3 -README.zh.md: 00016ebc2f2cffa489072695cdc280ed2d875145 +README.md: b37ec9ed61ea4e9899a51316065d4188f30997ad +README.zh.md: ca29808a6c8e670f0d0b82c59b1a2c1fa0e13565 diff --git a/apps/cli/reference/README.md b/apps/cli/reference/README.md index 6c83dd8312..b37ec9ed61 100644 --- a/apps/cli/reference/README.md +++ b/apps/cli/reference/README.md @@ -2,81 +2,75 @@ English | [中文](README.zh.md) -Argv is parsed once through a [Commander](https://github.com/tj/commander.js) adapter ([`src/args.ts`](../src/args.ts)): one program whose default (no subcommand) is the TUI/headless surface (`--config`, `-p`/`--prompt`, `--resume`, `--dump-config`, `--dump-default-config`), whose `meta` subcommand is the same TUI over this checkout, whose `upgrade` subcommand is a guided-session entry, and whose `web` subcommand is the browser UI. `meta` and `upgrade` are experimental: each runs only with its `--experimental` flag or with `DSH_EXPERIMENTAL=1` in the environment, and fails loud (stderr, exit 1) otherwise. `src/bin.ts` switches on the resolved mode and dynamic-imports only that mode's module. `dsh --help` lists every mode and `dsh web --help` renders the web usage, `dsh --version` prints this app's version, and an unknown option or a mistyped `--resume` fails loud (stderr, exit 1) instead of misrouting. Every subcommand that shares no option with the default surface — `upgrade`, `web`, `meta` — rejects a leaked `--config`/`-p`/`--resume`/dump flag rather than running and dropping it. `dsh web`'s `--host`/`--port` are unvalidated pass-through overrides: the `dsh-host-webserver` schema is the single source of both the default (the shipped Web overlay value when a flag is absent) and validity, and rejects a bad value at boot. `--trusted-host` appends named authorities for the /api browser-trust fence; an all-interfaces bind additionally derives the machine's LAN IP literals itself ([`src/app-cli-entry.ts`](../src/app-cli-entry.ts)), so the printed LAN URL works without flags. +This reference defines the raw-config, Web, and headless command modes. Argv is parsed once through [`src/args.ts`](../src/args.ts), and [`src/bin.ts`](../src/bin.ts) dynamically imports only the selected runner. -The TUI surface: +## Raw config -- boots `base.cordis.yml` plus `tui.cordis.yml` through [`dsh-app-boot`](../../../packages/ui/app-boot/README.md); `--config ` applies a patch-list overlay instead of the personal overlay, while `--config-replace ` boots that file as the complete tree; -- resumes a persisted session with `dsh --resume ` and, when the Node host exposes `process.execve`, supplies the TUI's in-place handoff host: after selector preflight and current-session flush, the host disposes the app and replaces the process with a normalized resume invocation; runtimes without process replacement leave the session running and say so. This CLI owns session identity and the exit line rather than the config: it mints or selects the `main` session id and provides it, plus the exact command that reproduces this invocation, on the boot context ([`MAIN_SESSION_ID_KEY`](../../../packages/ui/tui/README.md) and `TUI_GOODBYE_MESSAGE_KEY`). No `cordis.yml` key can drop resume, and a missing or unreadable id fails loud instead of creating a fresh session; -- treats the **invoking directory** as the workspace — sessions, relative paths, and workspace instructions resolve from the cwd (`dsh meta` is the sole exception, below); -- tells the agent where its own source lives: after boot it adds a prompt section naming this harness checkout, resolved from the launcher's real path so it holds under a PATH symlink and an arbitrary cwd, so the self-referential `cordis` toolset can read and modify it; -- applies the personal overlay from `~/.dsh` (see [app-boot's Personal config](../../../packages/ui/app-boot/README.md#personal-config)): `config.yaml` patches the booted tree, while `.env` there is the credential provider's own store (never hoisted into the environment, so keys stay rotatable). Environment precedence is ambient > project `.env`. The shipped tree's Cordis HMR keeps `config.yaml` live; an explicit `--config` tree replaces that overlay, and a tree without HMR reads it at startup only. -- presents the [versioned first-run welcome](../../../.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md) through the mounted TUI overlay service when its immutable marker is absent under `DSH_HOME`; only Enter creates that version's marker, while Escape, disposal, or process exit leaves it eligible. The official DeepSeek icon, responsive terminal rasters, all-locale Chinese copy, and notice version are static local owners; the overlay never writes a session event or model context. -- registers bare `/compact`: while the agent is idle, it summarizes useful older history even below automatic pressure, rejects arguments, and reports success only after the standalone replacement bracket is durable. A prompt submitted during compaction keeps its queue identity and starts after that checkpoint; injected context remains visible. - -`dsh meta` is that same TUI with this harness checkout as the workspace, so working on dsh itself needs no `cd`. It chdirs to the checkout root — resolved from the launcher's real path, the same root the source-path prompt section names — after the environment is settled, so precedence is unchanged while the session cwd and HMR watch root move together. Meta always starts a fresh session and accepts no default-surface options; use ordinary `dsh --resume ` to resume a persisted session. - -`dsh upgrade` is a guided fresh-session entry over the default TUI surface: it mints a fresh session in the invoking directory and seeds its first turn with the bundled `dsh-upgrade` skill, exactly as if the user typed `/skill:`. The launcher passes the skill name on the boot context ([`INITIAL_SKILL_KEY`](../../../packages/ui/tui/README.md)), which the TUI auto-invokes once the chat is live. The command takes no options beyond the experimental gate — `--config`, `-p`, and `--resume` fail loud — and seeds only on this first launch, so a later `dsh --resume ` of the session is an ordinary TUI session with no re-injection. - -`dsh --dump-config` and `dsh web --dump-config` print the composed config tree — the shipped base, the surface overlay, and the `--config` or personal overlay, exactly the layers that surface would boot — as YAML on stdout and exit without booting; `--dump-default-config` stops at the surface overlay, so diffing the two shows precisely what the user layer changes. Each run of rows is preceded by a `# ==` comment naming the file it comes from and the layers that patched it (e.g. `# == base.cordis.yml, patched by tui.cordis.yml`), so the output shows provenance while staying one loadable document. Composition runs through the include's own patch algorithm and YAML dialect (`applyEntryPatches`/`entryListSchema` from `@cordisjs/plugin-include`), so the dump cannot drift from what boots; `!!js` expressions print verbatim and unevaluated, and a patch whose target row is absent is reported on stderr with its layer, mirroring the Loader's boot-time warning. Launcher-owned boot-context values (session identity, CLI-flag patches) are per-invocation facts outside the config tree and do not appear. The dump flags reject boot-only flags (`-p`, `--resume`, `--config-replace`) rather than silently ignoring them, and `--dump-default-config` takes no `--config`. - -The Web and headless surfaces boot `base.cordis.yml` plus `web.cordis.yml`, then apply `$DSH_HOME/config.yaml`; an explicit `--config ` replaces that personal overlay. Both surfaces otherwise share the same composition: both tell the coding agent its resolved model and session working directory, treat the invoking directory as the default project and Workspace root, create named Workspaces beneath that root unless `--workspace-root ` overrides it, load applicable `AGENTS.md`/`CLAUDE.md` instructions into each agent-loop request prefix with a 65,536-byte render budget, opt into first-message model titles, use the same bounded transient model-request retry policy as the TUI, and mount a disposable in-memory SQLite content-index service. Web additionally names the DeepSeek Harness Web GUI as the interaction surface, this checkout as its own source location, and the process's canonical local URL and mode in both the prompt and managed `$DSH_WEB_URL`/`$DSH_WEB_MODE`; references such as “this page” therefore identify the GUI without claiming access to implicit DOM, route, or screenshot state. In production mode the host reads rebuilt frontend dist and client bundles on the next request, so refreshing the existing URL updates that GUI without replacing its process. `dsh web --dev` mounts the client-plugin HMR receiver, but no-refresh updates additionally require `pnpm run dev:web` in the same checkout to watch and rebuild plugin bundles; shell and ordinary package changes still require a rebuild and page refresh. Bare `apps/web` Vite serving fails before listening because it cannot inject `window.__DSH_BOOT__`. The index service is ACTIVE at boot, while its `node:sqlite` module and database handle open only on the first content search. This keeps Node 22 startup output free of SQLite's experimental warning before search is used; the first actual search may still emit the runtime warning. Each service instance owns its database, so parallel invocations neither share unsupported SQLite state nor leave derived index files behind, and the first search lazily reconciles live and persisted logs. Headless differs only in listening on an OS-assigned port (parallel `dsh -p` runs never collide; the stderr-printed URL opens the live session in a browser). Both need the frontend dist and client bundles built (`pnpm run build && pnpm run build:web`). - -The shared composition defaults new TUI, Web, and headless sessions to the `workspace-write` permission preset (`workspace-write` file mode plus `ask` approval policy). Sandbox-enforced bash and filesystem mutations may write only under the session workspace and platform temporary roots; reads, network access, and process visibility are not confined. The browser answers one-shot approval requests and exposes the Access picker; the TUI exposes `/permission`, but has no approval-request answerer, so an automatic wider retry there fails closed until the user deliberately changes the session preset. `DSH_PERMISSION_MODE` changes the process fallback, while a stored General-settings Permission value applies to later sessions without changing an open one. - -All three surfaces consume `$DSH_HOME/config.yaml`; the TUI and Web apply valid edits live, while one-shot headless runs read it at startup. The shipped trees include an empty `repository-plugins` row, so a standalone user can add prepared GitHub Plugins without an SDK project or install command: - -```yaml -- id: repository-plugins - name: '@deepseek-ai/dsh-repository-plugin' - config: - repositories: - - 'github:PolyArch/humanize#' -``` - -The repository must contain a prepared `.dsh-plugin` package; the [repository Plugin contract](../../../packages/cordis/repository-plugin/README.md#standalone-app-configuration) documents authoring, nested Plugin paths, the immutable cache, trust boundary, and failure semantics. A failed live edit keeps the last good tree and emits Cordis's `hmr/config-update-failed` event. - -The shipped TUI and Web compositions register the native DeepSeek adapter plus pi-ai OpenAI and Anthropic profiles. Credentials and endpoint overrides come from the provider-standard `DEEPSEEK_API_KEY` / `DEEPSEEK_BASE_URL`, `OPENAI_API_KEY` / `OPENAI_BASE_URL`, and `ANTHROPIC_API_KEY` / `ANTHROPIC_BASE_URL` pairs in the boot's layered environment. - -Every surface also registers `web_search` and only `web_search`. Search uses DeepSeek's Anthropic-compatible Messages endpoint, resolves the same `DEEPSEEK_API_KEY` reference for every call, and accepts the separate `DEEPSEEK_SEARCH_BASE_URL` endpoint override; each search is an auxiliary model request with its own latency and token cost. `web_fetch` remains disabled and the composition mounts no default fetch provider, so deployments that need arbitrary page retrieval must opt in through an overlay. The deployment decision and its security boundary live in the [default Web search Agent Note](../../../.agents/notes/implemented/feature/2026-07-31-web-default-search.md). - -`DSH_TOOLS_MODE` selects the tool presentation mode for the whole Web/headless process: `native` (the schema default when unset), `code` (the `run_code`-only Code Mode wire), or `both`; any other value fails loud at boot through the `dsh-tools` config schema. The setting is process-wide because Loader composition is static. The TUI surface ignores it and pins `native`. - -[`core-web.cordis.yml`](../config/core-web.cordis.yml) is an opt-in `dsh web --config` overlay that keeps the shipped Web host, browser, Workspace, persistence, and permission composition while reducing the default native model surface to owner-scoped persistent `bash` and `str_replace_editor`. The PTY backend and editor consume the existing Web sandbox and filesystem providers. An open persistent shell prevents changing that session's permission mode until the shell closes, so a shell created under wider access cannot survive a downgrade. `DSH_TOOLS_MODE` still controls native/Code Mode presentation for the resulting two-tool registry. - -From a source checkout, start this minimal Web profile with: +Raw `dsh` requires an explicit patch-list config: ```sh -pnpm run dsh web --config apps/cli/config/core-web.cordis.yml +dsh --config ./app.cordis.yml ``` -Every `dsh` surface — TUI, Web, and headless — reports session telemetry by default (the row lives in the shared `base.cordis.yml`): every session-log event streams as OTLP/HTTP log records to `https://harness-telemetry.deepseeksvc.com/v1/logs` on a 10-second batch cadence. `DSH_TELEMETRY_OTLP_URL` points the exporter at a different collector; setting `DSH_TELEMETRY_DISABLED` to ANY non-empty value — including `0` or `false` — disables the row before it loads (a privacy switch prefers off-by-mistake over on-by-mistake). No redaction rule is mounted in this composition yet: exported records are the raw captured copy, including message text, tool arguments and results, and the session's working-directory path. The deployment rulings live in the [web-telemetry-default-mount Agent Note](../../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md). +The named file is applied directly over [`config/base.cordis.yml`](../config/base.cordis.yml) through the Include plugin's patch algorithm. It is not a complete replacement tree, and neither the personal `$DSH_HOME/config.yaml` nor another surface overlay is added. The base deliberately contains no startup agent or interaction front door; the required overlay selects those deployment details. Relative config paths resolve from the invoking directory. A parse, schema, resolution, or plugin boot failure is reported and exits nonzero. SIGINT and SIGTERM dispose the mounted root before exit. -MCP servers are not a shipped default, because a default would have to name one: `@deepseek-ai/dsh-mcp-client` mounts exactly one server per row and spawns it as a child process, outside `ctx.bash` and so outside the sandbox policy. The package is a runtime dependency of this CLI, so an installed `dsh` can mount your own servers from `$DSH_HOME/config.yaml` or a `--config` overlay without a source checkout: +A patch targets a base row by `id` and replaces that row's complete `config` value rather than deep-merging keys. Patch lists may also insert new rows whose plugin modules the shipped Loader can resolve: ```yaml -- insert: - - id: mcp-github - name: '@deepseek-ai/dsh-mcp-client' - config: - serverName: github - transport: stdio - command: npx - args: ['-y', '@modelcontextprotocol/server-github'] - env: - GITHUB_TOKEN: !!js process.env.GITHUB_TOKEN +- id: agent-loop + config: + agents: + - id: main + provider: deepseek-official + model: deepseek-v4-flash ``` -The model then sees `mcp__github__*`. See the [mcp-client README](../../../packages/mcp/mcp-client/README.md) for the Streamable HTTP transport and the full field table. +Inspect the effective tree without booting it: -## Install (developer machine) +```sh +dsh --dump-default-config +dsh --config ./app.cordis.yml --dump-config +``` -Symlink the source-running launcher onto your PATH; it resolves the checkout through its own real path, so code changes apply on the next launch with no build step: +`--dump-default-config` prints only the shipped base. `--dump-config` requires `--config` and prints base plus overlay with provenance comments. Composition uses `applyEntryPatches` and `entryListSchema` from `@cordisjs/plugin-include`; `!!js` expressions remain unevaluated, and unmatched patch targets are reported on stderr. + +## Web and headless + +`dsh web` boots `base.cordis.yml` plus [`config/web.cordis.yml`](../config/web.cordis.yml), followed by `$DSH_HOME/config.yaml` when present. `dsh web --config ` replaces that personal layer with the explicit patch list. `--host`, `--port`, `--workspace-root`, and repeatable `--trusted-host` values become Web host patches; their owning plugin schemas validate them at boot. `--dev` mounts the client-plugin HMR receiver and expects a separate `pnpm run dev:web` watcher for no-refresh client bundle updates. + +```sh +dsh web +dsh web --config ./web-profile.cordis.yml +dsh web --dump-default-config +dsh web --dump-config +``` + +The production Web runner needs built package and frontend artifacts (`pnpm run build`). It serves `http://127.0.0.1:3080` by default. Binding all interfaces also trusts the machine's discovered LAN IP literals; `--trusted-host` adds named authorities accepted by the `/api` browser-trust fence. + +`dsh -p "task"` uses the same base and Web composition with the startup personal config, starts its Web host on an OS-assigned port, runs one fresh persisted session, prints the final answer, and exits. It accepts neither `--config` nor raw config-dump flags. + +Web and headless process shutdown gives the plugin tree up to five seconds to dispose. The first `SIGINT`/`SIGTERM` starts that graceful drain; a second signal forces immediate exit. If headless normal completion is already stuck in disposal, the first `Ctrl+C` is the escalation and exits immediately instead of being swallowed. + +Both modes treat the invoking directory as the default workspace root, load applicable `AGENTS.md` or `CLAUDE.md` instructions with a 65,536-byte render budget, and use an in-memory SQLite session content index. Web watches valid personal config edits; headless reads the file once at startup. The [app-boot personal-config contract](../../../packages/ui/app-boot/README.md#personal-config) owns layer precedence, credential storage, live-update failure behavior, and `$DSH_HOME` resolution. + +New sessions default to the `workspace-write` permission preset. Bash and filesystem mutations are restricted to the session workspace and platform temporary roots; reads, network access, and process visibility are not confined. `DSH_PERMISSION_MODE` changes the process fallback. Stored General-settings permissions affect later Web sessions, not an already-open one. + +`DSH_TOOLS_MODE` selects `native`, `code`, or `both` for the Web/headless process; another value fails at boot. [`config/core-web.cordis.yml`](../config/core-web.cordis.yml) is an optional Web overlay that reduces the native model surface to persistent `bash` and `str_replace_editor` while retaining the shipped host, browser, workspace, persistence, and permission composition. + +## Shared deployment behavior + +The base mounts the native DeepSeek adapter, settings and credential providers, stable `web_search`, repository Plugin support, and session telemetry. Provider credentials live in `$DSH_HOME/.env` or the ambient environment and remain rotatable because the launcher never hoists the credential file into `process.env`. Search uses `DEEPSEEK_API_KEY` and accepts `DEEPSEEK_SEARCH_BASE_URL`; `web_fetch` is disabled unless an overlay inserts a provider and enables it. + +Session events stream as OTLP/HTTP logs by default. `DSH_TELEMETRY_OTLP_URL` selects another collector. Any non-empty `DSH_TELEMETRY_DISABLED` disables the telemetry row before boot. The shipped base has no telemetry redaction rule, so exported records can contain message text, tool arguments and results, and workspace paths; the [telemetry Agent Note](../../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md) owns that deployment decision. + +The empty `repository-plugins` row lets Web/headless personal config and raw overlays mount prepared immutable repository Plugin generations. See the [repository Plugin contract](../../../packages/cordis/repository-plugin/README.md#standalone-app-configuration). The CLI also ships `@deepseek-ai/dsh-mcp-client` as a dependency for overlays, but no MCP server is enabled by default because each server command is trusted executable code outside the agent sandbox. + +## Source launcher + +Link the source-running launcher onto PATH: ```sh ln -sf "$(pwd)/bin/dsh" ~/.local/bin/dsh ``` -Source launches run `apps/cli/src/bin.ts` through tsx's ESM-only hook (`node --import tsx/esm`), which transforms TypeScript and projects the root tsconfig `paths` map into module resolution. Node's native TypeScript modes are not used: Node 26 removed `--experimental-transform-types`, and strip-only mode rejects syntax the source graph relies on (vendored parameter properties, decorators, runtime enums/namespaces). The CJS hook stays off because the source graph is ESM-only and the CJS resolver adds ~0.4s of startup. `bin/dsh` pins `TSX_TSCONFIG_PATH` to the checkout's root tsconfig so resolution is cwd-independent, and the `dsh-source-launch-smoke` node-compat gate runs this exact launch vector on every supported Node line. tsx applies the `paths` map without checking dependency declarations, so declaration completeness rests on the static gates: the TUI configs resolve bare plugins through `examples/package.json`, the Web/headless `cordis.yml` through this package's `dependencies`, and `verify-cordis-config` requires every configured bare plugin to be declared, while allowing unrelated dependencies. - -`pnpm run dsh` runs the same entry from the repo root and forwards arguments directly, for example `pnpm run dsh -p "task"`. The built form (`lib/bin.js`, via `pnpm run build`) boots the same config under plain Node. +It resolves the checkout through its real path and launches `apps/cli/src/bin.ts` with `node --import tsx/esm`. `TSX_TSCONFIG_PATH` is pinned to the checkout root, so workspace package resolution is independent of the invoking directory. `pnpm run dsh` uses the same entry and forwards arguments. The built form is `apps/cli/lib/bin.js` after `pnpm run build`. diff --git a/apps/cli/reference/README.zh.md b/apps/cli/reference/README.zh.md index 00016ebc2f..ca29808a6c 100644 --- a/apps/cli/reference/README.zh.md +++ b/apps/cli/reference/README.zh.md @@ -2,81 +2,75 @@ [English](README.md) | 中文 -Argv 只会通过 [Commander](https://github.com/tj/commander.js) 适配器([`src/args.ts`](../src/args.ts))解析一次:同一个程序的默认形式(无子命令)是 TUI/无头界面(`--config`、`-p`/`--prompt`、`--resume`、`--dump-config`、`--dump-default-config`),`meta` 子命令是以本 checkout 为 workspace 的同一个 TUI,`upgrade` 子命令是引导会话入口,`web` 子命令则是浏览器 UI。`meta` 与 `upgrade` 是实验性命令:只有带上各自的 `--experimental` 标志或在环境中设置 `DSH_EXPERIMENTAL=1` 才会运行,否则明确报错(stderr,退出码 1)。`src/bin.ts` 按解析后的 mode 分支,仅动态导入该 mode 的模块。`dsh --help` 列出所有 mode,`dsh web --help` 渲染 Web 用法,`dsh --version` 打印此应用的版本;未知选项或拼错的 `--resume` 会明确报错(stderr,退出码 1),而不会被错路由。凡与默认界面不共享任何选项的子命令(`upgrade`、`web`、`meta`)都会拒绝泄漏进来的 `--config`/`-p`/`--resume`/dump 标志,而不会照常运行并丢弃它。`dsh web` 的 `--host`/`--port` 是未验证的直通覆盖:`dsh-host-webserver` schema 是默认值(标志缺失时使用已交付的 Web 覆盖层值)和有效性的唯一真源,并在启动时拒绝错误值。`--trusted-host` 为 /api 浏览器信任栅栏追加具名权威;全接口绑定还会自行推导本机的 LAN IP 字面量([`src/app-cli-entry.ts`](../src/app-cli-entry.ts)),因此打印出的 LAN URL 无需任何标志即可使用。 +本参考定义原始配置、Web 和无头命令模式。参数由 [`src/args.ts`](../src/args.ts) 统一解析,[`src/bin.ts`](../src/bin.ts) 只动态导入选中的运行器。 -TUI 界面: +## 原始配置 -- 通过 [`dsh-app-boot`](../../../packages/ui/app-boot/README.md) 启动 `base.cordis.yml` 与 `tui.cordis.yml`;`--config ` 应用一个补丁列表覆盖并替代个人覆盖,而 `--config-replace ` 将指定文件作为完整配置树启动; -- 使用 `dsh --resume ` 恢复已持久化会话。当 Node 宿主公开 `process.execve` 时,还会提供 TUI 的原地移交宿主:选择器预检并刷新当前会话后,宿主会对应用执行 dispose(资源释放),并以规范化的恢复调用替换进程;不支持进程替换的运行时会让会话继续运行并给出提示。会话身份与退出行由本 CLI 拥有,而非由配置指定:它创建或选定 `main` 会话 id,并把该 id 以及可复现本次调用的确切命令一起提供到启动上下文([`MAIN_SESSION_ID_KEY`](../../../packages/ui/tui/README.md) 与 `TUI_GOODBYE_MESSAGE_KEY`)。任何 `cordis.yml` 键都无法移除恢复能力;缺失或无法读取的 id 会明确报错,而不会创建新会话; -- 将 **调用目录** 视为 workspace:会话、相对路径和 workspace 指令都从 cwd 解析(`dsh meta` 是唯一例外,见下文); -- 告知 agent(智能体)自身源码所在位置:启动后添加一个命名此 harness checkout 的提示词段。该路径从启动器的真实路径解析,因此在 PATH 符号链接和任意 cwd 下仍然有效,使自指的 `cordis` 工具集可以读取并修改它; -- 应用 `~/.dsh` 中的个人覆盖(参见 [app-boot 的个人配置](../../../packages/ui/app-boot/README.md#personal-config)):`config.yaml` 修补已启动的树,而那里的 `.env` 是凭据提供方自己的存储(绝不会被提升进环境,因此密钥始终可轮换)。环境优先级为环境中已有的值 > 项目 `.env`。已交付配置树中的 Cordis HMR(热模块替换)会持续应用 `config.yaml` 的变更;显式 `--config` 配置树会替代该个人覆盖,未包含 HMR 的配置树只在启动时读取该文件。 -- 当 `DSH_HOME` 下不存在不可变确认标记时,通过已挂载的 TUI overlay 服务呈现[版本化首次运行欢迎页](../../../.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md);只有 Enter 会创建该版本的标记,Escape、资源释放或进程退出仍保留展示资格。官方 DeepSeek 图标、响应式终端栅格图、所有 locale 共用的中文文案和通知版本均由静态本地文件持有;overlay 不会写入会话事件或模型上下文。 -- 注册裸 `/compact`:agent 空闲时,即使未达到自动压力,也会摘要有效的较早历史;该命令拒绝参数,并只在独立替换标记对持久化后报告成功。压缩(compaction)期间提交的提示词保留其队列身份,并在该检查点之后启动;注入的上下文仍保持可见。 - -`dsh meta` 是以本 harness checkout 为 workspace 的同一个 TUI,因此开发 dsh 自身无需 `cd`。它在环境确定之后才 chdir 到 checkout 根目录(从启动器的真实路径解析,与源码路径提示词段所指的根目录相同),因此环境优先级不变,而会话 cwd 与 HMR 监视根目录会一并移动。Meta 始终创建新会话,不接受默认界面的任何选项;恢复已持久化会话应使用普通的 `dsh --resume `。 - -`dsh upgrade` 是默认 TUI 界面之上的引导式全新会话入口:它在调用目录中创建一个全新会话,并以内置 `dsh-upgrade` skill(技能)播种其首轮,效果等同于用户手动键入 `/skill:`。启动器将 skill 名称提供到启动上下文([`INITIAL_SKILL_KEY`](../../../packages/ui/tui/README.md)),TUI 在聊天就绪后自动调用它。该命令除实验性门槛外不接受任何选项——`--config`、`-p`、`--resume` 都会明确报错——且仅在首次启动时播种,因此之后 `dsh --resume ` 恢复该会话时是普通 TUI 会话,不会重复注入。 - -`dsh --dump-config` 和 `dsh web --dump-config` 把合成后的配置树——已交付的基础配置、界面覆盖层,以及 `--config` 或个人覆盖层,恰好是该界面启动时组装的那些层——以 YAML 打印到 stdout 后退出,不启动任何东西;`--dump-default-config` 止步于界面覆盖层,因此对两份输出做 diff 就能精确看出用户层改了什么。每段连续的行之前都有一条 `# ==` 注释,标明该段来自哪个文件以及被哪些层修补过(例如 `# == base.cordis.yml, patched by tui.cordis.yml`),因此输出既展示来源,又仍是一份可加载的文档。合成通过 include 自己的补丁算法和 YAML 方言(`@cordisjs/plugin-include` 的 `applyEntryPatches`/`entryListSchema`)完成,因此 dump 不可能与实际启动漂移;`!!js` 表达式原样打印、不求值,目标行不存在的补丁会连同其所在层报到 stderr,与 Loader 启动时的警告一致。由启动器持有的启动上下文值(会话身份、CLI 标志补丁)是每次调用的事实,位于配置树之外,不会出现。dump 标志会拒绝仅用于启动的标志(`-p`、`--resume`、`--config-replace`)而不是静默忽略它们,`--dump-default-config` 不接受 `--config`。 - -Web 和无头界面启动 `base.cordis.yml` 与 `web.cordis.yml`,随后应用 `$DSH_HOME/config.yaml`;显式的 `--config ` 会替代该个人覆盖。除此之外,两者共享同一套组合:两者都会告知编码 agent 所用模型和会话工作目录,将调用目录视为默认项目和 Workspace 根目录,除非通过 `--workspace-root ` 覆盖,否则会在该根目录下创建具名 Workspace;它们会把适用的 `AGENTS.md`/`CLAUDE.md` 指令加载到每个 agent-loop 请求前缀中,渲染预算为 65,536 字节,选用首条消息模型标题,采用与 TUI 相同的有界暂时性模型请求重试策略,并挂载一个可丢弃的内存 SQLite 内容索引服务。Web 还会明确说明交互界面是 DeepSeek Harness Web GUI、当前 checkout 是自身源码位置,并在提示词及受管的 `$DSH_WEB_URL`/`$DSH_WEB_MODE` 中提供该进程的规范本地 URL 和模式;因此,「这个页面」等表述会指向该 GUI,但 agent 不会声称可以访问未显式提供的 DOM、路由或截图状态。在生产模式下,宿主会在下次请求时读取重新构建的前端 dist 和客户端 bundle,因此刷新现有 URL 即可更新该 GUI,无须替换其进程。`dsh web --dev` 会挂载客户端插件的 HMR 接收端,但要实现无刷新更新,还需在同一 checkout 中运行 `pnpm run dev:web`,以监视并重新构建插件 bundle;shell 和普通包(package)的更改仍需重新构建并刷新页面。直接使用裸 `apps/web` Vite 服务会在开始监听前失败,因为它无法注入 `window.__DSH_BOOT__`。索引服务在启动时处于 ACTIVE 状态,但其 `node:sqlite` 模块与数据库句柄分别要到首次内容搜索才会导入和打开。这样可使 Node 22 在尚未使用搜索时的启动输出不出现 SQLite 实验性警告;首次实际搜索仍可能发出运行时警告。每个服务实例独占自己的数据库,因此并行调用既不会共享不受支持的 SQLite 状态,也不会留下派生索引文件,首次搜索还会惰性对账实时日志与持久化日志。无头界面唯一的差异是监听操作系统分配的端口(并行 `dsh -p` 运行绝不冲突;stderr 打印的 URL 会在浏览器中打开实时会话)。两者都需要先构建前端 dist 和客户端 bundle(`pnpm run build && pnpm run build:web`)。 - -共享组合把新建 TUI、Web 和无头会话的权限默认设为 `workspace-write` preset(`workspace-write` 文件模式加 `ask` 审批策略)。由沙箱强制约束的 bash 与文件系统修改只能写入会话工作区和平台临时根目录;读取、网络访问和进程可见性不受该策略约束。浏览器可以应答一次性审批请求,并提供 Access 选择器;TUI 提供 `/permission`,但没有审批请求应答者,因此自动请求更宽权限的重试会以拒绝方式关闭,直到用户主动更改会话 preset。`DSH_PERMISSION_MODE` 会更改进程回退值,而「通用」设置中已存储的「权限」值只适用于之后的会话,不会更改已打开的会话。 - -三个界面都会使用 `$DSH_HOME/config.yaml`;TUI 和 Web 实时应用有效编辑,而一次性无头运行只在启动时读取。已交付的配置树包含一个空的 `repository-plugins` 配置项,因此独立用户无需 SDK 项目或安装命令,只需配置即可添加已准备的 GitHub 插件: - -```yaml -- id: repository-plugins - name: '@deepseek-ai/dsh-repository-plugin' - config: - repositories: - - 'github:PolyArch/humanize#' -``` - -仓库必须包含已准备的 `.dsh-plugin` 包;[仓库插件契约](../../../packages/cordis/repository-plugin/README.md#standalone-app-configuration)说明创作方式、嵌套插件路径、不可变缓存、信任边界和失败语义。实时编辑失败时,最后一个可用树保持运行,并发出 Cordis 的 HMR 事件 `hmr/config-update-failed`。 - -已交付的 TUI 和 Web 组合会注册原生 DeepSeek 适配器,以及 pi-ai 的 OpenAI 和 Anthropic 提供方配置。凭据和端点覆盖来自启动分层环境中的提供方标准变量对:`DEEPSEEK_API_KEY` / `DEEPSEEK_BASE_URL`、`OPENAI_API_KEY` / `OPENAI_BASE_URL` 和 `ANTHROPIC_API_KEY` / `ANTHROPIC_BASE_URL`。 - -每个界面也都会注册 `web_search`,且只注册 `web_search`。搜索使用 DeepSeek 的 Anthropic 兼容 Messages 端点,每次调用都会解析同一个 `DEEPSEEK_API_KEY` 凭据引用,并接受独立的 `DEEPSEEK_SEARCH_BASE_URL` 端点覆盖;每次搜索都是一次辅助模型请求,会产生独立的延迟与 token 成本。`web_fetch` 仍处于禁用状态,组合也未挂载默认抓取提供方;需要任意页面抓取能力的部署必须通过覆盖层选择启用。部署决策及其安全边界见[默认 Web 搜索 Agent Note(agent 决策记录)](../../../.agents/notes/implemented/feature/2026-07-31-web-default-search.md)。 - -`DSH_TOOLS_MODE` 为整个 Web/无头进程选择工具呈现模式:`native`(未设置时的 schema 默认值)、`code`(仅含 `run_code` 的 Code Mode 线路)或 `both`;任何其他值都会经由 `dsh-tools` 配置 schema 在启动时明确报错。由于 Loader 组合是静态的,该设置作用于整个进程。TUI 界面会忽略该变量并固定为 `native`。 - -[`core-web.cordis.yml`](../config/core-web.cordis.yml) 是一个可选启用的 `dsh web --config` 覆盖层:它保留已交付的 Web 宿主、浏览器、Workspace、持久化与权限组合,同时将默认的原生模型界面精简为以所有者为作用域的持久 `bash` 以及 `str_replace_editor`。PTY 后端和编辑器分别消费现有的 Web 沙箱与文件系统提供方。持久 shell 处于打开状态时,会阻止所属会话更改权限模式;因此,在较宽权限下创建的 shell 无法在降权后继续存活。`DSH_TOOLS_MODE` 仍控制由此得到的双工具注册表采用原生/Code Mode 呈现。 - -在源码 checkout 中,用以下命令启动这个精简 Web profile: +原始 `dsh` 必须提供显式 patch 列表配置: ```sh -pnpm run dsh web --config apps/cli/config/core-web.cordis.yml +dsh --config ./app.cordis.yml ``` -每个 `dsh` 界面——TUI、Web 与无头——都默认上报会话遥测(该行位于共享的 `base.cordis.yml`):每条会话日志事件以 OTLP/HTTP 日志记录的形式、按 10 秒批处理节奏流向 `https://harness-telemetry.deepseeksvc.com/v1/logs`。`DSH_TELEMETRY_OTLP_URL` 可将 exporter 指向其他 collector;将 `DSH_TELEMETRY_DISABLED` 设为**任意非空值**——包括 `0` 或 `false`——都会在该行加载前将其关停(隐私开关取「宁可误关、不可误开」)。该组合当前未挂载任何脱敏规则:导出记录即原始捕获副本,包含消息正文、工具参数与结果、以及会话工作目录路径。部署口径见 [web-telemetry-default-mount Agent Note](../../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md)。 +指定文件通过 Include 插件的 patch 算法直接应用到 [`config/base.cordis.yml`](../config/base.cordis.yml) 之上。它不是完整替代树,也不会添加个人 `$DSH_HOME/config.yaml` 或其他 surface overlay。基础配置刻意不包含启动 agent(智能体)或交互前端入口;必填 overlay 负责选择这些部署细节。相对配置路径从调用目录解析。配置解析、schema 校验、模块解析或插件启动失败会得到报告并以非零状态退出。收到 SIGINT 或 SIGTERM 时,挂载的根节点会先 dispose(资源释放)再退出。 -MCP 服务器不是交付默认值,因为默认值必须点名一台服务器:`@deepseek-ai/dsh-mcp-client` 每一行只挂载一台服务器,并把它作为子进程 spawn;该进程不经 `ctx.bash`,因此也不受沙箱策略约束。该包是本 CLI 的运行时依赖,所以已安装的 `dsh` 无需源码检出即可从 `$DSH_HOME/config.yaml` 或 `--config` 覆盖层挂载你自己的服务器: +patch 通过 `id` 定位基础配置行,并替换该行完整的 `config` 值,而不是深度合并各键。patch 列表也可插入新行,只要随附 Loader 能解析其插件模块: ```yaml -- insert: - - id: mcp-github - name: '@deepseek-ai/dsh-mcp-client' - config: - serverName: github - transport: stdio - command: npx - args: ['-y', '@modelcontextprotocol/server-github'] - env: - GITHUB_TOKEN: !!js process.env.GITHUB_TOKEN +- id: agent-loop + config: + agents: + - id: main + provider: deepseek-official + model: deepseek-v4-flash ``` -模型随后会看到 `mcp__github__*`。Streamable HTTP 传输与完整字段表见 [mcp-client README](../../../packages/mcp/mcp-client/README.md)。 +可在不启动的情况下检查生效的配置树: -## 安装(开发机) +```sh +dsh --dump-default-config +dsh --config ./app.cordis.yml --dump-config +``` -将从源码运行的启动器符号链接到 PATH 上;它通过自身真实路径解析 checkout,因此代码更改会在下次启动时生效,无需构建: +`--dump-default-config` 只打印随附基础配置。`--dump-config` 必须与 `--config` 同时使用,并打印基础配置和带来源注释的 overlay。组合使用 `@cordisjs/plugin-include` 的 `applyEntryPatches` 与 `entryListSchema`;`!!js` 表达式保持未求值,找不到目标的 patch 会报告到 stderr。 + +## Web 与无头模式 + +`dsh web` 启动 `base.cordis.yml` 加 [`config/web.cordis.yml`](../config/web.cordis.yml),并在 `$DSH_HOME/config.yaml` 存在时继续加载它。`dsh web --config ` 用显式 patch 列表替代该个人层。`--host`、`--port`、`--workspace-root` 和可重复的 `--trusted-host` 值会成为 Web 宿主 patch;负责这些值的插件 schema 会在启动时验证它们。`--dev` 挂载客户端插件 HMR(热模块替换)接收器;若要无刷新更新客户端 bundle,还需单独运行 `pnpm run dev:web` watcher。 + +```sh +dsh web +dsh web --config ./web-profile.cordis.yml +dsh web --dump-default-config +dsh web --dump-config +``` + +生产 Web 运行器需要已构建的包和前端产物(`pnpm run build`)。默认服务地址是 `http://127.0.0.1:3080`。绑定所有接口时,还会信任机器自动发现的 LAN IP 字面量;`--trusted-host` 可添加 `/api` 浏览器信任围栏接受的具名 authority。 + +`dsh -p "task"` 使用同一基础配置和 Web 组合,并加载启动时的个人配置;它在 OS 分配的端口上启动 Web 宿主,运行一个新的持久化会话,打印最终答案并退出。它不接受 `--config` 或原始配置 dump flag。 + +Web 和无头进程关闭时会给插件树最多 5 秒完成 dispose。第一次 `SIGINT`/`SIGTERM` 启动该优雅排空;第二次信号强制立即退出。如果无头模式正常结束时已经卡在 dispose 中,第一次 `Ctrl+C` 就会升格并立即退出,而不会被吞掉。 + +两种模式都将调用目录作为默认 workspace 根目录,以 65,536 字节渲染预算加载适用的 `AGENTS.md` 或 `CLAUDE.md` 指令,并使用内存 SQLite 会话内容索引。Web 监视有效的个人配置编辑;无头模式只在启动时读取该文件。[app-boot 个人配置契约](../../../packages/ui/app-boot/README.md#personal-config)负责配置层优先级、凭据存储、实时更新失败行为和 `$DSH_HOME` 解析。 + +新会话默认使用 `workspace-write` 权限预设。Bash 和文件系统修改仅限于会话 workspace 与平台临时根目录;读取、网络访问和进程可见性不受限制。`DSH_PERMISSION_MODE` 更改进程后备值。General settings 中存储的权限影响后续 Web 会话,不改变已打开的会话。 + +`DSH_TOOLS_MODE` 为 Web/无头进程选择 `native`、`code` 或 `both`;其他值会导致启动失败。[`config/core-web.cordis.yml`](../config/core-web.cordis.yml) 是可选 Web overlay:它在保留随附宿主、浏览器、workspace、持久化和权限组合的同时,把原生模型 surface 缩减为持久 `bash` 和 `str_replace_editor`。 + +## 共享部署行为 + +基础配置挂载原生 DeepSeek 适配器、settings 与凭据提供方、稳定的 `web_search`、repository Plugin 支持和会话遥测。提供方凭据存放在 `$DSH_HOME/.env` 或环境中;启动器从不把凭据文件提升到 `process.env`,因此凭据可以轮换。搜索使用 `DEEPSEEK_API_KEY` 并接受 `DEEPSEEK_SEARCH_BASE_URL`;只有 overlay 插入提供方并启用 `web_fetch` 后,该工具才可用。 + +会话事件默认作为 OTLP/HTTP 日志流式发送。`DSH_TELEMETRY_OTLP_URL` 选择其他 collector。任何非空 `DSH_TELEMETRY_DISABLED` 都会在启动前禁用遥测配置行。随附基础配置没有遥测脱敏规则,因此导出的记录可能包含消息文本、工具参数与结果以及 workspace 路径;该部署决策由[遥测 Agent Note](../../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md)负责。 + +空 `repository-plugins` 行让 Web/无头个人配置和原始 overlay 能够挂载已准备的不可变 repository Plugin generation。参见 [repository Plugin 契约](../../../packages/cordis/repository-plugin/README.md#standalone-app-configuration)。CLI 还随附 `@deepseek-ai/dsh-mcp-client` 作为 overlay 的依赖,但默认不启用 MCP 服务器,因为每条服务器命令都是 agent 沙箱之外的受信任可执行代码。 + +## 源码启动器 + +把源码运行启动器链接到 PATH: ```sh ln -sf "$(pwd)/bin/dsh" ~/.local/bin/dsh ``` -源码启动会通过 tsx 的 ESM-only hook(`node --import tsx/esm`)运行 `apps/cli/src/bin.ts`,由它转换 TypeScript 并将根 tsconfig 的 `paths` 映射投射到模块解析中。不使用 Node 原生 TypeScript 模式:Node 26 移除了 `--experimental-transform-types`,而 strip-only 模式无法接受源码图依赖的语法(vendor 中的参数属性、装饰器、运行时 enum/namespace)。CJS hook 保持关闭,因为源码图是纯 ESM,而 CJS 解析器会增加约 0.4s 启动耗时。`bin/dsh` 将 `TSX_TSCONFIG_PATH` 固定到 checkout 的根 tsconfig,使解析与 cwd 无关;node-compat 门禁 `dsh-source-launch-smoke` 会在每条受支持的 Node 版本线上运行这一精确启动向量。tsx 应用 `paths` 映射时不检查依赖声明,声明完整性由静态门禁保障:TUI 配置通过 `examples/package.json` 解析裸插件,Web/无头 `cordis.yml` 通过本包的 `dependencies` 解析;`verify-cordis-config` 要求每个已配置的裸插件均已声明,同时允许存在无关依赖。 - -`pnpm run dsh` 从仓库根目录运行同一入口并直接转发参数,例如 `pnpm run dsh -p "task"`。构建形式(`lib/bin.js`,通过 `pnpm run build`)会在普通 Node 下启动同一配置。 +它通过 real path 解析 checkout,并使用 `node --import tsx/esm` 启动 `apps/cli/src/bin.ts`。`TSX_TSCONFIG_PATH` 固定到 checkout 根目录,因此 workspace 包解析不依赖调用目录。`pnpm run dsh` 使用同一入口并转发参数。运行 `pnpm run build` 后,构建形式为 `apps/cli/lib/bin.js`。 diff --git a/apps/web/tests/scaffold.ts b/apps/web/tests/scaffold.ts index ac6c92d5f0..a4498e0cf2 100644 --- a/apps/web/tests/scaffold.ts +++ b/apps/web/tests/scaffold.ts @@ -56,7 +56,7 @@ import type {} from '@deepseek-ai/dsh-agent' import { prepareWebRuntimeContext } from '../../cli/src/web.ts' import { DIST_INDEX, REPO_ROOT, requireDist } from './support.ts' -/** Snapshot mode for the lane, from $DSH_SNAPSHOT (same vocabulary as the ACP/TUI suites). */ +/** Snapshot mode for the lane, from $DSH_SNAPSHOT (same vocabulary as the other snapshot suites). */ export type WebSnapshotMode = 'replay' | 'record' | 'refresh' /** @@ -592,9 +592,9 @@ export async function compareOrRefreshGolden(goldenPath: string, actual: string, } /** - * Fixture-inventory guard (the TUI afterAll shape): the scenario directory - * holds exactly the expected files and every committed JSONL is a scrub - * fixed-point without a run-local browser RPC id. + * Fixture-inventory guard: the scenario directory holds exactly the expected + * files and every committed JSONL is a scrub fixed-point without a run-local + * browser RPC id. * @param dir - the scenario snapshot directory. * @param expected - the exact expected file inventory. */ diff --git a/docs/architecture.i18n.yaml b/docs/architecture.i18n.yaml index b5136c31df..720c38de26 100644 --- a/docs/architecture.i18n.yaml +++ b/docs/architecture.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/architecture.md -architecture.md: ee50e249f8e588a3f92f57db929c6bbfb1c853dd -architecture.zh.md: c2e596cd10c21be2bcaad11323277d04ef9e74a1 +architecture.md: 296aea2056294d08693845e1780a9ac268b21bfc +architecture.zh.md: c5f87c967d95c8934e919c4c01bc4acd4d3a1a70 diff --git a/docs/architecture.md b/docs/architecture.md index c4665bce6e..296aea2056 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -186,4 +186,4 @@ New behavior attaches to a documented extension point; a loop change updates thi | Fork a live session | call `ctx.sessions.fork(source, boundary?, childSessionId?)` | | Scope a registration to one agent | use its `agent.ctx` (see Agent Scope) | -The [extension cookbook](cookbook/extension-cookbook.md) has plugin skeletons and the feature-to-seam map; guides cover [packages](cookbook/adding-a-package.md), [tools](cookbook/adding-a-tool.md), [LLM adapters](cookbook/adding-an-llm-adapter.md), and [vendored packages](cookbook/adding-a-vendored-package.md). +The [extension cookbook](cookbook/extension-cookbook.md) has plugin skeletons; guides cover [packages](cookbook/adding-a-package.md), [tools](cookbook/adding-a-tool.md), [LLM adapters](cookbook/adding-an-llm-adapter.md), and [vendored packages](cookbook/adding-a-vendored-package.md). diff --git a/docs/architecture.zh.md b/docs/architecture.zh.md index 41a5a9de7c..c5f87c967d 100644 --- a/docs/architecture.zh.md +++ b/docs/architecture.zh.md @@ -186,4 +186,4 @@ idle inject: | fork 活跃会话 | 调用 `ctx.sessions.fork(source, boundary?, childSessionId?)` | | 将注册项限定到单个 agent | 使用其 `agent.ctx`(参见 Agent 作用域) | -[扩展实操手册(cookbook)](cookbook/extension-cookbook.md)提供插件骨架和功能到服务边界的映射;指南涵盖[包](cookbook/adding-a-package.md)、[工具](cookbook/adding-a-tool.md)、[LLM 适配器](cookbook/adding-an-llm-adapter.md)和 [vendored 包](cookbook/adding-a-vendored-package.md)。 +[扩展实操手册(cookbook)](cookbook/extension-cookbook.md)提供插件骨架;指南涵盖[包](cookbook/adding-a-package.md)、[工具](cookbook/adding-a-tool.md)、[LLM 适配器](cookbook/adding-an-llm-adapter.md)和 [vendored 包](cookbook/adding-a-vendored-package.md)。 diff --git a/docs/cookbook/adding-a-tool.i18n.yaml b/docs/cookbook/adding-a-tool.i18n.yaml index e317b64965..ca64363bd6 100644 --- a/docs/cookbook/adding-a-tool.i18n.yaml +++ b/docs/cookbook/adding-a-tool.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/cookbook/adding-a-tool.md -adding-a-tool.md: 0688e4a46b1eb2ca282a7ee970546a34b3c1cc68 -adding-a-tool.zh.md: e12d5e23a93d08f15fb0d1ac40229c5c4ab26e38 +adding-a-tool.md: cb418a9118901cc6572fb17125351bdda922434b +adding-a-tool.zh.md: d8b73a51d1dde8647e7a032648f8a6344fcc83eb diff --git a/docs/cookbook/adding-a-tool.md b/docs/cookbook/adding-a-tool.md index 0688e4a46b..cb418a9118 100644 --- a/docs/cookbook/adding-a-tool.md +++ b/docs/cookbook/adding-a-tool.md @@ -1,8 +1,8 @@ -# Cookbook: adding a tool +# Tool authoring reference English | [中文](adding-a-tool.zh.md) -How to give the model a new capability. The minimal shape below shows the contract; `packages/bash/tool-bash` is the production-grade three-package seam. +Reference for the contracts a model-facing tool must satisfy. For an ordered first tool, follow [Build a tool](../user/develop/basic/tool.md). `packages/bash/tool-bash` is the production-grade three-package example. ## The minimal shape @@ -35,7 +35,7 @@ export function apply(ctx: Context) { } ``` -Registration is effect-based: disposing the plugin fiber unregisters the tool (write the HMR test). Schemas flow into the system-prompt assembly automatically. +Registration is effect-based: disposing the plugin fiber unregisters the tool. Schemas flow into the system-prompt assembly automatically. ## Rules of the execute() contract @@ -89,6 +89,6 @@ Hard rules (they bite if broken): The neutral vocabulary lives in `dsh-tools`; tools never import a UI or transport type. Host/client runtimes map each `card` into their own view. The design and the why are in [the render-intent-union Agent Note](../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md); `dsh-tool-fs` (generic/diff) and `dsh-tool-bash` (terminal) are the reference implementations. -## Tests every tool needs +## Verification -Cover argument rejection, every canonical value and Native rendering shape, output-schema rejection, and HMR disposal. For a side-effecting tool, drive the real tool through the agent loop with a scripted `MockAdapter` and assert its `tool/call` and projected `tool/result` session events; prove the canonical value itself is not persisted. For a UI card, assert the exact `presentCall` and `presentResult` views and exercise the owning host/client projection. Add an assembled snapshot for the shipped model or UI behavior the tool changes. +Follow the [repository testing policy](../testing.md) and the owning package's test documentation. A shipped model- or UI-visible change requires the assembled coverage specified there. diff --git a/docs/cookbook/adding-a-tool.zh.md b/docs/cookbook/adding-a-tool.zh.md index e12d5e23a9..d8b73a51d1 100644 --- a/docs/cookbook/adding-a-tool.zh.md +++ b/docs/cookbook/adding-a-tool.zh.md @@ -1,8 +1,8 @@ -# 实操手册:添加工具 +# 工具编写参考 [English](adding-a-tool.md) | 中文 -如何为模型赋予一项新能力。下文的最小形态展示这项契约;`packages/bash/tool-bash` 是生产级、由三个包(package)构成的 seam。 +面向模型的工具必须满足哪些契约,均以本文为准。如需按步骤构建第一个工具,请阅读[构建工具](../user/develop/basic/tool.md)。`packages/bash/tool-bash` 是生产级的三包示例。 ## 最小形态 @@ -35,7 +35,7 @@ export function apply(ctx: Context) { } ``` -注册基于副作用:dispose(资源释放)插件 fiber 即注销该工具(请编写 HMR(热模块替换)测试)。schema 会自动流入系统提示词的组装过程。 +注册基于副作用:dispose(资源释放)插件 fiber 即注销该工具。schema 会自动流入系统提示词的组装过程。 ## execute() 契约的规则 @@ -89,6 +89,6 @@ producer 提供同步的 `cancel`、在资源清理后 settle 且不 reject 的 中性词汇定义在 `dsh-tools` 中;工具绝不导入 UI 或传输类型。host/client 运行时将每个 `card` 映射到各自的视图。设计与原因见[渲染意图联合体 Agent Note](../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md);`dsh-tool-fs`(generic/diff)和 `dsh-tool-bash`(terminal)是参考实现。 -## 每个工具必须的测试 +## 验证 -覆盖参数拒绝、每种规范值和 Native 渲染形态、输出 schema 拒绝以及 HMR dispose。对于有副作用的工具,使用脚本化的 `MockAdapter` 驱动真实工具通过 agent loop(智能体循环),并断言其 `tool/call` 和投影后的 `tool/result` 会话事件;同时证明规范值本身未被持久化。对于 UI 卡片,断言 `presentCall` 和 `presentResult` 的精确视图,并实际运行所属 host/client 投影。如果工具改变了已交付的模型或 UI 行为,请添加组装应用快照。 +遵循[仓库测试策略](../testing.md)和所属包的测试文档。已交付且面向模型或 UI 的变更必须提供其中规定的组装覆盖。 diff --git a/docs/cookbook/extension-cookbook.i18n.yaml b/docs/cookbook/extension-cookbook.i18n.yaml index a4c20672c0..632e840682 100644 --- a/docs/cookbook/extension-cookbook.i18n.yaml +++ b/docs/cookbook/extension-cookbook.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/cookbook/extension-cookbook.md -extension-cookbook.md: 07073c39f8a9b998b09b0815257d995b174c8be7 -extension-cookbook.zh.md: 10664af9a39f0a1663c316869ba8ef02f67c29fe +extension-cookbook.md: 66cdee676936029e4db03bf0c1a0436d58f29bcd +extension-cookbook.zh.md: 7e29d34b5777a4a4b19c8d3da2fc078df519c6d4 diff --git a/docs/cookbook/extension-cookbook.md b/docs/cookbook/extension-cookbook.md index 07073c39f8..66cdee6769 100644 --- a/docs/cookbook/extension-cookbook.md +++ b/docs/cookbook/extension-cookbook.md @@ -2,9 +2,7 @@ English | [中文](extension-cookbook.zh.md) -> FIXME: This important guide has not received sufficient human design review; complete that review before the first release. - -The three plugin shapes you write against the harness extension surface, as illustrative snippets (elided imports and helper stubs — not copy-paste-complete). For the full step-by-step guides see [adding a package](adding-a-package.md), [adding a tool](adding-a-tool.md), and [adding an LLM adapter](adding-an-llm-adapter.md); for the seams these hook into see [docs/architecture.md](../architecture.md). +Reference shapes for the harness extension surface. The snippets omit imports and helper implementations and are not copy-paste-complete. For concrete authoring paths, see the [package checklist](adding-a-package.md), [first-tool tutorial](../user/develop/basic/tool.md), [tool reference](adding-a-tool.md), and [LLM adapter guide](adding-an-llm-adapter.md); the [architecture](../architecture.md) owns the system and extension-seam map. ## A tool plugin @@ -92,38 +90,3 @@ export function apply(ctx: Context) { ## Runnable wirings Runnable leaves load their plugin trees from `examples/*/cordis.yml`; the root `demo:*` scripts and those leaf directories are the authoritative inventory. Non-interactive leaves use [`@deepseek-ai/dsh-cli-demo`](../../packages/examples/cli-demo), ACP leaves use [`@deepseek-ai/dsh-acp-demo`](../../packages/examples/acp-demo), JSON-RPC leaves use [`@deepseek-ai/dsh-jsonrpc-demo`](../../packages/examples/jsonrpc-demo), and the app packages share [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo). - -## The feature → mechanism map - -Every product feature maps to a listener on a documented extension seam — the microkernel claim made checkable ([microkernel Agent Note](../../.agents/notes/implemented/architecture/2026-06-11-microkernel-event-taxonomy.md)). No row modifies the loop. - -`system-prompt/assemble` is an expert cooperative whole-assembly transform: its returned assembly is authoritative, so listener authors own preserving active Code Mode and structured-output protocol contributions. Prefer `ctx.tools.restrict()` for tool filtering that must stay aligned across presentation, lookup, and execution. - -| Product feature | Plugin mechanism | -|---|---| -| Hook system (user + project level) | listeners on `agent/session-start`, `agent/prompt-submit`, `agent/request`, `tools/pre-execute`, `tools/post-execute`, and `agent/turn-stopping`; the waterfall seams return typed decisions, while `agent/turn-stopping` may steer another step; the `dsh-hooks-claude` / `dsh-hooks-codex` bridges map hook config files onto these seams | -| `/goal` | `ctx.goals` owns durable state, `dsh-goal-session` schedules same-session rounds through the public `Agent`, and separate command/tool producers expose human/model control | -| `/loop` | on the `turn/end` session event, `followup()` the next iteration; or force-continue | -| Dynamic workflow | `ctx.workflows` + the worker-thread engine + the `workflow` tool; structured in-process children enforce output with scoped prompt/tool registrations, a monotonic tool guard, final `tools/result` commit (including enclosing `run_code`), and the structured-output execution's monotonic `concludeTurn()` marker | -| Queued + steering messages | core `Agent.followup()` / `Agent.steer()` | -| Context compaction (auto + manual) | the `ctx.compact` seam + `dsh-compact-basic`; automatic pressure runs on serial `agent/step`, canonical overflow recovery runs on `agent/request-error`, and manual callers use the same compact service ([compaction Agent Note](../../.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md) — the model-facing `/compact` consumer tool is deferred) | -| System prompt configurability | `ctx.systemPrompt.section()` with ordering and scope-local shadowing | -| AGENTS.md (root) | a section provider reading the file | -| AGENTS.md (subdir, on-touch) + file-change notices | `agent.inject()` from a watcher / tool-result listener | -| Built-in tools | `ctx.tools.register()`; schemas flow into the assembly automatically — the `dsh-tool-*` families (bash, fs, web, subagent, todo) are the shipped examples | -| ToolSearch / progressive disclosure | replace a scoped `ctx.tools.restrict()` registration as the visible set changes; the registry keeps presentation, lookup, and execution aligned | -| Tool deadline / retry / metrics | wrap core dispatch with `tools/execute`; a wrapper may replace `exec.signal`, delegate, and inspect the normalized result in one lexical lifetime | -| Final tool-result metrics / audit / capture | observe immutable authoritative outcomes with `tools/result`; use `tools/post-execute` instead only when the plugin must transform the result or attach context | -| Monotonic terminal turn policy | call `ToolExecution.concludeTurn()` from the successful terminal tool; later tool calls in the same response remain guardable, and the loop stops after the step | -| Subprocess sandbox (landlock / sandbox-exec) | use a `ctx.sandbox` backend through `dsh-bash-sandbox`; use `tools/pre-execute` for capability-level denial | -| Permission system / AskUserQuestion | return `ask` from `tools/pre-execute` and answer through `ctx.approval`; register a separate model-facing ask tool for ordinary user questions | -| Plan mode | Shipped: [`@deepseek-ai/dsh-plan-mode`](../../packages/plan/plan-mode/README.md) — logged `plan/mode` state, the `plan:policy` guidance section, `/plan [message]` entry, `/plan off` direct exit, and the user-reviewed `exit_plan_mode` exit; enforcement stays on the independent sandbox/approval axes | -| Sub-agent delegation | the `ctx.subagents` provider registry (`dsh-subagent-spawn`/`-fork`/`-acp`) + `dsh-tool-subagent` exposing one configured provider to the model | -| MCP | one plugin per server: discover tools → `ctx.tools.register()` | -| Skills | section + tool registration; `inject()` skill content on invocation | -| Memory | section provider + tool | -| Scheduled tasks (cron) | a plugin registers model-callable scheduling tools; timer fires → `followup(…, {source: {kind: 'cron', …}})` when idle / `inject()` notification when busy | -| UI (GUI; CLI emits JSONL) | listen `session/event` (assistant chunks, boundaries, tool activity); input → `followup()` | -| Telemetry / replayable trace | `session/event` → JSONL; replay = `sessions.create(id, { seed })` | -| Model adapters | `LlmAdapter` subclass via `registerAdapter` (`dsh-llm-deepseek`, `dsh-llm-pi-ai`) | -| Plugin hot-reload | every registration is a `ctx.effect` → vendored HMR just works | diff --git a/docs/cookbook/extension-cookbook.zh.md b/docs/cookbook/extension-cookbook.zh.md index 10664af9a3..7e29d34b57 100644 --- a/docs/cookbook/extension-cookbook.zh.md +++ b/docs/cookbook/extension-cookbook.zh.md @@ -2,9 +2,7 @@ [English](extension-cookbook.md) | 中文 -> FIXME:这篇重要指南尚未经过充分的人工设计审查;请在首次发布前完成审查。 - -针对 harness 扩展表面编写的三种插件形态,以示意性代码片段呈现(省略了 import 和辅助桩——不可直接复制运行)。完整的分步指南见[添加包(package)](adding-a-package.md)、[添加工具](adding-a-tool.md)和[添加 LLM(大语言模型)适配器](adding-an-llm-adapter.md);这些插件所挂接的 seam 见 [docs/architecture.md](../architecture.md)。 +harness 扩展表面的参考形态。代码片段省略了 import 和辅助实现,无法直接复制运行。具体编写路径见[包检查清单](adding-a-package.md)、[第一个工具教程](../user/develop/basic/tool.md)、[工具参考](adding-a-tool.md)和 [LLM(大语言模型)适配器指南](adding-an-llm-adapter.md);系统与扩展 seam 映射由[架构文档](../architecture.md)负责。 ## 工具插件 @@ -92,38 +90,3 @@ export function apply(ctx: Context) { ## 可运行的组装示例 可运行叶子从 `examples/*/cordis.yml` 加载各自的插件树;根目录的 `demo:*` 脚本和这些叶子目录是权威清单。非交互式叶子使用 [`@deepseek-ai/dsh-cli-demo`](../../packages/examples/cli-demo),ACP 叶子使用 [`@deepseek-ai/dsh-acp-demo`](../../packages/examples/acp-demo),JSON-RPC 叶子使用 [`@deepseek-ai/dsh-jsonrpc-demo`](../../packages/examples/jsonrpc-demo),应用包共享 [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo)。 - -## 功能→机制映射 - -每个产品功能都映射到一个文档化扩展 seam 上的监听器——微内核声明由此可验证([微内核 Agent Note](../../.agents/notes/implemented/architecture/2026-06-11-microkernel-event-taxonomy.md))。没有任何一行修改循环本身。 - -`system-prompt/assemble` 是一个专家协作式的整体装配变换:其返回的装配结果具有权威性,因此监听器作者有责任保留活跃的 Code Mode 和结构化输出协议的贡献。对于需要在展示、查找和执行之间保持对齐的工具过滤,优先使用 `ctx.tools.restrict()`。 - -| 产品功能 | 插件机制 | -|---|---| -| 钩子系统(用户级 + 项目级) | `agent/session-start`、`agent/prompt-submit`、`agent/request`、`tools/pre-execute`、`tools/post-execute` 和 `agent/turn-stopping` 上的监听器;waterfall seam 返回类型化决策,`agent/turn-stopping` 则可通过 steering 触发下一步;`dsh-hooks-claude` / `dsh-hooks-codex` 桥接器将钩子配置文件映射到这些 seam 上 | -| `/goal` | `ctx.goals` 管理持久状态,`dsh-goal-session` 通过公共 `Agent` 调度同会话回合,独立的命令/工具生产方分别提供人类/模型控制 | -| `/loop` | 在 `turn/end` 会话事件上 `followup()` 下一次迭代;或强制继续 | -| 动态工作流 | `ctx.workflows` + worker-thread 引擎 + `workflow` 工具;结构化的进程内子任务通过作用域化的 prompt/工具注册、单调工具守卫、最终 `tools/result` 提交(包括外层 `run_code`)和结构化输出执行的单调 `concludeTurn()` 标记来强制输出 | -| 排队消息 + steering(中途引导) | 核心 `Agent.followup()` / `Agent.steer()` | -| 上下文压缩(context compaction)(自动 + 手动) | `ctx.compact` seam + `dsh-compact-basic`;自动压力检查运行在串行 `agent/step`,规范化溢出恢复运行在 `agent/request-error`,手动调用方使用同一个压缩服务([压缩 Agent Note](../../.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md)——面向模型的 `/compact` 消费方工具已推迟) | -| 系统提示词可配置性 | `ctx.systemPrompt.section()`,支持排序与作用域局部覆盖 | -| AGENTS.md(根目录) | 一个读取该文件的 section provider | -| AGENTS.md(子目录,按需触发)+ 文件变更通知 | 从 watcher / tool-result 监听器调用 `agent.inject()` | -| 内置工具 | `ctx.tools.register()`;schema 自动流入装配——`dsh-tool-*` 系列(bash、fs、web、subagent、todo)是已交付的示例 | -| ToolSearch / 渐进式披露 | 当可见集变化时替换一个作用域化的 `ctx.tools.restrict()` 注册;注册表保持展示、查找和执行三者对齐 | -| 工具截止时间 / 重试 / 指标 | 用 `tools/execute` 包裹核心分发;包装器可替换 `exec.signal`、委托执行,并在同一词法生命周期内检视规范化结果 | -| 最终工具结果指标 / 审计 / 捕获 | 用 `tools/result` 观察不可变的权威结果;仅当插件需要变换结果或附加上下文时才使用 `tools/post-execute` | -| 单调终端轮次策略 | 从成功的终端工具调用 `ToolExecution.concludeTurn()`;同一响应中后续工具调用仍可由守卫阻止,循环在该步骤后停止 | -| 子进程沙箱(landlock / sandbox-exec) | 通过 `dsh-bash-sandbox` 使用 `ctx.sandbox` 后端;能力级别的拒绝使用 `tools/pre-execute` | -| 权限系统 / AskUserQuestion | 从 `tools/pre-execute` 返回 `ask` 并通过 `ctx.approval` 应答;为普通用户提问注册一个独立的面向模型的 ask 工具 | -| Plan mode | 已交付:[`@deepseek-ai/dsh-plan-mode`](../../packages/plan/plan-mode/README.md) — 落日志的 `plan/mode` 状态、`plan:policy` 引导段、`/plan [message]` 入口、`/plan off` 直接退出,以及经用户评审的 `exit_plan_mode` 出口;强制约束留在独立的沙箱/审批轴上 | -| 子 agent 委派 | `ctx.subagents` 提供方注册表(`dsh-subagent-spawn`/`-fork`/`-acp`)+ `dsh-tool-subagent` 向模型暴露一个已配置的提供方 | -| MCP | 每个服务器一个插件:发现工具 → `ctx.tools.register()` | -| Skill(技能) | section + 工具注册;调用时通过 `inject()` 注入 skill 内容 | -| 记忆 | section provider + 工具 | -| 定时任务(cron) | 插件注册面向模型的调度工具;定时器触发 → 空闲时 `followup(…, {source: {kind: 'cron', …}})`/忙碌时 `inject()` 通知 | -| UI(GUI;CLI 输出 JSONL) | 监听 `session/event`(助手分片、边界、工具活动);输入 → `followup()` | -| 遥测 / 可回放 trace | `session/event` → JSONL;回放 = `sessions.create(id, { seed })` | -| 模型适配器 | 通过 `registerAdapter` 注册 `LlmAdapter` 子类(`dsh-llm-deepseek`、`dsh-llm-pi-ai`) | -| 插件热重载 | 每个注册都是一个 `ctx.effect` → vendor 的 HMR(热模块替换)直接生效 | diff --git a/docs/core-data-structures/llm-streaming.i18n.yaml b/docs/core-data-structures/llm-streaming.i18n.yaml index 2919081f15..11d492eb23 100644 --- a/docs/core-data-structures/llm-streaming.i18n.yaml +++ b/docs/core-data-structures/llm-streaming.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/core-data-structures/llm-streaming.md -llm-streaming.md: 28f775f81049d3aa0198e75fbb6c4544302e772d -llm-streaming.zh.md: db6b28acb844033025b3c5ffef075c9c3153151e +llm-streaming.md: 8f5b917ee19044eeef70aadf68cff752b8fba76a +llm-streaming.zh.md: 10e04f0856a96f86c2145cdca51edb9e28020e67 diff --git a/docs/development.i18n.yaml b/docs/development.i18n.yaml index 619aa92d76..95ed34cce0 100644 --- a/docs/development.i18n.yaml +++ b/docs/development.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/development.md -development.md: 77087a95fdb4cfbeb02111ef2560d6989cde5686 -development.zh.md: 18e130e2f3aa1c53f6efa70366cdb629df71ad85 +development.md: 30a2bd0a2c97df8d3d75ec50f47b861b3a65590e +development.zh.md: 5582a85429c97c3e31517a495c69392b80885f7d diff --git a/docs/development.md b/docs/development.md index 2ebe4e0869..30a2bd0a2c 100644 --- a/docs/development.md +++ b/docs/development.md @@ -2,16 +2,18 @@ English | [中文](development.zh.md) -This onboarding guide helps project contributors get started with the local environment, daily workflow, and CI flow; see the Agent Notes for design rationale and technical trade-offs. +The setup tutorial takes a new contributor from prerequisites to a checked checkout. The contributor reference that follows covers repository layout, daily workflow, and CI shape. Design rationale and implementation details belong to the linked Agent Notes and scripts. -## Prerequisites +## Setup tutorial + +### Prerequisites - Node.js supports 22.19+ and 24+. CI covers 22.19, 24, and 26; see the [Node engine floor Agent Note](../.agents/notes/implemented/process/2026-07-06-node-engine-floor.md). - Corepack-enabled pnpm. The repo pins `pnpm@11.7.0` in `package.json`; run `corepack enable` if `pnpm --version` does not resolve through Corepack. - Git 2.26 or newer; hook setup enables Git's worktree-specific configuration extension. - Optional: a DeepSeek API key for the Web, headless, and ACP automation demos and real-API e2e tests. -## First-time setup +### First-time setup Install dependencies from the repo root: @@ -19,7 +21,7 @@ Install dependencies from the repo root: pnpm install ``` -The install also runs the root `postinstall` script, which installs lefthook from the repo dev dependency through `scripts/install-lefthook.mjs`. With `CI=true` or `GITHUB_ACTIONS=true`, the wrapper returns before Git discovery because automated jobs do not consume contributor hooks. Otherwise, it requires Git 2.26 or newer and gives the current worktree an explicit hook directory under its own Git directory; linked worktrees therefore use their own lefthook binary and configuration instead of rewriting common hooks. The first install enables Git's worktree-specific configuration extension and repository format 1; see the [worktree-local hooks Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md). +The install also configures worktree-local lefthook hooks through `scripts/install-lefthook.mjs`. The [worktree-local hooks Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md) owns the safety and migration contract. If hooks are missing because dependencies were restored from cache or `postinstall` was skipped, install them manually: @@ -27,11 +29,7 @@ If hooks are missing because dependencies were restored from cache or `postinsta node scripts/install-lefthook.mjs ``` -The wrapper refuses user-owned `core.hooksPath` values. An inherited system, global, or common-repository path requires `DSH_LEFTHOOK_ALLOW_HOOKS_PATH_OVERRIDE=1`. When Git seeds a new worktree with another registered worktree's marker-backed hook path, the wrapper replaces that copied value with the new worktree's own path; command-scoped and other worktree-scoped paths must be integrated or removed explicitly. - -Before enabling worktree config, migrate direct `extensions.*` in a format-0 common config, direct `core.worktree` or `core.bare=true`, and any non-empty dormant `config.worktree`. The common config and every worktree config must be regular files, while the owned hook directory may contain only unaliased regular files. - -After moving a checkout, rerun the wrapper to relocate its owned path and regenerate hooks. For a stale or invalid installer lock, first confirm no installer is running, then remove the reported lock and retry. If installation and hook-path rollback both fail, inspect the reported worktree config before retrying. The [worktree-local hooks Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md) owns the full safety contract. +If the wrapper rejects existing Git configuration or reports a stale lock, follow its diagnostic and the linked Agent Note rather than editing worktree metadata speculatively. After moving a checkout, rerun the wrapper to regenerate the owned path. Run typecheck once after a fresh clone: @@ -39,9 +37,13 @@ Run typecheck once after a fresh clone: pnpm run typecheck ``` -That first typecheck runs the whole-repo `tsc -b` graph: it emits every package/vendor `lib/types` and checks examples, tests, and scripts through the two no-emit aggregates described below. +Setup is complete when `pnpm run typecheck` exits successfully. -## TypeScript project layout +## Contributor reference + +### TypeScript project layout + +The repository typecheck runs the whole-repo `tsc -b` graph: it emits every package/vendor `lib/types` and checks examples, tests, and scripts through two no-emit aggregates. The repository's TypeScript configuration has exactly three roles; every tsconfig file plays one of them. @@ -68,7 +70,7 @@ pnpm run build `pnpm run hygiene` includes `publint`, which validates package entrypoints against the built `lib/*.js` files, and `verify-node-next-types`, which validates built declarations against a temporary NodeNext consumer. A fresh worktree has no bundled JS or declarations until `pnpm run build` runs; ordinary commits and pushes do not require that build unless their selected checks consume it. -## Environment variables +### Environment variables The real DeepSeek adapter and key-backed agent demos read credentials from the environment or from a gitignored `.env` at the repo root: @@ -79,7 +81,7 @@ DEEPSEEK_BASE_URL=https://... # optional `DEEPSEEK_BASE_URL` is optional and defaults to the public API. Never commit real credentials. The real-API e2e suites self-skip when `DEEPSEEK_API_KEY` is not set. -## Git hooks +### Git hooks lefthook is configured in `lefthook.yml` as a fast local checkpoint: @@ -92,15 +94,15 @@ The hooks intentionally do not run tests, snapshots, documentation checks, build Contributors can opt into the comprehensive local gate set with `pnpm run check:all`. The command is independent of both Git hooks and is not an agent instruction. -## CI gates +### CI gates The keyless [CI workflow](../.github/workflows/ci.yml) groups independent gates into broad lanes and runs a smaller compatibility signal across supported Node versions. Artifact consumers wait for one build within their lane. The separate real-API workflow runs `pnpm run test:e2e` with its configured worker bound. See [scripts/run-gates.ts](../scripts/run-gates.ts) and the workflow files for the current gate and job inventory. -## Daily commands +### Daily commands The root [contributor instructions](../AGENTS.md#commands) summarize common commands, while [`package.json`](../package.json) and [scripts/run-gates.ts](../scripts/run-gates.ts) own the current script and gate inventories. Select the smallest checks that cover the changed surface. Documentation changes use `pnpm run doc-sync`; package-public behavior changes also update the owning README or JSDoc, and built-artifact checks require `pnpm run build` first. -## Demos +### Demos The one-shot Headless coding agent needs `DEEPSEEK_API_KEY` in the environment or repo-root `.env`: @@ -120,7 +122,7 @@ The ACP automation server exposes fresh agent sessions over JSON-RPC stdio and a pnpm run demo:acp ``` -## TODO markers +### TODO markers Use one of three comment tags to flag known issues in the code, ordered by urgency: @@ -130,7 +132,7 @@ Use one of three comment tags to flag known issues in the code, ordered by urgen Pick the tag that matches the urgency so anyone scanning the code can tell a release blocker from a someday-maybe. -## Documenting types verbatim (`ts type-equiv`) +### Documenting types verbatim (`ts type-equiv`) The [core data structures](core-data-structures/core.md) docs paste source-equivalent declarations together with their original JSDoc so a reader sees the exact shape and source contract. To keep a paste from drifting when source changes, fence it as ` ```ts type-equiv ` (instead of ` ```ts `) and register it in `scripts/type-equiv.manifest.json` with the source file and symbol it mirrors: @@ -139,7 +141,3 @@ The [core data structures](core-data-structures/core.md) docs paste source-equiv ``` `pnpm run verify-type-equiv` (part of `doc-sync`) then extracts that symbol's declaration and attached JSDoc from source via the TypeScript parser and asserts the block matches both. For a class whose implementation bodies do not belong in the catalog, use ` ```ts public-api ` and set `"projection": "public-api"`; the checked projection retains the public fields, constructor, accessors, methods, and original class/member JSDoc while omitting bodies and private or protected members. Comparison ignores whitespace and non-JSDoc comments but requires every original JSDoc comment, including member documentation, so readers see the source contract beside the exact shape. The gate enforces a 1:1 correspondence by document, symbol, and projection between primary blocks and manifest entries; a paired `.zh.md` block reuses its unsuffixed sibling's entry only when the whole tracked fence sequence is byte-identical and ordered identically. `doc-typecheck` applies the same derivative rule to compilable fences, while skipping both source-equivalence fence kinds from compilation and its opt-out ratio. When you change a documented declaration or its JSDoc, the gate fails until you update the paste; when you add or remove a primary block, update the manifest in the same change. - -## Architecture context - -Read `docs/architecture.md` before changing anything under `packages/`. The codebase is built around Cordis plugins, event-sourced sessions, typed service seams, and explicit extension points. diff --git a/docs/development.zh.md b/docs/development.zh.md index 995b479ea6..5582a85429 100644 --- a/docs/development.zh.md +++ b/docs/development.zh.md @@ -2,16 +2,18 @@ [English](development.md) | 中文 -本指南覆盖参与 DeepSeek Harness 开发所需的本地环境搭建、日常工作流与 CI 流程;设计动机与技术权衡请查阅相应 Agent Note。 +搭建教程引导新贡献者从准备前置条件开始,直到检出通过检查。后面的贡献者参考介绍仓库布局、日常工作流和 CI 形态。设计依据与实现细节属于链接的 Agent Note 和脚本。 -## 前置条件 +## 搭建教程 + +### 前置条件 - Node.js 支持 22.19+ 与 24+。CI 覆盖 22.19、24 和 26;见 [Node 引擎下限 Agent Note](../.agents/notes/implemented/process/2026-07-06-node-engine-floor.md)。 - 启用了 Corepack 的 pnpm。仓库在 `package.json` 中固定使用 `pnpm@11.7.0`;如果 `pnpm --version` 无法通过 Corepack 解析,请先运行 `corepack enable`。 - Git 2.26 或更高版本;钩子设置会启用 Git 的 worktree 专属配置扩展。 - 可选:一个 DeepSeek API key,用于 Web、headless 和 ACP(Agent Client Protocol)自动化 agent(智能体)演示以及真实 API 的 e2e 测试。 -## 首次搭建 +### 首次搭建 在仓库根目录安装依赖: @@ -19,7 +21,7 @@ pnpm install ``` -安装过程同时会运行根目录的 `postinstall` 脚本,该脚本通过 `scripts/install-lefthook.mjs` 从仓库 dev 依赖安装 lefthook。当 `CI=true` 或 `GITHUB_ACTIONS=true` 时,该脚本会在探测 Git 前返回,因为自动化任务不会使用贡献者钩子。否则,包装脚本要求使用 Git 2.26 或更高版本,并会为当前 worktree 在其自身的 Git 目录下设置显式钩子目录;因此,关联 worktree 会使用各自的 lefthook 二进制文件和配置,而不会改写共用钩子。首次安装会启用 Git 的 worktree 专属配置扩展和仓库格式 1;见 [worktree 本地钩子 Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md)。 +安装过程还会通过 `scripts/install-lefthook.mjs` 配置 worktree 本地的 lefthook 钩子。其安全与迁移契约由 [worktree 本地钩子 Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md) 负责。 如果依赖是从缓存恢复或 `postinstall` 被跳过而导致缺少钩子,请手动安装: @@ -27,11 +29,7 @@ pnpm install node scripts/install-lefthook.mjs ``` -包装层会拒绝用户自有的 `core.hooksPath` 值。继承自系统、全局或共用仓库配置的路径必须设置 `DSH_LEFTHOOK_ALLOW_HOOKS_PATH_OVERRIDE=1`。当 Git 使用另一个已注册 worktree 中由所有权标记佐证的钩子路径初始化新 worktree 时,包装层会将这个复制值替换为新 worktree 自有的路径;命令作用域和其他 worktree 作用域的路径必须显式集成或移除。 - -启用 worktree 配置之前,请迁移格式 0 共用配置中直接设置的 `extensions.*`,并迁移直接设置的 `core.worktree` 或 `core.bare=true`,以及任何非空且尚未生效的 `config.worktree`。共用配置和每个 worktree 配置都必须是常规文件,而自有钩子目录只能包含不带别名的常规文件。 - -检出目录移动后,请重新运行包装层,使其重新定位自有路径并重新生成钩子。对于陈旧或无效的安装程序锁,请先确认没有安装程序正在运行,再移除报告的锁并重试。若安装和钩子路径回滚都失败,请在重试前检查报告的 worktree 配置。完整安全契约由 [worktree 本地钩子 Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md) 统一定义。 +如果包装脚本拒绝现有 Git 配置或报告陈旧锁,请遵循其诊断和所链接的 Agent Note,不要凭猜测编辑 worktree 元数据。移动检出目录后,请重新运行包装脚本以重新生成自有路径。 新克隆后请先运行一次类型检查: @@ -39,9 +37,13 @@ node scripts/install-lefthook.mjs pnpm run typecheck ``` -首次类型检查会执行全仓 `tsc -b tsconfig.json` 图:发射每个 package/vendor 的 `lib/types`,并通过下述两个 no-emit 聚合检查示例、测试和脚本。 +`pnpm run typecheck` 成功退出即表示搭建完成。 -## TypeScript 项目布局 +## 贡献者参考 + +### TypeScript 项目布局 + +仓库类型检查会执行全仓 `tsc -b` 图:它会发射每个 package/vendor 的 `lib/types`,并通过两个 no-emit 聚合检查示例、测试和脚本。 仓库的 TypeScript 配置只有三种角色;每个 tsconfig 文件恰好扮演其中一种。 @@ -68,7 +70,7 @@ pnpm run build `pnpm run hygiene` 包含 `publint`(用构建出的 `lib/*.js` 文件校验 package 入口点)和 `verify-node-next-types`(用一个临时的 NodeNext 消费方校验构建出的声明文件)。新 worktree 在 `pnpm run build` 运行之前没有打包的 JS 和声明文件;普通提交和推送无需构建,除非所选检查会使用这些产物。 -## 环境变量 +### 环境变量 真实的 DeepSeek 适配器和需要密钥的 agent 演示从环境变量或仓库根目录一个被 gitignore 的 `.env` 文件读取凭证: @@ -79,7 +81,7 @@ DEEPSEEK_BASE_URL=https://... # optional `DEEPSEEK_BASE_URL` 可选,默认为公开 API。请勿提交真实凭证。未设置 `DEEPSEEK_API_KEY` 时,真实 API 的 e2e 套件会自动跳过。 -## Git 钩子 +### Git 钩子 lefthook 在 `lefthook.yml` 中配置,作为快速的本地检查点: @@ -92,15 +94,15 @@ vendor manifest 守卫检查 `vendor/*/src` 下的改动是否连同对应的 `v 贡献者可以选择运行 `pnpm run check:all`,执行全面的本地门禁集。该命令独立于两个 Git 钩子,也不是对 agent 的指令。 -## CI 门禁 +### CI 门禁 keyless [CI 工作流](../.github/workflows/ci.yml) 将独立门禁分组到若干宽粒度 lane,并在受支持的 Node 版本上运行一组较小的兼容性检查。产物消费方在各自 lane 内等待一次 build。单独的真实 API 工作流按其配置的 worker 上限运行 `pnpm run test:e2e`。当前门禁和 job 清单以 [scripts/run-gates.ts](../scripts/run-gates.ts) 和工作流文件为准。 -## 日常命令 +### 日常命令 根目录的[贡献者说明](../AGENTS.md#commands)概述常用命令,[`package.json`](../package.json) 与 [scripts/run-gates.ts](../scripts/run-gates.ts) 则负责当前脚本和门禁清单。请选择覆盖变更表面的最小检查集。文档变更使用 `pnpm run doc-sync`;package 公开行为变更还需更新所属 README 或 JSDoc,而基于构建产物的检查需要先运行 `pnpm run build`。 -## 演示 +### 演示 单次运行的 Headless coding agent 需要环境变量或仓库根目录 `.env` 中的 `DEEPSEEK_API_KEY`: @@ -120,7 +122,7 @@ ACP 自动化服务器通过 JSON-RPC stdio 提供全新 agent 会话,同样 pnpm run demo:acp ``` -## TODO 标记 +### TODO 标记 请使用以下三种注释标签之一标记代码中的已知问题,按紧急程度排序: @@ -130,7 +132,7 @@ pnpm run demo:acp 请选择与紧急程度匹配的标签,让浏览代码的人一眼分清「发布阻塞」和「有空再说」。 -## 逐字记录类型(`ts type-equiv`) +### 逐字记录类型(`ts type-equiv`) [核心数据结构](core-data-structures/core.md)文档会把与源码等价的声明及其原始 JSDoc 一并粘贴,让读者看到确切形状和源码契约。为防止粘贴内容在源码变化时漂移,请将其围栏为 ` ```ts type-equiv `(而不是 ` ```ts `),并在 `scripts/type-equiv.manifest.json` 中登记它镜像的源文件和符号: @@ -139,7 +141,3 @@ pnpm run demo:acp ``` `pnpm run verify-type-equiv`(`doc-sync` 的一环)随后通过 TypeScript 解析器从源码提取该符号的声明及其附带的 JSDoc,并断言代码块同时匹配两者。对于不应把实现体写进目录的类,请使用 ` ```ts public-api ` 并设置 `"projection": "public-api"`;门禁检查的投影会保留公共字段、构造函数、访问器、方法以及类和成员的原始 JSDoc,同时省略实现体和私有或受保护成员。比对会忽略空白和非 JSDoc 注释,但要求保留每条原始 JSDoc(包括成员文档),让读者同时看到源码契约和确切形状。该门禁按文档、符号和投影,在主块与 manifest 条目之间强制 1:1 对应;只有当配对 `.zh.md` 块的完整受跟踪围栏序列与其无后缀兄弟文件按字节一致且顺序相同时,才会复用后者的条目。`doc-typecheck` 对可编译围栏应用同一派生规则,同时跳过两种源码等价围栏的编译,并将其排除在 opt-out 比例之外。当你改动一个已记录的类型声明或其 JSDoc 时,门禁会失败直到你更新粘贴内容;当你增删一个主块时,请在同一个变更里更新 manifest。 - -## 架构上下文 - -在修改 `packages/` 目录下的任何内容之前,请先阅读 `docs/architecture.md`。这套代码围绕 Cordis 插件、事件溯源的会话、类型化的服务 seam 与显式扩展点构建。 diff --git a/docs/testing.i18n.yaml b/docs/testing.i18n.yaml index 81929ba150..2fcbea62e4 100644 --- a/docs/testing.i18n.yaml +++ b/docs/testing.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/testing.md -testing.md: 728c65f1911cbaa098f653af9436a92336fbe068 -testing.zh.md: 72b7c3bcfa69f4c65fc128dbcbb19c080fb0fbc4 +testing.md: 514ca4e1df7505b02350470d5de4a5ee3647634b +testing.zh.md: 6d24d8e74d726d53fa3500f57e34238483c5481c diff --git a/docs/testing.md b/docs/testing.md index 728c65f191..514ca4e1df 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -9,7 +9,7 @@ How this repo tests, tier by tier, and the rules that keep a green suite meaning - **Unit** (`pnpm run test`): vitest over package and example specs under their `tests/**` directories plus repository script specs under `scripts/**/*.spec.ts`; tests stay with the code area they exercise. Every registry gets an HMR-safety test (dispose the contributing fiber, assert cleanup). Prefer edge cases, error paths, event ordering, concurrency races, and permanent contract regressions (see `packages/core/agent-loop/tests/contract-regressions.spec.ts`). - **Coverage gate** (`pnpm run test:coverage`): the gating run, per-file 100% on `packages/*/*/src`. An uncovered line is often dead code the gate is correctly flagging for deletion, not a missing test to bolt on. Line coverage is necessary, never sufficient — it proves lines ran, not that the feature works as shipped. Per-file 100% on `packages/bash/pwsh-local/src` needs a real `pwsh`: without one its executor suites self-skip and `vitest.config.ts` exempts the file so pwsh-less hosts stay green, while CI runners ship pwsh and enforce the full bar. - **Real-API e2e** (`pnpm run test:e2e`): with-key tests against live provider APIs — the DeepSeek model plus provider-specific smokes that gate on their own keys (`EXA_API_KEY`, `PERPLEXITY_API_KEY`, …); each suite self-skips without its key so keyless CI stays green ([real-API e2e Agent Note](../.agents/notes/implemented/testing/2026-06-19-real-api-e2e-ci.md)). -- **Snapshot** (`pnpm run test:snapshot`): keyless expected outputs cover external behavior — transport contracts and presentation, while persisted logs pin assembled backend behavior. ACP boots the real automation-server example, replays a recorded session, and diffs normalized JSON-RPC plus the re-persisted log ([ACP snapshot Agent Note](../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md)); headless pins `stream-json` through its real one-shot process. TUI journeys replay primary/child JSONL through the real loop and tools, then project ANSI into semantic terminal-state outputs; package snapshots retain transient states and a real PTY covers the process boundary ([TUI snapshot Agent Note](../.agents/notes/archived/testing/2026-07-18-tui-terminal-state-snapshots.md)). Use `pnpm run test:snapshot:record` when a model transcript changes and `pnpm run test:snapshot:refresh` when replay input remains valid; review every JSONL and expected-output diff. One ACP scenario (`text-turn`) pins full system-prompt/tool-schema content; other fixtures tokenize it so an edit churns one line ([pinned-header Agent Note](../.agents/notes/archived/testing/2026-07-06-pin-request-header-content-in-one-scenario.md)). +- **Snapshot** (`pnpm run test:snapshot`): keyless expected outputs cover external behavior — transport contracts and presentation, while persisted logs pin assembled backend behavior. ACP boots the real automation-server example, replays a recorded session, and diffs normalized JSON-RPC plus the re-persisted log ([ACP snapshot Agent Note](../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md)); headless pins `stream-json` through its real one-shot process. Use `pnpm run test:snapshot:record` when a model transcript changes and `pnpm run test:snapshot:refresh` when replay input remains valid; review every JSONL and expected-output diff. One ACP scenario (`text-turn`) pins full system-prompt/tool-schema content; other fixtures tokenize it so an edit churns one line ([pinned-header Agent Note](../.agents/notes/archived/testing/2026-07-06-pin-request-header-content-in-one-scenario.md)). - **Web browser snapshot** (`pnpm run test:web`; required Linux PR gate): Chromium compares replayed browser output with `apps/web/tests/snapshots/`. CI forces read-only `DSH_SNAPSHOT=replay`, never writing expected outputs; record/refresh stay local and every diff is reviewed ([web e2e lane](../.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.md), [CI gate decision](../.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.md)). `test:web` [builds first](../.agents/notes/implemented/bug-fix/2026-07-28-themed-scrollbars-and-reserved-gutter.md) for plugin CSS. Committed session-format JSONL uses the canonical packed-row layout, and the keyless snapshot gate discovers every such fixture by its `session` header. In-flight branches carrying older fixture edits merge current `master` and run the [temporary migrator](../scripts/migrate-packed-session-fixtures.ts) through `pnpm run migrate:packed-session-fixtures`; the [removal proposal](../.agents/notes/proposed/process/2026-07-26-remove-packed-session-fixture-migrator.md) retires that command and these links after all affected branches converge. diff --git a/docs/testing.zh.md b/docs/testing.zh.md index 72b7c3bcfa..6d24d8e74d 100644 --- a/docs/testing.zh.md +++ b/docs/testing.zh.md @@ -9,7 +9,7 @@ - **单元测试**(`pnpm run test`):vitest 运行包(package)和示例各自的 `tests/**` 目录下的测试,以及匹配 `scripts/**/*.spec.ts` 的仓库脚本测试;测试文件与其所覆盖的代码区域放在一起。每个注册表都有一个 HMR(热模块替换)安全测试(dispose(资源释放)贡献的 fiber,断言清理完成)。优先覆盖边界情况、错误路径、事件顺序、并发竞态,以及永久性契约回归(见 `packages/core/agent-loop/tests/contract-regressions.spec.ts`)。 - **覆盖率门禁**(`pnpm run test:coverage`):门禁级运行,对 `packages/*/*/src` 按文件 100% 覆盖。未覆盖的行往往是门禁正确标记出的死代码(应删除),而非需要补写的测试。行覆盖率是必要条件,但永远不是充分条件:它证明行被执行过,不证明功能按交付预期工作。`packages/bash/pwsh-local/src` 的按文件 100% 覆盖需要真实的 `pwsh`:缺少它时其 executor 套件会自动跳过,`vitest.config.ts` 会豁免该文件以使无 pwsh 的主机保持绿色,而 CI runner 自带 pwsh,仍按完整标准执行门禁。 - **真实 API e2e**(`pnpm run test:e2e`):带密钥测试调用真实提供方 API,包括 DeepSeek 模型以及各提供方特有的冒烟测试;这些测试各自由自己的密钥控制(`EXA_API_KEY`、`PERPLEXITY_API_KEY` 等),缺少密钥时套件会自动跳过,使 keyless CI 保持绿色([真实 API e2e Agent Note](../.agents/notes/implemented/testing/2026-06-19-real-api-e2e-ci.md))。 -- **快照**(`pnpm run test:snapshot`):无密钥预期输出覆盖对外行为(传输契约与呈现),持久化日志则固定组装后的后端行为。ACP 启动真实的自动化服务器示例、回放录制会话,并对归一化 JSON-RPC 与重新持久化的日志执行 diff([ACP 快照 Agent Note](../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md));headless 通过真实单次运行进程固定 `stream-json`。TUI 旅程通过真实循环与工具回放主会话与子会话 JSONL,再将 ANSI 投影为语义化终端状态输出;包级快照保留瞬态状态,真实 PTY 覆盖进程边界([TUI 快照 Agent Note](../.agents/notes/archived/testing/2026-07-18-tui-terminal-state-snapshots.md))。当模型 transcript(文本记录)发生变化时使用 `pnpm run test:snapshot:record`,回放输入仍然有效时使用 `pnpm run test:snapshot:refresh`;请审查每一处 JSONL 与预期输出差异。一个 ACP 场景(`text-turn`)固定完整的系统提示词与工具 schema 内容;其他 fixture(测试前置数据)将其 token 化,因此修改只会扰动一行([pinned-header Agent Note](../.agents/notes/archived/testing/2026-07-06-pin-request-header-content-in-one-scenario.md))。 +- **快照**(`pnpm run test:snapshot`):无密钥预期输出覆盖对外行为(传输契约与呈现),持久化日志则固定组装后的后端行为。ACP 启动真实的自动化服务器示例、回放录制会话,并对归一化 JSON-RPC 与重新持久化的日志执行 diff([ACP 快照 Agent Note](../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md));headless 通过真实单次运行进程固定 `stream-json`。当模型 transcript(文本记录)发生变化时使用 `pnpm run test:snapshot:record`,回放输入仍然有效时使用 `pnpm run test:snapshot:refresh`;请审查每一处 JSONL 与预期输出差异。一个 ACP 场景(`text-turn`)固定完整的系统提示词与工具 schema 内容;其他 fixture(测试前置数据)将其 token 化,因此修改只会扰动一行([pinned-header Agent Note](../.agents/notes/archived/testing/2026-07-06-pin-request-header-content-in-one-scenario.md))。 - **Web 浏览器快照**(`pnpm run test:web`;必需的 Linux PR(Pull Request)门禁):Chromium 将回放后的浏览器输出与 `apps/web/tests/snapshots/` 比较。CI 强制只读的 `DSH_SNAPSHOT=replay`,绝不写入预期输出;record/refresh 留在本地,每处 diff 都须评审([web e2e 车道](../.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.md)、[CI 门禁决策](../.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.md))。`test:web` 会[先构建](../.agents/notes/implemented/bug-fix/2026-07-28-themed-scrollbars-and-reserved-gutter.md)以交付插件 CSS。 签入仓库的会话格式 JSONL 使用规范打包行布局,无密钥快照门禁会通过 `session` header 发现每一份此类 fixture。仍携带旧版 fixture 改动的在途分支应合并当前 `master`,并通过 `pnpm run migrate:packed-session-fixtures` 运行[临时迁移器](../scripts/migrate-packed-session-fixtures.ts);待所有受影响分支收敛后,[移除提案](../.agents/notes/proposed/process/2026-07-26-remove-packed-session-fixture-migrator.md)会移除该命令及这些链接。 diff --git a/docs/user/develop/basic/config.i18n.yaml b/docs/user/develop/basic/config.i18n.yaml index 2287626cde..7fca045189 100644 --- a/docs/user/develop/basic/config.i18n.yaml +++ b/docs/user/develop/basic/config.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/user/develop/basic/config.md -config.md: 26d2d48ebede74194fbf306aa97d214bdb99b722 -config.zh.md: 1ca727b56b661df315026902d48fbcaf507fe08b +config.md: 11a2311464789f74537cc7c4435f83ec07ca26fd +config.zh.md: 4e827ecafa6bfaf87c3e3f118425e656e1254787 diff --git a/docs/user/develop/basic/config.md b/docs/user/develop/basic/config.md index 26d2d48ebe..11a2311464 100644 --- a/docs/user/develop/basic/config.md +++ b/docs/user/develop/basic/config.md @@ -31,13 +31,15 @@ export function apply(ctx: Context, config: Config) { } ``` -Configure it in `cordis.yml`: +Add the configuration to the inserted local plugin row in `scratch-plugin/cordis.yml`: ```yaml -- name: './src/my-plugin.ts' - config: - greeting: 'Hi there' - maxRetries: 5 +- insert: + - id: hello + name: './src/my-plugin.ts' + config: + greeting: 'Hi there' + maxRetries: 5 ``` When loading the plugin, Cordis uses the exported schema to validate configuration and fill defaults. Do not export a plain object as `Config`; it does not implement the Standard Schema interface required by Cordis. @@ -91,22 +93,7 @@ The test is whether `cordis.yml` can change the value without a code edit. ### Fail loudly on invalid configuration -If configuration refers to an unregistered LLM provider route or another nonexistent resource, fail early instead of silently skipping it: - -```ts -import type { Context } from 'cordis' -import type {} from '@deepseek-ai/dsh-llm' - -export interface ModelConfig { - provider: string -} - -export function apply(ctx: Context, config: ModelConfig) { - if (!ctx.llm.listProviders().some(provider => provider.id === config.provider)) { - throw new Error(`LLM provider "${config.provider}" is not registered`) - } -} -``` +Express self-contained constraints in the schema so invalid configuration fails while the plugin loads. References to services or registered resources require dependency injection; the [services tutorial](../framework/service.md) introduces that contract. ## Work with HMR diff --git a/docs/user/develop/basic/config.zh.md b/docs/user/develop/basic/config.zh.md index 1ca727b56b..4e827ecafa 100644 --- a/docs/user/develop/basic/config.zh.md +++ b/docs/user/develop/basic/config.zh.md @@ -31,13 +31,15 @@ export function apply(ctx: Context, config: Config) { } ``` -用户在 `cordis.yml` 中这样使用: +在 `scratch-plugin/cordis.yml` 新插入的本地插件行中添加配置: ```yaml -- name: './src/my-plugin.ts' - config: - greeting: 'Hi there' - maxRetries: 5 +- insert: + - id: hello + name: './src/my-plugin.ts' + config: + greeting: 'Hi there' + maxRetries: 5 ``` 插件加载时,Cordis 会通过导出的 schema 校验配置,并填充未提供字段的默认值。不要导出普通对象作为 `Config`,因为它不满足 Cordis 要求的 Standard Schema 接口。 @@ -91,22 +93,7 @@ export interface Config { ### 配置错误要响亮 -如果配置引用了未注册的 LLM 提供方路由或其他不存在的资源,应该尽早报错,而不是静默跳过: - -```ts -import type { Context } from 'cordis' -import type {} from '@deepseek-ai/dsh-llm' - -export interface ModelConfig { - provider: string -} - -export function apply(ctx: Context, config: ModelConfig) { - if (!ctx.llm.listProviders().some(provider => provider.id === config.provider)) { - throw new Error(`LLM provider "${config.provider}" is not registered`) - } -} -``` +在 schema 中表达自身完备的约束,使无效配置在插件加载时失败。对服务或已注册资源的引用需要依赖注入;[服务教程](../framework/service.md)会介绍这项契约。 ## 配合 HMR diff --git a/docs/user/develop/basic/index.i18n.yaml b/docs/user/develop/basic/index.i18n.yaml index 1ad27114bd..4298808fc7 100644 --- a/docs/user/develop/basic/index.i18n.yaml +++ b/docs/user/develop/basic/index.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/user/develop/basic/index.md -index.md: 5a9f8dfb8f2d87dfbd2ba30b4d09d002ae9b635c -index.zh.md: 6f8926aeac5dcf667e4d311505ef7b2a321659c2 +index.md: 45c8dfe495cd99da46a7b259b407af8deff570b3 +index.zh.md: 9d8ee47e6f07fb4a897f49487cb7327904573c1b diff --git a/docs/user/develop/basic/index.md b/docs/user/develop/basic/index.md index 95efe5c232..45c8dfe495 100644 --- a/docs/user/develop/basic/index.md +++ b/docs/user/develop/basic/index.md @@ -2,7 +2,15 @@ English | [中文](index.zh.md) -This guide creates a minimal Harness plugin and loads it into an agent. +This tutorial creates a minimal Harness plugin and loads it into the Web UI. Start from a repository checkout that has completed the [quick start](../../guide/quickstart.md). + +## Create a local project + +From the repository root, create a scratch project for the tutorial: + +```sh +mkdir -p scratch-plugin/src +``` ## What is a plugin? @@ -22,7 +30,7 @@ That is the complete shape. ## Create the plugin file -Create `src/my-plugin.ts` in your project: +Create `scratch-plugin/src/my-plugin.ts`: ```ts import type { Context } from 'cordis' @@ -37,14 +45,21 @@ export function apply(ctx: Context) { ## Register it in cordis.yml -Add an entry to `cordis.yml`: +Create `scratch-plugin/cordis.yml` as a Web overlay that inserts the local plugin: ```yaml -- id: hello - name: './src/my-plugin.ts' +- insert: + - id: hello + name: './src/my-plugin.ts' ``` -After startup, the console prints `[hello-plugin] plugin loaded!`. +Start the Web UI with that overlay: + +```sh +pnpm run dsh web --config ./scratch-plugin/cordis.yml +``` + +Open `http://127.0.0.1:3080`. The terminal prints `[hello-plugin] plugin loaded!` during startup. ## Automatic cleanup diff --git a/docs/user/develop/basic/index.zh.md b/docs/user/develop/basic/index.zh.md index 6f8926aeac..9d8ee47e6f 100644 --- a/docs/user/develop/basic/index.zh.md +++ b/docs/user/develop/basic/index.zh.md @@ -2,7 +2,15 @@ [English](index.md) | 中文 -本文带你编写一个最小的 Harness 插件并加载到 agent(智能体)中。 +本教程会创建一个最小的 Harness 插件,并将其加载到 Web UI 中。请从已完成[快速开始](../../guide/quickstart.md)的仓库检出开始。 + +## 创建本地项目 + +在仓库根目录创建本教程使用的临时项目: + +```sh +mkdir -p scratch-plugin/src +``` ## 插件是什么 @@ -22,7 +30,7 @@ export function apply(ctx: Context) { ## 创建插件文件 -在你的项目目录下创建 `src/my-plugin.ts`: +创建 `scratch-plugin/src/my-plugin.ts`: ```ts import type { Context } from 'cordis' @@ -37,14 +45,21 @@ export function apply(ctx: Context) { ## 注册到 cordis.yml -在你的 `cordis.yml` 中添加一条: +创建 `scratch-plugin/cordis.yml`,作为插入本地插件的 Web 覆盖层: ```yaml -- id: hello - name: './src/my-plugin.ts' +- insert: + - id: hello + name: './src/my-plugin.ts' ``` -启动后你会在控制台看到 `[hello-plugin] plugin loaded!`。 +使用该覆盖层启动 Web UI: + +```sh +pnpm run dsh web --config ./scratch-plugin/cordis.yml +``` + +打开 `http://127.0.0.1:3080`。启动期间,终端会打印 `[hello-plugin] plugin loaded!`。 ## 自动清理 @@ -120,35 +135,6 @@ export default class MyService extends Service { 大多数情况下,函数形式足够了。当插件需要向其他插件提供服务时,可使用类形式(见 [服务与依赖](../framework/service.md))。 -## 完整示例 - -最小的工具插件会在 `ctx.tools` 上注册其定义: - -```ts -import type { Context } from 'cordis' -import { defineTool } from '@deepseek-ai/dsh-tools' - -export const name = 'greet-tool' -export const inject = ['tools'] - -export function apply(ctx: Context) { - ctx.tools.register(defineTool({ - name: 'greet', - description: 'Greet the named person.', - parameters: { - name: { type: 'string', required: true }, - }, - output: { - schema: { type: 'string' }, - render: (_args, value) => [{ type: 'text', text: value }], - }, - async execute(args) { - return `Hello, ${args.name}!` - }, - })) -} -``` - ## 下一步 - [开发一个工具](./tool.md) — 详细了解工具定义 DSL diff --git a/docs/user/develop/basic/tool.i18n.yaml b/docs/user/develop/basic/tool.i18n.yaml index f38706cead..0aa1bbb4cb 100644 --- a/docs/user/develop/basic/tool.i18n.yaml +++ b/docs/user/develop/basic/tool.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/user/develop/basic/tool.md -tool.md: 0d7cbc3f0b86f88fb67aeff6aa61181dff2912ee -tool.zh.md: 37362c510a07fcc32e9eee2b578d82907788e471 +tool.md: 93a1a96feba814a564f8800c8e7b865fe9c0cb73 +tool.zh.md: 18e6b9b5d9c17b00c26aa7b98e6ac4531315dc5e diff --git a/docs/user/develop/basic/tool.md b/docs/user/develop/basic/tool.md index 0d7cbc3f0b..93a1a96feb 100644 --- a/docs/user/develop/basic/tool.md +++ b/docs/user/develop/basic/tool.md @@ -2,15 +2,17 @@ English | [中文](tool.zh.md) -A tool is a capability the model can call. This guide builds one with `defineTool`. +This tutorial adds a `greet` tool to the Web UI. Complete [Your first plugin](./) first and keep its `scratch-plugin` directory. -## Minimal example +## Create the tool plugin + +Replace `scratch-plugin/src/my-plugin.ts` with: ```ts import type { Context } from 'cordis' import { defineTool } from '@deepseek-ai/dsh-tools' -export const name = 'my-tool' +export const name = 'greet-tool' export const inject = ['tools'] export function apply(ctx: Context) { @@ -25,221 +27,26 @@ export function apply(ctx: Context) { render: (_args, value) => [{ type: 'text', text: value }], }, async execute(args) { - // args is inferred as { name: string }. return `Hello, ${args.name}!` }, })) } ``` -## Parameter definitions +`inject` makes Cordis wait for the tool registry. `defineTool` infers and validates `args` from `parameters`; `execute` returns the canonical value declared by `output.schema`, and `output.render` converts that value to model-facing content. -`parameters` uses a compact format that the framework converts to the JSON Schema sent to the model. +## Run and call the tool -### Primitive types +Restart the development command if it is not running: -```ts -export const parameters = { - path: { type: 'string', required: true }, - limit: { type: 'integer' }, - recursive: { type: 'boolean' }, - parent: { type: 'null' }, -} -// Inferred type: { path: string; limit?: number; recursive?: boolean; parent?: null } +```sh +pnpm run dsh web --config ./scratch-plugin/cordis.yml ``` -### Enums - -```ts -export const parameters = { - mode: { type: 'string', required: true, enum: ['read', 'write', 'append'] }, -} -// Inferred type: { mode: 'read' | 'write' | 'append' } -``` - -### Nested objects - -```ts -export const parameters = { - options: { - type: 'object', - additionalProperties: true, - properties: { - timeout: { type: 'number' }, - retries: { type: 'number' }, - }, - }, -} -// The declared fields are inferred; additional JSON-valued keys are allowed. -``` - -### Arrays - -```ts -export const parameters = { - tags: { - type: 'array', - items: { type: 'string' }, - }, -} -// Inferred type: { tags?: string[] } -``` - -### Property fields - -| Field | Type | Meaning | -|------|------|------| -| `type` | `'string' \| 'number' \| 'integer' \| 'boolean' \| 'null' \| 'object' \| 'array' \| 'json'` | Value type; `json` accepts any lossless JSON value | -| `required` | `true` | Marks the property required and affects inference | -| `description` | `string` | Description sent to the model | -| `enum` / `const` | matching scalar values | Allowed literal values, checked at author and runtime boundaries | -| `properties` | `ParameterSchemaSpec` | Nested properties for an object | -| `additionalProperties` | `true \| false` | Required on every explicit object node | -| `items` | `ValueSchemaSpec` | Element schema for an array | -| `oneOf` | at least two `ValueSchemaSpec` branches | Requires exactly one matching branch; used instead of `type` | - -The outer `parameters` map is an implicit open object. Explicit nested objects choose their openness; raw JSON Schema registered without `defineTool` keeps JSON Schema's open-by-default behavior. - -## The execute function - -`execute` receives validated, inferred `args` and an `exec` execution context: - -```ts -import { defineTool } from '@deepseek-ai/dsh-tools' - -export const tool = defineTool({ - name: 'example', - description: 'Return an example result.', - parameters: {}, - output: { - schema: { type: 'string' }, - render: (_args, value) => [{ type: 'text', text: value }], - }, - async execute(args, exec) { - // args: inferred from parameters - // exec: ToolExecution context - - // Return the value declared by output.schema. - void args - void exec - return 'result here' - }, -}) -``` - -### Return value - -`execute` returns the lossless JSON value declared by `output.schema`. `output.render(args, value)` separately turns that validated value into the Native/model-facing content: - -```ts ignore-check -output: { - schema: { - type: 'object', - additionalProperties: false, - properties: { - path: { type: 'string', required: true }, - content: { type: 'string', required: true }, - }, - }, - render: (_args, value) => [{ type: 'text', text: value.content }], -}, -async execute(args) { - return { path: args.path, content: await readFile(args.path, 'utf8') } -} -``` - -The canonical value is available to execution-time programmatic callers and is not persisted in `tool/result`; the rendered content and optional `presentationMeta` are the replayable projections. A body value that does not satisfy the schema, or is not lossless JSON, becomes an `INVALID_TOOL_OUTPUT` failure. - -### Argument validation - -Before calling `execute`, `defineTool` validates model-generated arguments. Invalid input raises `ToolArgsError`; the framework turns it into an `isError` result so the model can correct its call. - -Do not repeat type validation inside `execute`. - -## Presentation - -A tool can define transport-neutral presentation methods for terminal and web clients: - -```ts ignore-check -defineTool({ - name: 'bash', - // ... - output: { - schema: { type: 'string' }, - render: (_args, value) => [{ type: 'text', text: value }], - }, - presentCall(args) { - return { - card: 'terminal', - title: args.command, - } - }, - presentResult(args, result) { - return { - card: 'terminal', - output: result.content.map(b => b.type === 'text' ? b.text : '').join(''), - } - }, -}) -``` - -`presentCall` and `presentResult` are **pure functions**. Streaming UI and session replay may call them more than once. - -## Registration and unloading - -`ctx.tools.register()` returns a disposer, but a registration made through `ctx` is already tracked by the framework. Unloading the plugin removes the tool automatically, so the plugin does not call the disposer itself. - -```ts ignore-check -// This is sufficient: -ctx.tools.register(defineTool({ /* ... */ })) - -// No saved disposer or extra cleanup registration is needed. -``` - -## Complete example - -This tool counts files in a directory: - -```ts -import type { Context } from 'cordis' -import { defineTool } from '@deepseek-ai/dsh-tools' -import { readdir } from 'node:fs/promises' - -export const name = 'file-counter' -export const inject = ['tools'] - -export function apply(ctx: Context) { - ctx.tools.register(defineTool({ - name: 'count_files', - description: 'Count files in a directory.', - parameters: { - path: { type: 'string', required: true, description: 'Directory path' }, - extension: { type: 'string', description: 'Filter by extension (e.g. ".ts")' }, - }, - output: { - schema: { - type: 'object', - additionalProperties: false, - properties: { - count: { type: 'integer', required: true }, - files: { type: 'array', required: true, items: { type: 'string' } }, - }, - }, - render: (_args, value) => [{ type: 'text', text: `Found ${value.count} files.` }], - }, - async execute(args) { - const entries = await readdir(args.path, { withFileTypes: true }) - let files = entries.filter(e => e.isFile()) - if (args.extension) { - files = files.filter(f => f.name.endsWith(args.extension!)) - } - return { count: files.length, files: files.map(file => file.name) } - }, - })) -} -``` +Open `http://127.0.0.1:3080` and ask: `Use the greet tool to greet Ada.` The model can call `greet` and receives `Hello, Ada!` as the tool result. ## Next steps -- [Plugin configuration](./config.md) — make the tool configurable -- [Capability layering](../practice/) — understand the interface/implementation/consumer pattern +- [Plugin configuration](./config.md) — make the greeting configurable. +- [Tool authoring reference](../../../cookbook/adding-a-tool.md) — look up nested schemas, canonical values, background work, policy hooks, Code Mode, and UI cards. +- [Capability layering](../practice/) — split a replaceable capability into interface, implementation, and consumer packages. diff --git a/docs/user/develop/basic/tool.zh.md b/docs/user/develop/basic/tool.zh.md index 37362c510a..18e6b9b5d9 100644 --- a/docs/user/develop/basic/tool.zh.md +++ b/docs/user/develop/basic/tool.zh.md @@ -1,16 +1,18 @@ -# 开发一个工具 +# 构建工具 [English](tool.md) | 中文 -工具是模型可以调用的能力。本文介绍如何用 `defineTool` 编写一个工具。 +本教程会在 Web UI 中添加一个 `greet` 工具。请先完成[第一个插件](./),并保留其中的 `scratch-plugin` 目录。 -## 最小示例 +## 创建工具插件 + +将 `scratch-plugin/src/my-plugin.ts` 替换为: ```ts import type { Context } from 'cordis' import { defineTool } from '@deepseek-ai/dsh-tools' -export const name = 'my-tool' +export const name = 'greet-tool' export const inject = ['tools'] export function apply(ctx: Context) { @@ -25,221 +27,26 @@ export function apply(ctx: Context) { render: (_args, value) => [{ type: 'text', text: value }], }, async execute(args) { - // args is inferred as { name: string }. return `Hello, ${args.name}!` }, })) } ``` -## 参数定义 +`inject` 让 Cordis 等待工具注册表就绪。`defineTool` 根据 `parameters` 推导并校验 `args`;`execute` 返回 `output.schema` 声明的规范值,`output.render` 再将该值转换为面向模型的内容。 -`parameters` 用一种简洁的格式描述参数,框架会自动转换为模型需要的 JSON Schema。 +## 运行并调用工具 -### 基本类型 +如果开发命令未在运行,请重新启动: -```ts -export const parameters = { - path: { type: 'string', required: true }, - limit: { type: 'integer' }, - recursive: { type: 'boolean' }, - parent: { type: 'null' }, -} -// Inferred type: { path: string; limit?: number; recursive?: boolean; parent?: null } +```sh +pnpm run dsh web --config ./scratch-plugin/cordis.yml ``` -### 枚举 - -```ts -export const parameters = { - mode: { type: 'string', required: true, enum: ['read', 'write', 'append'] }, -} -// Inferred type: { mode: 'read' | 'write' | 'append' } -``` - -### 嵌套对象 - -```ts -export const parameters = { - options: { - type: 'object', - additionalProperties: true, - properties: { - timeout: { type: 'number' }, - retries: { type: 'number' }, - }, - }, -} -// The declared fields are inferred; additional JSON-valued keys are allowed. -``` - -### 数组 - -```ts -export const parameters = { - tags: { - type: 'array', - items: { type: 'string' }, - }, -} -// Inferred type: { tags?: string[] } -``` - -### 每个属性的字段 - -| 字段 | 类型 | 说明 | -|------|------|------| -| `type` | `'string' \| 'number' \| 'integer' \| 'boolean' \| 'null' \| 'object' \| 'array' \| 'json'` | 值类型;`json` 接受任意无损 JSON 值 | -| `required` | `true` | 标记为必填(影响类型推导) | -| `description` | `string` | 发送给模型的描述 | -| `enum` / `const` | 匹配类型的标量值 | 允许的字面量值,在编写和运行时边界校验 | -| `properties` | `ParameterSchemaSpec` | 对象的嵌套属性 | -| `additionalProperties` | `true \| false` | 每个显式对象节点都必须声明 | -| `items` | `ValueSchemaSpec` | 数组的元素 schema | -| `oneOf` | 至少两个 `ValueSchemaSpec` 分支 | 要求恰好匹配一个分支;代替 `type` 使用 | - -外层 `parameters` 映射是一个隐式的开放对象。显式嵌套对象需自行选择是否开放;不通过 `defineTool` 注册的原始 JSON Schema 保持 JSON Schema 的默认开放语义。 - -## execute 函数 - -`execute` 接收经过校验的 `args`(类型自动推导)和一个 `exec` 上下文对象: - -```ts -import { defineTool } from '@deepseek-ai/dsh-tools' - -export const tool = defineTool({ - name: 'example', - description: 'Return an example result.', - parameters: {}, - output: { - schema: { type: 'string' }, - render: (_args, value) => [{ type: 'text', text: value }], - }, - async execute(args, exec) { - // args: inferred from parameters - // exec: ToolExecution context - - // Return the value declared by output.schema. - void args - void exec - return 'result here' - }, -}) -``` - -### 返回值 - -`execute` 返回由 `output.schema` 声明的无损 JSON 值。`output.render(args, value)` 会将经过校验的值另外转换为 Native/模型可见的内容: - -```ts ignore-check -output: { - schema: { - type: 'object', - additionalProperties: false, - properties: { - path: { type: 'string', required: true }, - content: { type: 'string', required: true }, - }, - }, - render: (_args, value) => [{ type: 'text', text: value.content }], -}, -async execute(args) { - return { path: args.path, content: await readFile(args.path, 'utf8') } -} -``` - -执行期间的程序化调用方可以使用规范值,但 `tool/result` 不会持久化该值;渲染后的内容和可选的 `presentationMeta` 才是可回放的投影。工具主体返回的值若不满足 schema 或不是无损 JSON,就会变为 `INVALID_TOOL_OUTPUT` 失败。 - -### 参数校验 - -`defineTool` 在调用 `execute` 之前会自动校验模型生成的参数。如果参数不合法,会抛出 `ToolArgsError`,框架将其转换为 `isError` 结果返回给模型,让模型自行修正。 - -你不需要在 `execute` 里手动校验参数类型。 - -## 展示层(Presentation) - -工具可以定义与传输方式无关的展示方法,供终端和 Web 客户端使用: - -```ts ignore-check -defineTool({ - name: 'bash', - // ... - output: { - schema: { type: 'string' }, - render: (_args, value) => [{ type: 'text', text: value }], - }, - presentCall(args) { - return { - card: 'terminal', - title: args.command, - } - }, - presentResult(args, result) { - return { - card: 'terminal', - output: result.content.map(b => b.type === 'text' ? b.text : '').join(''), - } - }, -}) -``` - -`presentCall` 和 `presentResult` 是**纯函数**,不能有副作用——UI 可能在流式传输中和会话回放中多次调用它们。 - -## 注册与卸载 - -`ctx.tools.register()` 返回值就是 disposer。但由于你在 `ctx` 上调用,框架已经自动追踪了这个注册——插件卸载时会自动移除工具。你不需要手动调用 disposer。 - -```ts ignore-check -// This is sufficient: -ctx.tools.register(defineTool({ /* ... */ })) - -// No saved disposer or extra cleanup registration is needed. -``` - -## 完整示例 - -一个文件计数工具: - -```ts -import type { Context } from 'cordis' -import { defineTool } from '@deepseek-ai/dsh-tools' -import { readdir } from 'node:fs/promises' - -export const name = 'file-counter' -export const inject = ['tools'] - -export function apply(ctx: Context) { - ctx.tools.register(defineTool({ - name: 'count_files', - description: 'Count files in a directory.', - parameters: { - path: { type: 'string', required: true, description: 'Directory path' }, - extension: { type: 'string', description: 'Filter by extension (e.g. ".ts")' }, - }, - output: { - schema: { - type: 'object', - additionalProperties: false, - properties: { - count: { type: 'integer', required: true }, - files: { type: 'array', required: true, items: { type: 'string' } }, - }, - }, - render: (_args, value) => [{ type: 'text', text: `Found ${value.count} files.` }], - }, - async execute(args) { - const entries = await readdir(args.path, { withFileTypes: true }) - let files = entries.filter(e => e.isFile()) - if (args.extension) { - files = files.filter(f => f.name.endsWith(args.extension!)) - } - return { count: files.length, files: files.map(file => file.name) } - }, - })) -} -``` +打开 `http://127.0.0.1:3080`,然后输入:`Use the greet tool to greet Ada.` 模型可以调用 `greet`,并收到 `Hello, Ada!` 这一工具结果。 ## 下一步 -- [插件配置](./config.md) — 让你的工具可配置 -- [能力分层](../practice/) — 了解接口/实现/消费方模式 +- [插件配置](./config.md) — 让问候语可配置。 +- [工具编写参考](../../../cookbook/adding-a-tool.md) — 查阅嵌套 schema、规范值、后台工作、策略钩子、Code Mode 和 UI 卡片。 +- [能力分层](../practice/) — 将可替换能力拆分为接口、实现和消费方包。 diff --git a/docs/user/develop/practice/index.i18n.yaml b/docs/user/develop/practice/index.i18n.yaml index 7446e7f633..65057da78e 100644 --- a/docs/user/develop/practice/index.i18n.yaml +++ b/docs/user/develop/practice/index.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/user/develop/practice/index.md -index.md: e197d499d7f5bd9911ea60bebf584251cd4ed915 -index.zh.md: cacf13e8b23060ea5c309d30e51d732f09c76000 +index.md: c3306725f47993aa9d3322423261a260754c1d5f +index.zh.md: 3609cbce21e17ce9ca0a40b69999b293dee29012 diff --git a/docs/user/develop/practice/index.md b/docs/user/develop/practice/index.md index ea517aa925..c3306725f4 100644 --- a/docs/user/develop/practice/index.md +++ b/docs/user/develop/practice/index.md @@ -2,6 +2,10 @@ English | [中文](index.zh.md) +This page has two parts: a concept reference for the three-layer capability pattern, followed by an advanced tutorial that builds one capability. Complete the [basic plugin path](../basic/) and [services tutorial](../framework/service.md) first. + +## Concept reference + When a capability is general enough to need replaceable implementations, such as Bash execution, Harness splits it into three packages: an **interface**, an **implementation**, and a **consumer**. Each layer can evolve or be replaced independently. ## Bash example @@ -49,17 +53,9 @@ The interface and tool remain unchanged while the implementation changes. - The consumer depends on the interface. - The implementation and consumer **do not depend on each other**. -## Built-in three-layer capabilities +The [capability-seam reference](../../../capability-seams.md) owns the current built-in families and package links. -| Capability | Interface | Implementation | Consumer | -|------|-------------|------|---------------| -| Bash | `dsh-bash` | `dsh-bash-local` | `dsh-tool-bash` | -| Filesystem | `dsh-fs` | `dsh-fs-local` + `dsh-fs-policy` | `dsh-tool-fs` | -| Web | `dsh-web` | `dsh-web-fetch-local` / `dsh-web-search-*` | `dsh-tool-web` | -| Subagent | `dsh-subagent` | `dsh-subagent-spawn` / `dsh-subagent-fork` | `dsh-tool-subagent` | -| Compaction | `dsh-compact` | `dsh-compact-basic` | The implementation consumes agent-loop extension events | - -## Develop a three-layer capability +## Tutorial: develop a three-layer capability ### Step 1: define the interface diff --git a/docs/user/develop/practice/index.zh.md b/docs/user/develop/practice/index.zh.md index 7aaa5abb5f..3609cbce21 100644 --- a/docs/user/develop/practice/index.zh.md +++ b/docs/user/develop/practice/index.zh.md @@ -2,6 +2,10 @@ [English](index.md) | 中文 +本文分为两部分:先参考三层能力模式的概念,再通过高级教程构建一项能力。请先完成[基础插件路径](../basic/)和[服务教程](../framework/service.md)。 + +## 概念参考 + 当一项能力足够通用,需要支持可替换的实现时(例如 Bash 执行),Harness 会将其拆成三个包:**接口**、**实现**和**消费方**。这样便可独立替换其中任何一层。 ## 以 Bash 为例 @@ -49,17 +53,9 @@ - 消费方依赖接口。 - 实现和消费方**互不依赖**。 -## Harness 中内置的三件套 +当前内置系列及其包链接由[能力 seam 参考](../../../capability-seams.md)负责。 -| 能力 | 接口(seam) | 实现 | 消费方(工具) | -|------|-------------|------|---------------| -| Bash | `dsh-bash` | `dsh-bash-local` | `dsh-tool-bash` | -| 文件系统 | `dsh-fs` | `dsh-fs-local` + `dsh-fs-policy` | `dsh-tool-fs` | -| Web | `dsh-web` | `dsh-web-fetch-local` / `dsh-web-search-*` | `dsh-tool-web` | -| 子代理 | `dsh-subagent` | `dsh-subagent-spawn` / `dsh-subagent-fork` | `dsh-tool-subagent` | -| 压缩 | `dsh-compact` | `dsh-compact-basic` | 由实现插件消费 agent-loop 的扩展事件 | - -## 开发你自己的三件套 +## 教程:开发三层能力 ### 第一步:定义接口 diff --git a/docs/user/guide/config.i18n.yaml b/docs/user/guide/config.i18n.yaml index 7d99e45437..372e0e6c73 100644 --- a/docs/user/guide/config.i18n.yaml +++ b/docs/user/guide/config.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/user/guide/config.md -config.md: 4e438cc3a400de71934d047108024ff5be4ef7d2 -config.zh.md: e0b0285b110a808b1284209a84f78e114634df52 +config.md: ddf4df264e5534fc3b74991941c2f3f82376d53f +config.zh.md: 56ac0146ddae83dbfc86f479030efdb5772a3aaf diff --git a/docs/user/guide/config.md b/docs/user/guide/config.md index 4e438cc3a4..ddf4df264e 100644 --- a/docs/user/guide/config.md +++ b/docs/user/guide/config.md @@ -47,7 +47,7 @@ A minimal configuration is a list of plugin entries: toolName: my_tool ``` -Plugins load in file order. Place plugins that depend on services after the applications or capability plugins that provide them. Missing models, tools, and plugins fail as early as possible instead of being silently ignored. +Cordis starts sibling entries concurrently. A plugin declares required services through `inject`; Cordis waits for those services before applying the plugin, so file order does not establish dependency readiness. Missing models, tools, and plugins fail as early as possible instead of being silently ignored. ## CLI overlays diff --git a/docs/user/guide/config.zh.md b/docs/user/guide/config.zh.md index e0b0285b11..56ac0146dd 100644 --- a/docs/user/guide/config.zh.md +++ b/docs/user/guide/config.zh.md @@ -2,7 +2,7 @@ [English](config.md) | 中文 -Harness 使用 `cordis.yml` 描述 Agent 加载哪些插件以及每个插件的参数。配置文件负责组合能力;每个包真正支持的字段和默认值由源码生成的配置目录负责记录,避免两份手写表格逐渐不一致。 +Harness 使用 `cordis.yml` 描述 agent(智能体)加载哪些插件以及每个插件的参数。配置文件负责组合能力;每个包真正支持的字段和默认值由源码生成的配置目录负责记录,避免两份手写表格逐渐不一致。 ## 从真实配置开始 @@ -47,7 +47,7 @@ Harness 使用 `cordis.yml` 描述 Agent 加载哪些插件以及每个插件的 toolName: my_tool ``` -插件按文件中的顺序加载。依赖其他服务的插件应该排在提供这些服务的应用或能力插件之后;引用不存在的模型、工具或插件会尽早报错,而不是被静默忽略。 +Cordis 会并发启动同级配置项。插件通过 `inject` 声明必需服务;Cordis 会等到这些服务就绪后再应用该插件,因此文件顺序不能保证依赖已就绪。引用不存在的模型、工具或插件会尽早报错,而不是被静默忽略。 ## CLI 覆盖层 diff --git a/docs/web-styling.i18n.yaml b/docs/web-styling.i18n.yaml index 9c79aa8a99..0cbbafeec9 100644 --- a/docs/web-styling.i18n.yaml +++ b/docs/web-styling.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/web-styling.md -web-styling.md: 173ce4482ec01f95cf38c62ebc2879e03a2d8192 -web-styling.zh.md: b38ba267cc7cc66ed12f000a93744dc81d940d06 +web-styling.md: 5296cc7f83f712532262eadda6da098ae9f63ec7 +web-styling.zh.md: 6beec368f2f7fd5eb7606acfaae315d56899c739 diff --git a/docs/web-styling.md b/docs/web-styling.md index 173ce4482e..5296cc7f83 100644 --- a/docs/web-styling.md +++ b/docs/web-styling.md @@ -1,109 +1,25 @@ -# Web GUI Style Guide +# Web UI style reference English | [中文](web-styling.zh.md) -> **[The token system has been replaced—the table in § 1 is retained only for historical reference]** The `--bg-*`/`--text-*`/`--accent` token families documented here and their host package, `packages/client/web-ui`, were retired during the plugin refactor. The sole current token source is `packages/client/ui-theme/src/styles/`, which defines the `--dsw-*` system (a static color scale plus a semantic alias layer, with dark-mode overrides under `body[data-ds-dark-theme]`). The sheet is authoritative and component audits use it as the baseline. **The following rules remain in force**: CSS Modules + clsx, no component library, no Tailwind, no hard-coded color values in components, pair every font size with a line height, use spacing in multiples of 4, and do not put monospace last in the code font stack. +This reference defines styling ownership and component rules for browser client packages. The current token values live in [`packages/client/ui-theme/src/styles/`](../packages/client/ui-theme/src/styles/); this document does not duplicate that generated-by-source inventory. -> Status: formerly a “living document” that evolved with `packages/client/web-ui`. The visual baseline came from empirical study of the deepseekchat frontend repository. The [web-styling-system RFC](../.agents/notes/implemented/process/2026-07-19-web-styling-system.md) owns the framework decisions and engineering constraints; this document does not repeat their rationale. +## Ownership -## 1. Design token table (authoritative definitions) +[`ui-theme`](../packages/client/ui-theme/README.md) owns the `--dsw-*` static scale, semantic aliases, typography, motion, gradients, shadows, scrollbar styles, and light/dark preference. [`ui-layout`](../packages/client/ui-layout/README.md) applies the resolved theme snapshot to the document. Feature packages consume semantic aliases and do not define another global theme. -All tokens live in `packages/client/web-ui/src/style/global.css`: `:root` contains the light-theme values, and the `[data-theme='dark']` block overrides the same variables (columns that were not complete are marked as placeholders). Component CSS references tokens only and contains no literal color values. +Global style sheets belong in `ui-theme/src/styles/`. Component styles live beside their component as CSS Modules. A component may define a local custom property when its value is part of that component's layout or presentation contract; shared colors, typography, elevation, and motion belong to the theme package. -### 1.1 Colors (two layers: comments identify the base-palette source, while variable names are semantic aliases) +## Component rules -| token | Light value | Dark value (placeholder) | Purpose | -| --- | --- | --- | --- | -| `--bg-base` | `#ffffff` | `#151517` | Page background | -| `--bg-layer` | `#ffffff` | `#232324` | Floating layer/panel | -| `--bg-sidebar` | `#f9fafb` | `#1b1b1c` | Sidebar background | -| `--text-primary` | `#0f1115` | `#f9fafb` | Body text | -| `--text-secondary` | `#61666b` | `#cfd3d6` | Secondary text | -| `--text-tertiary` | `#81858c` | `#adb2b8` | Supporting/descriptive text | -| `--border-l1` | `rgba(0,0,0,.04)` | `rgba(255,255,255,.06)` | Subtle separator (sidebar right edge) | -| `--border-l2` | `rgba(0,0,0,.1)` | `rgba(255,255,255,.12)` | Standard border | -| `--hover-bg` | `rgba(38,49,72,.06)` | `rgba(255,255,255,.08)` | Hover-state background | -| `--active-bg` | `rgba(38,49,72,.1)` | `rgba(255,255,255,.14)` | Pressed/active-state background | -| `--accent` | `#3964fe` | `#5686fe` | Brand blue (deepseek-500; one step lighter in dark mode) | -| `--accent-soft` | `#edf3fe` | `#28313f` | Soft brand background (emphasis blocks) | -| `--accent-item` | `#e4edfd` | `#35363a` | Selected sidebar-item background | -| `--bubble-bg` | `#edf3fe` | `#2c2c2e` | User-message bubble background | -| `--ok` / `--error` / `--warn` | `#22c55e` / `#ec1313` / `#f59e0b` | Same values | Semantic status colors | -| `--text-on-solid` | `#ffffff` | Same value | Text on solid backgrounds (accent/error badges, etc.) | -| `--ok-soft` / `--error-soft` | `#e6faed` / `#fee2e2` | `#233c2c` / `#570c0c` | Soft semantic status backgrounds (badges); green-100/red-100, with the 900 shades in dark mode | -| `--color-frame-mux` / `--color-frame-host` | `#8250df` / `#0969da` | Same values | RPC debugger direction colors (project-specific, not part of the baseline) | -| `--frame-mux-soft` / `--frame-host-soft` | `rgba(130,80,223,.1)` / `rgba(9,105,218,.1)` | Same colors at `.24` | Soft direction-color backgrounds (badges) | -| `--scroll-color` / `--scroll-color-hover` | `rgba(0,0,0,.08)` / `.15` | `rgba(255,255,255,.15)` / `.24` | Scrollbar colors (for `.scrollable` only) | +- Use CSS Modules and `clsx`; do not add a component library or Tailwind. +- Use `--dsw-alias-*` semantic tokens in feature components. Do not copy static palette values or write literal colors there. +- Keep theme selectors out of feature component CSS. Light/dark overrides belong to the theme owner. +- Pair font sizes with line heights and use the theme typography variables when an existing role matches. +- Keep source text, terminal output, and diff lines unwrapped when their component contract requires column preservation; use the shared scrollbar styles rather than component-specific scrollbar selectors. +- Put presentation in CSS. Inline React styles may pass component-local custom-property values but must not encode theme branches. +- Preserve keyboard focus visibility and reduced-motion behavior when adding transitions or hover-only controls. -### 1.2 Non-color tokens +## Changing the system -| token | Value | Description | -| --- | --- | --- | -| `--font-ui` | `Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif` | Body-text stack | -| `--font-mono` | `Menlo, Monaco, Consolas, 'JetBrains Mono', 'Courier New', sans-serif` | Code stack; **do not put monospace last** (prevents SimSun fallback for Chinese on Windows) | -| `--fw-strong` | `600` | Unified bold weight | -| `--ease` | `cubic-bezier(.4,0,.2,1)` | Sole easing curve | -| `--dur` / `--dur-fast` / `--dur-slow` | `.2s` / `.1s` / `.3s` | Three transition durations | -| `--radius-s` / `--radius-m` / `--radius-l` / `--radius-bubble` / `--radius-xl` | `8px` / `12px` / `16px` / `22px` / `24px` | Semantic radius steps: small controls / list items and blocks inside panels / floating layers / bubbles / input cards (same as the baseline inputWrapper); use `999px` directly for pills | -| `--shadow-panel` | `0 0 1px rgba(0,0,0,.2), 0 0 4px rgba(0,0,0,.02), 0 12px 32px rgba(0,0,0,.08)` | Floating-layer shadow (baseline lv3) | -| `--shadow-float` | `0 0 1px rgba(0,0,0,.24), 0 4px 12px rgba(0,0,0,.06), 0 16px 48px rgba(0,0,0,.16)` | Strong floating panel (enhanced lv3, such as the RPC debugger overlay) | -| `--shadow-card` | `0 4px 10px rgba(0,0,0,.02), 0 2px 4px rgba(0,0,0,.04)`; dark value `none` | Subtle input-card shadow (baseline: borders plus a subtle shadow distinguish same-color light surfaces; a lighter background distinguishes dark surfaces, with the shadow disabled) | - -Font sizes and spacing are **not tokenized** (matching the baseline repository's decision): components specify font sizes in px and **always pair them with line heights**. Common pairs are 16/24 (bubbles), 14/22 (UI default), and 12/18 (supporting text); spacing uses multiples of 4. - -## 2. Visual baseline (from deepseekchat) - -- Sidebar: width `260px + 1px` right border (`--border-l1`); background `--bg-sidebar`. -- Sidebar items: height `40px`, radius `--radius-m`, font size 14px; hover background `--hover-bg` or a sidebar-specific gray; **selected items use `--accent-item` without changing text color**. -- Sidebar group headings: 12px / weight 500 / `--text-tertiary` / sticky at the top (using the sidebar background to cover scrolling content). -- Conversation column: centered at `max-width: 840px`, reduced to 712px below 1024px. -- Message stream: **only user messages have bubbles**: background `--bubble-bg`, radius `--radius-bubble`, padding `10px 16px`, font size 16px/24px, and `max-width: calc(100% - 88px)`; **assistant messages are a plain document flow without a background**. -- Message action bar: `opacity: 0` by default; fades in when its parent is hovered or contains focus (`--dur` + `--ease`). -- Input card: floats centered at the same width as the conversation column (840px, reduced to 712px below 1024px) with bottom spacing; radius `--radius-xl`, border `--border-l2`, background `--bg-base`, shadow `--shadow-card`; two internal vertical sections = textarea (16px/24px, minimum 2 lines, maximum 14 lines = 336px, auto-growing through a mirror div) + action row (a 34px primary round button nested at bottom right); focus does not change the border or shadow (matching the baseline). -- Primary input button (the three decisions made on 2026-07-20, visually based on the Codex App): a 32px solid circular icon button (inline SVG). Idle = `--accent` background with a white ↑ “Send” arrow; while running it changes in place to an accent ■ “Stop” icon on `--accent-soft` (the same color family, not a warning, and not red). **Input is locked while running** (decision 3, replacing the earlier hover-menu design): the textarea is disabled (gray, with draft content still visible), there is no queue/interjection menu, and Stop is the only action. When the turn ends, input unlocks and regains focus. Enter sends; Ctrl/Meta+Enter inserts a newline (the keyboard path is disabled with the locked input while running). -- Scrollbars: nearly invisible, darkening on hover, with `scrollbar-gutter: stable` so they do not consume layout space (always use `.scrollable`; see § 3.9). -- Four-quadrant RPC direction symbols (the official visual vocabulary, using the spatial metaphor that up goes to the server, down comes from the server; single line = client-initiated exchange, double line = server-initiated exchange): - -| Symbol | Quadrant | Badge colors | -| --- | --- | --- | -| `↑` | client-request (unary outbound) | `--accent` / `--accent-soft` | -| `↓` | server-response (unary response) | ok `--ok`/`--ok-soft`, error `--error`/`--error-soft` | -| `⇟` | server-request (downlink stream) | mux `--color-frame-mux`/`--frame-mux-soft`, host `--color-frame-host`/`--frame-host-soft` | -| `⇞` | client-response (reply to server request) | `--accent`/`--accent-soft` at reduced opacity | - -## 3. Style implementation rules (review checklist) - -1. Colors, radii, motion, and font stacks reference only the § 1 tokens. Reject literal color values in component CSS (except for special effects such as gradient masks, which require an explanatory comment). -2. Component CSS must not contain `[data-theme]` selectors; dark-mode differences belong only in the global.css token table. If a theme must change a non-token value such as a gradient endpoint, define a local CSS variable in the component and have the theme block override only that variable (a variable bridge). -3. Use camelCase class names; use a single adjective for state classes (`.active` `.show`) and attach them with clsx: `clsx(styles.x, cond && styles.active, className)`. -4. Public components must accept `className` and merge it into the root element. -5. Do not use `composes`; share through tokens and extracted components. -6. Use `:global` only to pierce third-party or cross-package class names; do not use it to define new global classes. -7. All interaction transitions use `var(--dur*) var(--ease)` and transition only opacity / transform / background color / shadow. Wrap hover-only reveal elements in `@media (hover: hover)`. -8. Prefer opacity-based tokens for hover/active backgrounds because they compose over any elevation background; do not add new solid grays. -9. Apply the `.scrollable` utility class from global.css to every scroll container; do not write `::-webkit-scrollbar` inside components. -10. Put media queries at the end of the component CSS, next to the rules they override. The only current breakpoint is 1024px (where the conversation column steps down); record a second breakpoint in this document before adding it. -11. Dynamic styles in JS set only CSS variables (`style={{'--x': v}}`), while rules remain in CSS; do not assemble style objects in TSX to branch by theme or state. -12. Use only the three `--text-primary/secondary/tertiary` levels for gray text; do not add another gray. - -## 4. File organization - -- `src/style/global.css` always uses this section order: ① token table (`:root` + `[data-theme='dark']`), ② global foundations (box-sizing, body, button reset), ③ global utility classes (`.scrollable`, etc.; keep the total in single digits). -- Place each `*.module.css` beside the component with the same name; use one module file per component. -- Use the existing `css-modules.d.ts` wildcard declaration. Reassess introducing tcm to generate exact `.css.d.ts` files only after the component count exceeds 20. -- PostCSS feature allowlist: currently **no plugins** (flat CSS plus native nesting when needed). Record nested/custom-media in this document before introducing either. - -## 5. Evolution rules and deviation log - -- **Adding a token**: add it to the § 1 table first (including the dark-placeholder column), then use it in the component. Reject any new `--` variable that has not been added to the table (except for component-local variable bridges). -- **Deviating from the baseline**: if an implementation differs from any constant in § 2, add one row to the deviation table below (date / item / rationale). -- **Dark-table completion acceptance**: after `[data-theme='dark']` overrides every placeholder column in § 1, compare the RPC panel, sidebar, and conversation stream manually or with screenshots. Acceptance requires all three to match and no component-level theme selector to remain. - -| Date | Deviation | Rationale | -| --- | --- | --- | -| (none) | | | - -## 6. Related documentation - -- [web-styling-system RFC](../.agents/notes/implemented/process/2026-07-19-web-styling-system.md) (decision record for the five framework rules and engineering constraints) -- Client consumption architecture and layered protocols: [Web client architecture RFC](../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md), [GUI layering and RPC protocol RFC](../.agents/notes/implemented/architecture/2026-07-19-gui-layering-and-rpc-protocol.md) +Add or change a shared token in the owning `ui-theme` sheet, then consume its semantic alias from feature packages. Update the owning package reference when a public styling contract changes. Visual behavior follows the [testing policy](testing.md); the [styling-system Agent Note](../.agents/notes/implemented/process/2026-07-19-web-styling-system.md) records framework rationale. diff --git a/docs/web-styling.zh.md b/docs/web-styling.zh.md index b38ba267cc..6beec368f2 100644 --- a/docs/web-styling.zh.md +++ b/docs/web-styling.zh.md @@ -1,109 +1,25 @@ -# Web GUI 样式规范 +# Web UI 样式参考 [English](web-styling.md) | 中文 -> **【token 体系已换代——§1 表格仅历史参考】** 本文的 `--bg-*`/`--text-*`/`--accent` token 族与其宿主包 `packages/client/web-ui` 已随插件化重构退役。现行 token 唯一来源=`packages/client/ui-theme/src/styles/` 的 `--dsw-*` 体系(static 色阶+alias 语义层,暗色=`body[data-ds-dark-theme]` 覆写),sheet 即权威、组件对账以它为准。**仍然有效**:工程约束(CSS Modules + clsx、无组件库、无 tailwind、组件禁 hardcode 色值)、字号成对写行高、间距 4 倍数、代码字体栈末位不放 monospace。 +本文规定浏览器客户端包的样式职责归属与组件规则。当前 token 值位于 [`packages/client/ui-theme/src/styles/`](../packages/client/ui-theme/src/styles/);本文不重复这份由源码生成的清单。 -> 状态:原「活文档」(随 `packages/client/web-ui` 演进)。视觉基线源自对 deepseekchat 前端仓的实测调研。框架决策与工程约束由 [web-styling-system RFC](../.agents/notes/implemented/process/2026-07-19-web-styling-system.md) 拍板,本文不重复论证。 +## 职责归属 -## 1. 设计 token 表(权威定义) +[`ui-theme`](../packages/client/ui-theme/README.md) 负责 `--dsw-*` 静态色阶、语义别名、排版、动效、渐变、阴影、滚动条样式以及明暗主题偏好。[`ui-layout`](../packages/client/ui-layout/README.md) 将解析后的主题快照应用到文档。功能包使用语义别名,不得另行定义全局主题。 -所有 token 住 `packages/client/web-ui/src/style/global.css`:`:root` 亮色实值,`[data-theme='dark']` 块覆盖同名变量(未补全前列为占位)。组件 CSS 只引 token,不出现字面量色值。 +全局样式表归 `ui-theme/src/styles/` 所有。组件样式以 CSS Modules 形式放在组件旁。当某个值属于组件自身的布局或呈现契约时,组件可以定义局部自定义属性;共享颜色、排版、层级和动效属于主题包。 -### 1.1 颜色(两层:注释里是 base 色板出处,变量名即语义别名) +## 组件规则 -| token | 亮色实值 | 暗色(占位) | 用途 | -| --- | --- | --- | --- | -| `--bg-base` | `#ffffff` | `#151517` | 页面底 | -| `--bg-layer` | `#ffffff` | `#232324` | 浮层/面板 | -| `--bg-sidebar` | `#f9fafb` | `#1b1b1c` | 侧边栏底 | -| `--text-primary` | `#0f1115` | `#f9fafb` | 正文 | -| `--text-secondary` | `#61666b` | `#cfd3d6` | 次要文字 | -| `--text-tertiary` | `#81858c` | `#adb2b8` | 辅助/说明 | -| `--border-l1` | `rgba(0,0,0,.04)` | `rgba(255,255,255,.06)` | 弱分隔(侧边栏右缘) | -| `--border-l2` | `rgba(0,0,0,.1)` | `rgba(255,255,255,.12)` | 常规边框 | -| `--hover-bg` | `rgba(38,49,72,.06)` | `rgba(255,255,255,.08)` | hover 态底 | -| `--active-bg` | `rgba(38,49,72,.1)` | `rgba(255,255,255,.14)` | 按压/激活态底 | -| `--accent` | `#3964fe` | `#5686fe` | 品牌蓝(deepseek-500;暗提亮一档) | -| `--accent-soft` | `#edf3fe` | `#28313f` | 淡品牌底(强调块) | -| `--accent-item` | `#e4edfd` | `#35363a` | 侧边栏选中条目底 | -| `--bubble-bg` | `#edf3fe` | `#2c2c2e` | 用户消息气泡底 | -| `--ok` / `--error` / `--warn` | `#22c55e` / `#ec1313` / `#f59e0b` | 同值 | 语义状态色 | -| `--text-on-solid` | `#ffffff` | 同值 | 实色底(accent/error 徽标等)上的文字 | -| `--ok-soft` / `--error-soft` | `#e6faed` / `#fee2e2` | `#233c2c` / `#570c0c` | 语义状态软底(徽章);green-100/red-100,暗为 900 档 | -| `--color-frame-mux` / `--color-frame-host` | `#8250df` / `#0969da` | 同值 | RPC 调试面板方向色(自有,非基线) | -| `--frame-mux-soft` / `--frame-host-soft` | `rgba(130,80,223,.1)` / `rgba(9,105,218,.1)` | 同色 `.24` | 方向色软底(徽章) | -| `--scroll-color` / `--scroll-color-hover` | `rgba(0,0,0,.08)` / `.15` | `rgba(255,255,255,.15)` / `.24` | 滚动条(`.scrollable` 专用) | +- 使用 CSS Modules 和 `clsx`;不得添加组件库或 Tailwind。 +- 功能组件使用 `--dsw-alias-*` 语义 token。不得复制静态色板值或在其中写入颜色字面量。 +- 功能组件 CSS 不得包含主题选择器。明暗主题覆盖属于主题所有方。 +- 字体大小必须与行高配对;已有角色匹配时使用主题排版变量。 +- 当组件契约要求保留列结构时,源码文本、终端输出和 diff 行不得换行;使用共享滚动条样式,不得定义组件专用滚动条选择器。 +- 呈现规则写在 CSS 中。React 内联样式可以传递组件局部自定义属性值,但不得编码主题分支。 +- 添加过渡动画或仅悬停可见的控件时,保留清晰可见的键盘焦点和减少动态效果行为。 -### 1.2 非颜色 +## 变更系统 -| token | 值 | 说明 | -| --- | --- | --- | -| `--font-ui` | `Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif` | 正文栈 | -| `--font-mono` | `Menlo, Monaco, Consolas, 'JetBrains Mono', 'Courier New', sans-serif` | 代码栈;**末位不放 monospace**(防 Windows 中文回退宋体) | -| `--fw-strong` | `600` | 粗体统一权重 | -| `--ease` | `cubic-bezier(.4,0,.2,1)` | 唯一缓动曲线 | -| `--dur` / `--dur-fast` / `--dur-slow` | `.2s` / `.1s` / `.3s` | 过渡三档 | -| `--radius-s` / `--radius-m` / `--radius-l` / `--radius-bubble` / `--radius-xl` | `8px` / `12px` / `16px` / `22px` / `24px` | 圆角语义档:小控件 / 列表条目与面板内块 / 浮层 / 气泡 / 输入卡片(基线 inputWrapper 同值);胶囊直接写 `999px` | -| `--shadow-panel` | `0 0 1px rgba(0,0,0,.2), 0 0 4px rgba(0,0,0,.02), 0 12px 32px rgba(0,0,0,.08)` | 浮层阴影(基线 lv3) | -| `--shadow-float` | `0 0 1px rgba(0,0,0,.24), 0 4px 12px rgba(0,0,0,.06), 0 16px 48px rgba(0,0,0,.16)` | 强浮动面板(lv3 加强档,如 RPC 调试浮层) | -| `--shadow-card` | `0 4px 10px rgba(0,0,0,.02), 0 2px 4px rgba(0,0,0,.04)`;暗色 `none` | 输入卡片微阴影(基线:亮色同底靠边框+微影区分,暗色靠提亮底、阴影关闭) | - -字号与间距**不 token 化**(基线仓同款决策):字号在组件里写 px 且**成对写行高**,常用对 16/24(气泡)、14/22(UI 默认)、12/18(辅助);间距用 4 的倍数。 - -## 2. 视觉基线(源自 deepseekchat) - -- 侧边栏:宽 `260px + 1px` 右边框(`--border-l1`);底色 `--bg-sidebar`。 -- 侧边栏条目:高 `40px`、圆角 `--radius-m`、字号 14px;hover 底 `--hover-bg` 或 sidebar 专属灰、**选中底 `--accent-item` 且不改文字色**。 -- 侧边栏分组标题:12px / weight 500 / `--text-tertiary` / sticky 顶部(底色同侧边栏遮滚动内容)。 -- 会话列:`max-width: 840px` 居中,<1024px 降 712px。 -- 消息流:**仅用户侧有气泡**——`--bubble-bg` 底、圆角 `--radius-bubble`、padding `10px 16px`、字号 16px/24px、`max-width: calc(100% - 88px)`;**助手侧纯文档流无底色**。 -- 消息操作条:默认 `opacity: 0`,父块 hover/focus-within 淡入(`--dur` + `--ease`)。 -- 输入卡片:与会话列同宽(840px,<1024px 降 712px)居中悬浮(距底留白带);圆角 `--radius-xl`、边框 `--border-l2`、底 `--bg-base`、阴影 `--shadow-card`;内部上下两段=textarea(16px/24px,min 2 行 max 14 行=336px,镜像 div 自增高)+ 操作行(右下嵌 34px 主圆钮);focus 无边框/阴影变化(基线同款)。 -- 输入主按钮(拍板 2026-07-20 三连,视觉参照 Codex App):32px 实心正圆图标钮(内联 SVG)——空闲=`--accent` 底白↑箭头「发送」,运行中原地变 `--accent-soft` 底 accent ■「停止」(同色系不告警、不用红)。**运行中锁输入**(拍板 3,取代早先 hover 菜单方案):textarea disabled(灰、草稿内容保留可见)、无任何排队/插话菜单,停止是唯一动作;turn 结束解禁并 refocus。键盘 Enter=发送、Ctrl/Meta+Enter=换行(运行中键盘路径随锁失效)。 -- 滚动条:近隐形、hover 加深、`scrollbar-gutter: stable` 不占布局(统一走 `.scrollable`,见 §3-9)。 -- RPC 四象限方向符(官方视觉词汇,空间隐喻:上=去 server、下=来自 server;单线=客户端发起的交互、双线=服务端发起的交互): - -| 符号 | 象限 | 徽章配色 | -| --- | --- | --- | -| `↑` | client-request(unary 出站) | `--accent` / `--accent-soft` | -| `↓` | server-response(unary 回包) | ok `--ok`/`--ok-soft`,error `--error`/`--error-soft` | -| `⇟` | server-request(下行流) | mux `--color-frame-mux`/`--frame-mux-soft`,host `--color-frame-host`/`--frame-host-soft` | -| `⇞` | client-response(对 server request 的回应) | `--accent`/`--accent-soft` 降透明度 | - -## 3. 样式编码规范(review 对照打勾) - -1. 颜色/圆角/动效/字体栈只引 §1 token;组件 CSS 出现字面量色值即打回(渐变遮罩等特效除外,须注释说明)。 -2. 组件 CSS 禁止出现 `[data-theme]` 选择器;暗色差异只在 global.css token 表做。确需按主题换非 token 值(渐变端点等),组件定义局部 CSS 变量、主题块只覆写变量(变量桥)。 -3. 类名 camelCase;状态类用单形容词(`.active` `.show`),由 clsx 挂载:`clsx(styles.x, cond && styles.active, className)`。 -4. 对外组件必须透传 `className` 并合入根元素。 -5. 禁用 `composes`;复用靠 token 与组件抽取。 -6. `:global` 仅用于穿透第三方/跨包类名;禁止用它定义新全局类。 -7. 交互过渡一律 `var(--dur*) var(--ease)`,只过渡 opacity / transform / 背景色 / 阴影;纯 hover 展示型元素包 `@media (hover: hover)`。 -8. hover/active 底色优先用透明度制 token(叠任意海拔底色都成立),不新造实色灰。 -9. 滚动容器统一挂 global.css 的 `.scrollable` 工具类;组件内禁写 `::-webkit-scrollbar`。 -10. 媒体查询写在组件 css 尾部、贴着被覆盖规则;断点当前仅 1024px 一档(会话列降档),加第二档需先记入本文档。 -11. 动态样式 JS 侧只写 CSS 变量(`style={{'--x': v}}`),规则留在 CSS;禁止在 TSX 里拼接样式对象做主题/状态分支。 -12. 文字灰阶只用 `--text-primary/secondary/tertiary` 三级,不新造灰色。 - -## 4. 文件组织 - -- `src/style/global.css` 固定分区顺序:① token 表(`:root` + `[data-theme='dark']`)② 全局基础(box-sizing、body、button reset)③ 全局工具类(`.scrollable` 等,总数保持个位数)。 -- `*.module.css` 与组件同目录同名;一个组件一个 module 文件。 -- 类型声明用现有 `css-modules.d.ts` 通配;组件数超 20 再评估引入 tcm 生成精确 `.css.d.ts`。 -- PostCSS 特性白名单:当前**零插件**(平铺 CSS + 原生嵌套按需);引入 nested/custom-media 需先记入本文档。 - -## 5. 演进规则与偏离记录 - -- **加新 token**:先进 §1 表(含暗色占位列)再在组件使用;review 见到未入表的 `--` 新变量即打回(组件局部变量桥除外)。 -- **偏离基线**:与 §2 任一常数不一致的实现,须在下方偏离表记一行(日期/项/理由)。 -- **暗色表补全验收**:`[data-theme='dark']` 覆盖 §1 全部占位列后,用 RPC 面板 + 侧边栏 + 会话流三个界面人工/截图核对一遍,无组件级主题选择器即达标。 - -| 日期 | 偏离项 | 理由 | -| --- | --- | --- | -| (空) | | | - -## 6. 相关文档 - -- [web-styling-system RFC](../.agents/notes/implemented/process/2026-07-19-web-styling-system.md)(框架五条与工程约束的裁决记录) -- 客户端消费架构与分层协议:[Web 客户端架构 RFC](../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md)、[GUI 分层与 RPC 协议 RFC](../.agents/notes/implemented/architecture/2026-07-19-gui-layering-and-rpc-protocol.md) +在所属 `ui-theme` 样式表中添加或修改共享 token,然后在功能包中使用其语义别名。公共样式契约发生变化时,更新所属包的参考文档。视觉行为遵循[测试策略](testing.md);[样式系统 Agent Note](../.agents/notes/implemented/process/2026-07-19-web-styling-system.md) 记录框架依据。 diff --git a/examples/README.i18n.yaml b/examples/README.i18n.yaml index 8841eca967..f898c19e9a 100644 --- a/examples/README.i18n.yaml +++ b/examples/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write examples/README.md -README.md: 64e9804eb69367588e926791039c453b0a9aede9 -README.zh.md: f0a174a2d59a21ed89fa1a83fc80d3fb05764244 +README.md: 5d021d9d9c7abae90b5f96bccd6447f4e2c3dc57 +README.zh.md: 66b355a93c0a0e6b53d1353de4024b7f86e82f7c diff --git a/examples/README.zh.md b/examples/README.zh.md index f0a174a2d5..66b355a93c 100644 --- a/examples/README.zh.md +++ b/examples/README.zh.md @@ -2,32 +2,24 @@ [English](README.md) | 中文 -展示 harness 如何接线的可运行演示(不是 workspace)。每个示例都是一个 **轻量叶节点**:要么是一份选择可替换后端、加载一个应用包的 `cordis.yml` 配置树,要么是一个 **overlay**——由 `dsh --config` 叠加到交付组合([`apps/cli/config/base.cordis.yml`](../apps/cli/config/base.cordis.yml) 加一份 surface overlay)之上的 patch 列表。成组的组合位于 [`@deepseek-ai/dsh-cli-demo`](../packages/examples/cli-demo)、[`@deepseek-ai/dsh-acp-demo`](../packages/examples/acp-demo) 及它们共享的 [`@deepseek-ai/dsh-agent-spine-demo`](../packages/examples/agent-spine-demo) 组合包中;`dsh` 的各 surface 则改用平铺 config tree。没有 `start.ts`;终端 `demo:*` 脚本通过 [`dsh`](../apps/cli/README.md) CLI(命令行界面)启动,无头/ACP(Agent Client Protocol)脚本则调用 `cli-demo`/`acp-demo` bin。 +展示 DeepSeek Harness 主要接口和扩展点的可运行演示。每个子目录负责自己的配置、前置条件、命令和详细行为。 ## mcp-memory -三份默认关闭的参考 overlay 通过通用 MCP 客户端连接一个记忆 MCP 服务器。选择其中一份文件传给 `dsh --config`;DSH 不负责安装或配置上游记忆系统。版本固定的前置条件、身份映射、可选的共用提示词,以及「写入 → 新会话召回 → 使用」验证流程详见 [mcp-memory/README.md](mcp-memory/README.md)。 +通过通用 MCP 客户端连接受支持第三方记忆服务器的可选 overlay。详见[记忆示例参考](mcp-memory/README.md)。 ## headless-agent -非交互式 agent(智能体)演示:接受一个位置任务,在 `@deepseek-ai/dsh-cli-demo` 应用上运行一个完整模型/工具轮次,持久化新会话,打印 `text`、`json` 或 `stream-json`,然后退出。 - -运行:`pnpm run demo:headless "task"`(需要 `DEEPSEEK_API_KEY`)。输出契约、安全边界和快照套件详见 [headless-agent/README.md](headless-agent/README.md)。 +非交互式 agent(智能体):接受一项任务并运行,然后以选定的机器可读或人类可读格式输出结果。详见[无头示例参考](headless-agent/README.md)。 ## jsonrpc-agent -通过 Python SDK 驱动的无人值守编码 agent:JSON-RPC stdio、仅前台 `bash`、`read`/`write`/`edit`、一个前台 `subagent`、`todo_write`、JSONL 持久化和压缩。它不包含终端 UI、stdout 日志、批准、skill 和后台任务控制。详见 [jsonrpc-agent/README.md](jsonrpc-agent/README.md)。 +由 Python SDK 和 JSON-RPC 驱动的无人值守编码 agent。详见 [JSON-RPC 示例参考](jsonrpc-agent/README.md)。 ## web-cordis -**自指** 演示:编码主干加 [`@deepseek-ai/dsh-tool-cordis`](../packages/cordis/tool-cordis),其三个工具(`cordis_inspect`/`cordis_mount`/`cordis_unmount`)使 agent 可以检查当前 DSH 进程、挂载模型编写的临时 Plugin(事件监听器、一个全新工具,或一个供另一临时 Plugin 注入的服务),并再次卸载它们。这些 Plugin 只存在于内存中,共享一个内部 `cordis-dynamic` fiber 子树;`ctx.fs`/`ctx.web` 仅作为它们可用的能力提供方。 - -使用 `pnpm run demo:cordis` 在 `http://127.0.0.1:3081` 启动浏览器 UI,或使用 `pnpm run demo:cordis acp` 启动 ACP 服务器(两者均需 `DEEPSEEK_API_KEY`)。设计与沙箱注意事项详见[工具集 Agent Note](../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md)。 +能够检查并更改内存中 Cordis 插件树的自指 agent。详见 [web-cordis 示例参考](web-cordis/README.md)。 ## acp-agent -作为 **Agent Client Protocol (ACP)** 自动化服务器通过 JSON-RPC stdio 公开的 agent,由 [`@deepseek-ai/dsh-acp-demo`](../packages/examples/acp-demo) 提供。程序化客户端可以创建新会话、发送文本提示词、消费已提交的 assistant 文本、回答一次性权限请求并取消工作。它拥有 ACP 无密钥快照套件。 - -运行:`pnpm run demo:acp`(需要 `DEEPSEEK_API_KEY`);`pnpm run demo:code-mode` 通过 `code-mode.cordis.yml` 覆盖以 Code Mode 启动同一服务器。协议与快照测试契约详见 [acp-agent/README.md](acp-agent/README.md)。 - -默认 `cordis.yml` 组合 [`@deepseek-ai/dsh-sandbox-local`](../packages/sandbox/sandbox-local)、[`@deepseek-ai/dsh-bash-sandbox`](../packages/bash/bash-sandbox) 和 [`@deepseek-ai/dsh-user-approval`](../packages/ui/user-approval)。`workspace-write` 将 bash 和文件系统变更限制在每个会话 workspace 中;范围更广的重试会通过 ACP 成为一次性机器权限请求。 +面向程序化客户端的 ACP(Agent Client Protocol)自动化服务器,支持会话、权限和取消操作。详见 [ACP 示例参考](acp-agent/README.md)。 diff --git a/examples/acp-agent/README.i18n.yaml b/examples/acp-agent/README.i18n.yaml index 5c7abee2a4..0bfa1735ce 100644 --- a/examples/acp-agent/README.i18n.yaml +++ b/examples/acp-agent/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write examples/acp-agent/README.md README.md: 61c6efafe9dde4f91385beebdfd426c57006187b -README.zh.md: 0b0dec487dda0e7915f8e6eed82ed338dc23cf81 +README.zh.md: 343e55722a4dda7c5cc5e0de6b5fecf250b9ab13 diff --git a/examples/acp-agent/README.zh.md b/examples/acp-agent/README.zh.md index 0b0dec487d..343e55722a 100644 --- a/examples/acp-agent/README.zh.md +++ b/examples/acp-agent/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -通过 JSON-RPC stdio 提供的面向自动化的 [ACP(Agent Client Protocol)](https://agentclientprotocol.com)服务器。它面向 parent agent(父智能体)、subagent 提供方和其他程序化客户端,而非产品 UI。 +通过 JSON-RPC stdio 提供的面向自动化的 [ACP(Agent Client Protocol)](https://agentclientprotocol.com) 服务器。它面向 parent agent(父智能体)、subagent 提供方和其他程序化客户端,而非产品 UI。 ```sh pnpm run demo:acp # needs DEEPSEEK_API_KEY (repo-root .env or env) diff --git a/examples/headless-agent/README.i18n.yaml b/examples/headless-agent/README.i18n.yaml index 179ab416ed..de50cce818 100644 --- a/examples/headless-agent/README.i18n.yaml +++ b/examples/headless-agent/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write examples/headless-agent/README.md -README.md: 445804a2611e5e8093eadf345ad10a2a7984c012 -README.zh.md: f1cf1964bbe15bc2b15abcd78a12fc1ffe7ea767 +README.md: 670a91ab402cf98585f2ece70787beb3b4aaf4dc +README.zh.md: 6c8b3b5694403c5e09f2904f5c3ca18fe569163e diff --git a/native/README.i18n.yaml b/native/README.i18n.yaml index ef2ada251f..a55273be29 100644 --- a/native/README.i18n.yaml +++ b/native/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write native/README.md -README.md: 84808b2ee9dafa4f9f980c35a81ebe12480a4f5d -README.zh.md: d413c519b8460037af9f47ab39f6d533c927e22e +README.md: a79d9ca5747d4c4fbfa50745b3eece07b96aea58 +README.zh.md: 708430c0a087e5a9da1dae6fd078cb477db65d9b diff --git a/native/README.zh.md b/native/README.zh.md index d413c519b8..708430c0a0 100644 --- a/native/README.zh.md +++ b/native/README.zh.md @@ -2,21 +2,12 @@ [English](README.md) | 中文 -`node-addon-landlock-run` 的权威源码位于此处:这是 harness 从 npm 引入并使用的 Landlock「先限制自身、再执行」启动器(`packages/sandbox/sandbox-local`、`packages/bash/bash-sandbox`)。启动器在此处开发,与消费方相邻;独立仓库是打包并发布 npm 包系列的发布镜像。 +`node-addon-landlock-run` 的真源;它是供 harness 使用、先施加 Landlock 自限再执行命令的启动器。[`landlock-run/` workspace](landlock-run/README.md)负责其架构、包家族、平台支持、开发工作流和发布流程。独立仓库是发布镜像。 ## 发布镜像 -| 目录 | 镜像仓库 | 上次导出的发布版 | Commit | +| 目录 | 镜像仓库 | 最近导出的版本 | Commit | |---|---|---|---| | `landlock-run/` | https://github.com/deepseek-harness/node-addon-landlock-run | `v0.0.1` | `614f7fd7dc11e6eaceefba9e7ff1fbe28b51ba22` | -该子树是一个自包含的 pnpm workspace,拥有自己的 `AGENTS.md`、文档、门禁和锁文件;它不属于 harness workspace(`pnpm-workspace.yaml` 不包含它),因此 harness 的安装、构建和 CI 门禁绝不会触及它。镜像的 `.github/` 不进入该子树;[.github/workflows/landlock-run.yml](../.github/workflows/landlock-run.yml)(手动触发)在此处运行子树的 CI 任务,对这些任务的更改会在下次导出时镜像到镜像仓库的 `ci.yml`。 - -## 导出流程(发布新版本) - -1. 先通过常规 harness PR 将启动器更改落地于此;触发 `Landlock Run` 工作流,并确保其所有任务通过。 -2. 在镜像 checkout 中替换 `.github/` 以外的所有内容:`git -C rm -rq -- . ':!.github'`,然后执行 `git -C archive HEAD:native/landlock-run | tar -x -C `,最后执行 `git -C add -A` 并提交。 -3. 在镜像中按照其发布清单(`docs/release.md`)操作:`pnpm release:commit ` → 合并 → 标记 `vX.Y.Z` → 两阶段 `Release` 工作流(先以 `publish=false` 预演,再从标签以 `publish=true` 发布)。 -4. 使用已发布的标签/commit 更新上方 manifest(元数据清单)表,并在同一更改中上调 harness 消费方的依赖版本范围。 - -发布镜像不得与此处的权威源码产生分歧:如果更改直接提交到镜像中(例如发布期间的热修复),必须在下次导出前将其移植回此处。 +该子树是自包含的 pnpm workspace,不属于 harness workspace。[启动器发布参考](landlock-run/docs/release.md)负责导出和发布工作流。镜像不得发生分歧:下次导出前,必须把任何直接施加于镜像的热修复移植回此处。 diff --git a/packages/acp/acp/README.i18n.yaml b/packages/acp/acp/README.i18n.yaml index 6ee3e25ed1..441b6b9b7a 100644 --- a/packages/acp/acp/README.i18n.yaml +++ b/packages/acp/acp/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/acp/acp/README.md -README.md: 583025e94d72c1ab03d282f8f4eb101c4e6f4740 -README.zh.md: ffffd97daa911636336ee9a515bf01bad33d31ae +README.md: 7bcf3c2581b25cb3c5223d00c05d9df6a7f6d61e +README.zh.md: 97a8e9c164bab71cb367ad54ce0bcc19ab4314a9 diff --git a/packages/acp/acp/README.md b/packages/acp/acp/README.md index 583025e94d..7bcf3c2581 100644 --- a/packages/acp/acp/README.md +++ b/packages/acp/acp/README.md @@ -4,7 +4,7 @@ English | [中文](README.zh.md) Automation-only [Agent Client Protocol](https://agentclientprotocol.com) server over JSON-RPC stdio. Programmatic clients create fresh harness agents, send text prompts, collect committed assistant text, resolve one-shot permission requests by policy, and cancel work. The primary in-repository client is [`dsh-subagent-acp`](../../subagent/subagent-acp/README.md). -This package is a transport adapter, not a UI integration or a capability seam. It does not expose editor navigation, transcript replay, commands, modes, configuration pickers, elicitation, reasoning, plans, titles, or tool presentation. Interactive rendering and human questions belong to the web and TUI modules. +This package is a transport adapter, not a UI integration or a capability seam. It does not expose editor navigation, transcript replay, commands, modes, configuration pickers, elicitation, reasoning, plans, titles, or tool presentation. Interactive rendering and human questions belong to the Web host and client modules. ## Plugin diff --git a/packages/acp/acp/README.zh.md b/packages/acp/acp/README.zh.md index ffffd97daa..97a8e9c164 100644 --- a/packages/acp/acp/README.zh.md +++ b/packages/acp/acp/README.zh.md @@ -4,7 +4,7 @@ 通过 JSON-RPC stdio 提供的仅面向自动化的 [ACP(Agent Client Protocol)](https://agentclientprotocol.com) 服务器。程序化客户端可以创建新 harness agent(智能体)、发送文本提示词、收集已提交的 assistant 文本、按策略响应一次性权限请求并取消工作。仓库中的主要客户端是 [`dsh-subagent-acp`](../../subagent/subagent-acp/README.md)。 -此包是传输适配器,而非 UI 集成或能力 seam。它不公开编辑器导航、transcript(文本记录)回放、命令、模式、配置选择器、信息征集、推理(reasoning)、计划、标题或工具展示。交互式渲染与向用户提问属于 Web 和 TUI 模块。 +此包是传输适配器,而非 UI 集成或能力 seam。它不公开编辑器导航、transcript(文本记录)回放、命令、模式、配置选择器、信息征集、推理(reasoning)、计划、标题或工具展示。交互式渲染与向用户提问属于 Web 宿主和客户端模块。 ## 插件 diff --git a/packages/bash/README.i18n.yaml b/packages/bash/README.i18n.yaml index 66cc852e04..4c4a2c6180 100644 --- a/packages/bash/README.i18n.yaml +++ b/packages/bash/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/bash/README.md -README.md: ef82e9f4684ecf551ac7701d812088dd6b2ef6d0 -README.zh.md: 84ff244ec3d1ff5d385a3eb334e4cf9f1fe31e03 +README.md: 601782caad24d3555a206365a3f1954d81af1cf0 +README.zh.md: 8b96c4f80ba8776bfd8bdde178178cea946ff36c diff --git a/packages/bash/README.md b/packages/bash/README.md index ef82e9f468..601782caad 100644 --- a/packages/bash/README.md +++ b/packages/bash/README.md @@ -2,16 +2,16 @@ English | [中文](README.zh.md) -The capability family spans the canonical executor seam, its implementations, the shared shell environment, and the model-facing tools. All **product** packages. +The capability family spans the canonical executor seam, its implementations, the shared shell environment, and the model-facing tools. All are **product** packages. | Package | Role | ctx key | |---|---|---| -| `bash/` | Abstract bash executor seam (interface + vocabulary; sandbox result facts carry the [`sandbox/`](../sandbox/README.md) seam's mode/enforcement vocabulary, and the managed-env/output vocabulary is re-exported from the [`subprocess/`](../subprocess/README.md) seam) | `ctx.bash` | -| `bash-local/` | Local `BashExecutor` implementation over the [`subprocess/`](../subprocess/README.md) service (command defaulting, deadlines, terminal env, background-read merge) | (registers `ctx.bash`) | -| `bash-sandbox/` | Sandbox-consuming `BashExecutor` (wraps every command argv via `ctx.sandbox`, stamps denial/enforcement facts; extends `bash-local`'s mechanics) | (registers `ctx.bash`) | -| `pwsh-local/` | Local PowerShell `BashExecutor` implementation over the [`subprocess/`](../subprocess/README.md) service (executable resolution, UTF-8-pinned spawn, Windows termination semantics) | (registers `ctx.bash`) | -| `bash-env/` | Tool-independent managed `DSH_*` shell environment registry shared by the shell tools (built-in facts + effect-scoped contributors) | (registers `ctx.bashEnv`) | -| `tool-bash/` | Model-facing `bash` schema; background processes register with the generic [`tasks/`](../tasks/README.md) runtime | (registers on `ctx.tools`) | -| `tool-pwsh/` | Model-facing PowerShell-dialect `pwsh` schema (behavior mirrors `tool-bash` minus the sandbox surface); background processes register with the generic [`tasks/`](../tasks/README.md) runtime | (registers on `ctx.tools`) | +| [`bash/`](bash/README.md) | Defines the executor contract shared by implementations and consumers. | `ctx.bash` | +| [`bash-local/`](bash-local/README.md) | Executes commands through the local [`subprocess`](../subprocess/README.md) service. | (registers `ctx.bash`) | +| [`bash-sandbox/`](bash-sandbox/README.md) | Applies the configured [`sandbox`](../sandbox/README.md) backend before local execution. | (registers `ctx.bash`) | +| [`pwsh-local/`](pwsh-local/README.md) | Executes PowerShell commands with Windows-specific process behavior. | (registers `ctx.bash`) | +| [`bash-env/`](bash-env/README.md) | Provides the managed `DSH_*` environment shared by shell tools. | `ctx.bashEnv` | +| [`tool-bash/`](tool-bash/README.md) | Exposes Bash execution and background-task integration to the model. | (registers on `ctx.tools`) | +| [`tool-pwsh/`](tool-pwsh/README.md) | Exposes PowerShell execution to the model. | (registers on `ctx.tools`) | -The interface lives at `bash/bash/`. `bash-sandbox` replacing `bash-local` without touching the interface or the tool is the split doing exactly what it exists for — a leaf `cordis.yml` picks one executor entry, plus a `ctx.sandbox` provider entry for the confined one (see [the acp-agent example's default composition](../../examples/acp-agent/)). +A leaf `cordis.yml` selects one executor implementation and the model-facing tools it needs. A sandboxed composition also selects a `ctx.sandbox` provider; the [ACP example](../../examples/acp-agent/) shows one complete wiring. diff --git a/packages/bash/README.zh.md b/packages/bash/README.zh.md index 84ff244ec3..8b96c4f80b 100644 --- a/packages/bash/README.zh.md +++ b/packages/bash/README.zh.md @@ -1,17 +1,17 @@ -# bash/:bash 能力家族 +# bash/ — bash 能力家族 [English](README.md) | 中文 -能力家族横跨规范执行器 seam、其实现、共享 shell 环境与面向模型的工具。这些全是**产品**包。 +该能力家族涵盖规范执行器 seam、其实现、共享 shell 环境和面向模型的工具。这些全是**产品**包。 | 包 | 职责 | ctx key | |---|---|---| -| `bash/` | 抽象 bash 执行器 seam(接口 + 词汇;沙箱结果事实携带 [`sandbox/`](../sandbox/README.md) seam 的模式/强制执行词汇,受管环境/输出词汇则从 [`subprocess/`](../subprocess/README.md) seam 重导出) | `ctx.bash` | -| `bash-local/` | 构建在 [`subprocess/`](../subprocess/README.md) 服务之上的本地 `BashExecutor` 实现(命令默认值补全、deadline、终端环境、后台读取合并) | (注册 `ctx.bash`) | -| `bash-sandbox/` | 消费沙箱的 `BashExecutor`(通过 `ctx.sandbox` 包装每个命令 argv,标记拒绝/强制执行事实;扩展 `bash-local` 的机制) | (注册 `ctx.bash`) | -| `pwsh-local/` | 构建在 [`subprocess/`](../subprocess/README.md) 服务之上的本地 PowerShell `BashExecutor` 实现(可执行文件解析、UTF-8 固定 spawn、Windows 终止语义) | (注册 `ctx.bash`) | -| `bash-env/` | 工具无关的受管 `DSH_*` shell 环境注册表,由 shell 工具共享(内置事实 + 受 effect 作用域约束的 contributor) | (注册 `ctx.bashEnv`) | -| `tool-bash/` | 面向模型的 `bash` schema;后台进程注册到通用 [`tasks/`](../tasks/README.md) 运行时 | (注册到 `ctx.tools`) | -| `tool-pwsh/` | 面向模型的 PowerShell 方言 `pwsh` schema(行为镜像 `tool-bash`,减去 sandbox 面);后台进程注册到通用 [`tasks/`](../tasks/README.md) 运行时 | (注册到 `ctx.tools`) | +| [`bash/`](bash/README.md) | 定义实现与消费方共享的执行器契约。 | `ctx.bash` | +| [`bash-local/`](bash-local/README.md) | 通过本地 [`subprocess`](../subprocess/README.md) 服务执行命令。 | (注册 `ctx.bash`) | +| [`bash-sandbox/`](bash-sandbox/README.md) | 在本地执行前应用已配置的 [`sandbox`](../sandbox/README.md) 后端。 | (注册 `ctx.bash`) | +| [`pwsh-local/`](pwsh-local/README.md) | 以 Windows 专用进程行为执行 PowerShell 命令。 | (注册 `ctx.bash`) | +| [`bash-env/`](bash-env/README.md) | 提供 shell 工具共享的托管 `DSH_*` 环境。 | `ctx.bashEnv` | +| [`tool-bash/`](tool-bash/README.md) | 向模型公开 Bash 执行和后台任务集成。 | (注册到 `ctx.tools`) | +| [`tool-pwsh/`](tool-pwsh/README.md) | 向模型公开 PowerShell 执行。 | (注册到 `ctx.tools`) | -接口位于 `bash/bash/`。以 `bash-sandbox` 替换 `bash-local`,同时不改动接口或工具,正是这种拆分存在的意义:叶级 `cordis.yml` 选择一个执行器插件条目;受限实现还需再选择一个 `ctx.sandbox` 提供方插件条目(见 [acp-agent 示例的默认组合](../../examples/acp-agent/))。 +叶节点 `cordis.yml` 选择一个执行器实现和所需的面向模型工具。沙箱化组合还会选择一个 `ctx.sandbox` 提供方;[ACP(Agent Client Protocol)示例](../../examples/acp-agent/)展示一套完整接线。 diff --git a/packages/bash/bash-env/README.i18n.yaml b/packages/bash/bash-env/README.i18n.yaml index 90d43daa7b..47f4cf01cc 100644 --- a/packages/bash/bash-env/README.i18n.yaml +++ b/packages/bash/bash-env/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/bash/bash-env/README.md README.md: 7b939326d4effd14fc83ef0ad4e133f019f1011f -README.zh.md: aeb33629def3fcc10294bbca19b37d43dcc73a0c +README.zh.md: b6f3aca41771f1ca990a4b708cd53119b8e8ad78 diff --git a/packages/bash/bash-env/README.zh.md b/packages/bash/bash-env/README.zh.md index aeb33629de..b6f3aca417 100644 --- a/packages/bash/bash-env/README.zh.md +++ b/packages/bash/bash-env/README.zh.md @@ -17,7 +17,7 @@ ## Managed environment -每次前台与后台模型 shell 调用都会收到一份新收集的受信任 `DSH_*` 环境。`DSH_HOME` 是由 [`@deepseek-ai/dsh-paths`](../../util/paths/README.md) 解析的 Harness 主目录绝对路径(`dshHome` 配置,然后环境变量 `$DSH_HOME`,然后 `~/.dsh`),`DSH_SHELL=1` 标识受管理的子进程。带 agent 的调用额外收到 `DSH_SESSION_ID=agent.session.header.id`;当活动的持久化 seam 定位到 JSONL 工件时,它们还会收到 `DSH_SESSION_JSONL=<绝对目标路径>`。JSONL 路径只是位置提示:首次 flush 之前它可能不存在,也不一定包含当前缓冲中的轮次,并且它不是授权凭据。 +每次前台与后台模型 shell 调用都会收到一份新收集的受信任 `DSH_*` 环境。`DSH_HOME` 是由 [`@deepseek-ai/dsh-paths`](../../util/paths/README.md) 解析的 Harness 主目录绝对路径(`dshHome` 配置,然后环境变量 `$DSH_HOME`,然后 `~/.dsh`),`DSH_SHELL=1` 标识受管理的子进程。带 agent(智能体)的调用额外收到 `DSH_SESSION_ID=agent.session.header.id`;当活动的持久化 seam 定位到 JSONL 工件时,它们还会收到 `DSH_SESSION_JSONL=<绝对目标路径>`。JSONL 路径只是位置提示:首次 flush 之前它可能不存在,也不一定包含当前缓冲中的轮次,并且它不是授权凭据。 `ctx.bashEnv` 负责收集。其他插件可以注册一个受 effect 作用域约束的 contributor,带有稳定名称、已声明的键/描述以及 `resolve(execution: ToolExecution)`;重复所有权与未声明的运行时键会响亮失败,而 `list()` 只枚举声明、不执行 provider。Harness 内置键保留 `DSH_HOME`、`DSH_SHELL` 与 `DSH_SESSION_ID`;本插件的持久化翻译器通过读取与后端无关的 `sessionPersistence.locate()` seam 拥有 `DSH_SESSION_JSONL`。 diff --git a/packages/bash/bash-local/README.i18n.yaml b/packages/bash/bash-local/README.i18n.yaml index 9235db170e..4d0214386f 100644 --- a/packages/bash/bash-local/README.i18n.yaml +++ b/packages/bash/bash-local/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/bash/bash-local/README.md -README.md: 694b7a7686ea6c38da5a354ff6b6e6d2c4520706 -README.zh.md: 8d6e3b8d2fdd92faeb7b1e6e373a938ae2064cd4 +README.md: bb87ad6fe021e3144cef4adced3d798bf3d94d67 +README.zh.md: d2f8c9091072bbf3d75909f6826432601001ab88 diff --git a/packages/bash/bash-local/README.zh.md b/packages/bash/bash-local/README.zh.md index 8d6e3b8d2f..d2f8c90910 100644 --- a/packages/bash/bash-local/README.zh.md +++ b/packages/bash/bash-local/README.zh.md @@ -20,15 +20,13 @@ graceMs: 3000 # kill escalation and post-exit pipe-drain grace ``` -## 行为(以及设计来源) +## 行为 -设计时调研了 Claude Code、OpenCode、Codex 和 pi 的 bash 工具,主要取舍如下: - -- **每次调用都 spawn,不保留 shell 状态**:每次调用都启动新的非登录 `bash -c`(行为确定,不读取 rc 文件)。调研的四种工具均会每次调用单独 spawn。`XXX(stateful-shell)` 位于 `src/index.ts`,记录了两种已验证的有状态设计(Claude Code 仅持久化 cwd;Codex 使用 PTY exec 会话),供真实工作流需要时采用。 -- **在受管进程组之上应用配置预算**:`resolve()` 从配置补全 `workdir`/`timeoutMs`/`stdoutMaxBytes`,每次 spawn 都向服务传入显式的字节上限、spill 上限与 `graceMs`(默认 3 秒,沿用 OpenCode 的升级策略)。进程组终止、退出后的管道排空宽限期、尾部保留截断与有界 spill 文件是 [`dsh-subprocess-local`](../../subprocess/subprocess-local/README.md) 的机制。前台 `BashExecRequest.stdoutMaxBytes` 可为某个受信任调用方提高单次 stdout 捕获预算;stderr 和后台运行仍使用 `maxOutputBytes`。 +- **每次调用都 spawn,不保留 shell 状态**:每次调用都启动新的非登录 `bash -c`,且不读取 rc 文件。 +- **在受管进程组之上应用配置预算**:`resolve()` 从配置补全 `workdir`/`timeoutMs`/`stdoutMaxBytes`,每次 spawn 都向服务传入显式的字节上限、spill 上限与 `graceMs`。进程组终止、退出后管道排空、尾部保留与有界 spill 文件是 [`dsh-subprocess-local`](../../subprocess/subprocess-local/README.md) 的机制。前台 `BashExecRequest.stdoutMaxBytes` 可为某个受信任调用方提高单次 stdout 捕获预算;stderr 和后台运行仍使用 `maxOutputBytes`。 - **超时与取消分类**:`run()` 通过同一个 deadline 把经配置钳位的超时与调用方的信号融合;只有执行器自身的超时报告 `timedOut`,上游取消报告 `aborted`,自身因信号终止的命令两者皆不报告(见[超时库 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md))。 -- **适合模型的终端环境**:设置 `NO_COLOR=1 TERM=dumb PAGER=cat GIT_PAGER=cat`(Codex 硬编码的集合),防止分页器与 ANSI 颜色破坏结果;这些条目作为普通 env 合并,遵循服务的凭据清除与 `DSH_*` 通道规则;调用方的显式条目依旧优先。详见 [stdin/env Agent Note](../../../.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.md) 与 [受管环境 Agent Note](../../../.agents/notes/implemented/feature/2026-07-10-agent-session-identity-and-log-location.md)。 -- **后台进程**:`start()` 会立即返回活动的 `BashProcess` 句柄,不应用超时(Claude Code 在转为后台时会解除超时);句柄的 `readOutput()` 把服务基于偏移量的 stdout/stderr 读取合并为一条带分节标记的增量,并以消费游标记录读取进度。仍在运行的进程则由 subprocess 服务负责,因此它能在执行器重载后存活,并随服务的 dispose 被终止且等待退出。所有具有任务形态的事项(id、所有权、轮询、通知)都属于通用 [`ctx.tasks` 运行时](../../tasks/tasks/README.md),工具层会在其中注册该句柄;本执行器不会接触会话或注册表。 +- **适合模型的终端环境**:`NO_COLOR=1 TERM=dumb PAGER=cat GIT_PAGER=cat` 防止分页器与 ANSI 颜色破坏结果。这些值作为普通 env 合并,遵循服务的凭据清除与 `DSH_*` 通道规则;调用方的显式条目依旧优先。详见 [stdin/env Agent Note](../../../.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.md) 与 [受管环境 Agent Note](../../../.agents/notes/implemented/feature/2026-07-10-agent-session-identity-and-log-location.md)。 +- **后台进程**:`start()` 会立即返回活动的 `BashProcess` 句柄且不应用超时;`readOutput()` 把基于偏移量的 stdout/stderr 读取合并为一条消费式增量,并在存在 stderr 时将其置于 `[stderr]` 标记下。运行中的进程属于 subprocess 服务,可在执行器重载后存活,并在服务 dispose 时被终止且等待退出。task id、所有权、轮询和通知属于通用 [`ctx.tasks` 运行时](../../tasks/tasks/README.md),工具层会在其中注册该句柄。 ## 模型体验 diff --git a/packages/bash/pwsh-local/README.i18n.yaml b/packages/bash/pwsh-local/README.i18n.yaml index 1b78ca75f9..ef6a984d07 100644 --- a/packages/bash/pwsh-local/README.i18n.yaml +++ b/packages/bash/pwsh-local/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/bash/pwsh-local/README.md -README.md: 9deba9c1b63ccfdb9e1805b9896db33f144839bf -README.zh.md: e45c820e1d5e31aebd9ed365c6130850f1db2a62 +README.md: 2914c46ab91dd9555dab04551e52321f6eac05bf +README.zh.md: ce9696b276a2e60acf116d7124cd5cd256d7ebde diff --git a/packages/bash/pwsh-local/README.md b/packages/bash/pwsh-local/README.md index 9deba9c1b6..2914c46ab9 100644 --- a/packages/bash/pwsh-local/README.md +++ b/packages/bash/pwsh-local/README.md @@ -46,7 +46,7 @@ No direct invalidation; the named consumer owns any request-prefix changes. ## Known Limitations and Deferred Work - **Unconfined by itself** — this executor always runs commands with the harness process's authority; deployments needing confinement compose a sandboxing bash executor or policy instead. -- **No persistent shell or PTY** — every call starts a fresh `pwsh -Command`; interactive terminal sessions remain deferred until the roadmap's pwsh TUI/GUI rendering work lands. +- **No persistent shell or PTY** — every call starts a fresh `pwsh -Command`. - **The command string is PowerShell text** — the `-Command` domain has no shell-quoting layer, but a model-facing command is parsed by PowerShell itself, so PowerShell syntax errors are command failures, not launch failures. - **A background spawn-failure note is single-delivery** — the subprocess service buffers no output for a process that never ran, so the executor injects `spawn failed: …` into exactly one `readOutput()` delta; a reader that discards that delta cannot recover it. - **Windows termination reports no signal** — a force-killed process settles as exit 1 with `signal: null`, so signal-based status classification (POSIX `killed`) does not apply on Windows; `kill()`-initiated stops still stamp `killed` directly. diff --git a/packages/bash/pwsh-local/README.zh.md b/packages/bash/pwsh-local/README.zh.md index e45c820e1d..ce9696b276 100644 --- a/packages/bash/pwsh-local/README.zh.md +++ b/packages/bash/pwsh-local/README.zh.md @@ -46,7 +46,7 @@ ## 已知局限与延期工作 - **自身不设沙箱**——本执行器始终以 harness 进程的权限运行命令;需要约束的部署应组合沙箱化 bash 执行器或策略。 -- **无持久 shell 或 PTY**——每次调用都是全新的 `pwsh -Command`;交互式终端会话在路线图的 pwsh TUI/GUI 渲染工作落地之前保持延期。 +- **无持久 shell 或 PTY**——每次调用都是全新的 `pwsh -Command`。 - **命令字符串是 PowerShell 文本**——`-Command` 域没有 shell 引号层,但面向模型的命令由 PowerShell 自己解析,因此 PowerShell 语法错误是命令失败,而非启动失败。 - **后台 spawn 失败提示只投递一次**——subprocess 服务不会为从未运行的进程缓冲输出,因此执行器只把 `spawn failed: …` 注入一次 `readOutput()` 增量;丢弃该增量的读取方无法恢复它。 - **Windows 终止不报告信号**——被强制终止的进程以退出码 1、`signal: null` 结束,因此基于信号的状态分类(POSIX `killed`)在 Windows 上不适用;`kill()` 发起的停止仍会直接盖上 `killed`。 diff --git a/packages/bash/tool-bash/README.i18n.yaml b/packages/bash/tool-bash/README.i18n.yaml index fb33cffefb..e37f3a2859 100644 --- a/packages/bash/tool-bash/README.i18n.yaml +++ b/packages/bash/tool-bash/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/bash/tool-bash/README.md README.md: 35a5647365dab6daa903c14cba8b83702b50305d -README.zh.md: eb7901f3445117988d77e6d39b73681480b7a754 +README.zh.md: 0c7b1c1ec83c5d30dd97bf5cdae1f626f4065ab7 diff --git a/packages/bash/tool-bash/README.zh.md b/packages/bash/tool-bash/README.zh.md index eb7901f344..0c7b1c1ec8 100644 --- a/packages/bash/tool-bash/README.zh.md +++ b/packages/bash/tool-bash/README.zh.md @@ -42,7 +42,7 @@ ## 工具仅使用具名参数构建请求 -`BashExecRequest` seam 携带可选的 `stdoutMaxBytes`、`stdin`、普通 `env` 和托管 `dshEnv`,供可信进程内插件及此工具的环境注册表使用。模型侧工具不公开 `stdoutMaxBytes`、`stdin` 或 `env`:它使用具名的命令/工作目录/超时/信号/沙箱字段,加上从注册表收集的 `dshEnv` 来构建请求。额外模型键会被忽略,无法替换托管值。Shell 语法可以提供等价的命令级行为,而本地执行器会清除环境中的凭据和陈旧 `DSH_*` 值。参见 [stdin/env Agent Note(agent 决策记录)](../../../.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.md)。 +`BashExecRequest` seam 携带可选的 `stdoutMaxBytes`、`stdin`、普通 `env` 和托管 `dshEnv`,供可信进程内插件及此工具的环境注册表使用。模型侧工具不公开 `stdoutMaxBytes`、`stdin` 或 `env`:它使用具名的命令/工作目录/超时/信号/沙箱字段,加上从注册表收集的 `dshEnv` 来构建请求。额外模型键会被忽略,无法替换托管值。Shell 语法可以提供等价的命令级行为,而本地执行器会清除环境中的凭据和陈旧 `DSH_*` 值。参见 [stdin/env Agent Note](../../../.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.md)。 ## 权限与升权 diff --git a/packages/bash/tool-pwsh/README.i18n.yaml b/packages/bash/tool-pwsh/README.i18n.yaml index 030d24c7c2..303648b718 100644 --- a/packages/bash/tool-pwsh/README.i18n.yaml +++ b/packages/bash/tool-pwsh/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/bash/tool-pwsh/README.md README.md: dfe26a63684d61dcdd6f969c2c2261dac79325c7 -README.zh.md: 2344f8477e5b15f2c4d366dd82b46358eacbc1b7 +README.zh.md: 87a30130c2f4c56be34199dca39399c45eb4b323 diff --git a/packages/bash/tool-pwsh/README.zh.md b/packages/bash/tool-pwsh/README.zh.md index 2344f8477e..87a30130c2 100644 --- a/packages/bash/tool-pwsh/README.zh.md +++ b/packages/bash/tool-pwsh/README.zh.md @@ -19,8 +19,8 @@ | `command` | string (required) | 通过 `pwsh -Command` 运行。调用之间不保留状态——用 `workdir`,不要用 `cd`。 | | `description` | string (required) | 命令的一行主动语态摘要(5-10 词),仅用于 UI/日志展示——不影响执行。 | | `timeoutMs` | number | 超时覆盖值(毫秒)。执行器应用其配置的默认值与上限。 | -| `workdir` | string | 本次调用的工作目录。默认取调用 agent 的会话 cwd(`session.header.cwd`),使每个会话在自己的工作区运行;相对 `workdir` 基于同一身份解析。 | -| `run_in_background` | boolean | 立即返回任务 id;不适用超时。 | +| `workdir` | string | 本次调用的工作目录。默认取调用 agent(智能体)的会话 cwd(`session.header.cwd`),使每个会话在自己的工作区运行;相对 `workdir` 基于同一身份解析。 | +| `run_in_background` | boolean | 立即返回 task id;不适用超时。 | `command`、`workdir` 与 `timeoutMs` 在执行前经 `ctx.bash.resolve()` 按执行器配置默认值解析。workdir 默认值在工具层于 `resolve()` 之前从调用 agent 的 `session.header.cwd` 取得——每次会话的 cwd 必须来自 `exec.agent`,因为 N 个会话共享一个执行器;仅当没有会话 cwd 时执行器才回退到自己的配置 / `process.cwd()`。 diff --git a/packages/client/AGENTS.md b/packages/client/AGENTS.md index 8bf351220c..0378137364 100644 --- a/packages/client/AGENTS.md +++ b/packages/client/AGENTS.md @@ -60,16 +60,16 @@ One UI feature = one plugin package (`src/client/` browser half). A multi-domain ## Styling -[docs/web-styling.md](../../docs/web-styling.md) is authoritative. In short: design tokens live in `web-ui/src/style/global.css` (`:root` light values, `[data-theme='dark']` overrides); component CSS references tokens only — no literal color values. CSS Modules + `clsx`; no component library, no tailwind ([framework ruling](../../.agents/notes/implemented/process/2026-07-19-web-styling-system.md)). Product copy is Chinese; code comments are English. +[docs/web-styling.md](../../docs/web-styling.md) is authoritative. Shared `--dsw-*` tokens and global sheets live in `ui-theme/src/styles/`; feature components consume semantic aliases through CSS Modules and `clsx`, with no literal colors, component library, or Tailwind. Product copy is Chinese; code comments are English. ## Testing and coverage The GUI test structure (three tiers, lane map) is settled in the [GUI testing system note](../../.agents/notes/implemented/process/2026-07-20-gui-testing-system.md); repo-wide policy in [docs/testing.md](../../docs/testing.md). -- **Both client packages are inside the per-file 100% coverage gate** (`pnpm run test:coverage`). `web-runtime` is covered by node-env object/protocol suites; `web-ui` rides the jsdom lane. Genuinely unreachable defensive arms take a `/* v8 ignore -- */` comment with a real reason, never a bare ignore. -- **web-ui specs are end-to-end behavior checks, not unit tests.** A jsdom spec renders the component with realistic props (or a driven fixture runtime) and asserts what the user would see — never class names, hook internals, or render counts. Components are consumables: behavior-shaped specs survive a rewrite, implementation-shaped specs don't. -- The jsdom environment comes from a per-file `// @vitest-environment jsdom` pragma on the spec's first line — the shared config stays node-env. Start a new spec from an existing one (`web-ui/tests/tool-card.spec.tsx` is a good template). -- **Each tier asserts its own layer.** Data-layer semantics (state machines, wire shapes, reference stability) belong to the `web-runtime` and `apiproxy` suites — don't re-assert them from component specs. +- Client source packages are inside the per-file 100% coverage gate (`pnpm run test:coverage`). Genuinely unreachable defensive arms take a `/* v8 ignore -- */` comment with a real reason, never a bare ignore. +- Component specs render with realistic props or a driven fixture runtime and assert user-visible behavior, not class names, hook internals, or render counts. +- The jsdom environment comes from a per-file `// @vitest-environment jsdom` pragma on the spec's first line; the shared config stays node-env. +- Each tier asserts its own layer. Data-layer semantics belong to the runtime and host suites; component specs cover presentation behavior. ## Before you push: the local check ladder diff --git a/packages/client/README.i18n.yaml b/packages/client/README.i18n.yaml index 210228ae88..503a747087 100644 --- a/packages/client/README.i18n.yaml +++ b/packages/client/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/README.md -README.md: 31d884c04a8b0233713b77b82b3d9cc7052003ca -README.zh.md: 9c95db529306519136d6d68758889350e5dd65e4 +README.md: b950772d4cad6d873426f8aee6416fa56afca2ee +README.zh.md: 8f1f7f46777b7037e8baa04c9ec16ef74ffd478d diff --git a/packages/client/README.md b/packages/client/README.md index 31d884c04a..b950772d4c 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -2,33 +2,38 @@ English | [中文](README.zh.md) -The browser side of the dsh web GUI: shell kernel, module system, wire consumer, React-free object services, the slot system, and the `ui-*` feature-plugin roster. Authoring rules live in [AGENTS.md](AGENTS.md); the host half is [`host/`](../host/README.md). All **product** packages, named `@deepseek-ai/dsh-client-`. +The browser side of the dsh web GUI: shell boot, browser-host communication, shared UI services, and feature plugins. Authoring rules live in [AGENTS.md](AGENTS.md); the host half is [`host/`](../host/README.md). All except `test-runtime` are **product** packages named `@deepseek-ai/dsh-client-`. -| Package | Role | ctx key / slot | -|---|---|---| -| `web/` | Shell kernel: `AppWebEntry` runs the two-stage boot over the host-pushed entry graph | (boots the tree) | -| `modules/` | Client module system: browser peer of Node's ESM loader as a lazy CJS table under the vendored cordis Loader | (module face) | -| `web-react/` | Shell-side React glue: `createSlotRenderer` + `SessionProvider` render seats | (renderer install) | -| `connection/` | Wire consumer both ends: browser `ctx.connection` (shared api client + stream loop) and the node half mounting the `/api` route with its browser-trust fence | `ctx.connection` | -| `runtime/` | Client cordis boot and React-free object services: slots, Sessions, Workspaces, per-session bindings | `ctx.slots` `ctx.sessions` `ctx.workspaces` | -| `hmr/` | Dev-only hot reload for script-loaded client plugins (`--dev` graphs) | (dev entry) | -| `locale/` | Browser locale preference (`zh`/`en`) plus the ns×locale dictionary registry | `ctx.locale` | -| `ui-slots/` | Slot registry pure core: SlotMap merging, single `register` API, the four-share props family | (types + core) | -| `ui-theme/` | Theme preference over the `--dsw-*` token stylesheets (`light`/`dark`/`system`) | `ctx.theme` | -| `ui-primitives/` | Pure React atoms: icons, Button/Pill/Menu/Modal/Input, markdown family | (component library) | -| `ui-layout/` | Shell three-column AppFrame; declares `sidebar` / `conversation` / `details` / `conversation.empty` | `ctx.layout` | -| `ui-sidebar/` | Sidebar shell: Workspace/session rail, search, collapse; declares `sidebar.workspaces` | (slot host) | -| `ui-workspace/` | Shared Workspace picker: browser region + hero picker over the same creation flow | (fills `sidebar.workspaces`, `conversation.hero.workspace`) | -| `ui-conversation/` | Conversation domain: skeleton, chat view, input dock, per-tool row slots | (slot host) | -| `ui-trajectory/` | Trajectory/Waterfall view tabs; the minimal pure-consumer plugin exemplar | (fills `conversation.view`) | -| `ui-command/` | Command surface: session-keyed directory cache, `/` source, three-kind dispatch | `ctx.command` | -| `ui-slash/` | Input trigger pipeline: `/` and `@` detection, grouped candidate menu, source roster | `ctx.slash` | -| `ui-skill/` | `/`-trigger skill reference source over the `skill.list` RPC | (registers into `ctx.slash`) | -| `ui-subagent/` | `@`-trigger subagent reference source over the sessions snapshot | (registers into `ctx.slash`) | -| `ui-model/` | Model selection: `/model` popupSelect + the composer model seat over `ModelService` | `ctx.models` | -| `ui-question/` | Web `ask_user_question`: host half mounts the tool, browser half fills the composer seat | (fills `conversation.composer`) | -| `ui-settings/` | Settings shell: trigger chrome + modal panel; declares the `settings.*` slots | (slot host) | -| `ui-settings-general/` | Settings ownerless copy: chrome content + General section skeleton | (fills `settings.*`) | -| `ui-models/` | Models settings nav entry (content column lands in a later phase) | (fills `settings.section`) | +| Package | Purpose | +|---|---| +| [`web/`](web/README.md) | Boots the browser shell from the client entry graph. | +| [`modules/`](modules/README.md) | Loads browser-side client modules. | +| [`web-react/`](web-react/README.md) | Connects the shell runtime to React rendering. | +| [`connection/`](connection/README.md) | Maintains browser-host RPC communication and event delivery. | +| [`runtime/`](runtime/README.md) | Provides shared client services for sessions, workspaces, and UI composition. | +| [`hmr/`](hmr/README.md) | Refreshes client plugins during development. | +| [`locale/`](locale/README.md) | Provides localization preferences and message dictionaries. | +| [`schema-form/`](schema-form/README.md) | Provides schema-backed draft handling for settings editors. | +| [`test-runtime/`](test-runtime/README.md) | Provides shared repository test support for client feature packages. | +| [`ui-slots/`](ui-slots/README.md) | Defines how UI features register and compose extension slots. | +| [`ui-theme/`](ui-theme/README.md) | Applies the selected color theme. | +| [`ui-primitives/`](ui-primitives/README.md) | Provides shared React controls, icons, and content renderers. | +| [`ui-layout/`](ui-layout/README.md) | Arranges the main application regions. | +| [`ui-sidebar/`](ui-sidebar/README.md) | Presents workspace and session navigation. | +| [`ui-workspace/`](ui-workspace/README.md) | Provides workspace selection and creation surfaces. | +| [`ui-conversation/`](ui-conversation/README.md) | Presents the active conversation and its input surface. | +| [`ui-goal/`](ui-goal/README.md) | Presents and manages the current goal. | +| [`ui-trajectory/`](ui-trajectory/README.md) | Presents alternate views of agent activity. | +| [`ui-command/`](ui-command/README.md) | Provides session-aware command discovery and dispatch. | +| [`ui-slash/`](ui-slash/README.md) | Coordinates inline command and reference suggestions. | +| [`ui-skill/`](ui-skill/README.md) | Adds skill references to inline suggestions. | +| [`ui-subagent/`](ui-subagent/README.md) | Provides subagent navigation, child transcript states, and inline references. | +| [`ui-model/`](ui-model/README.md) | Provides model selection in conversation surfaces. | +| [`ui-permission/`](ui-permission/README.md) | Configures default permissions and switches the current session's access. | +| [`ui-plan/`](ui-plan/README.md) | Presents active plan-mode status and its exit control. | +| [`ui-question/`](ui-question/README.md) | Presents interactive questions requested by the agent. | +| [`ui-settings/`](ui-settings/README.md) | Hosts the settings interface and its extension areas. | +| [`ui-settings-general/`](ui-settings-general/README.md) | Provides the general settings section. | +| [`ui-models/`](ui-models/README.md) | Provides model-provider configuration and DeepSeek onboarding. | -Feature UI composes only through the slot system (`ctx.slots.register`) — the [slot system standard](../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md) is the definitive model; the [web client architecture note](../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md) owns the loading chain and object layer. +Each child reference owns its contract and detailed behavior. The [slot system standard](../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md) and [web client architecture note](../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md) own the cross-package composition and loading decisions. diff --git a/packages/client/README.zh.md b/packages/client/README.zh.md index 9c95db5293..8f1f7f4677 100644 --- a/packages/client/README.zh.md +++ b/packages/client/README.zh.md @@ -2,33 +2,38 @@ [English](README.md) | 中文 -dsh web GUI 的浏览器侧:shell 内核、模块系统、协议消费层、无 React 依赖的对象服务、slot 系统,以及 `ui-*` 特性插件阵列。编写规则见 [AGENTS.md](AGENTS.md);宿主半侧是 [`host/`](../host/README.md)。全部为**产品**包,命名为 `@deepseek-ai/dsh-client-`。 +dsh web GUI 的浏览器侧:shell 启动、浏览器与宿主通信、共享 UI 服务和特性插件。编写规则见 [AGENTS.md](AGENTS.md);宿主半侧是 [`host/`](../host/README.md)。除 `test-runtime` 外,均为命名成 `@deepseek-ai/dsh-client-` 的**产品**包。 -| 包 | 角色 | ctx 键/slot | -|---|---|---| -| `web/` | shell 内核:`AppWebEntry` 基于宿主推送的条目图运行两阶段启动 | (启动整棵树) | -| `modules/` | 客户端模块系统:Node ESM 加载器的浏览器对等物,是 vendored cordis Loader 之下的惰性 CJS 表 | (模块面) | -| `web-react/` | shell 侧 React 胶水:`createSlotRenderer` + `SessionProvider` 渲染座位 | (渲染器安装) | -| `connection/` | 协议两端的消费者:浏览器侧 `ctx.connection`(共享 api 客户端 + 流循环),node 半侧挂载带浏览器信任栅栏的 `/api` 路由 | `ctx.connection` | -| `runtime/` | 客户端 cordis 启动与无 React 对象服务:slots、Session、Workspace、逐会话绑定 | `ctx.slots` `ctx.sessions` `ctx.workspaces` | -| `hmr/` | 仅开发用的外部脚本加载型客户端插件热重载(`--dev` 图) | (开发条目) | -| `locale/` | 浏览器语言偏好(`zh`/`en`)与 ns×locale 词典注册表 | `ctx.locale` | -| `ui-slots/` | slot 注册表纯核心:SlotMap 合并、单一 `register` API、四份额 props 族 | (类型 + 核心) | -| `ui-theme/` | 基于 `--dsw-*` token 样式表的主题偏好(`light`/`dark`/`system`) | `ctx.theme` | -| `ui-primitives/` | 纯 React 原子:图标、Button/Pill/Menu/Modal/Input、markdown 族 | (组件库) | -| `ui-layout/` | shell 三栏 AppFrame;声明 `sidebar`/`conversation`/`details`/`conversation.empty` | `ctx.layout` | -| `ui-sidebar/` | 侧栏 shell:Workspace/会话栏、搜索、折叠;声明 `sidebar.workspaces` | (slot 宿主) | -| `ui-workspace/` | 共享 Workspace 选择器:浏览区域 + hero 选择器共用同一创建流程 | (填充 `sidebar.workspaces`、`conversation.hero.workspace`) | -| `ui-conversation/` | 会话域:骨架、聊天视图、输入坞、逐工具行 slot | (slot 宿主) | -| `ui-trajectory/` | Trajectory/Waterfall 视图标签;最小纯消费者插件范例 | (填充 `conversation.view`) | -| `ui-command/` | 命令面:按会话键控的目录缓存、`/` 源、三类分发 | `ctx.command` | -| `ui-slash/` | 输入触发流水线:光标下的 `/` 与 `@` 检测、分组候选菜单、源名册 | `ctx.slash` | -| `ui-skill/` | 基于 `skill.list` RPC 的 `/` 触发技能引用源 | (注册进 `ctx.slash`) | -| `ui-subagent/` | 基于会话快照的 `@` 触发子代理引用源 | (注册进 `ctx.slash`) | -| `ui-model/` | 模型选择:`/model` popupSelect + 输入坞模型座位,均由 `ModelService` 驱动 | `ctx.models` | -| `ui-question/` | Web `ask_user_question`:宿主半侧挂载工具,浏览器半侧填充输入坞座位 | (填充 `conversation.composer`) | -| `ui-settings/` | 设置 shell:触发 chrome + 模态面板;声明 `settings.*` slot | (slot 宿主) | -| `ui-settings-general/` | 设置的无主文案:chrome 内容 + General 分区骨架 | (填充 `settings.*`) | -| `ui-models/` | 模型设置导航项(内容列留待后续阶段) | (填充 `settings.section`) | +| 包 | 目的 | +|---|---| +| [`web/`](web/README.md) | 从客户端条目图启动浏览器 shell。 | +| [`modules/`](modules/README.md) | 加载浏览器侧客户端模块。 | +| [`web-react/`](web-react/README.md) | 连接 shell 运行时与 React 渲染。 | +| [`connection/`](connection/README.md) | 维护浏览器与宿主之间的 RPC 通信和事件传递。 | +| [`runtime/`](runtime/README.md) | 为会话、Workspace 和 UI 组合提供共享客户端服务。 | +| [`hmr/`](hmr/README.md) | 在开发期间刷新客户端插件。 | +| [`locale/`](locale/README.md) | 提供本地化偏好与消息词典。 | +| [`schema-form/`](schema-form/README.md) | 为设置编辑器提供 schema 驱动的草稿处理。 | +| [`test-runtime/`](test-runtime/README.md) | 为客户端特性包提供共享的仓库测试支持。 | +| [`ui-slots/`](ui-slots/README.md) | 定义 UI 特性注册和组合扩展 slot 的方式。 | +| [`ui-theme/`](ui-theme/README.md) | 应用所选颜色主题。 | +| [`ui-primitives/`](ui-primitives/README.md) | 提供共享 React 控件、图标和内容渲染器。 | +| [`ui-layout/`](ui-layout/README.md) | 排列应用的主要区域。 | +| [`ui-sidebar/`](ui-sidebar/README.md) | 展示 Workspace 与会话导航。 | +| [`ui-workspace/`](ui-workspace/README.md) | 提供 Workspace 选择与创建界面。 | +| [`ui-conversation/`](ui-conversation/README.md) | 展示当前会话及其输入界面。 | +| [`ui-goal/`](ui-goal/README.md) | 展示和管理当前目标。 | +| [`ui-trajectory/`](ui-trajectory/README.md) | 提供 agent(智能体)活动的其他视图。 | +| [`ui-command/`](ui-command/README.md) | 提供会话感知的命令发现与分发。 | +| [`ui-slash/`](ui-slash/README.md) | 协调内联命令和引用建议。 | +| [`ui-skill/`](ui-skill/README.md) | 向内联建议添加 skill(技能)引用。 | +| [`ui-subagent/`](ui-subagent/README.md) | 提供 subagent 导航、子会话记录状态和内联引用。 | +| [`ui-model/`](ui-model/README.md) | 在会话界面中提供模型选择。 | +| [`ui-permission/`](ui-permission/README.md) | 配置默认权限并切换当前会话的访问模式。 | +| [`ui-plan/`](ui-plan/README.md) | 展示生效中的 plan mode 状态及其退出控件。 | +| [`ui-question/`](ui-question/README.md) | 展示 agent 请求的交互式问题。 | +| [`ui-settings/`](ui-settings/README.md) | 承载设置界面及其扩展区域。 | +| [`ui-settings-general/`](ui-settings-general/README.md) | 提供常规设置分区。 | +| [`ui-models/`](ui-models/README.md) | 提供模型提供方配置与 DeepSeek 配置引导。 | -特性 UI 只通过 slot 系统组合(`ctx.slots.register`)——[slot 系统标准](../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md)是权威模型;[web 客户端架构 Note](../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md) 拥有加载链与对象层。 +每个子文档负责自身的契约和详细行为。[slot 系统标准](../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md)与 [Web 客户端架构 Agent Note](../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md)负责跨包组合与加载决策。 diff --git a/packages/client/connection/README.i18n.yaml b/packages/client/connection/README.i18n.yaml index 8662c365e1..79102ffddc 100644 --- a/packages/client/connection/README.i18n.yaml +++ b/packages/client/connection/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/connection/README.md -README.md: be521feb4826f9ad804f61532dee0aa85f39de7e -README.zh.md: bd58ce649c75143b94c1e5dea0241e1f9a934e69 +README.md: a2ca8a1d2cdebaeb7c4223aae33e5cd9e1a60884 +README.zh.md: 6b031e58425c53a8adc4153a70a04ec1c1486b97 diff --git a/packages/client/connection/README.md b/packages/client/connection/README.md index be521feb48..a2ca8a1d2c 100644 --- a/packages/client/connection/README.md +++ b/packages/client/connection/README.md @@ -6,7 +6,7 @@ Wire consumer layer: the client plugin's apply mounts `ctx.connection` (shared a ## /api browser-trust fence -The node half guards every entry under `/api` before bridging or upgrading (`src/api-request-trust.ts`). Every request — browser-marked or not — must present a `Host` that is a loopback authority or matches a `trustedHosts` entry: exact on `host:port` entries, any port on port-less entries, both sides compared through WHATWG normalization (DNS-rebinding defense). There is deliberately no shortcut for unmarked HTTP requests: over plain HTTP a browser attaches neither `Origin` nor Fetch-Metadata to image and navigation reads, so an unmarked request may still be a rebound browser read with a readable response, and Host is the one header rebinding cannot forge; a browser WebSocket handshake carries `Origin` and passes the same comparison. Non-browser clients pass the same fence via loopback, the CLI-derived LAN IP literals, or a declared authority. When markers are present, an attached `Origin` must equal the Host authority, and an explicit `sec-fetch-site: cross-site` marker is refused. A `trustedHosts` entry that is not a bare, canonical `host[:port]` authority — one WHATWG parsing reads back exactly as written — fails the plugin load loudly: parsing would otherwise quietly authorize the hostname inside `harness.internal/path`, or broaden a dangling-colon or zero-padded port to an any-port grant. HTTP failures answer plain 403 before any RPC dispatch; upgrade failures reject the handshake before any event stream starts. A non-loopback (`--host 0.0.0.0`) deployment therefore needs its serving authorities trusted: the dsh CLI derives the machine's LAN IP literals itself and its `--trusted-host` flag declares named ones, so `trustedHosts` in cordis.yml is for compositions the CLI does not boot. The fence is deliberately not an authentication layer — reachability policy stays with the webserver binding, and auth remains deferred work. Decision record: [the api browser-trust boundary Agent Note](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.md). +The node half guards every entry under `/api` before bridging or upgrading (`src/api-request-trust.ts`). Every request — browser-marked or not — must present a `Host` that is a loopback authority or matches a `trustedHosts` entry: exact on `host:port` entries, any port on port-less entries, both sides compared through WHATWG normalization (DNS-rebinding defense). There is deliberately no shortcut for unmarked HTTP requests: over plain HTTP a browser attaches neither `Origin` nor Fetch-Metadata to image and navigation reads, so an unmarked request may still be a rebound browser read with a readable response, and Host is the one header rebinding cannot forge; a browser WebSocket handshake carries `Origin` and passes the same comparison. Non-browser clients pass the same fence via loopback, the CLI-derived LAN IP literals, or a declared authority. When markers are present, an attached `Origin` must equal the Host authority, and an explicit `sec-fetch-site: cross-site` marker is refused. A `trustedHosts` entry that is not a bare, canonical `host[:port]` authority — one WHATWG parsing reads back exactly as written — fails the plugin load loudly: parsing would otherwise quietly authorize the hostname inside `harness.internal/path`, or broaden a dangling-colon or zero-padded port to an any-port grant. HTTP failures answer plain 403 before any RPC dispatch; upgrade failures reject the handshake before any event stream starts. A non-loopback (`--host 0.0.0.0`) deployment therefore needs its serving authorities trusted: the dsh CLI derives the machine's LAN IP literals itself and its `--trusted-host` flag declares named ones, so `trustedHosts` in cordis.yml is for compositions the CLI does not boot. The fence is a reachability policy, not authentication; the Web carrier provides no authentication layer. Decision record: [the api browser-trust boundary Agent Note](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.md). ## `/api` WebSocket downlinks @@ -26,5 +26,4 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work -- **history's implicit resume is arguable** — opening history on an unattached session pulls an agent up host-side; the pure-persistence-read alternative is recorded in the rt-core reconciliation ledger, unchanged in P-I. This package's consumers see it as latency on first open. -- **`ToolEventView`/`ToolCallView`/`ToolResultView` re-exports are scheduled for removal** — they fall when the toolview migration deletes the host `viewFor` line (presentation belongs to the client); the fixture keeps a local `viewFor` mirror until then. +- **History resumes an unattached session** — opening history may create the host-side agent and add latency to the first open; there is no persistence-only read path. diff --git a/packages/client/connection/README.zh.md b/packages/client/connection/README.zh.md index bd58ce649c..6b031e5842 100644 --- a/packages/client/connection/README.zh.md +++ b/packages/client/connection/README.zh.md @@ -6,7 +6,7 @@ ## /api 浏览器信任栅栏 -node 半侧在桥接或 upgrade 前守卫 `/api` 下的每个入口(`src/api-request-trust.ts`)。每个请求——无论是否带浏览器标记——`Host` 都必须是回环地址权威,或与某个 `trustedHosts` 条目匹配:带端口的 `host:port` 条目精确匹配,不带端口的条目匹配任意端口,两侧均经 WHATWG 归一化后比较(DNS rebinding 防御)。刻意不为无浏览器标记的 HTTP 请求开捷径:明文 HTTP 下浏览器的图片与导航读取既不带 `Origin` 也不带 Fetch-Metadata,因此无标记请求仍可能是被重绑页面发起的、响应可被读走的读取,而 Host 是重绑唯一伪造不了的请求头;WebSocket 浏览器握手会带 `Origin` 并通过同一道比较。非浏览器客户端经由回环地址、CLI 推导的 LAN IP 字面量或已声明的权威通过同一道栅栏。当标记存在时,`Origin` 必须与 Host 权威完全一致;显式的 `sec-fetch-site: cross-site` 标记一律拒绝。不是纯的、规范形 `host[:port]` 权威的 `trustedHosts` 条目——即 WHATWG 解析读回后与原文不完全一致的——会让插件加载大声失败:否则解析会悄悄授权 `harness.internal/path` 这类笔误里的 hostname,或把悬空冒号、补零端口放大成任意端口授权。HTTP 失败在任何 RPC 分发之前以纯 403 应答,upgrade 失败在启动任何 event stream 前拒绝握手。因此非回环(`--host 0.0.0.0`)部署需要让自己的服务权威被信任:dsh CLI 会自行推导本机的 LAN IP 字面量,其 `--trusted-host` flag 用于声明具名权威,所以 cordis.yml 中的 `trustedHosts` 面向 CLI 不参与引导的组合。这道栅栏刻意不承担认证职责——可达性策略归 webserver 绑定配置,认证仍是延期工作。决策记录:[api 浏览器信任边界 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.md)。 +node 半侧在桥接或 upgrade 前守卫 `/api` 下的每个入口(`src/api-request-trust.ts`)。每个请求——无论是否带浏览器标记——`Host` 都必须是回环地址权威,或与某个 `trustedHosts` 条目匹配:带端口的 `host:port` 条目精确匹配,不带端口的条目匹配任意端口,两侧均经 WHATWG 归一化后比较(DNS rebinding 防御)。刻意不为无浏览器标记的 HTTP 请求开捷径:明文 HTTP 下浏览器的图片与导航读取既不带 `Origin` 也不带 Fetch-Metadata,因此无标记请求仍可能是被重绑页面发起的、响应可被读走的读取,而 Host 是重绑唯一伪造不了的请求头;WebSocket 浏览器握手会带 `Origin` 并通过同一道比较。非浏览器客户端经由回环地址、CLI 推导的 LAN IP 字面量或已声明的权威通过同一道栅栏。当标记存在时,`Origin` 必须与 Host 权威完全一致;显式的 `sec-fetch-site: cross-site` 标记一律拒绝。不是纯的、规范形 `host[:port]` 权威的 `trustedHosts` 条目——即 WHATWG 解析读回后与原文不完全一致的——会让插件加载大声失败:否则解析会悄悄授权 `harness.internal/path` 这类笔误里的 hostname,或把悬空冒号、补零端口放大成任意端口授权。HTTP 失败在任何 RPC 分发之前以纯 403 应答,upgrade 失败在启动任何 event stream 前拒绝握手。因此非回环(`--host 0.0.0.0`)部署需要让自己的服务权威被信任:dsh CLI 会自行推导本机的 LAN IP 字面量,其 `--trusted-host` flag 用于声明具名权威,所以 cordis.yml 中的 `trustedHosts` 面向 CLI 不参与引导的组合。这道栅栏是可达性策略,而不是认证;Web 载体不提供认证层。决策记录:[api 浏览器信任边界 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.md)。 ## `/api` WebSocket 下行 @@ -26,5 +26,4 @@ node 半侧在桥接或 upgrade 前守卫 `/api` 下的每个入口(`src/api-r ## 已知限制与暂缓事项 -- **history 的隐式恢复存在争议**:在未附加的会话上打开 history,会在主机侧拉起 agent;纯持久化读取的替代方案记录在 rt-core 协调账本中,P-I 不作改变。该包的消费方会在首次打开时感受到这段延迟。 -- **计划移除 `ToolEventView`/`ToolCallView`/`ToolResultView` 的重新导出**:当 toolview 迁移删除主机 `viewFor` 行时,它们会一并移除(呈现属于客户端);在此之前,fixture 保留一份局部 `viewFor` 镜像。 +- **History 会恢复未附加的会话**:打开 history 可能创建宿主侧 agent,并增加首次打开的延迟;没有仅从持久化读取的路径。 diff --git a/packages/client/hmr/README.i18n.yaml b/packages/client/hmr/README.i18n.yaml index ce015be85f..86859e79f6 100644 --- a/packages/client/hmr/README.i18n.yaml +++ b/packages/client/hmr/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/hmr/README.md -README.md: f91a6c6f685c88a1ea19312985ad3e933222192a -README.zh.md: 1d20a22d211c13d62089fb5618f40636ab7ae60a +README.md: 454c03cc3cd11722943efd025d164d9ca8233d25 +README.zh.md: fc4100c48e5db9ed6781117dd652232bbd7c7aaa diff --git a/packages/client/hmr/README.md b/packages/client/hmr/README.md index f91a6c6f68..454c03cc3c 100644 --- a/packages/client/hmr/README.md +++ b/packages/client/hmr/README.md @@ -17,5 +17,5 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work - **Reload is coarse by design** — a fresh fiber and fresh components; React state inside the reloaded plugin is lost while the data layer (connection/runtime fibers, Session objects) is untouched. react-refresh-grade state preservation conflicts with "re-executing the bundle re-runs the factory" and is deliberately out. -- **No failure rollback** — a reload that fails leaves the entry FAILED and loud in the loader status projection; restoring the previous bundle automatically is deferred until a real need shows. -- **Graph rev is not refreshed by rebuilt frames** — the stale rev is harmless (the bundle endpoint serves no-cache); rev refresh lands with the reconnect-handshake mechanism. +- **No failure rollback** — a reload that fails leaves the entry FAILED and visible in the loader status projection; the previous bundle is not restored automatically. +- **Graph rev is not refreshed by rebuilt frames** — the stale rev is harmless because the bundle endpoint serves no-cache; reconnect is the only refresh boundary. diff --git a/packages/client/hmr/README.zh.md b/packages/client/hmr/README.zh.md index 1d20a22d21..fc4100c48e 100644 --- a/packages/client/hmr/README.zh.md +++ b/packages/client/hmr/README.zh.md @@ -17,5 +17,5 @@ ## 已知限制与暂缓事项 - **重载有意保持粗粒度**:会创建全新的 fiber 和组件;重载插件中的 React 状态会丢失,数据层(连接 fiber、运行时 fiber 和 Session 对象)不受影响。react-refresh 级状态保留与「重新执行组合包会重新运行 factory」冲突,因此有意排除。 -- **失败时不回滚**:失败的重载会使配置项处于 FAILED 状态,并在 loader 状态投影中明确显示;自动恢复先前组合包会等到实际需要出现后再实现。 -- **重建帧不会刷新图 rev**:陈旧 rev 无害(组合包端点以 no-cache 提供内容);rev 刷新将在重新连接握手机制中实现。 +- **失败时不回滚**:失败的重载会使配置项处于 FAILED 状态,并在 loader 状态投影中显示;系统不会自动恢复先前组合包。 +- **重建帧不会刷新图 rev**:陈旧 rev 无害,因为组合包端点以 no-cache 提供内容;只有重新连接时才会刷新。 diff --git a/packages/client/locale/README.i18n.yaml b/packages/client/locale/README.i18n.yaml index 6a0ddc4296..d1ef53207f 100644 --- a/packages/client/locale/README.i18n.yaml +++ b/packages/client/locale/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/locale/README.md -README.md: 7f780092af9bc7079cc5080c06e986bef2dfdbce -README.zh.md: 288982b1247f93fa1e8578a9ece2fcf8ed86666d +README.md: f1efefde4557e1c29c0556f8b670f1534430ab79 +README.zh.md: a8b5704d28ea121e668cbd500dd3d217d4f96291 diff --git a/packages/client/locale/README.md b/packages/client/locale/README.md index 7f780092af..f1efefde45 100644 --- a/packages/client/locale/README.md +++ b/packages/client/locale/README.md @@ -14,5 +14,5 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work -- **Most surfaces keep inline copy** — the standard seat is adopted by the Settings rows, sidebar, question composer, and model select; the remaining packages migrate in follow-up PRs. +- **Some surfaces keep inline copy** — Settings rows, the sidebar, question composer, and model select use locale seats; other packages still own static text directly. - **Registry-held text reads its translation once** — copy captured at registration time outside the slot render path (e.g. the `/model` command description in the command registry) keeps the language it was registered under until re-registration; slot-rendered copy follows switches live. diff --git a/packages/client/locale/README.zh.md b/packages/client/locale/README.zh.md index 288982b124..a8b5704d28 100644 --- a/packages/client/locale/README.zh.md +++ b/packages/client/locale/README.zh.md @@ -14,5 +14,5 @@ locale 插件:LocaleService——浏览器 locale 偏好(`zh`/`en`,以 ` ## 已知限制与暂缓事项 -- **多数界面仍保留内联文案**——标准席位已由设置行、侧边栏、问题作答器和模型选择接入;其余包在后续 PR 中迁移。 +- **部分界面仍保留内联文案**——设置行、侧边栏、问题作答器和模型选择使用 locale seat;其他包仍直接拥有静态文本。 - **注册表持有的文本只读取一次翻译**——在 slot 渲染路径之外于注册时捕获的文案(例如 command 注册表中的 `/model` 命令描述)在重新注册前保持注册时的语言;slot 渲染的文案随切换实时更新。 diff --git a/packages/client/runtime/README.i18n.yaml b/packages/client/runtime/README.i18n.yaml index 7a785911c3..ec7183a3dc 100644 --- a/packages/client/runtime/README.i18n.yaml +++ b/packages/client/runtime/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/runtime/README.md -README.md: dd780369a1d888dde2579e436afe2ce1e6dcfdd1 -README.zh.md: 5574ad6452c6a2d94fb63da7e53b98e8d074f1c9 +README.md: e921b86379173df59575c7f4a43eaedacedb9585 +README.zh.md: b3ed3fbf86a7003e552ef82f78f8cc34d613ae17 diff --git a/packages/client/runtime/README.md b/packages/client/runtime/README.md index dd780369a1..e921b86379 100644 --- a/packages/client/runtime/README.md +++ b/packages/client/runtime/README.md @@ -70,6 +70,6 @@ Changing the target can change or invalidate provider-side cache reuse; this pac ## Known Limitations and Deferred Work -- **`loader.unload` is a stub (throws not-implemented)** — the full chain (fiber dispose → registration cascade → style removal) lands with the HMR project. +- **`loader.unload` is a stub** — it throws not-implemented; the client has no unload chain from fiber disposal through registration and style removal. - **Scope teardown is stage-driven, single-occupant today** — the staged session follows `list.current` exactly (staging is the open signal: the event window opens ⟺ the session is on stage); a removed-while-staged session's scope survives frozen until the stage moves on, not until true observer count reaches zero. Resolution (`binding()`/`scope()`) is pure addressing, render-safe; the render layer reads the current bundle through the `currentProvideInfo` observable. The staged state can widen to a multi-pane list when concurrent panes land. - **Value imports of this package from plugin bundles must use the `/client` subpath** — the bare package name is not in the loader externals table and inlines a second module instance, whose private scope-tag Symbol never matches (the empty-state P0 postmortem). diff --git a/packages/client/runtime/README.zh.md b/packages/client/runtime/README.zh.md index 5574ad6452..b3ed3fbf86 100644 --- a/packages/client/runtime/README.zh.md +++ b/packages/client/runtime/README.zh.md @@ -70,6 +70,6 @@ Session 对象会在事件 wire 边界依据生产方的完整字段契约,验 ## 已知限制与暂缓事项 -- **`loader.unload` 是 stub(抛出 not-implemented)**:完整链路(fiber dispose → 注册级联 → 样式移除)随 HMR(热模块替换)项目落地。 +- **`loader.unload` 是 stub**:它会抛出 not-implemented;客户端没有从 fiber dispose 到注册与样式移除的卸载链。 - **scope 拆卸由阶段驱动,目前只能有一个占用者**:已 staged 的会话精确跟随 `list.current`(staging 就是打开信号:事件窗口打开 ⟺ 会话位于 stage);在 staged 状态下被移除的会话,其 scope 会冻结保留,直到 stage 转向其他会话,而非直到真实观察者数量降为零。解析(`binding()`/`scope()`)只是纯寻址,可安全用于渲染;渲染层经 `currentProvideInfo` observable 读取当前 bundle。并发 pane 落地时,staged 状态可以扩展为多 pane 列表。 - **插件组合包从该包导入值时必须使用 `/client` 子路径**:裸包名不在 loader externals 表中,会内联第二个模块实例;其私有 scope-tag Symbol 永远无法匹配。这是空状态 P0 的事故复盘(postmortem)所记录的问题。 diff --git a/packages/client/schema-form/README.i18n.yaml b/packages/client/schema-form/README.i18n.yaml index 4038b8ae7d..1c3c759ceb 100644 --- a/packages/client/schema-form/README.i18n.yaml +++ b/packages/client/schema-form/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/schema-form/README.md -README.md: 5dcef89cbffc8b03c3f2d874e870fa9767360d3c -README.zh.md: ebaa9e0d0a134a6fade5729215168a1a47fd375c +README.md: 716cc7ba3c24f3a4de081e2d26f803b905235fac +README.zh.md: 65b8767df84f90eedd70e9f8ac27d7d419f06f46 diff --git a/packages/client/schema-form/README.md b/packages/client/schema-form/README.md index 5dcef89cbf..716cc7ba3c 100644 --- a/packages/client/schema-form/README.md +++ b/packages/client/schema-form/README.md @@ -18,6 +18,6 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work -- **Rehydration executes the served envelope** — `rehydrateSchema` reconstructs a live schemastery validator, and schemastery revives serialized callbacks through `new Function`, so the schema envelope is executable content rather than inert data. That is acceptable only because the envelope comes from the same host that serves the page; a browser schema protocol should carry a description the client cannot execute, which is deferred with the settings seam's [wire-boundary work](../../settings/settings/README.md#known-limitations-and-deferred-work). -- **Validation is draft-level, not per-field** — `validateDraft` reports schemastery's first failure message (which names the `$.path`); per-field error mapping is deferred until a consumer needs it. -- **No generic renderer** — a schema-driven form component was built and then replaced by the hand-written Models editor ([Agent Note](../../../.agents/notes/implemented/architecture/2026-07-30-web-config-plane.md)); if a future page needs to edit arbitrary sections, it starts from these helpers, not from a resurrected generic renderer, unless the note's trade-off changes. +- **Rehydration executes the served envelope** — `rehydrateSchema` reconstructs a live schemastery validator, and schemastery revives serialized callbacks through `new Function`, so the schema envelope is executable content rather than inert data. This is safe only for an envelope from the same trusted host that serves the page; the protocol provides no inert cross-trust representation. +- **Validation is draft-level, not per-field** — `validateDraft` reports schemastery's first failure message, including its `$.path`; it does not map errors onto individual controls. +- **No generic renderer** — consumers build feature-specific forms over these helpers. The [Web config-plane Agent Note](../../../.agents/notes/implemented/architecture/2026-07-30-web-config-plane.md) records that trade-off. diff --git a/packages/client/schema-form/README.zh.md b/packages/client/schema-form/README.zh.md index ebaa9e0d0a..65b8767df8 100644 --- a/packages/client/schema-form/README.zh.md +++ b/packages/client/schema-form/README.zh.md @@ -18,6 +18,6 @@ ## Known Limitations and Deferred Work -- **重建 schema 会执行所收到的信封**——`rehydrateSchema` 会重建一个活的 schemastery 校验器,而 schemastery 通过 `new Function` 复活序列化过的 callback,因此 schema 信封是可执行内容,而非惰性数据。这只有在信封来自提供该页面的同一 host 时才可接受;面向浏览器的 schema 协议应当传递客户端无法执行的描述,此项与 settings seam 的[协议边界工作](../../settings/settings/README.md#known-limitations-and-deferred-work)一并暂缓。 -- **校验是草稿级的,而非逐字段**——`validateDraft` 报告 schemastery 的第一条失败消息(其中会点名 `$.path`);逐字段的报错映射延后到出现需要它的消费方再做。 -- **没有通用渲染器**——一个 schema 驱动的表单组件曾被构建出来,随后被手写的 Models 编辑器取代([Agent Note](../../../.agents/notes/implemented/architecture/2026-07-30-web-config-plane.md));若未来有页面需要编辑任意分节,起点是这些辅助函数,而不是复活后的通用渲染器——除非该 note 的权衡发生变化。 +- **重建 schema 会执行所收到的信封**——`rehydrateSchema` 会重建一个活的 schemastery 校验器,而 schemastery 通过 `new Function` 复活序列化过的 callback,因此 schema 信封是可执行内容,而非惰性数据。只有信封来自提供该页面的同一受信任 host 时才安全;该协议没有跨信任边界使用的惰性表示。 +- **校验是草稿级的,而非逐字段**——`validateDraft` 报告 schemastery 的第一条失败消息及其 `$.path`;它不会把错误映射到各个控件。 +- **没有通用渲染器**——消费方在这些辅助函数上构建功能专用表单。[Web 配置面 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-30-web-config-plane.md)记录该权衡。 diff --git a/packages/client/test-runtime/README.i18n.yaml b/packages/client/test-runtime/README.i18n.yaml index fe40088b11..26845c1d5c 100644 --- a/packages/client/test-runtime/README.i18n.yaml +++ b/packages/client/test-runtime/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/test-runtime/README.md -README.md: dc8ee8cadf5e61af15f04b1b9842af1eb658c031 -README.zh.md: a4c889d8a0291b52c8509403748df6b93567788e +README.md: 74da8fde7fd9cc3733d2d1ae03dd3d213e4d553e +README.zh.md: a86b9e469a5632886891628267002a14588afeaa diff --git a/packages/client/test-runtime/README.md b/packages/client/test-runtime/README.md index dc8ee8cadf..74da8fde7f 100644 --- a/packages/client/test-runtime/README.md +++ b/packages/client/test-runtime/README.md @@ -20,5 +20,5 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work -- **Consumed through repository source aliases only.** Specs resolve the package through tsconfig `paths` to `src`; the built `lib/` artifact re-exports `@deepseek-ai/dsh-client-runtime/client`, whose bundle is a browser loader script with no Node ESM exports, so `lib/index.js` is not importable under plain Node. Acceptable while every consumer is an in-repo Vitest suite; a Node-compatible runtime entry is deferred until an out-of-repo consumer exists. +- **Consumed through repository source aliases only.** Specs resolve the package through tsconfig `paths` to `src`; the built `lib/` artifact re-exports `@deepseek-ai/dsh-client-runtime/client`, whose bundle is a browser loader script with no Node ESM exports, so `lib/index.js` is not importable under plain Node. Every consumer is an in-repository Vitest suite; there is no Node-compatible runtime entry. - **Conversation snapshots are fixture data, not replayed history.** `updateSnapshot` writes the snapshot store directly; the wire-to-snapshot computation stays covered by the runtime package's own tests and the replay e2e. A fixture can therefore express states the production projection would never produce. diff --git a/packages/client/test-runtime/README.zh.md b/packages/client/test-runtime/README.zh.md index a4c889d8a0..a86b9e469a 100644 --- a/packages/client/test-runtime/README.zh.md +++ b/packages/client/test-runtime/README.zh.md @@ -20,5 +20,5 @@ ## Known Limitations and Deferred Work -- **仅可经仓内源码别名消费。** spec 通过 tsconfig `paths` 解析到 `src`;构建产物 `lib/` 再导出 `@deepseek-ai/dsh-client-runtime/client`,而该 bundle 是无 Node ESM 导出的浏览器 loader 脚本,故 `lib/index.js` 在纯 Node 下不可导入。当前所有消费方都是仓内 Vitest 套件,可接受;Node 兼容的运行时入口待出现仓外消费方再补。 +- **仅可经仓内源码别名消费。** spec 通过 tsconfig `paths` 解析到 `src`;构建产物 `lib/` 再导出 `@deepseek-ai/dsh-client-runtime/client`,而该 bundle 是无 Node ESM 导出的浏览器 loader 脚本,故 `lib/index.js` 在纯 Node 下不可导入。所有消费方都是仓内 Vitest 套件;不存在 Node 兼容的运行时入口。 - **会话快照是 fixture 数据,不是重放历史。** `updateSnapshot` 直写快照 store;wire 到快照的运算仍由 runtime 包自身测试与 replay e2e 把守。因此 fixture 可以表达生产投影永不产出的状态。 diff --git a/packages/client/ui-command/README.i18n.yaml b/packages/client/ui-command/README.i18n.yaml index e1b579faee..4c29ba83d2 100644 --- a/packages/client/ui-command/README.i18n.yaml +++ b/packages/client/ui-command/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-command/README.md -README.md: c892f2f244d7924014ad1b4d6e9fe16ff4e044e4 -README.zh.md: e5d109af8ca94515e0b574c62c57968796af5ce8 +README.md: 7846af7789f20cc8e19bf2b2f25d867063ce6936 +README.zh.md: fa703c8e94d906ede8108ae73b9a885bcf40cc9c diff --git a/packages/client/ui-command/README.md b/packages/client/ui-command/README.md index c892f2f244..7846af7789 100644 --- a/packages/client/ui-command/README.md +++ b/packages/client/ui-command/README.md @@ -22,5 +22,5 @@ None directly; this package neither assembles nor sends a provider request. Comm ## Known Limitations and Deferred Work -- **The popupSelect shell has no shipped business consumer** — model selection (host `selectModel`) is the design's reference case and lands with its own feature work; until then the shell is exercised by package tests only. +- **The popupSelect shell has no product consumer** — package tests exercise the shell directly. - **Detached-result notices fall back to the console off-session** — the fire-and-forget paths route results to the triggering session's composer via `SessionInput.notify`; after session teardown the console line is the only remaining surface. diff --git a/packages/client/ui-command/README.zh.md b/packages/client/ui-command/README.zh.md index e5d109af8c..fa703c8e94 100644 --- a/packages/client/ui-command/README.zh.md +++ b/packages/client/ui-command/README.zh.md @@ -22,5 +22,5 @@ ## 已知限制与暂缓事项 -- **popupSelect 壳还没有已上架的业务消费方**:模型选择(host `selectModel`)是设计的参照用例,将随其自身的功能工作落地;在此之前,壳只由包测试演练。 +- **popupSelect 壳没有产品消费方**:包测试会直接演练该壳。 - **脱离会话后,detached result 的 notice 回退到 console**:fire-and-forget 路径经 `SessionInput.notify` 把结果送到触发会话的编辑器;会话拆除后,console 输出行是仅剩的呈现面。 diff --git a/packages/client/ui-conversation/README.i18n.yaml b/packages/client/ui-conversation/README.i18n.yaml index 6ebbbed744..ddd024e34e 100644 --- a/packages/client/ui-conversation/README.i18n.yaml +++ b/packages/client/ui-conversation/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-conversation/README.md -README.md: 0d00eac1db5aed7feec9bb714fe3d8976cd39943 -README.zh.md: 4219950a9d51eb1037051ca00d61fa0d5ffa6fd2 +README.md: d7433c151b7ab8da66d319ce71dd24f533739176 +README.zh.md: 338cb3a8673a24a387a96c4c5e65a528e01c4c68 diff --git a/packages/client/ui-conversation/README.md b/packages/client/ui-conversation/README.md index 0d00eac1db..d7433c151b 100644 --- a/packages/client/ui-conversation/README.md +++ b/packages/client/ui-conversation/README.md @@ -8,7 +8,7 @@ Compaction renders as one collapsed row at the checkpoint's flow position withou The resident conversation shell survives no-session and session transitions. Without a current session it renders a disabled input bar; its root-scoped `conversation.hero.workspace` slot hosts the Workspace picker. Selecting a Workspace connects or reuses its Host-owned blank session and opens that session without replacing the shell. Blank sessions render the same composer body as active sessions, while the InputHub carries drafts across Workspace switches and mirrors them into the session store. In the active phase the session header shows only the current session title and view tabs as ordinary column chrome; fork lineage remains session data and is not projected into the header. Beneath it a scrollport (`data-conversation-scroll`) holds the flowing views and the sticky composer stack (stats dock + input docks + bar). Wheel over the textarea chains: the capped draft scrolls locally until its edge, then forwards to that host. -The view ring IS a slot: the conversation registration declares the `'conversation.view'` list slot (session scope) in its `children` table, ConversationRoot renders the active entry through its renderSlot share (`only: `), and view tabs project from the ring ledger's registration options (`id`/`order`/`label`). The chat view is this package's own ring entry; other plugins (ui-trajectory) contribute tabs through plain `ctx.slots.register` — the former package-local view registry (`registerView`/`ViewEntry`/`ConversationViewMap` and the chrome attachment table) is retired, with per-view chrome dissolved into the view components themselves. +The view ring is a slot: the conversation registration declares the session-scoped `'conversation.view'` list in its `children` table, ConversationRoot renders the active entry through its renderSlot share (`only: `), and view tabs project from registration options (`id`/`order`/`label`). The chat view is this package's own entry; plugins such as ui-trajectory contribute tabs through `ctx.slots.register`, and each view owns its chrome. Approvals take over the composer through the chain this package declares: `ApprovalPanel` registers as a selector-routed `'conversation.composer'` entry (the ui-question pattern) and occupies the composer in place of the InputBar while an approval wait is pending (amber strip, justification headline, paired command line from the running call's args, one-shot refuse/allow). The `PendingApproval` domain face in `contract/slots.ts` owns the wire encoding — the `ApprovalResponsePayload` value with the audit correlation — over the runtime's `PendingWait` carrier; the broadcast `approval/resolved` frame settles the wait and restores the composer. The runtime manager projects every approval or question wait through `SessionSummary.pendingInteraction`, including sessions never instantiated; `ui-workspace` owns its sidebar presentation. Pending waits leave the message flow entirely: questions (ui-question) and approvals (ApprovalPanel) both answer through the composer takeover, so no display-only placeholder card remains. The composer's bottom-row Access seat mounts `PermissionSelect`, fed by the host-computed `permissions` projection through the standard-kit `useProjection` (key absence hides the chip); the chip opens a Menu-primitive dropdown whose kebab-case preset names render as title-case labels. Safe preset picks submit `/permission ` immediately through the bar's injected `command` callback, while `danger-full-access` is presented as `Full access` and first opens an in-page Modal risk confirmation. The enabling action stays disabled until the user checks the acknowledgement; cancel, Escape, close, and mask click submit nothing. @@ -20,7 +20,7 @@ A Think row stays collapsed by default and exposes live reasoning throughput wit Generic tool rows classify the built-in bash, read, search, write, edit, and run_code names into dedicated visual variants. The filesystem variants render the edit icon and a path summary; that path is a hover-underline link that opens the file with the host OS default application (`host.openPath`, relative paths resolve against the session cwd). Tool rows are not whole-row click targets and do not open the details panel. The code variant summarizes with the model-authored `description` and expands to the program itself; its logged sub-dispatches render as always-visible nested rows through the SAME keyed toolview hole (custom registrations and the GenericToolCard fallback apply to sub-rows unchanged). Cordis lifecycle tools reuse those generic variants while presenting `Inspect`, `Mount temporary Plugin`, and `Unmount temporary Plugin` with a shared Cordis accent; mount keeps the code variant's expandable source rendering. -A tool call declaring the `terminal` render intent renders its command output inline, at both conversation render sites, through ui-primitives' `TerminalBlock`. `contract/terminal-card-model.ts` is the single derivation from the snapshot's `callView`/`resultView` pair, so the sites cannot disagree about a command, its cwd, or its exit status; it yields null — the generic path — for any other card tag, including one this client version does not know. Both sites therefore also show the card's run-state dot, which is the same `StateDot` semantic a tool row's leading icon carries, so a row and its own card always agree about one command's state. A multi-line command gets one prompt row per line, with the dot marking the call once on the first row — the exit status is the whole call's, so a dot per line would claim a per-line outcome bash does not report. The keyed `BashRow` carries the card resident below its summary row; since tool rows are no longer details-panel click targets, the card's copy and expand controls are the row's only interactions. The render-site fallback row keeps the card behind its existing expand control. Rows cap at `CHAT_TERMINAL_MAX_LINES` (8) against the panel's 16, which is what keeps a summary surface bounded — the panel stays the single-call reading surface. Inline output is licensed per render intent — the terminal and web cards, each with its own bound. A Bash execution failure that settles on the generic path instead exposes its original arguments and full error through the same bounded IN/OUT disclosure, while successful generic results such as a background-start acknowledgement remain summary-only ([decision](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md)). +A tool call declaring the `terminal` render intent renders its command output inline, at both conversation render sites, through ui-primitives' `TerminalBlock`. `contract/terminal-card-model.ts` is the single derivation from the snapshot's `callView`/`resultView` pair, so the sites cannot disagree about a command, its cwd, or its exit status; it yields null — the generic path — for any other card tag, including one this client version does not know. Both sites therefore also show the card's run-state dot, which is the same `StateDot` semantic a tool row's leading icon carries, so a row and its own card always agree about one command's state. A multi-line command gets one prompt row per line, with the dot marking the call once on the first row — the exit status is the whole call's, so a dot per line would claim a per-line outcome bash does not report. The keyed `BashRow` carries the card below its summary row; tool rows are summary surfaces, so the card's copy and expand controls are the row's only interactions. The render-site fallback row keeps the card behind its existing expand control. Rows cap at `CHAT_TERMINAL_MAX_LINES` (8) against the panel's 16, which keeps the summary bounded; the panel stays the single-call reading surface. Inline output is licensed per render intent — the terminal and web cards, each with its own bound. A Bash execution failure that settles on the generic path instead exposes its original arguments and full error through the same bounded IN/OUT disclosure, while successful generic results such as a background-start acknowledgement remain summary-only ([decision](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md)). A tool call declaring the `web` render intent renders its web retrieval inline, at both conversation render sites, through ui-primitives' `WebBlock`. `contract/web-card-model.ts` is the single derivation from the snapshot's `resultView`, mirroring the terminal card, so the sites cannot disagree about what a web call shows; it yields null — the generic path — for a running call, a non-web result view, a generic result view, a `card` tag this client version does not know, or a web card whose `kind` this client version does not know (a newer host's value, which the wire cannot be trusted to be `search` or `fetch`). The keyed `WebRow` registers one component under both `web_search` and `web_fetch`, discriminating on the tool name only for its icon and title; it composes the shared `ToolRow`, feeding the card as ToolRow's `web` body, so the retrieval is the row's collapsed-by-default expanded card (the same unified expand every card row has). A web-declaring tool without a keyed row lands on the `GenericToolCard` fallback, which routes the card through ToolRow the same way, and the details panel renders it and, below the card, the flattened model-visible result content — a fetch body is readable only there, since its card carries only the URL and status. Both render sites show the same complete source list — the one the tool returned and the model saw — bounded only by the card's own scroll container height, with no row-versus-panel cap ([decision](../../../.agents/notes/implemented/feature/2026-07-30-web-result-card-frontend.md), [source scroll](../../../.agents/notes/implemented/feature/2026-08-03-web-search-source-scroll.md)). @@ -32,7 +32,7 @@ The chat flow projects consecutive model-retry nodes across retry turns into one A `grep`/`glob` call declaring the `search` render intent renders its result inline, at the same render sites, through ui-primitives' `SearchBlock` — grep's matches grouped by file (each a collapsible header of `lineNumber: line` rows), glob's flat path list. `contract/search-card-model.ts` is the single derivation from the snapshot's `resultView`; unlike the terminal card it reads no `callView`, since a search has no matches or paths before `execute`, so a running search shows its summary alone. It yields null — the generic path — for any non-search result view, a `card` or `kind` this client version does not compile, and (because those ride the untrusted wire frame) a known kind whose `files`/`paths` is malformed. The keyed `SearchRow`, registered under both `grep` and `glob` since the derived `kind` decides the shape, composes the shared `ToolRow`, feeding the card as ToolRow's `search` body, so it is the row's collapsed-by-default expanded card; the render-site fallback routes it the same way. Both cap at `CHAT_SEARCH_MAX_LINES` (8) against the panel's 16. A capped search drops rows from the card, but the locator to the rest — grep/glob's `Full … stored at …` footer — lives only in the result text, so the derivation surfaces that as a recovery footer below the card when (and only when) the result was truncated; a settled call with no card at all (an errored search, a nested `run_code` sub-dispatch, a legacy generic result) surfaces its flattened result text through ToolRow's Output section so nothing is lost behind a bare summary ([decision](../../../.agents/notes/implemented/feature/2026-07-30-web-search-card.md)). -Tool rows are slots too — the standalone tool ring (`ToolViewRegistry`/`ctx.toolviews`/outlet) is retired. The chat entry declares the keyed `'conversation.chat.toolview'` hole (session scope; the key space is runtime-open); its render site dispatches per row via `entryKey: toolName` with `GenericToolCard` as the call-site `fallback`. The owner payload is the uniform `ToolRowOwnerProps` (`callId`/`toolName`/`block`/`openFile`) and `ToolRowProps` pre-composes it with the session standard kit. A registrant is a plain plugin: `ctx.slots.register({ name: 'conversation.chat.toolview', key: '', inject? }, Row)` with `inject: ['slots', 'conversation']` as the load-order seam (apply mounts ConversationService after the chat registration, so the service being present guarantees the slot is declared); the bash sample is the third-party-posture exemplar. Trajectory/waterfall toolview slots share this shape and land with their own render sites (RendersCheck rejects a declaration nobody renders). +Tool rows use the keyed, session-scoped `'conversation.chat.toolview'` slot; its render site dispatches via `entryKey: toolName` with `GenericToolCard` as the call-site fallback. The owner payload is the uniform `ToolRowOwnerProps` (`callId`/`toolName`/`block`/`openFile`), and `ToolRowProps` composes it with the session standard kit. A registrant is a plain plugin: `ctx.slots.register({ name: 'conversation.chat.toolview', key: '', inject? }, Row)` with `inject: ['slots', 'conversation']` as the load-order seam. Trajectory and waterfall toolview slots share this shape and use their own render sites; RendersCheck rejects a declaration nobody renders. The todo surfaces are two registrations over that shape, both plain registrant plugins with `inject: ['slots', 'conversation']`. `TodoRow` takes the `'conversation.chat.toolview'` key `todo_write` and summarizes what the call attempted (`/ 已完成 · ` parsed from its args, falling back to the generic summary on malformed or wrongly-shaped model JSON, and keeping the generic dot for non-ok execution states so a cancelled call never reads as a completed update). `TodoDock` takes the `'conversation.input.dock'` list slot at `order: 0` — before Goal and Queue — and is the plan strip: it reads the host-computed `todos` projection via `useProjection` (standing plan: latest `todo/write` with no later `turn/start`) and renders `TodoPanel`, which takes the plain list, hides itself while the list is empty, and starts collapsed as a header of title plus `"/ tasks · in progress"` (status glyphs are the figma check / progress / dashed-pending set). The dock adapter owns the selection so the panel stays a pure function of its props; the standing list lives here rather than in the row so the row stays one line. Anything the input-zone composer chain hides (a `conversation.composer` takeover such as ui-question's) hides the whole dock, this strip included. @@ -48,7 +48,7 @@ The composer bar declares session-scoped single seats for `'conversation.input.p The chat stats line takes its token accounting from two generic token-meter projections read through the standard-kit `useProjection`: `tokenUsage` for full-log billing (billed input is uncached input plus cache reads and writes; cache hit divides cache reads by that total) and `contextPressure` for context occupancy. Visible nodes supply only the turn and step counts plus the LLM and tool wall times, which are window-scoped facts about what is on screen rather than accounting; durable token and context groups remain visible when compaction leaves no assistant node in the loaded window. A deployment without token-meter drops the token groups, and occupancy stays hidden until both provider pressure and route capacity are known. Occupancy is deliberately an approximation — its numerator and capacity are independent last-wins projection fields, not one atomic request observation ([rationale](../../llm/token-meter/README.md)). The inline stats row remains the sole context UI; the model selector has no circle or accessory. -`src/client/` is organized for the future package split: `contract/` is the sole inter-domain shared face (`slots.ts` slot declarations + composed slot props including the tool-row contract, `views.ts` shared primitives, `tool-call-model.ts`); the `skeleton/`, `chat/`, and `toolviews/` (sample registrants) domain directories import contract files and never each other; `apply.ts` is the only assembly point allowed to import all three domains. The `/client` export surface is the contract only — `apply`/`inject`, the two service classes, and the `contract/` type families; implementation components (skeleton, chat rows) and the store factory stay internal and reach the page exclusively through apply's slot registrations (tests take them via the `./src/*` subpath). +`src/client/` is organized by domain. `contract/` is the sole inter-domain shared face (`slots.ts` slot declarations and composed props, `views.ts` shared primitives, `tool-call-model.ts`); the `skeleton/`, `chat/`, and `toolviews/` directories import contract files and never each other. `apply.ts` is the only assembly point allowed to import all three domains. The `/client` export surface is the contract only — `apply`/`inject`, the two service classes, and the `contract/` type families; implementation components and the store factory stay internal and reach the page through apply's slot registrations. ## Model Experience @@ -62,11 +62,11 @@ None; this package neither assembles nor sends a provider request. - **Compaction markers show no scale** — the row does not yet report how many messages or which range the checkpoint replaced. - **Stats-line durations cover the in-window flow only** — LLM and tool wall times fold the snapshot's assistant `timing` and tool call/result pairs, so nodes outside the loaded event window (older history) are not counted. -- **Details panel is the minimal form and currently has no entry point** — selected call args/result raw display; the Input/Output/Metadata switch, Prev/Next stepping, and See-in-trajectory deep link are deferred. Tool rows stopped being details-panel click targets and nothing replaced that gesture, so `ChatViewInjected.openDetails` is implemented but uncalled and the panel (including its terminal card) is unreachable in the assembled application; its rendering stays covered by mounting it with a selection directly. +- **The details panel has no entry point** — `ChatViewInjected.openDetails` is implemented but uncalled, so the raw selected-call display is unreachable in the assembled application. There is no Input/Output/Metadata switch, Prev/Next stepping, or trajectory deep link. - **Assistant per-message paging is a reserved slot** — drawn in the design, not implemented. The finalized content IconActions row (copy / clock / branch) ships under the last content-text assistant of each turn only; mid-turn narration and Think-only nodes stay chrome-free. Branch stays disabled unless that message is also the last transcript node of a completed turn; when enabled, it forks through that turn, increments the inherited title on the client, and opens the child. A fork or rename failure leaves the source selected ([decision](../../../.agents/notes/implemented/bug-fix/2026-08-02-message-fork-actions-require-completed-turn-tail.md)). - **Sent user messages cannot be edited** — user bubbles retain clock, copy, and branch; branch stays disabled unless a completed turn's transcript ends at that user message. Editing returns with the capability behind it: a client mutation over a settled user message, plus the host behavior for the turn that already consumed it ([decision](../../../.agents/notes/implemented/simplification/2026-07-31-drop-user-message-edit-stub.md)). - **The sparkle icon for the others tool row is a hand-drawn approximation** — the design glyph's vector geometry is not exportable locally; promotion into ui-primitives waits on an exact export. -- **The approval panel's "Always allow this type" is deferred** — durable grants need a grant-storage design; only allow-once/reject answer today. +- **The approval panel has no durable grant control** — it supports allow-once and reject only. - **TodoPanel truncates long item text to one ellipsized line** — the figma strip has no wrap or expand affordance; full text is not readable inline. - **Queue edit is text-only** — rows containing non-text blocks still show a flattened preview, but their edit control is disabled because the inline editor cannot preserve those blocks. A text row's edit mode replaces delete and strict steer with save and cancel; Enter saves and Escape cancels. - **Queue strict steer preserves complete messages** — while the Agent is running, the steer action atomically transfers the addressed Queue occurrence into the current next-step window. Mixed-content rows remain eligible because the action forwards the immutable message instead of the text projection. The placement-aware Host snapshot renders pending steering at the conversation tail until the consumed `steering/message` folds into the durable transcript, so immediate display, reconnect, and replay share one linear authority. diff --git a/packages/client/ui-conversation/README.zh.md b/packages/client/ui-conversation/README.zh.md index 4219950a9d..338cb3a867 100644 --- a/packages/client/ui-conversation/README.zh.md +++ b/packages/client/ui-conversation/README.zh.md @@ -8,7 +8,7 @@ 常驻会话壳会跨无会话与会话状态切换而保留。没有当前会话时,它会渲染禁用输入栏;其根作用域的 `conversation.hero.workspace` slot 承载 Workspace 选择器。选择 Workspace 会连接或复用由 Host 拥有的空白会话,并在不替换会话壳的情况下打开该会话。空白会话与活跃会话渲染相同的输入区主体;InputHub 则在 Workspace 切换间携带草稿,并将草稿镜像到会话 store。活跃阶段,会话标题栏作为普通列 chrome,仅显示当前会话标题和视图标签;fork 谱系仍保留为会话数据,不投影到标题栏。其下滚动容器(`data-conversation-scroll`)承载流动排版的各视图与 sticky 编辑器栈(统计 dock+输入区 dock+输入栏)。textarea 上的滚轮会链式处理:限高草稿先在本地滚动,到达边缘后再转交给该宿主。 -视图环本身就是 slot:会话注册声明 `'conversation.view'` 列表 slot(Session scope),并将其列在 `children` 表中;ConversationRoot 通过 renderSlot share 渲染活跃配置项(`only: `);视图标签页从环账本的注册选项(`id`/`order`/`label`)投影而来。聊天视图是该包(package)自身的环配置项;其他插件(ui-trajectory)通过普通的 `ctx.slots.register` 贡献标签页。先前包内的视图注册表(`registerView`/`ViewEntry`/`ConversationViewMap` 及 chrome 附加表)已退役,逐视图 chrome 则被拆入视图组件自身。 +视图环是一个 slot:会话注册在 `children` 表中声明 Session scope 的 `'conversation.view'` 列表,ConversationRoot 通过 renderSlot share 渲染活跃配置项(`only: `),视图标签页则从注册选项(`id`/`order`/`label`)投影而来。聊天视图是该包自身的配置项;ui-trajectory 等插件通过 `ctx.slots.register` 贡献标签页,每个视图负责自己的 chrome。 会话页头会在标题旁声明并渲染 Session scope 的 `'conversation.session.header.actions'` 列表,使功能插件无需进入骨架即可贡献控件。编辑器链的 currency 包含当前对话 `session`;ui-subagent 会选取 one-shot 或 parent 不可用的已寻址会话,并按原因显示只读文案,而普通 InputBar 会让所有已寻址 child 仅保留 Send,因为继续执行服务不公开逐 Activation 取消操作,`session.cancel` 也会绕过其所有权。 @@ -18,7 +18,7 @@ Think 行默认保持折叠,并在不展开思维链的情况下暴露实时 通用工具行把内置的 bash、read、search、write、edit 和 run_code 名称归入专用视觉变体。文件系统变体会渲染 edit 图标和路径摘要;该路径是悬停下划线链接,点击后通过宿主操作系统的默认应用打开文件(`host.openPath`,相对路径相对会话 cwd 解析)。工具行不再是整行点击目标,也不会打开 details 面板。code 变体以模型撰写的 `description` 作摘要,展开后显示程序本身;其已记录的子调用经由同一个键控 toolview 空位渲染为始终可见的嵌套行(自定义注册和 GenericToolCard fallback 原样适用于子行)。Cordis 生命周期工具复用这些通用变体,同时以统一的 Cordis 强调色呈现 `Inspect`、`Mount temporary Plugin` 和 `Unmount temporary Plugin`;mount 行保留 code 变体的可展开源码渲染。 -声明 `terminal` 渲染意图的工具调用,会在两个对话渲染点上都通过 ui-primitives 的 `TerminalBlock` 内联渲染其命令输出。`contract/terminal-card-model.ts` 是从快照的 `callView`/`resultView` 对推导的唯一位置,因此两个渲染点不可能在命令、cwd 或退出状态上产生分歧;对任何其他 card 标签——包括当前客户端版本不认识的标签——它返回 null,落回通用路径。因此两个渲染点也都显示卡片的运行状态点,它与工具行行首图标承载同一套 `StateDot` 语义,所以一行与其自身的卡片对同一条命令的状态总是一致。多行命令的每一行各占一个提示行,状态点只在第一行为整次调用标记一次——退出状态属于整次调用,因此每行一枚就会声称一个 bash 并不报告的逐行结果。键控的 `BashRow` 把卡片常驻在摘要行下方;由于工具行已不再是详情面板的点击目标,卡片的复制与展开控件就是该行唯一的交互。渲染点兜底行则保持其既有的展开控件。行的上限是 `CHAT_TERMINAL_MAX_LINES`(8),面板为 16,正是这一点让摘要面保持有界——面板仍是单次调用的阅读面。内联输出按渲染意图开放——终端卡片与 web 卡片,各有自己的上限。若 Bash 执行失败时落在通用路径,则改用同样有界的 IN/OUT 展开区暴露原始参数和完整错误;后台启动确认等成功的通用结果仍只显示摘要([决策](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md))。 +声明 `terminal` 渲染意图的工具调用,会在两个对话渲染点上都通过 ui-primitives 的 `TerminalBlock` 内联渲染其命令输出。`contract/terminal-card-model.ts` 是从快照的 `callView`/`resultView` 对推导的唯一位置,因此两个渲染点不可能在命令、cwd 或退出状态上产生分歧;对任何其他 card 标签——包括当前客户端版本不认识的标签——它返回 null,落回通用路径。因此两个渲染点也都显示卡片的运行状态点,它与工具行行首图标承载同一套 `StateDot` 语义,所以一行与其自身的卡片对同一条命令的状态总是一致。多行命令的每一行各占一个提示行,状态点只在第一行为整次调用标记一次——退出状态属于整次调用,因此每行一枚就会声称一个 bash 并不报告的逐行结果。键控的 `BashRow` 把卡片放在摘要行下方;工具行是摘要 surface,因此卡片的复制与展开控件是该行唯一的交互。渲染点兜底行则保持其既有的展开控件。行的上限是 `CHAT_TERMINAL_MAX_LINES`(8),面板为 16,因此摘要保持有界;面板仍是单次调用的阅读 surface。内联输出按渲染意图开放——终端卡片与 web 卡片各有自己的上限。若 Bash 执行失败时落在通用路径,则改用同样有界的 IN/OUT 展开区暴露原始参数和完整错误;后台启动确认等成功的通用结果仍只显示摘要([决策](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md))。 声明 `web` 渲染意图的工具调用,会在两个对话渲染点上都通过 ui-primitives 的 `WebBlock` 内联渲染其 web 检索。`contract/web-card-model.ts` 是从快照的 `resultView` 推导的唯一位置,镜像终端卡片,因此两个渲染点不可能对一次 web 调用的显示产生分歧;对运行中的调用、非 web 的 result view、generic result view、本客户端版本不认识的 `card` 标签,或本客户端版本不认识 `kind` 的 web 卡片(更新的 host 发来的值,wire 上不可信其为 `search` 或 `fetch`),它返回 null,落回通用路径。键控的 `WebRow` 把一个组件注册在 `web_search` 与 `web_fetch` 两个键下,仅根据工具名判别以选取图标与标题;它组合共享的 `ToolRow`,把卡片作为 ToolRow 的 `web` body 传入,因此检索成为该行默认折叠的展开卡片(与每个卡片行相同的统一展开交互)。没有自己键控行的 web 声明工具落到 `GenericToolCard` 兜底,它以同样方式经 ToolRow 渲染卡片,详情面板渲染它,并在卡片下方渲染摊平的模型可见结果内容——fetch 正文只在此处可读,因为其卡片只携带 URL 和状态。两个渲染点显示同一份完整来源列表——工具返回、模型看到的那一份——仅受卡片自身滚动容器的高度约束,不存在行与面板的两级上限([决策](../../../.agents/notes/implemented/feature/2026-07-30-web-result-card-frontend.md)、[来源滚动](../../../.agents/notes/implemented/feature/2026-08-03-web-search-source-scroll.md))。 @@ -30,7 +30,7 @@ Think 行默认保持折叠,并在不展开思维链的情况下暴露实时 声明 `search` 渲染意图的 `grep`/`glob` 调用,会在同样的渲染点上通过 ui-primitives 的 `SearchBlock` 内联渲染其结果——grep 的匹配按文件分组(每个是一个可折叠的头,下辖 `lineNumber: line` 行),glob 是扁平路径列表。`contract/search-card-model.ts` 是从快照的 `resultView` 推导的唯一位置;与终端卡片不同,它不读 `callView`,因为搜索在 `execute` 前没有匹配或路径,所以运行中的搜索只显示摘要。对任何非搜索的结果视图、当前客户端版本无法编译的 `card` 或 `kind`、以及(因为这些都与不可信的 wire 帧同行)一个 `files`/`paths` 格式错误的已知 kind,它都返回 null,落回通用路径。键控的 `SearchRow` 因推导出的 `kind` 决定形态而同时注册在 `grep` 与 `glob` 下,组合共享的 `ToolRow`,把卡片作为 ToolRow 的 `search` body 传入,因此它是该行默认折叠的展开卡片;渲染点兜底行以同样方式渲染它。两者上限都是 `CHAT_SEARCH_MAX_LINES`(8),面板为 16。被截断的搜索会从卡片里丢掉一些行,但通往其余部分的定位符——grep/glob 的 `Full … stored at …` 脚注——只存在于结果文本里,因此推导在(且仅在)结果被截断时把它作为恢复脚注画在卡片下方;一个完全没有卡片的已结算调用(出错的搜索、嵌套 `run_code` 子派发、旧日志的 generic 结果)则经 ToolRow 的 Output 区呈现其压平后的结果文本,从而不让任何内容丢失在一个光秃秃的摘要之后([决策](../../../.agents/notes/implemented/feature/2026-07-30-web-search-card.md))。 -工具行同样是 slot:独立工具环(`ToolViewRegistry`/`ctx.toolviews`/outlet)已经退役。聊天配置项声明键控的 `'conversation.chat.toolview'` 空位(Session scope;key 空间在运行时开放);其渲染点逐行通过 `entryKey: toolName` 分发,并以 `GenericToolCard` 作为调用点 `fallback`。owner 载荷是统一的 `ToolRowOwnerProps`(`callId`/`toolName`/`block`/`openFile`),`ToolRowProps` 则预先将其与 Session 标准工具包组合。注册方只是普通插件:`ctx.slots.register({ name: 'conversation.chat.toolview', key: '', inject? }, Row)`,以 `inject: ['slots', 'conversation']` 作为加载顺序 seam(apply 在聊天注册后挂载 ConversationService,因此服务存在即可保证 slot 已声明);bash 示例是第三方姿态的范例。Trajectory/waterfall(瀑布式事件)工具视图 slot 共享此形状,并随各自的渲染点落地(RendersCheck 会拒绝没有任何渲染方的声明)。 +工具行使用键控、Session scope 的 `'conversation.chat.toolview'` slot;其渲染点通过 `entryKey: toolName` 分发,并以 `GenericToolCard` 作为调用点 fallback。owner 载荷是统一的 `ToolRowOwnerProps`(`callId`/`toolName`/`block`/`openFile`),`ToolRowProps` 将其与 Session 标准工具包组合。注册方只是普通插件:`ctx.slots.register({ name: 'conversation.chat.toolview', key: '', inject? }, Row)`,以 `inject: ['slots', 'conversation']` 作为加载顺序 seam。Trajectory 与 waterfall(瀑布式事件)工具视图 slot 共享此形状并使用各自的渲染点;RendersCheck 会拒绝没有任何渲染方的声明。 审批经由本包声明的链接管编辑器:`ApprovalPanel` 注册为按选择器路由的 `'conversation.composer'` 配置项(ui-question 模式),在审批等待未决期间取代 InputBar 占据编辑器(琥珀色条、理由标题、来自运行中调用参数的配对命令行、一次性的拒绝/允许)。`contract/slots.ts` 中的 `PendingApproval` 领域面在运行时 `PendingWait` 载体之上拥有 wire 编码——带审计关联的 `ApprovalResponsePayload` 值;广播的 `approval/resolved` 帧使等待落定并恢复编辑器。运行时 manager 会将所有审批或问题等待通过 `SessionSummary.pendingInteraction` 投影出来,未实例化的 Session 也不例外;`ui-workspace` 负责其侧边栏呈现。未决等待完全离开消息流:问题(ui-question)与审批(ApprovalPanel)都经编辑器接管作答,不再保留只读占位卡。编辑器底行的 Access 席位挂载 `PermissionSelect`,由 host 计算的 `permissions` 投影经标准工具包 `useProjection` 供数(key 缺席即隐藏 chip);chip 打开 Menu 原语下拉,其中 kebab-case 预设名渲染为 Title Case 标签;普通安全预设会立即经输入栏注入的 `command` 回调提交 `/permission `,而 `danger-full-access` 在界面中显示为 `Full access`,选择后先打开页面内的 Modal 风险确认。用户勾选确认项前启用按钮始终不可用;取消、Escape、关闭按钮与点击遮罩都不会提交命令。 diff --git a/packages/client/ui-conversation/src/client/contract/diff-card-model.ts b/packages/client/ui-conversation/src/client/contract/diff-card-model.ts index bc914e4820..ec19f1cce0 100644 --- a/packages/client/ui-conversation/src/client/contract/diff-card-model.ts +++ b/packages/client/ui-conversation/src/client/contract/diff-card-model.ts @@ -76,11 +76,8 @@ function narrowDiffs(diffs: unknown): DiffHunk[] | null { * * This derivation consumes only `diffs`; the render intent's `title` field is * deliberately dropped. The row supplies its own title (`Edit`/`Write · path` - * from the args) and that outranks the view's `title`, matching the TUI diff - * branch, which likewise draws no view title. A tool that names its own diff - * header therefore does not surface that text on the Web row — an accepted - * product choice, recorded here as the one asymmetry with the terminal card, - * whose derivation does consume the view's title. + * from the args), which outranks the view's `title`. A tool that names its own + * diff header therefore does not surface that text on the Web row. * @param block - RunningToolCall or ToolResultNode off the snapshot caches. * @returns the diff-card props, or null for the generic path. */ diff --git a/packages/client/ui-goal/README.i18n.yaml b/packages/client/ui-goal/README.i18n.yaml index 1897f1ba8a..3b0ab1b1bb 100644 --- a/packages/client/ui-goal/README.i18n.yaml +++ b/packages/client/ui-goal/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-goal/README.md -README.md: 3da9d97c801a0a742de2601e5261c09ba193cf33 -README.zh.md: 8a4c01394508d9ceb3eabb6cd38ad58abd7f0e38 +README.md: c9a8f330949ed0db9c4986e7043a5063b8a26805 +README.zh.md: 9df1a0091545436642ef5644d3258364e63a20ec diff --git a/packages/client/ui-goal/README.md b/packages/client/ui-goal/README.md index 3da9d97c80..c9a8f33094 100644 --- a/packages/client/ui-goal/README.md +++ b/packages/client/ui-goal/README.md @@ -16,4 +16,4 @@ None beyond the goal mutation's own context event, which appends to the log tail ## Known Limitations and Deferred Work -- **Durable phase only** — the projection value deliberately omits process-local activation (armed/disarmed), so the strip cannot distinguish an active-but-disarmed goal from an armed one; resume re-arms through the RPC side. A host-live-value channel is deferred until a real consumer needs it. +- **Durable phase only** — the projection omits process-local activation, so the strip cannot distinguish an active-but-disarmed goal from an armed one; resume re-arms through the RPC side. There is no host-live activation channel. diff --git a/packages/client/ui-goal/README.zh.md b/packages/client/ui-goal/README.zh.md index 8a4c013945..9df1a00915 100644 --- a/packages/client/ui-goal/README.zh.md +++ b/packages/client/ui-goal/README.zh.md @@ -16,4 +16,4 @@ Goal 界面插件(浏览器端部分):`GoalBar` 条带是 `conversation.in ## 已知限制与暂缓事项 -- **只反映持久 phase**——投影值有意省略进程本地的 activation(armed/disarmed),条带无法区分 active-but-disarmed 与 armed 状态;resume 通过 RPC 重新置为 armed 状态。host 活值通道待出现真实消费方后再议。 +- **只反映持久 phase**——投影省略进程本地 activation,因此条带无法区分 active-but-disarmed 与 armed 状态;resume 通过 RPC 重新置为 armed 状态。不存在 host 实时 activation 通道。 diff --git a/packages/client/ui-layout/README.i18n.yaml b/packages/client/ui-layout/README.i18n.yaml index 6cde469078..f021dbb3ca 100644 --- a/packages/client/ui-layout/README.i18n.yaml +++ b/packages/client/ui-layout/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-layout/README.md -README.md: cb99023e6a9e3364c6f48190cf4a0cd71da2cbba -README.zh.md: a24dfa4d4eeb28fdc8df1d21daa3f6e9476d0062 +README.md: b01b35e9ec37e9b5b88046c3834c3aaeec70eb6e +README.zh.md: c3e4bf1e1b48feb87e68b3b7c73f7f9d11d7fea3 diff --git a/packages/client/ui-layout/README.md b/packages/client/ui-layout/README.md index cb99023e6a..b01b35e9ec 100644 --- a/packages/client/ui-layout/README.md +++ b/packages/client/ui-layout/README.md @@ -20,4 +20,4 @@ None; this package neither assembles nor sends a provider request. - **Panel geometry is transient** — reload restores the sidebar default and details closed; switching between distinct Session ids also closes details and forgets its dragged width, while unselected surfaces render details at zero width without modifying geometry. - **Concession-chain auto-close derives a zero width without touching the preferred width** — the panel restores itself when the window widens; consumers must not read the stored details width as the rendered truth. -- **Scroll anchoring during squeeze reflow is not implemented** — deferred with the virtualized-list project. +- **No scroll anchoring during squeeze reflow** — layout changes may move the reader's viewport. diff --git a/packages/client/ui-layout/README.zh.md b/packages/client/ui-layout/README.zh.md index a24dfa4d4e..c3e4bf1e1b 100644 --- a/packages/client/ui-layout/README.zh.md +++ b/packages/client/ui-layout/README.zh.md @@ -20,4 +20,4 @@ AppFrame 始终挂载会话栏和详情栏;已连接 Session 通过 `SessionPr - **面板几何信息是瞬时状态**:重新加载会恢复侧边栏默认值,并使详情栏保持关闭;在不同会话 id 之间切换同样会关闭详情栏,并忘记拖动后的宽度,而未选中表面会以零宽度渲染详情栏,但不会修改几何信息。 - **让步链自动关闭通过推导零宽度实现,不会改动宽度偏好**:窗口变宽时面板会自行恢复;消费方禁止把 store 中的详情宽度当作实际渲染状态。 -- **挤压重排期间尚未实现滚动锚定**:与虚拟化列表项目一并暂缓。 +- **挤压重排期间不提供滚动锚定**:布局变化可能移动读者的 viewport。 diff --git a/packages/client/ui-primitives/README.i18n.yaml b/packages/client/ui-primitives/README.i18n.yaml index 8c597efd5a..dabb138264 100644 --- a/packages/client/ui-primitives/README.i18n.yaml +++ b/packages/client/ui-primitives/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-primitives/README.md -README.md: 00e9560f43c83e1edc61c185a4fc562c6c923e8b -README.zh.md: 21226ab211106b7722139828762605cb71a4b498 +README.md: d4e6c2508f07f4832f2e12a836c2d447b656421e +README.zh.md: 76dfbebd5e9db494b49d65a2528977b7ac9fed15 diff --git a/packages/client/ui-primitives/README.md b/packages/client/ui-primitives/README.md index 00e9560f43..d4e6c2508f 100644 --- a/packages/client/ui-primitives/README.md +++ b/packages/client/ui-primitives/README.md @@ -14,7 +14,7 @@ Pure React atoms (zero cordis): StateDot, ic_ds_* icons, Button/Pill/Menu/Modal/ ## Terminal output -`TerminalBlock` renders a shell command as a terminal surface: one prompt row per line of the command (the shortened `cwd` label on the first row only, since the view knows one working directory and a `cd` moves later lines elsewhere, then that line), the command's output, a status pill for a non-zero exit code or a terminating signal, and a copy control that writes the raw `output` prop. A run-state `StateDot` marks the call once, on the first row, out of flow in a gutter the card reserves as its own left padding, so the dot sits inside the card box yet left of the prompt text. It reaches three of `StateDot`'s states — the chase while `running`, red for the same exit status that renders the pill, green otherwise — so a card states whether its command is still running rather than leaving that to be inferred from the presence of output; it carries one visually hidden text label because `StateDot` is `aria-hidden`. One dot regardless of line count is deliberate: the exit status is the whole call's, so a dot per line would claim a per-line outcome the view does not carry. Command text is `white-space: pre`, so repeated spaces, tabs, and an indented continuation render verbatim while the row stays single-line and ellipsizes. ANSI escape sequences are parsed with the `anser` runtime dependency into React spans; cursor movements replay into a per-line column buffer before inert controls are stripped, since carriage return and backspace only MOVE the cursor: `100%` + CR + `OK` alone shows `OK0%`, while the `\x1b[K` a spinner writes with its redraw erases the tail so `100%\r\x1b[KOK` shows `OK`. Erase-in-line is honored in all three parameter forms, the cursor advances by terminal columns (8-column tab stops, two for emoji and CJK, none for a combining mark), and SGR state is normalized per cell as a terminal stores it, threading across lines and closing at the state the line ended in; basic-16 foreground colors map onto `--dsw-*` tokens, while 256-palette and truecolor values pass through as literal rgb. Output keeps `white-space: pre` with horizontal scrolling, so column-aligned output holds its alignment instead of soft-wrapping, and collapses to a head slice plus a tail slice past `maxLines` (default 16, the TUI transcript's split arithmetic) behind an expand button. Rationale: [the web terminal card note](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md). +`TerminalBlock` renders a shell command as a terminal surface: one prompt row per line of the command (the shortened `cwd` label on the first row only, since the view knows one working directory and a `cd` moves later lines elsewhere, then that line), the command's output, a status pill for a non-zero exit code or a terminating signal, and a copy control that writes the raw `output` prop. A run-state `StateDot` marks the call once, on the first row, out of flow in a gutter the card reserves as its own left padding, so the dot sits inside the card box yet left of the prompt text. It reaches three of `StateDot`'s states — the chase while `running`, red for the same exit status that renders the pill, green otherwise — so a card states whether its command is still running rather than leaving that to be inferred from the presence of output; it carries one visually hidden text label because `StateDot` is `aria-hidden`. One dot regardless of line count is deliberate: the exit status is the whole call's, so a dot per line would claim a per-line outcome the view does not carry. Command text is `white-space: pre`, so repeated spaces, tabs, and an indented continuation render verbatim while the row stays single-line and ellipsizes. ANSI escape sequences are parsed with the `anser` runtime dependency into React spans; cursor movements replay into a per-line column buffer before inert controls are stripped, since carriage return and backspace only MOVE the cursor: `100%` + CR + `OK` alone shows `OK0%`, while the `\x1b[K` a spinner writes with its redraw erases the tail so `100%\r\x1b[KOK` shows `OK`. Erase-in-line is honored in all three parameter forms, the cursor advances by terminal columns (8-column tab stops, two for emoji and CJK, none for a combining mark), and SGR state is normalized per cell as a terminal stores it, threading across lines and closing at the state the line ended in; basic-16 foreground colors map onto `--dsw-*` tokens, while 256-palette and truecolor values pass through as literal rgb. Output keeps `white-space: pre` with horizontal scrolling, so column-aligned output holds its alignment instead of soft-wrapping, and collapses to a head slice plus a tail slice past `maxLines` (default 16) behind an expand button. Rationale: [the web terminal card note](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md). ## Read rendering @@ -22,7 +22,7 @@ Pure React atoms (zero cordis): StateDot, ic_ds_* icons, Button/Pill/Menu/Modal/ ## Diff rendering -`DiffBlock` renders a file mutation as an inline diff surface: one bold path header per file, the removed lines (`- `, error token) above the added lines (`+ `, success token), a `⋯` gap before a same-file second hunk, and a dim `└ +A -R · N file(s)` footer. Lines are `white-space: pre` with horizontal scrolling, so a source line holds its indentation instead of soft-wrapping, and the body collapses to a head slice plus a tail slice past `maxLines` (default 16, `TerminalBlock`'s split arithmetic) behind an expand button. A create (`oldText: null`) has no removed side. The copy control writes the prefixed diff text (path headers, `- `/`+ ` lines, the gap) so a multi-file copy stays attributable, and floats in the top-right corner rather than on a banner row of its own. Geometry mirrors `CodeBlock`/`TerminalBlock`. The `+`/`-` block form mirrors the TUI transcript's diff card so a diff reads the same across front ends. Rationale: [the web diff card note](../../../.agents/notes/implemented/feature/2026-07-30-web-diff-card.md). +`DiffBlock` renders a file mutation as an inline diff surface: one bold path header per file, the removed lines (`- `, error token) above the added lines (`+ `, success token), a `⋯` gap before a same-file second hunk, and a dim `└ +A -R · N file(s)` footer. Lines are `white-space: pre` with horizontal scrolling, so a source line holds its indentation instead of soft-wrapping, and the body collapses to a head slice plus a tail slice past `maxLines` (default 16, `TerminalBlock`'s split arithmetic) behind an expand button. A create (`oldText: null`) has no removed side. The copy control writes the prefixed diff text (path headers, `- `/`+ ` lines, the gap) so a multi-file copy stays attributable, and floats in the top-right corner rather than on a banner row of its own. Geometry mirrors `CodeBlock`/`TerminalBlock`. Rationale: [the web diff card note](../../../.agents/notes/implemented/feature/2026-07-30-web-diff-card.md). ## Search results @@ -44,6 +44,6 @@ None; this package neither assembles nor sends a provider request. - **Glyph-level icons are redrawn approximations** — the fish logo (and the sparkle held by ui-conversation) come from font glyphs whose vector geometry is not exportable from the local design data; hand-authored recreations stand in until an exact export path exists. - **Pill and Input have no design source** — both atoms are self-defined; the sidebar search field and view-tab strip that resemble them are consumer-owned compositions, not these atoms. -- **StateDot `Active` variant is a hidden placeholder in the design** — not implemented; the four shipped states (done/warning/ongoing/error) are the complete P-I surface. +- **No `Active` StateDot variant** — the supported states are done, warning, ongoing, and error. - **User-facing copy localizes through label props, defaulting to the original Chinese literals** — the atoms are zero-cordis and cannot reach `ctx.locale`, so `HoverCard` (`copyLabel`/`copiedLabel`), `TerminalBlock` (`labels`), `JsonTree` (`labels`), `CodeBlock` (`copyLabel`/`copiedLabel`), `MarkdownText` (`codeLabels`), `JsonBlock` (`truncatedLabel`), `ConnectionBanner` (`label`), and `Modal` (`closeLabel`) take their copy as optional props with the previous hardcoded strings as defaults. Localized plugins pass dictionary-driven labels from their own `t` seat; a consumer that passes nothing renders exactly the pre-localization output. `WebBlock` does not yet follow this pattern: its source-list and fetch truncation notes and its empty-search note stay inline Chinese, pending the same label-prop treatment. - **`TerminalBlock` is not a terminal emulator** — it renders settled or still-running command output, not an interactive session: SGR color and attributes are honored, and so are the in-line cursor movements a progress line uses — carriage return, backspace, erase-in-line, tab stops and character width. Absolute cursor positioning, screen clearing, and alternate-screen sequences are stripped. Basic-16 magenta and cyan have no token equivalent and stay literal rgb. diff --git a/packages/client/ui-primitives/README.zh.md b/packages/client/ui-primitives/README.zh.md index 21226ab211..76dfbebd5e 100644 --- a/packages/client/ui-primitives/README.zh.md +++ b/packages/client/ui-primitives/README.zh.md @@ -14,7 +14,7 @@ ## 终端输出 -`TerminalBlock` 将一条 shell 命令渲染为终端表层:命令的每一行各占一个提示行(缩短后的 `cwd` 标签只出现在第一行,因为视图只知道一个工作目录,而一个 `cd` 就会让后面的行去到别处,标签之后是该行)、命令输出、非零退出码或终止信号对应的状态胶囊,以及写入原始 `output` prop 的复制控件。一枚运行状态 `StateDot` 为整次调用标记一次,位于第一行,以脱离文档流的方式落在卡片以自身左内边距预留的落区中,因此它位于卡片盒之内、提示文字之左。它用到 `StateDot` 的三种状态——`running` 期间为追逐动画,与渲染状态胶囊相同的退出状态为红色,其余为绿色——因此卡片直接陈述其命令是否仍在运行,而不是让人从有无输出中推断;由于 `StateDot` 是 `aria-hidden`,它携带一处视觉隐藏的文本标签。无论多少行都只有一枚状态点是有意为之:退出状态属于整次调用,因此每行一枚就会声称一个视图并不携带的逐行结果。命令文本使用 `white-space: pre`,因此重复空格、制表符与缩进续行都原样呈现,同时该行仍保持单行并以省略号截断。ANSI 转义序列通过运行时依赖 `anser` 解析为 React span;光标移动在剥除无显示意义控制符之前先重放进逐行的列缓冲,因为回车与退格**只移动**光标:单是 `100%` 加回车再加 `OK` 显示为 `OK0%`,而 spinner 随重绘写出的 `\x1b[K` 会擦掉尾巴,因此 `100%\r\x1b[KOK` 显示为 `OK`。行内擦除的三种参数形式都被遵循,光标按终端列推进(8 列制表位;emoji 与 CJK 占两列;组合标记不占列),SGR 状态按单元格归一化存储,与终端一致,并跨行延续、在行结束时的状态处收束;基础 16 色前景色映射到 `--dsw-*` token,而 256 色板与真彩色值按字面 rgb 透传。输出保持 `white-space: pre` 并支持横向滚动,因此按列对齐的输出保留其对齐而不会软换行;超过 `maxLines`(默认 16,与 TUI 转录相同的切分算法)时折叠为头部切片加尾部切片,由展开按钮控制。原理:[Web 终端卡片笔记](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md)。 +`TerminalBlock` 将一条 shell 命令渲染为终端表层:命令的每一行各占一个提示行(缩短后的 `cwd` 标签只出现在第一行,因为视图只知道一个工作目录,而一个 `cd` 就会让后面的行去到别处,标签之后是该行)、命令输出、非零退出码或终止信号对应的状态胶囊,以及写入原始 `output` prop 的复制控件。一枚运行状态 `StateDot` 为整次调用标记一次,位于第一行,以脱离文档流的方式落在卡片以自身左内边距预留的落区中,因此它位于卡片盒之内、提示文字之左。它用到 `StateDot` 的三种状态——`running` 期间为追逐动画,与渲染状态胶囊相同的退出状态为红色,其余为绿色——因此卡片直接陈述其命令是否仍在运行,而不是让人从有无输出中推断;由于 `StateDot` 是 `aria-hidden`,它携带一处视觉隐藏的文本标签。无论多少行都只有一枚状态点是有意为之:退出状态属于整次调用,因此每行一枚就会声称一个视图并不携带的逐行结果。命令文本使用 `white-space: pre`,因此重复空格、制表符与缩进续行都原样呈现,同时该行仍保持单行并以省略号截断。ANSI 转义序列通过运行时依赖 `anser` 解析为 React span;光标移动在剥除无显示意义控制符之前先重放进逐行的列缓冲,因为回车与退格**只移动**光标:单是 `100%` 加回车再加 `OK` 显示为 `OK0%`,而 spinner 随重绘写出的 `\x1b[K` 会擦掉尾巴,因此 `100%\r\x1b[KOK` 显示为 `OK`。行内擦除的三种参数形式都被遵循,光标按终端列推进(8 列制表位;emoji 与 CJK 占两列;组合标记不占列),SGR 状态按单元格归一化存储,与终端一致,并跨行延续、在行结束时的状态处收束;基础 16 色前景色映射到 `--dsw-*` token,而 256 色板与真彩色值按字面 rgb 透传。输出保持 `white-space: pre` 并支持横向滚动,因此按列对齐的输出保留其对齐而不会软换行;超过 `maxLines`(默认 16)时折叠为头部切片加尾部切片,由展开按钮控制。原理:[Web 终端卡片笔记](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md)。 ## Read 渲染 @@ -22,7 +22,7 @@ ## Diff 渲染 -`DiffBlock` 将一次文件改动渲染为内联 diff 表层:每个文件一个粗体路径头、删除行(`- `,error token)在新增行(`+ `,success token)之上、同文件第二个 hunk 前一个 `⋯` gap,以及暗色 `└ +A -R · N file(s)` 页脚。各行使用 `white-space: pre` 并横向滚动,因此源码行保留其缩进而不软换行;超过 `maxLines`(默认 16,与 `TerminalBlock` 相同的切分算法)时折叠为头部切片加尾部切片,由展开按钮控制。新建(`oldText: null`)没有删除侧。复制控件写入带前缀的 diff 文本(路径头、`- `/`+ ` 行、gap),使多文件复制保持可归属,并浮在右上角而非占据自己的 banner 行。几何镜像 `CodeBlock`/`TerminalBlock`。`+`/`-` 块形式镜像 TUI 转录的 diff 卡片,使 diff 在两个前端读起来一致。原理:[Web diff 卡片笔记](../../../.agents/notes/implemented/feature/2026-07-30-web-diff-card.md)。 +`DiffBlock` 将一次文件改动渲染为内联 diff 表层:每个文件一个粗体路径头、删除行(`- `,error token)在新增行(`+ `,success token)之上、同文件第二个 hunk 前一个 `⋯` gap,以及暗色 `└ +A -R · N file(s)` 页脚。各行使用 `white-space: pre` 并横向滚动,因此源码行保留其缩进而不软换行;超过 `maxLines`(默认 16,与 `TerminalBlock` 相同的切分算法)时折叠为头部切片加尾部切片,由展开按钮控制。新建(`oldText: null`)没有删除侧。复制控件写入带前缀的 diff 文本(路径头、`- `/`+ ` 行、gap),使多文件复制保持可归属,并浮在右上角而非占据自己的 banner 行。几何镜像 `CodeBlock`/`TerminalBlock`。原理:[Web diff 卡片笔记](../../../.agents/notes/implemented/feature/2026-07-30-web-diff-card.md)。 ## 搜索结果 diff --git a/packages/client/ui-primitives/src/head-tail-cap.ts b/packages/client/ui-primitives/src/head-tail-cap.ts index 1ac540dd21..f0852052a4 100644 --- a/packages/client/ui-primitives/src/head-tail-cap.ts +++ b/packages/client/ui-primitives/src/head-tail-cap.ts @@ -1,6 +1,5 @@ // Head/tail height-cap arithmetic shared by the block primitives (TerminalBlock, -// SearchBlock) and matching the TUI transcript's collapsed tool card, so a long -// result's head and tail slices agree across every surface. The split is +// SearchBlock), so long results use consistent head and tail slices. The split is // `ceil(maxLines / 2)` head rows and the remainder as tail rows; a result within // the cap shows every row and hides none. diff --git a/packages/client/ui-sidebar/README.i18n.yaml b/packages/client/ui-sidebar/README.i18n.yaml index 1bd10f89a6..609542a3b7 100644 --- a/packages/client/ui-sidebar/README.i18n.yaml +++ b/packages/client/ui-sidebar/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-sidebar/README.md -README.md: 5bb697b3d2f9b5eaea9c382765d2510fa24806ce -README.zh.md: 302f66c540774b1f209fc797201e41c56b849310 +README.md: f251a211153b2fb317947501a051030c431698a1 +README.zh.md: f22ebfa34e51a267a21a548570770ba51cedc116 diff --git a/packages/client/ui-sidebar/README.md b/packages/client/ui-sidebar/README.md index 5bb697b3d2..f251a21115 100644 --- a/packages/client/ui-sidebar/README.md +++ b/packages/client/ui-sidebar/README.md @@ -24,6 +24,6 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work -- **Session state-dot rendering is owned by [ui-workspace](../ui-workspace/README.md)** — done/error notification sources remain deferred. -- **Group-by menu ships by-workspace only** — Update/Status grouping strategies are drawn without specs and deferred. +- **Session state-dot rendering is owned by [ui-workspace](../ui-workspace/README.md)** — no done/error notification sources are available. +- **Group-by supports Workspace only** — Update and Status are not available strategies. - **"New task completed" unread marking is local viewing state** — completion-time > last-seen never reaches the host. diff --git a/packages/client/ui-sidebar/README.zh.md b/packages/client/ui-sidebar/README.zh.md index 302f66c540..f22ebfa34e 100644 --- a/packages/client/ui-sidebar/README.zh.md +++ b/packages/client/ui-sidebar/README.zh.md @@ -24,6 +24,6 @@ New Session 会启动运行时的页面局部前端 Session Intent;真实 Work ## 已知限制与暂缓事项 -- **Session 状态点渲染由 [ui-workspace](../ui-workspace/README.md) 持有**:done/error 的通知数据源仍暂缓实现。 -- **分组选单只提供按 Workspace 分组**:Update/Status 分组策略只有图稿而没有规范,暂缓实现。 +- **Session 状态点渲染由 [ui-workspace](../ui-workspace/README.md) 持有**:没有可用的 done/error 通知数据源。 +- **分组只支持 Workspace**:Update 和 Status 不是可用策略。 - **「New task completed」未读标记是本地查看状态**:完成时间 > 上次查看时间这一事实永远不会到达宿主。 diff --git a/packages/client/ui-slash/README.i18n.yaml b/packages/client/ui-slash/README.i18n.yaml index d27bf07bed..340f420504 100644 --- a/packages/client/ui-slash/README.i18n.yaml +++ b/packages/client/ui-slash/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-slash/README.md -README.md: 5d277a83c5f0bc4bcec5871e0618af28afb7b6d2 -README.zh.md: 08cc477edf657279c142a5e54a1471a118b9a021 +README.md: efb289aaf6b4a442b00e7a0b26ed044b7b061070 +README.zh.md: e2d99514d29b1b5aeeb1d7f42136ec73d961f3e7 diff --git a/packages/client/ui-slash/README.md b/packages/client/ui-slash/README.md index 5d277a83c5..efb289aaf6 100644 --- a/packages/client/ui-slash/README.md +++ b/packages/client/ui-slash/README.md @@ -22,4 +22,4 @@ None; this package neither assembles nor sends a provider request. - **Global source layer only** — session-scope source registration (per-session shadowing, ScopedLayers-alike) is designed but not enabled; the ledger tracks the trigger condition (a real per-session source need). - **`SlashCandidate.icon` renders as text** — MenuView drops the string into the icon slot verbatim; wiring to the design-system icon enum (iconFile five-variant family) lands when that enum ships. -- **Overlay SlotMap merge home is split from slot ownership** — the `conversation.input.overlay` merge lives here (sole copy) while the slot's owner semantics (anchor, children declaration, lifecycle) stay with ui-conversation; the dependency direction (ui-conversation → ui-slash) forces the split, so a future dependency reshuffle should revisit it. +- **Overlay SlotMap merge home is split from slot ownership** — the sole `conversation.input.overlay` merge lives here, while ui-conversation owns its anchor, children declaration, and lifecycle because the dependency direction is ui-conversation → ui-slash. diff --git a/packages/client/ui-slash/README.zh.md b/packages/client/ui-slash/README.zh.md index 08cc477edf..e2d99514d2 100644 --- a/packages/client/ui-slash/README.zh.md +++ b/packages/client/ui-slash/README.zh.md @@ -22,4 +22,4 @@ MenuView 把菜单 store 渲染进 `conversation.input.overlay` slot(列表类 - **只有全局 source 层**:会话 scope 的 source 注册(逐会话遮蔽、类 ScopedLayers 机制)已有设计但未启用;台账记录着触发条件(出现真实的逐会话 source 需求)。 - **`SlashCandidate.icon` 以文本渲染**:MenuView 把该字符串原样放进图标位;与设计系统图标枚举(iconFile 五变体家族)的接入将在该枚举交付后完成。 -- **overlay 的 SlotMap 合并归属与 slot 所有权分离**:`conversation.input.overlay` 的合并放在本包(唯一副本),而该 slot 的 owner 语义(锚点、children 声明、生命周期)留在 ui-conversation;依赖方向(ui-conversation → ui-slash)迫使这一拆分,未来依赖关系调整时应重新审视。 +- **overlay 的 SlotMap 合并归属与 slot 所有权分离**:唯一的 `conversation.input.overlay` 合并放在本包,而 ui-conversation 负责其锚点、children 声明和生命周期,因为依赖方向是 ui-conversation → ui-slash。 diff --git a/packages/client/ui-theme/README.i18n.yaml b/packages/client/ui-theme/README.i18n.yaml index ea7237402a..76bcbaf608 100644 --- a/packages/client/ui-theme/README.i18n.yaml +++ b/packages/client/ui-theme/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-theme/README.md README.md: 88e21fe214ec806b101050949690283d811be36d -README.zh.md: 4ed45070234acb78a2e5edef52578b504ae53077 +README.zh.md: ba781ba89a62292928a7b05ab94ea1cd930b4f50 diff --git a/packages/client/ui-theme/README.zh.md b/packages/client/ui-theme/README.zh.md index 4ed4507023..ba781ba89a 100644 --- a/packages/client/ui-theme/README.zh.md +++ b/packages/client/ui-theme/README.zh.md @@ -8,7 +8,7 @@ 滚动条重新绑定契约:`scrollbar.css` 在 `body` 上把 `--dsh-scrollbar-thumb` 与 `--dsh-scrollbar-thumb-hover` 绑定到 l1(基础表面)token,两条渲染路径都读取这一组变量。高层级表面(菜单、浮层、对话框)在自己的容器上设置 `--dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2)` 与 `--dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2)`;一次重新绑定即可为引擎实际走的那条路径换色。这组变量另一个合法的目标是 `transparent`,即完全不绘制滑块——[ui-sidebar](../ui-sidebar/README.md) 在指针不在栏内时就这样重新绑定自己的列。绑回 l1 那组不算重新绑定,它只是重述基础表面的默认值。 -两条路径在构造上互斥。`scrollbar-width`/`scrollbar-color` 写在 `@supports not selector(::-webkit-scrollbar)` 之内,因为这两个属性中的任一个只要取非 `auto` 值,Chromium 与 Safari 就会丢弃该元素上的全部 `::-webkit-scrollbar*` 规则,`::-webkit-scrollbar-thumb:hover` 也在其中——若无条件地同时声明,`--dsh-scrollbar-thumb-hover` 在任何引擎上都不会被渲染。因此 Firefox 走标准属性,WebKit 系引擎走伪元素,hover token 只经由伪元素这条路径渲染。推理过程与实测计算值见[滚动条 Agent Note(agent 决策记录)](../../../.agents/notes/implemented/bug-fix/2026-07-28-themed-scrollbars-and-reserved-gutter.md)。 +两条路径在构造上互斥。`scrollbar-width`/`scrollbar-color` 写在 `@supports not selector(::-webkit-scrollbar)` 之内,因为这两个属性中的任一个只要取非 `auto` 值,Chromium 与 Safari 就会丢弃该元素上的全部 `::-webkit-scrollbar*` 规则,`::-webkit-scrollbar-thumb:hover` 也在其中——若无条件地同时声明,`--dsh-scrollbar-thumb-hover` 在任何引擎上都不会被渲染。因此 Firefox 走标准属性,WebKit 系引擎走伪元素,hover token 只经由伪元素这条路径渲染。推理过程与实测计算值见[滚动条 Agent Note](../../../.agents/notes/implemented/bug-fix/2026-07-28-themed-scrollbars-and-reserved-gutter.md)。 ## 模型体验 diff --git a/packages/client/ui-trajectory/README.i18n.yaml b/packages/client/ui-trajectory/README.i18n.yaml index 86953e8e94..5ef1a8eb68 100644 --- a/packages/client/ui-trajectory/README.i18n.yaml +++ b/packages/client/ui-trajectory/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-trajectory/README.md -README.md: 5d0ea3bbbbfca2b8c0ee02ed07ca956fbd377e11 -README.zh.md: d88e4562296ccef8653f85ee74e2e6e856bd7d96 +README.md: 771e1a68e8027f02f20b17f78d0a8dd48d2d5bff +README.zh.md: ceb4d696e5fda117afff89cf9cd0a5426dfed2be diff --git a/packages/client/ui-trajectory/README.md b/packages/client/ui-trajectory/README.md index 5d0ea3bbbb..771e1a68e8 100644 --- a/packages/client/ui-trajectory/README.md +++ b/packages/client/ui-trajectory/README.md @@ -14,4 +14,4 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work -- **In-flight Time stays blank** — `partial` / `runningCalls` rows show their running state without a fabricated duration until a live clock policy lands, so the Overview renders a start marker rather than inventing a live span; record and timeline selection are intentionally local to Trajectory; anchor deep-linking remains deferred. +- **In-flight Time stays blank** — `partial` and `runningCalls` rows show their running state without a fabricated duration, so the Overview renders a start marker rather than inventing a live span. Record and timeline selection are local to Trajectory, with no anchor deep links. diff --git a/packages/client/ui-trajectory/README.zh.md b/packages/client/ui-trajectory/README.zh.md index d88e456229..ceb4d696e5 100644 --- a/packages/client/ui-trajectory/README.zh.md +++ b/packages/client/ui-trajectory/README.zh.md @@ -14,4 +14,4 @@ Trajectory 渲染按轮次组织的事件记录表,其中可选择用户、助 ## 已知限制与暂缓事项 -- **进行中时,Time 保持空白**:`partial`/`runningCalls` 行会显示运行状态,但在实时钟策略落地前不会虚构耗时,因此 Overview 区域只渲染开始标记,而不会杜撰实时跨度;记录选择与时间线选择有意保持在 Trajectory 内部;锚点深链接仍暂缓实现。 +- **进行中时,Time 保持空白**:`partial` 与 `runningCalls` 行会显示运行状态,但不会虚构耗时,因此 Overview 区域只渲染开始标记,而不会杜撰实时跨度。记录选择与时间线选择位于 Trajectory 内部,不提供锚点深链接。 diff --git a/packages/client/ui-workspace/README.i18n.yaml b/packages/client/ui-workspace/README.i18n.yaml index 43d753fae5..1f3645727c 100644 --- a/packages/client/ui-workspace/README.i18n.yaml +++ b/packages/client/ui-workspace/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-workspace/README.md -README.md: 855bcdc7fa1850ee30a1887add10dc019d9887e2 -README.zh.md: acb35ab169f10444c26ae6d68aefda9c5b8703df +README.md: 7f17eb841368b5c04df0a8192716e2c9a3c6896a +README.zh.md: 574ac5b74e233d9366681991ede89a37c6fae79c diff --git a/packages/client/ui-workspace/README.md b/packages/client/ui-workspace/README.md index 855bcdc7fa..7f17eb8413 100644 --- a/packages/client/ui-workspace/README.md +++ b/packages/client/ui-workspace/README.md @@ -29,6 +29,6 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work - **No fuzzy content search or event deep links** — the content backend uses literal token/phrase matching, and selecting a result opens the Session rather than the matching event. -- **No Session deletion or unarchive control** — archiving replaces the former Delete placeholder; archived sessions have no viewing or unarchive surface yet, and Workspace registration deletion does not delete Sessions. +- **No Session deletion or unarchive control** — sessions can be archived, but archived sessions have no viewing or unarchive surface, and Workspace registration deletion does not delete Sessions. - **Pending user interaction is not aggregated into collapsed groups** — a waiting row inside a collapsed group lights no group-header indicator and becomes visible only after that group is expanded. - **Native folder selection depends on the local Host carrier** — under the `-native` composition, fixture-only or remote browser deployments cannot open a local operating-system dialog; platform failures are shown in a retryable modal. Remote-capable picking is the `-browse` composition's in-app flow. diff --git a/packages/client/ui-workspace/README.zh.md b/packages/client/ui-workspace/README.zh.md index acb35ab169..574ac5b74e 100644 --- a/packages/client/ui-workspace/README.zh.md +++ b/packages/client/ui-workspace/README.zh.md @@ -29,6 +29,6 @@ Session 行渲染运行时的实时 `pendingInteraction` 分类:审批显示** ## 已知限制与暂缓事项 - **没有模糊内容搜索或事件深链接**:内容后端采用字面 token/短语匹配,选择结果会打开 Session,而不是匹配的事件。 -- **没有 Session 删除与取消归档控件**:归档取代了原先的 Delete 占位;已归档会话尚无查看或取消归档入口;删除 Workspace 注册记录不会删除 Session。 +- **没有 Session 删除与取消归档控件**:会话可以归档,但已归档会话没有查看或取消归档入口;删除 Workspace 注册记录不会删除 Session。 - **待处理的用户交互不会聚合到折叠的分组上**:折叠分组内正在等待的行不会点亮分组头指示,只有展开该分组后才可见。 - **原生文件夹选择依赖本地 Host 载体**:在 `-native` 组合下,仅使用 fixture(测试前置数据)的部署或远程浏览器部署无法打开本地操作系统对话框;模态框会显示平台故障,并允许重试。可远程的选取是 `-browse` 组合的应用内流程。 diff --git a/packages/client/web-react/README.i18n.yaml b/packages/client/web-react/README.i18n.yaml index c88190f135..72afe765f0 100644 --- a/packages/client/web-react/README.i18n.yaml +++ b/packages/client/web-react/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/web-react/README.md -README.md: 7cc80f22bd5527838288d11c819e81b7ec4d17c4 -README.zh.md: 9019a9618d35b6fc92dcc2cc84f8f903fa1ee346 +README.md: 8f1a525af1e249282f2cc473e7f9652f68f914a8 +README.zh.md: 55ed3a0fec4bfba4c4934db8728bc6fa23a7321a diff --git a/packages/client/web-react/README.md b/packages/client/web-react/README.md index 7cc80f22bd..8f1a525af1 100644 --- a/packages/client/web-react/README.md +++ b/packages/client/web-react/README.md @@ -16,4 +16,4 @@ None; this package neither assembles nor sends a provider request. - **The persist middleware corrupts primitive-state stores** — it object-spreads state on save, so a `SnapshotStore` round-trips as a character map; the engine hand-rolls persistence instead (see `attachPersistence`). - **`UseSession` is deliberately wide (`object` snapshot)** — the dependency direction (runtime → web-react, never the reverse) keeps the real `ConversationSnapshot` type out of reach; session-slot consumers narrow once at their boundary. -- **renderSlot is the single P-I form** — no Suspense, no per-entry lazy loading; the progressive-rendering surface returns with its own project. +- **`renderSlot` is the only rendering form** — there is no Suspense integration or per-entry lazy loading. diff --git a/packages/client/web-react/README.zh.md b/packages/client/web-react/README.zh.md index 9019a9618d..55ed3a0fec 100644 --- a/packages/client/web-react/README.zh.md +++ b/packages/client/web-react/README.zh.md @@ -16,4 +16,4 @@ slot 终端设计的外壳侧 React 胶水:createSlotRenderer(外壳安装 - **persist 中间件会损坏原始值状态 store**:保存时它会对状态执行对象展开,因此 `SnapshotStore` 往返后会变成字符映射;引擎改为自行实现持久化(见 `attachPersistence`)。 - **`UseSession` 有意保持宽泛(`object` 快照)**:依赖方向(runtime → web-react,绝不反向)使真实 `ConversationSnapshot` 类型不可访问;会话 slot 消费方在其边界处缩窄一次。 -- **renderSlot 是唯一的 P-I 形式**:没有 Suspense,也没有逐配置项惰性加载;渐进式渲染能力将在其独立项目中恢复。 +- **`renderSlot` 是唯一的渲染形式**:没有 Suspense 集成或逐配置项惰性加载。 diff --git a/packages/client/web/README.i18n.yaml b/packages/client/web/README.i18n.yaml index 323f244b1e..1e0c8b649b 100644 --- a/packages/client/web/README.i18n.yaml +++ b/packages/client/web/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/web/README.md -README.md: b8b03dcb58442116cc01a2ff4c30e266e3233ee9 -README.zh.md: 280ec52602321367715ae2a71c22cff265908299 +README.md: 8425cb1b33db50a8b7e3667323b6b65bfb43ad32 +README.zh.md: 656cdcb1f2fc1edf0d8387ee9745b04476923797 diff --git a/packages/client/web/README.md b/packages/client/web/README.md index b8b03dcb58..8425cb1b33 100644 --- a/packages/client/web/README.md +++ b/packages/client/web/README.md @@ -23,4 +23,4 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work - **One-shot rendering by design** — the UI waits for the boot settle; a single entry failure keeps the loading page with a loud per-entry report, no partial availability (progressive rendering returns with its own project). -- **Narrow-window acceptance is deferred** — the concession chain is implemented in ui-layout but the shell-level narrow-viewport walkthrough is a P-II acceptance item. +- **Narrow-window shell behavior lacks an assembled walkthrough** — ui-layout implements the concession chain, but this package has no shell-level narrow-viewport acceptance case. diff --git a/packages/client/web/README.zh.md b/packages/client/web/README.zh.md index 280ec52602..656cdcb1f2 100644 --- a/packages/client/web/README.zh.md +++ b/packages/client/web/README.zh.md @@ -23,4 +23,4 @@ Web 外壳内核:`new AppWebEntry(el, seams?).run()` 通过两阶段启动(w ## 已知限制与暂缓事项 - **有意采用一次性渲染**:UI 等待启动 settle;只要一个配置项失败,加载页面就会保留并逐项显示醒目的报告,不提供部分可用性(渐进式渲染将作为独立项目恢复)。 -- **窄窗口验收暂缓**:ui-layout 已实现让步链,但外壳级窄视口演练是 P-II 验收项。 +- **窄窗口外壳行为缺少组装后演练**:ui-layout 已实现让步链,但该包没有外壳级窄 viewport 验收用例。 diff --git a/packages/code-runtime/README.i18n.yaml b/packages/code-runtime/README.i18n.yaml index d8eebec7aa..a04f1985c2 100644 --- a/packages/code-runtime/README.i18n.yaml +++ b/packages/code-runtime/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/code-runtime/README.md -README.md: dbe6b37ffa01d07c6902672a06ebf6f88548ff99 -README.zh.md: a5acbad3cce19366ca9ca4729f5285905ab026eb +README.md: 2d32a05071efdfa05c336211196bb769ed5a5fc7 +README.zh.md: 62c9a395ac3cf2b5cd55455ab5a273a1e276f6b7 diff --git a/packages/code-runtime/README.zh.md b/packages/code-runtime/README.zh.md index f3dfa3e7e9..62c9a395ac 100644 --- a/packages/code-runtime/README.zh.md +++ b/packages/code-runtime/README.zh.md @@ -1,12 +1,12 @@ -# code-runtime/:代码执行能力家族 +# code-runtime/ — 代码执行能力家族 [English](README.md) | 中文 -代码执行能力 seam(参见[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)):一个抽象运行时接口,用于针对宿主提供的异步绑定执行一段模型编写的程序,并捕获程序打印和返回的内容。消费方是工具注册表的 [Code Mode](../core/tools/README.md)(`tools: { mode: code }`,即 `run_code` 工具与生成的 TypeScript SDK);设计记录在 [Code Mode Agent Note](../../.agents/notes/implemented/feature/2026-06-15-code-mode.md) 中。这些都是**产品**包。 +代码执行能力 seam(参见[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)):一个抽象运行时接口,用于对宿主提供的异步绑定执行模型编写的程序,并捕获它打印和返回的内容。消费方是工具注册表的 [Code Mode](../core/tools/README.md)(`tools: { mode: code }`,即 `run_code` 工具和生成的 TypeScript SDK);设计见 [Code Mode Agent Note](../../.agents/notes/implemented/feature/2026-06-15-code-mode.md)。这些全是**产品**包。 -| 包 | 职责 | ctx 键 | +| 包 | 职责 | ctx key | |---|---|---| | [`code-runtime/`](code-runtime/README.md) | 代码执行 seam 与共享词汇 | `ctx.codeRuntime` | -| [`code-runtime-worker/`](code-runtime-worker/README.md) | worker 线程后端 | 注册 `ctx.codeRuntime` | +| [`code-runtime-worker/`](code-runtime-worker/README.md) | Worker 线程后端 | 注册 `ctx.codeRuntime` | -后端注册该 seam,无需改动消费方。语言、隔离与执行预算细节由子级 README 负责。 +后端在不改变消费方的情况下注册该 seam。子 README 负责语言、隔离和执行预算细节。 diff --git a/packages/compact/README.i18n.yaml b/packages/compact/README.i18n.yaml index b20270f991..17357bea7b 100644 --- a/packages/compact/README.i18n.yaml +++ b/packages/compact/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/compact/README.md -README.md: aa9fa6d9419de87a7df23a437f5ea8694d981b28 -README.zh.md: 7c8752d891880cdb3fd3015a5bd088c7b5e0f2b6 +README.md: 509ea2764250f42e492f787deba959a8dd9967b7 +README.zh.md: adc52690dbe9e904d8f24199cb78493dab3333a4 diff --git a/packages/compact/README.zh.md b/packages/compact/README.zh.md index 7c8752d891..adc52690db 100644 --- a/packages/compact/README.zh.md +++ b/packages/compact/README.zh.md @@ -1,14 +1,14 @@ -# compact/:压缩能力家族 +# compact/ — 压缩能力家族 [English](README.md) | 中文 -一个压缩(compaction)能力家族(见[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)):抽象接口、摘要生成后端、不依赖模型的工具结果剪枝配套组件,以及面向用户的命令适配器。这些全是**产品**包。 +一个压缩(compaction)能力家族(参见[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)):抽象接口、摘要后端、无模型工具结果修剪配套工具,以及用户命令适配器。这些全是**产品**包。 | 包 | 职责 | ctx key | |---|---|---| -| `compact/` | 抽象压缩 seam(接口 + `compact/*` 事件 + `CompactionResult`) | `ctx.compact` | -| `compact-basic/` | 后端:`ctx.tokenMeter` 压力 + 按 token 预算保留内容 + `llm.stream()` 摘要生成 | (注册 `ctx.compact`) | -| `compact-tool-result-prune/` | 可选的不依赖模型的头/中/尾重写,在摘要压缩之前运行 | `ctx.toolResultPrune` | -| `command-compact/` | 面向用户的 `/compact` 命令,基于后端无关的 `compactNow()` seam | (注册到 `ctx.commands`) | +| [`compact/`](compact/README.md) | 压缩 seam 与事件词汇 | `ctx.compact` | +| [`compact-basic/`](compact-basic/README.md) | token 压力与摘要后端 | 注册 `ctx.compact` | +| [`compact-tool-result-prune/`](compact-tool-result-prune/README.md) | 可选的无模型工具结果修剪 | `ctx.toolResultPrune` | +| [`command-compact/`](command-compact/README.md) | 用户压缩命令 | 注册到 `ctx.commands` | -接口位于 `compact/compact/`,后端位于 `compact/compact-basic/`,确定性剪枝位于 `compact/compact-tool-result-prune/`,命令位于 `compact/command-compact/`。与 bash seam 不同,该接口依赖 `dsh-session` 和 `dsh-llm`,因为它的操作以 `Session` 为对象,输出则使用 `ContentBlock`。这项偏差记录在[压缩能力 seam Agent Note](../../.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md) 中。token 测量仍是可复用的 LLM(大语言模型)家族服务;基于模板或模型的压缩器可以替换 `compact-basic`,而无需更改计量器、剪枝器、命令或自动调用方。 +后端、可选修剪器和用户命令通过该 seam 组合;token 测量仍是独立的 LLM(大语言模型)家族服务。[压缩能力 seam Agent Note](../../.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md)负责依赖关系的设计原理。 diff --git a/packages/compact/command-compact/README.i18n.yaml b/packages/compact/command-compact/README.i18n.yaml index d03eb278c2..c39570db18 100644 --- a/packages/compact/command-compact/README.i18n.yaml +++ b/packages/compact/command-compact/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/compact/command-compact/README.md -README.md: 1445e76f8328a9ac1c5f9dd43094f1c1cd5d2ad4 -README.zh.md: b7357c0efc83c8ccce709518ac2dd2f5e66bbace +README.md: a32a6aeb9957f0fd5f8cff58b1edbb9bc29a4e3d +README.zh.md: c678f522115d9b0fd414b2f290b3cb54ce690722 diff --git a/packages/compact/command-compact/README.md b/packages/compact/command-compact/README.md index 1445e76f83..a32a6aeb99 100644 --- a/packages/compact/command-compact/README.md +++ b/packages/compact/command-compact/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -Human-facing `/compact` control over [`ctx.compact`](../compact/README.md). The plugin registers one global command through [`ctx.commands`](../../ui/commands/README.md), so every composed command adapter discovers it; the shipped TUI executes it without a model turn. The [queued manual compaction Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-queued-manual-compaction.md) owns the admission, lock, and durability decisions. +Human-facing `/compact` control over [`ctx.compact`](../compact/README.md). The plugin registers one global command through [`ctx.commands`](../../ui/commands/README.md), so every composed command adapter discovers and executes it without a model turn. The [queued manual compaction Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-queued-manual-compaction.md) owns the admission, lock, and durability decisions. ## Command contract @@ -41,7 +41,7 @@ The producer injects `commands` and `compact`. Mount the command registry, one b name: '@deepseek-ai/dsh-command-compact' ``` -The TUI example and CLI host mount it beside `compact-basic`. Automation surfaces that compose no command registry keep automatic compaction only. +The shipped `dsh` base mounts it beside `compact-basic`, and the Web client provides the command adapter. Automation surfaces that compose no command adapter keep automatic compaction only. ## Model Experience diff --git a/packages/compact/command-compact/README.zh.md b/packages/compact/command-compact/README.zh.md index b7357c0efc..c678f52211 100644 --- a/packages/compact/command-compact/README.zh.md +++ b/packages/compact/command-compact/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -通过 [`ctx.compact`](../compact/README.md) 提供面向用户的 `/compact` 压缩(compaction)控制。该插件通过 [`ctx.commands`](../../ui/commands/README.md) 注册一个全局命令,因此组合中的每个命令适配器都能发现它;随附 TUI 无需模型轮次即可执行该命令。[排队手动压缩 Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-queued-manual-compaction.md)拥有接纳、锁与持久性决策。 +通过 [`ctx.compact`](../compact/README.md) 提供面向用户的 `/compact` 压缩(compaction)控制。该插件通过 [`ctx.commands`](../../ui/commands/README.md) 注册一个全局命令,因此组合中的每个命令适配器都能发现并执行它,无需模型轮次。[排队手动压缩 Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-queued-manual-compaction.md)拥有接纳、锁与持久性决策。 ## 命令契约 @@ -41,7 +41,7 @@ busy 结果有意限定在进程范围内:活动的未匹配标记会阻塞, name: '@deepseek-ai/dsh-command-compact' ``` -TUI 示例与 CLI host 将它挂载在 `compact-basic` 旁。未组合命令注册表的自动化接口只保留自动压缩。 +随附 `dsh` 基础配置将它挂载在 `compact-basic` 旁,Web 客户端提供命令适配器。未组合命令适配器的自动化接口只保留自动压缩。 ## 模型体验 diff --git a/packages/context/README.i18n.yaml b/packages/context/README.i18n.yaml index 77176bd6ab..f3acd7f2bf 100644 --- a/packages/context/README.i18n.yaml +++ b/packages/context/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/context/README.md -README.md: b4adb18c912617588711d7a0641426ce2cf28d89 -README.zh.md: d129c63371682ae6fff3ca0c56f5906bdf6cfab3 +README.md: a0b5eef51740b3aa67b9e158a00357f849b8bf57 +README.zh.md: d6ed8050f3a64f740a951883f0dc321ac08e978a diff --git a/packages/context/README.md b/packages/context/README.md index b4adb18c91..a0b5eef517 100644 --- a/packages/context/README.md +++ b/packages/context/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -Product plugins that add model-visible request context without defining a tool. `workspace-context` is included by the default `dsh-agent-spine-demo` bundle and can be disabled through bundle config; `time-context` and `tmux-context` are opt-in, while the standard TUI bundle composes `session-reference` explicitly. +Product plugins that add model-visible request context without defining a tool. `workspace-context` is included by the default `dsh-agent-spine-demo` bundle and can be disabled through bundle config; `time-context`, `tmux-context`, and `session-reference` are opt-in. | Package | Role | ctx key | |---|---|---| diff --git a/packages/context/README.zh.md b/packages/context/README.zh.md index d129c63371..d6ed8050f3 100644 --- a/packages/context/README.zh.md +++ b/packages/context/README.zh.md @@ -1,14 +1,14 @@ -# context/:请求上下文扩展 +# context/ — 请求上下文扩展 [English](README.md) | 中文 -这些产品插件无需定义工具,即可增加模型可见的请求上下文。`workspace-context` 包含在默认的 `dsh-agent-spine-demo` 组合包中,且可通过组合包配置将其禁用;`time-context` 与 `tmux-context` 均需显式启用,标准 TUI 组合包则会显式组合 `session-reference`。 +在不定义工具的情况下添加面向模型请求上下文的产品插件。`workspace-context` 包含在默认 `dsh-agent-spine-demo` 组合包中,可通过组合包配置禁用;`time-context`、`tmux-context` 和 `session-reference` 需主动启用。 | 包 | 职责 | ctx key | |---|---|---| | [`session-reference/`](session-reference/README.md) | 其他会话的有界快照 | `ctx.sessionReferences` | -| [`time-context/`](time-context/README.md) | 当前时间与已用时上下文 | — | +| [`time-context/`](time-context/README.md) | 当前时间与耗时上下文 | — | | [`tmux-context/`](tmux-context/README.md) | tmux 位置上下文 | — | -| [`workspace-context/`](workspace-context/README.md) | 工作区指令上下文 | — | +| [`workspace-context/`](workspace-context/README.md) | workspace 指令上下文 | — | -[`workspace-context` 决策记录](../../.agents/notes/implemented/feature/2026-06-24-workspace-context.md)解释了每个 agent(智能体)和会话各自隔离的方式,以及相应的生命周期拆分。 +[`workspace-context` 决策记录](../../.agents/notes/implemented/feature/2026-06-24-workspace-context.md)解释其逐 agent(智能体)/会话隔离和生命周期拆分。 diff --git a/packages/context/session-reference/README.i18n.yaml b/packages/context/session-reference/README.i18n.yaml index fff2b196ad..ec21c9fd89 100644 --- a/packages/context/session-reference/README.i18n.yaml +++ b/packages/context/session-reference/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/context/session-reference/README.md -README.md: 66df45b18df6d859239c8d3216d6c8b9fa61ab23 -README.zh.md: b59a98b86baf88f53429bc2409ad1776b1638736 +README.md: b1c76ef6865d6782941e13c2698fe76e38fb3abd +README.zh.md: cd083c9d5a49f2bb6e2358bbfbc10d8d299c2783 diff --git a/packages/context/session-reference/README.md b/packages/context/session-reference/README.md index 66df45b18d..b1c76ef686 100644 --- a/packages/context/session-reference/README.md +++ b/packages/context/session-reference/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -`ctx.sessionReferences` prepares bounded, read-only snapshots of other sessions as sourced model-facing context. It consumes `ctx.sessionQuery` and the backend-independent compact checkpoint marker; SQLite FTS is not required. The standard TUI bundle mounts it, while other hosts may call the service directly. +`ctx.sessionReferences` prepares bounded, read-only snapshots of other sessions as sourced model-facing context. It consumes `ctx.sessionQuery` and the backend-independent compact checkpoint marker; SQLite FTS is not required. Hosts that support cross-session mentions may opt into the service. ## Public API @@ -14,7 +14,7 @@ English | [中文](README.zh.md) Preparation calls `ctx.sessionQuery.readSurface()` once per distinct source and never rereads it after enqueue. It projects only direct-user `user/message`, direct-user `steering/message`, assistant text, and `user/message` checkpoints carrying the canonical `dsh-compact` source marker from the folded current surface. For a source prompt that already contains baked prefix context, projection reads only its model-hidden display content, preventing recursive snapshot propagation. Shadowed pre-compaction events, tools, reasoning, context, plugin-generated user messages other than marked compact checkpoints, and unfinished assistant chunks are excluded. A compacted source therefore contributes its latest checkpoint plus retained later conversation, not restored shadowed text. -The context source is `{ kind: 'session-reference', version: 1, references }`; each reference records its source id and label, capture seq, compact presence, retained/omitted message counts, omitted UTF-8 bytes, and truncation state. The standard TUI preserves admission ownership without attaching context to the generic inbox record: outside the next-step acceptance window, a one-shot `agent/prompt-submit` wrapper adds the snapshot only to an allowed decision; during prompt admission or an open turn, `inject()` and `steer()` stage beside each other for the same safe boundary. The target log therefore records a sourced context `user/message` followed by the readable direct `user/message` or `steering/message`. Later source mutation, compaction, or deletion cannot change target replay. +The context source is `{ kind: 'session-reference', version: 1, references }`; each reference records its source id and label, capture seq, compact presence, retained/omitted message counts, omitted UTF-8 bytes, and truncation state. A host captures the snapshot before submitting the associated prompt and appends it as sourced context at the same safe admission boundary. The target log therefore records the snapshot separately from the readable direct message. Later source mutation, compaction, or deletion cannot change target replay. ## Configuration diff --git a/packages/context/session-reference/README.zh.md b/packages/context/session-reference/README.zh.md index b59a98b86b..cd083c9d5a 100644 --- a/packages/context/session-reference/README.zh.md +++ b/packages/context/session-reference/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -`ctx.sessionReferences` 会把其他会话准备为有界、只读快照,作为带来源信息、面向模型的上下文。它消费 `ctx.sessionQuery` 与后端无关的 compact 检查点标记;不需要 SQLite FTS。标准 TUI bundle 会装载它,其他宿主也可直接调用该服务。 +`ctx.sessionReferences` 会把其他会话准备为有界、只读快照,作为带来源信息、面向模型的上下文。它消费 `ctx.sessionQuery` 与后端无关的 compact 检查点标记;不需要 SQLite FTS。支持跨会话 mention 的宿主可以主动启用该服务。 ## 公开 API @@ -14,7 +14,7 @@ 准备阶段会对每个不同源调用一次 `ctx.sessionQuery.readSurface()`,入队后绝不重读。它仅投影折叠后当前表层中的用户直接发出的 `user/message`、用户直接发出的 `steering/message`、assistant 文本,以及 `user/message` 检查点;这类检查点携带规范 `dsh-compact` 源标记。对于已经包含固化前缀上下文的源提示词,投影只读取其对模型隐藏的显示内容,以防止快照递归传播。已遮蔽的压缩(compaction)前事件、工具、推理(reasoning)、上下文、除已标记 compact 检查点外的插件生成 user 消息,以及未完成的 assistant 分片均会被排除。因此,已压缩源只会提供最新检查点及其后保留的会话内容,不会还原已遮蔽的文本。 -上下文源为 `{ kind: 'session-reference', version: 1, references }`;每条引用会记录其源 id 与 label、捕获 seq、是否存在 compact、已保留/已省略消息数、已省略 UTF-8 字节数与截断状态。标准 TUI 在不把上下文附加到通用 inbox 记录的情况下保留接纳归属:next-step 接收窗口之外,一次性 `agent/prompt-submit` 包装层只为获准决策添加快照;提示词接纳期间或轮次打开时,`inject()` 与 `steer()` 会并排暂存到同一安全边界。目标日志因此会先记录一条带来源信息的上下文 `user/message`,再记录可读的直接 `user/message` 或 `steering/message`。后续源变更、压缩或删除都无法改变目标回放。 +上下文源为 `{ kind: 'session-reference', version: 1, references }`;每条引用会记录其源 id 与 label、捕获 seq、是否存在 compact、已保留/已省略消息数、已省略 UTF-8 字节数与截断状态。宿主会在提交关联提示词之前捕获快照,并在同一安全接纳边界把它追加为带来源信息的上下文。因此,目标日志会把快照与可读的直接消息分开记录。后续源变更、压缩或删除都无法改变目标回放。 ## 配置 diff --git a/packages/context/tmux-context/README.i18n.yaml b/packages/context/tmux-context/README.i18n.yaml index 804a08bc3b..c8489fed39 100644 --- a/packages/context/tmux-context/README.i18n.yaml +++ b/packages/context/tmux-context/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/context/tmux-context/README.md -README.md: 053206797398aa952522298e82992a7320daf74c -README.zh.md: 444d144da362b196e0d1025739a04f5f6c18aa92 +README.md: 801a1f42624b9ab96eb86ec4fec4467c84021561 +README.zh.md: 23510883ff35b35cb69b71da42720d01b574fcf3 diff --git a/packages/context/tmux-context/README.md b/packages/context/tmux-context/README.md index 0532067973..801a1f4262 100644 --- a/packages/context/tmux-context/README.md +++ b/packages/context/tmux-context/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -Opt-in durable context naming the tmux session, window, and pane this agent process runs in, plus the window's pane-tree layout. Sampled once per turn during model-request preparation. The shipped TUI mounts it; `dsh-agent-spine-demo` and the Web/headless surfaces do not. Decision record: [the tmux-context Agent Note](../../../.agents/notes/implemented/feature/2026-07-27-tmux-location-context.md). +Opt-in durable context naming the tmux session, window, and pane this agent process runs in, plus the window's pane-tree layout. It is sampled once per turn during model-request preparation and is not part of the shipped Web/headless composition. Decision record: [the tmux-context Agent Note](../../../.agents/notes/implemented/feature/2026-07-27-tmux-location-context.md). ## Config diff --git a/packages/context/tmux-context/README.zh.md b/packages/context/tmux-context/README.zh.md index 444d144da3..23510883ff 100644 --- a/packages/context/tmux-context/README.zh.md +++ b/packages/context/tmux-context/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -可选启用的持久上下文,记录本 agent 进程所在的 tmux session、window、pane,以及该 window 的 pane 树布局。在准备模型请求时每轮采样一次。已交付的 TUI 会挂载它;`dsh-agent-spine-demo` 与 Web/无头界面均不挂载。决策记录见:[tmux-context Agent Note](../../../.agents/notes/implemented/feature/2026-07-27-tmux-location-context.md)。 +可选启用的持久上下文,记录本 agent(智能体)进程所在的 tmux session、window、pane,以及该 window 的 pane 树布局。在准备模型请求时每轮采样一次;随附 Web/无头组合不包含它。决策记录见:[tmux-context Agent Note](../../../.agents/notes/implemented/feature/2026-07-27-tmux-location-context.md)。 ## 配置 diff --git a/packages/cordis/README.i18n.yaml b/packages/cordis/README.i18n.yaml index 67c5200fe9..2eacddb52c 100644 --- a/packages/cordis/README.i18n.yaml +++ b/packages/cordis/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/cordis/README.md -README.md: 485a6ce7858a77507c07b76138127faa411b354b -README.zh.md: d6fb91d8426f4a4d9dae0f948001f4fba4014757 +README.md: e8b0790f911d1acc1216d412191ca967e0e1ac27 +README.zh.md: c452f9a7ef69e7f5af8900153271d500f19f9e47 diff --git a/packages/cordis/README.zh.md b/packages/cordis/README.zh.md index d6fb91d842..c452f9a7ef 100644 --- a/packages/cordis/README.zh.md +++ b/packages/cordis/README.zh.md @@ -1,10 +1,10 @@ -# packages/cordis:Cordis 运行时集成 +# packages/cordis — Cordis 运行时集成 [English](README.md) | 中文 -这些插件把 harness 自有格式集成到 Cordis 运行时:包括自指的模型工具集,以及受限的 repository 插件运行时。 +把 Harness 所有的格式与 Cordis 运行时集成的插件:自指的面向模型工具集,以及受限的 repository Plugin 运行时。 -| 包 | 角色 | ctx 键 | +| 包 | 职责 | ctx key | |---|---|---| -| [`tool-cordis/`](tool-cordis/README.md) | `cordis_inspect`/`cordis_mount`/`cordis_unmount` 工具:读取当前进程运行时,并在一个自有分组 fiber 下管理内存中的临时插件 | 注册到 `ctx.tools` | -| [`repository-plugin/`](repository-plugin/README.md) | 通过 DSH 自有子插件准备并挂载静态 repository skill(技能)与通用 `.mcp.json` server | 注册一个 loader builtin | +| [`tool-cordis/`](tool-cordis/README.md) | 面向模型的运行时检查和临时插件工具 | 注册到 `ctx.tools` | +| [`repository-plugin/`](repository-plugin/README.md) | repository skill 与 MCP 组合 | 注册一个 Loader builtin | diff --git a/packages/cordis/repository-plugin/README.i18n.yaml b/packages/cordis/repository-plugin/README.i18n.yaml index e8dd5ddbf5..ea7a1305b3 100644 --- a/packages/cordis/repository-plugin/README.i18n.yaml +++ b/packages/cordis/repository-plugin/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/cordis/repository-plugin/README.md -README.md: 0ba1ce86d99a12e0f94e7a39fd3ae44dc29889a7 -README.zh.md: d3046db8155a908dd4afd8f50a5ab4774085a40f +README.md: 2555876734ddcab7b0bc0f25780010bef0c97a01 +README.zh.md: 41341f7d7ed92ac071dde274a0328784bc2888d4 diff --git a/packages/cordis/repository-plugin/README.md b/packages/cordis/repository-plugin/README.md index 0ba1ce86d9..2555876734 100644 --- a/packages/cordis/repository-plugin/README.md +++ b/packages/cordis/repository-plugin/README.md @@ -30,7 +30,7 @@ Place an ordinary package in the repository's `.dsh-plugin` directory: ## Standalone app configuration -The shipped `dsh` TUI, Web, and headless trees contain an empty `repository-plugins` row. A standalone user enables exact GitHub generations by replacing that row's config in `$DSH_HOME/config.yaml` (default `~/.dsh/config.yaml`): +The shipped `dsh` base used by raw-config, Web, and headless modes contains an empty `repository-plugins` row. A Web or headless user enables exact GitHub generations by replacing that row's config in `$DSH_HOME/config.yaml` (default `~/.dsh/config.yaml`); a raw-config deployment patches the same row in its explicit overlay: ```yaml - id: repository-plugins @@ -43,7 +43,7 @@ The shipped `dsh` TUI, Web, and headless trees contain an empty `repository-plug Each source must use `github:owner/repository#`. Omitting `&path:` selects `/.dsh-plugin`; an explicit path is absolute within the repository and must end in `.dsh-plugin`. A commit ref gives the clearest immutable identity, while tags and branches remain accepted exact config values. `cacheDir` may override the default `$DSH_HOME/cache/repository-plugins` cache root. -The TUI and Web watch `config.yaml` through Cordis HMR. A valid source-list change installs and swaps the complete repository Plugin generation; a failed fetch, prepare, import, or Plugin application keeps the last good tree and broadcasts `hmr/config-update-failed(filename, error)`. Headless runs consume the file only at startup. An identical source string permanently reuses its prepared cache entry, so selecting changed code requires a ref, path, or other source-config change. App integration rationale: [config-only repository Plugins Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-config-only-repository-plugins.md). +Web watches `config.yaml` through Cordis HMR. A valid source-list change installs and swaps the complete repository Plugin generation; a failed fetch, prepare, import, or Plugin application keeps the last good tree and broadcasts `hmr/config-update-failed(filename, error)`. Headless reads the file only at startup, and raw-config mode reads only its explicit overlay. An identical source string permanently reuses its prepared cache entry, so selecting changed code requires a ref, path, or other source-config change. App integration rationale: [config-only repository Plugins Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-config-only-repository-plugins.md). ## Preparation diff --git a/packages/cordis/repository-plugin/README.zh.md b/packages/cordis/repository-plugin/README.zh.md index d3046db815..41341f7d7e 100644 --- a/packages/cordis/repository-plugin/README.zh.md +++ b/packages/cordis/repository-plugin/README.zh.md @@ -30,7 +30,7 @@ ## 独立应用配置 -已交付的 `dsh` TUI、Web 和无头配置树包含一个空的 `repository-plugins` 配置项。独立用户只需在 `$DSH_HOME/config.yaml`(默认 `~/.dsh/config.yaml`)中替换该配置项的配置,即可启用精确指定的 GitHub generation: +随附 `dsh` 中供原始配置、Web 与无头模式使用的基础配置包含一个空 `repository-plugins` 配置项。Web 或无头用户可在 `$DSH_HOME/config.yaml`(默认 `~/.dsh/config.yaml`)中替换该配置项的配置,以启用精确指定的 GitHub generation;原始配置部署则在显式 overlay 中 patch 同一配置项: ```yaml - id: repository-plugins @@ -43,7 +43,7 @@ 每个源都必须采用 `github:owner/repository#`。省略 `&path:` 时选择 `/.dsh-plugin`;显式路径是仓库内的绝对路径,并且必须以 `.dsh-plugin` 结尾。commit ref 提供最清晰的不可变身份;tag 和 branch 仍可作为精确配置值使用。`cacheDir` 可覆盖默认缓存根 `$DSH_HOME/cache/repository-plugins`。 -TUI 和 Web 通过 Cordis HMR(热模块替换)监视 `config.yaml`。有效的源列表变更会安装并替换整套仓库插件 generation;拉取、准备、导入或插件应用失败时,最后一个可用树保持运行,并广播 `hmr/config-update-failed(filename, error)`。无头运行只在启动时使用该文件。相同的源字符串会永久复用其已准备缓存条目,因此必须改变 ref、路径或其他源配置,才能选择发生变化的代码。应用集成依据见[仅凭配置接入仓库插件的 Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-config-only-repository-plugins.md)。 +Web 通过 Cordis HMR(热模块替换)监视 `config.yaml`。有效的源列表变更会安装并替换整套 repository Plugin generation;拉取、准备、导入或插件应用失败时,最后一个可用树保持运行,并广播 `hmr/config-update-failed(filename, error)`。无头模式只在启动时读取该文件,原始配置模式只读取其显式 overlay。相同的源字符串会永久复用其已准备缓存条目,因此必须改变 ref、路径或其他源配置,才能选择发生变化的代码。应用集成依据见[仅凭配置接入 repository Plugin 的 Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-config-only-repository-plugins.md)。 ## 准备阶段 diff --git a/packages/core/README.i18n.yaml b/packages/core/README.i18n.yaml index 23e6e7d2b9..d43caa011e 100644 --- a/packages/core/README.i18n.yaml +++ b/packages/core/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/core/README.md -README.md: 63ca0f7711c2c9deb193d5a9a574ff909a04602e -README.zh.md: d4c1a85ef7dd33be5419245464d83da24756443a +README.md: d51ef73f7f545920f8cd527b2b0d597305ae86d0 +README.zh.md: 729f41fafb6b9585a66e58fe8d6ac63c1f235acc diff --git a/packages/core/README.zh.md b/packages/core/README.zh.md index d0f16fb033..729f41fafb 100644 --- a/packages/core/README.zh.md +++ b/packages/core/README.zh.md @@ -1,18 +1,18 @@ -# core/:产品 API 主干 +# core/ — 产品 API 主干 [English](README.md) | 中文 -会话日志、系统提示词组装、工具注册表、agent(智能体)词汇,以及构成 harness 默认控制主干的具体循环。这些是 **产品** 包,插件和消费方以其稳定接口为基础构建。 +构成 harness 默认控制主干的会话日志、系统提示词组装、工具注册表、agent(智能体)词汇和具体循环。这些是**产品**包,即插件和消费方构建所依赖的稳定 surface。 -| 包 | 角色 | ctx 键 | +| 包 | 职责 | ctx key | |---|---|---| -| [`scope/`](scope/README.md) | 带作用域的上下文注册原语 | 库,无 ctx 键 | -| [`session/`](session/README.md) | 事件溯源会话日志与内存存储 | `ctx.sessions` | -| [`system-prompt/`](system-prompt/README.md) | 提示词与工具 schema 组装注册表 | `ctx.systemPrompt` | -| [`tools/`](tools/README.md) | 带作用域的工具注册表与执行流水线 | `ctx.tools` | -| [`agent/`](agent/README.md) | Agent 接口、注册表与事件词汇 | `ctx.agents` | -| [`agent-loop/`](agent-loop/README.md) | 默认的具体 agent 驱动器 | `ctx.agentLoop` | +| [`scope/`](scope/README.md) | 作用域上下文注册原语 | 库,不使用 ctx key | +| [`session/`](session/README.md) | 事件溯源会话日志和内存存储 | `ctx.sessions` | +| [`system-prompt/`](system-prompt/README.md) | 提示词和工具 schema 组装注册表 | `ctx.systemPrompt` | +| [`tools/`](tools/README.md) | 作用域工具注册表和执行流水线 | `ctx.tools` | +| [`agent/`](agent/README.md) | Agent 接口、注册表和事件词汇 | `ctx.agents` | +| [`agent-loop/`](agent-loop/README.md) | 默认具体 agent 驱动器 | `ctx.agentLoop` | -`scope` 提供共享的作用域原语。`agent` 拥有公开 seam,`agent-loop` 则是其默认实现;扩展插件依赖 seam,使驱动器保持可替换。 +`scope` 提供共享作用域原语。`agent` 负责公开 seam,`agent-loop` 是其默认实现;扩展插件依赖该 seam,从而保持驱动器可替换。 -可运行组合属于 [`examples/agent-spine-demo`](../examples/agent-spine-demo/README.md);本组只负责可替换的主干组件。 +可运行组合属于 [`examples/agent-spine-demo`](../examples/agent-spine-demo/README.md);该分组只负责可替换的主干组件。 diff --git a/packages/examples/agent-spine-demo/README.i18n.yaml b/packages/examples/agent-spine-demo/README.i18n.yaml index 8cbf8ce122..3398b7296d 100644 --- a/packages/examples/agent-spine-demo/README.i18n.yaml +++ b/packages/examples/agent-spine-demo/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/examples/agent-spine-demo/README.md -README.md: 7ea2f4afbe5d0bea62cf0fc2c7ecdd1496d20aef -README.zh.md: cb4202ac0e72db7c85967425671144e743f31b62 +README.md: d1c3c28183956b4f66db7ed4a6f3a08588b5352c +README.zh.md: 936a317d23bf942802ecdd87f5ea4005974e4125 diff --git a/packages/examples/agent-spine-demo/README.md b/packages/examples/agent-spine-demo/README.md index 7ea2f4afbe..d1c3c28183 100644 --- a/packages/examples/agent-spine-demo/README.md +++ b/packages/examples/agent-spine-demo/README.md @@ -47,7 +47,7 @@ The spine is everything COMMON to every front door. The swappable and front-door - **model-backed session-title providers** — the bundle mounts the fallback service with overridable example limits (5 words, 40 fallback bytes, 80 accepted-title bytes); a leaf may opt into exactly one first-message or all-messages LLM provider. - **the bash executor** — the bundle ships `tool-bash` (the consumer schema); the leaf provides `ctx.bash` (`bash-local` or a sandboxed impl). - **non-local skill providers** — the bundle ships the skill registry, the local filesystem provider, and the `skill` tool; deployments can add other providers such as embedded or remote catalogs as siblings. -- **front-door + per-app infra** — the terminal TUI or ACP automation transport and `hmr`. App packages ([`dsh-cli-demo`](../cli-demo/README.md), [`dsh-acp-demo`](../acp-demo/README.md)) own those choices. `timer` is in the spine because it is common and stdout-silent; front doors own stdout and remain outside. +- **front-door + per-app infrastructure** — headless, ACP, and JSON-RPC app packages own transport, stdout, and reload choices. `timer` stays in the spine because it is common and stdout-silent. This is the [interface/implementation/consumer seam](../../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md) raised to the composition level: the bundle owns the shared spine, the leaf owns the backends, the app package owns the front door. @@ -59,7 +59,7 @@ import type { Config } from '@deepseek-ai/dsh-agent-spine-demo' // workspaceContext requires { maxBytes } or false; the other owner schemas supply defaults. ``` -The bundle FORWARDS each field to the child that owns it: `agents` and `maxParallelToolCalls` to `agent-loop` (`agents` defaults to `[]`; the cap defaults there), so each app supplies its own pre-created agents — TUI and headless apps pre-create `main`, while the ACP app creates agents on demand at `session/new`; `includeHarnessIdentity`, `persona`, and `toolOrder` to `dsh-system-prompt`; `tools` to the tool registry for its presentation mode; `sessionTitle` to the fallback title service; `skills.registry`, `skills.local`, and `skills.tool` to the skill registry, local provider, and model-facing consumer; the required `workspaceContext` choice to `dsh-workspace-context` (`{ maxBytes }` enables loading and `false` disables it); `invariants` to the invariant service; and `toolBash`/`toolTasks` to the two model-facing tool plugins the bundle owns. It always mounts `dsh-llm-retry`, while each leaf adapter owns its nested `retryPolicy`. Omitted `sessionTitle` uses the explicit example policy of 5 words, 40 fallback bytes, and 80 accepted-title bytes. A `goals` object opts into the persisted domain, model tools, and same-session driver while forwarding `goals.domain` and `goals.tool` to their owners; omission or `false` leaves the stack absent so headless callers retain one-turn settlement. Set `skills.enabled: false` to omit both the local provider and model-facing skill tool, set `toolBash: false` when another plugin owns the `bash` tool name, and set `toolTasks: false` to retain the task service for foreground producers without exposing `task_output` / `task_list` / `task_kill`. It resolves `dshHome` once through [`@deepseek-ai/dsh-paths`](../../util/paths/README.md) and forwards that absolute value to the shared `bash-env` managed environment and enabled local skill discovery. An absent top-level `dshHome` adopts `skills.local.dshHome`; supplying both with different resolved paths fails loudly. `toolBash.enableRunInBackground` controls only the bundled bash producer; independently loaded producers keep their own config. Workspace instructions register before the skill catalog so their session-prefix message renders first. App packages use `pickSpineConfig()` to copy only these bundle-owned fields. +The bundle forwards each field to the child that owns it. App packages supply any pre-created agents: headless and JSON-RPC compositions create `main`, while the ACP app creates agents on demand at `session/new`. Prompt, tool, title, skill, workspace-context, invariant, goal, and task settings retain the schemas and defaults documented by their owning packages. `pickSpineConfig()` copies only fields owned by this bundle, and conflicting `dshHome` values fail during composition. For example, `{ invariants: { enabled: true, package_allowlist: ['^@deepseek-ai/dsh-'], package_blocklist: ['agent-loop$'] } }` keeps the package-owned companions mounted but suppresses the blocked owner. Blocklist matches override allowlist matches; see [`dsh-invariants`](../../support/invariants/README.md) for regex and lifecycle rules. diff --git a/packages/examples/agent-spine-demo/README.zh.md b/packages/examples/agent-spine-demo/README.zh.md index cb4202ac0e..936a317d23 100644 --- a/packages/examples/agent-spine-demo/README.zh.md +++ b/packages/examples/agent-spine-demo/README.zh.md @@ -47,7 +47,7 @@ - **基于模型的会话标题提供方**:组合包挂载带可覆盖示例限制的后备服务(5 个词、40 个后备字节、80 个可接受标题字节);叶节点可以恰好选用一个首消息或全消息 LLM 提供方。 - **bash 执行器**:组合包交付 `tool-bash`(消费方 schema);叶节点提供 `ctx.bash`(`bash-local` 或沙箱化实现)。 - **非本地 skill 提供方**:组合包交付 skill 注册表、本地文件系统提供方和 `skill` 工具;部署可以把嵌入式目录或远程目录等其他提供方作为同级插件添加。 -- **前端入口与各应用基础设施**:终端 TUI 或 ACP(Agent Client Protocol)自动化传输,以及 `hmr`。应用包([`dsh-cli-demo`](../cli-demo/README.md)、[`dsh-acp-demo`](../acp-demo/README.md))拥有这些选择。`timer` 位于主干中,因为它是共有组件且不写 stdout;前端入口拥有 stdout,因此留在组合包外。 +- **前端入口与各应用基础设施**:无头、ACP(Agent Client Protocol)和 JSON-RPC 应用包负责传输、stdout 与重新加载选择。`timer` 保留在主干中,因为它是共有组件且不写 stdout。 这把[接口/实现/消费方 seam](../../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md) 提升到组合层:组合包拥有共享主干,叶节点拥有后端,应用包拥有前端入口。 @@ -59,7 +59,7 @@ import type { Config } from '@deepseek-ai/dsh-agent-spine-demo' // workspaceContext requires { maxBytes } or false; the other owner schemas supply defaults. ``` -组合包将每个字段转发给拥有它的子节点:`agents` 与 `maxParallelToolCalls` 交给 `agent-loop`(`agents` 默认为 `[]`,上限在该处默认),因此每个应用提供自己的预创建 agent;TUI 和无头应用预创建 `main`,ACP 应用则在 `session/new` 按需创建 agent;`includeHarnessIdentity`、`persona` 与 `toolOrder` 交给 `dsh-system-prompt`;`tools` 交给工具注册表以配置呈现模式;`sessionTitle` 交给后备标题服务;`skills.registry`、`skills.local` 与 `skills.tool` 分别交给 skill 注册表、本地提供方和面向模型的消费方;必填的 `workspaceContext` 选择交给 `dsh-workspace-context`(`{ maxBytes }` 启用加载,`false` 禁用);`invariants` 交给不变式服务;`toolBash`/`toolTasks` 交给组合包拥有的两个面向模型工具插件。组合包始终挂载 `dsh-llm-retry`,而每个叶节点适配器拥有自己的嵌套 `retryPolicy`。省略 `sessionTitle` 时采用显式示例策略:5 个词、40 个后备字节、80 个可接受标题字节。`goals` 对象会选用持久化领域、模型工具和同会话 Goal Round 驱动器,并将 `goals.domain` 与 `goals.tool` 转发给各自拥有者;省略或设为 `false` 会让整个栈缺席,使无头调用方继续以单轮次结算。设置 `skills.enabled: false` 会同时省略本地提供方和面向模型的 skill 工具;当另一个插件拥有 `bash` 工具名时设置 `toolBash: false`;设置 `toolTasks: false` 会保留供前台生产方使用的任务服务,但不公开 `task_output`/`task_list`/`task_kill`。它对 `dshHome` 只解析一次,解析通过 [`@deepseek-ai/dsh-paths`](../../util/paths/README.md) 完成,并将所得绝对值转发给共享 `bash-env` 的托管环境和已启用的本地 skill 发现。顶层 `dshHome` 缺席时采用 `skills.local.dshHome`;两者同时提供但解析后的路径不同会明确失败。`toolBash.enableRunInBackground` 只控制内置 bash 生产方;独立加载的生产方保留各自配置。工作区指令先于 skill 目录注册,因此其会话前缀消息先渲染。应用包使用 `pickSpineConfig()`,只复制这些由组合包拥有的字段。 +组合包将每个字段转发给拥有它的子节点。应用包提供预创建的 agent:无头和 JSON-RPC 组合会创建 `main`,ACP 应用则在 `session/new` 按需创建 agent。提示词、工具、标题、skill、workspace context、不变式、goal 和任务设置沿用其所属包记录的 schema 与默认值。`pickSpineConfig()` 只复制该组合包拥有的字段,`dshHome` 值冲突会在组合时失败。 例如,`{ invariants: { enabled: true, package_allowlist: ['^@deepseek-ai/dsh-'], package_blocklist: ['agent-loop$'] } }` 会让包拥有的配套插件保持挂载,但抑制被阻止的拥有者。Blocklist 匹配优先于 allowlist 匹配;正则表达式与生命周期规则见 [`dsh-invariants`](../../support/invariants/README.md)。 diff --git a/packages/examples/jsonrpc-demo/README.i18n.yaml b/packages/examples/jsonrpc-demo/README.i18n.yaml index 4532a72e66..4e7935dd3a 100644 --- a/packages/examples/jsonrpc-demo/README.i18n.yaml +++ b/packages/examples/jsonrpc-demo/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/examples/jsonrpc-demo/README.md -README.md: 75e9e3943982c08e53afdbb73d1e9085b2e332bc -README.zh.md: d7732582e0ab62cd3a61dfe3d8e4192c06b18992 +README.md: 2cc496da3e22baf9e87afe4c3ca183ae3f16a5ae +README.zh.md: 98d397b3c346ee47e9a667b2ccec0179f009a05b diff --git a/packages/fs/README.i18n.yaml b/packages/fs/README.i18n.yaml index d4ff2d5539..e435f166ad 100644 --- a/packages/fs/README.i18n.yaml +++ b/packages/fs/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/fs/README.md -README.md: b5e0ac9d1c0c550eb372b8a66fc6358711fddc07 -README.zh.md: 7a7cafb814a1f6c4a79671cf23c7d4577ac2b716 +README.md: 108d7d862a1dc6307268e2a93fa00789c952e440 +README.zh.md: 8b037cc3192bf6ceb0f0671d62911a8e035db24c diff --git a/packages/fs/README.zh.md b/packages/fs/README.zh.md index 7a7cafb814..8b037cc319 100644 --- a/packages/fs/README.zh.md +++ b/packages/fs/README.zh.md @@ -1,21 +1,17 @@ -# fs/:文件系统能力族 +# fs/ - 文件系统能力家族 [English](README.md) | 中文 -文件系统栈包括:提供方 seam(文本 I/O 与带可选版本防护的原子变更)、本地实现、策略门禁插件(已观察状态、编辑前读取、版本防护的写入/编辑)、面向模型的文件工具与执行器,以及基于 bash 的发现工具。全部都是**产品**包。 +文件系统能力家族:提供方 seam、可互换后端、策略和面向模型工具。这些全是**产品**包。 -| 包 | 角色 | ctx 键 | +| 包 | 职责 | ctx key | |---|---|---| -| `fs/` | 提供方 seam:文本 I/O 与原子变更原语(可选版本防护);拥有 `fs/*` 策略事件 | `ctx.fs` | -| `fs-local/` | 本地文件系统 `FileSystem` 实现 | (注册 `ctx.fs`) | -| `fs-sandbox/` | 强制沙箱的 `FileSystem`:扩展 `fs-local`,并按每次调用的模式与工作区根目录策略约束写入/编辑(只读模式拒绝,工作区写入模式限制在会话工作区与临时根目录内);读取则直接放行 | (注册 `ctx.fs`) | -| `fs-policy/` | 策略门禁插件:通过 `fs/*` 事件门禁提供已观察状态、编辑前读取和版本防护的写入/编辑 | (无服务,仅有 `fs/*` 监听器) | -| `tool-fs/` | 面向模型的 `read`/`write`/`edit` 工具以及执行器(通过 `ctx.fs` 读取,拥有读取窗口逻辑,分派 `fs/*`);为会话 cwd 相对路径保留文件系统语义,并在已挂载的 `ctx.fs` 实施约束时声明沙箱升权字段 | (注册到 `ctx.tools`) | -| `tool-fs-search/` | 面向模型的 `glob`/`grep` 发现工具;当 `rg` 位于 bash 执行器 `PATH` 上时注册,通过 `ctx.bash` 运行固定 ripgrep 命令,而不是使用 `ctx.fs` 提供方方法 | (注册到 `ctx.tools`) | -| `tool-str-replace-editor/` | 基于 `ctx.fs` 提供查看/创建/唯一字面量替换/按行插入的模型可见 `str_replace_editor` | (注册到 `ctx.tools`) | +| [`fs/`](fs/README.md) | 文件系统提供方 seam 和策略事件词汇 | `ctx.fs` | +| [`fs-local/`](fs-local/README.md) | 本地文件系统后端 | 注册 `ctx.fs` | +| [`fs-sandbox/`](fs-sandbox/README.md) | 强制执行沙箱的后端 | 注册 `ctx.fs` | +| [`fs-policy/`](fs-policy/README.md) | 已观察状态和修改策略 | `fs/*` 监听器 | +| [`tool-fs/`](tool-fs/README.md) | 面向模型的文件工具 | 注册到 `ctx.tools` | +| [`tool-fs-search/`](tool-fs-search/README.md) | 基于进程的发现工具 | 注册到 `ctx.tools` | +| [`tool-str-replace-editor/`](tool-str-replace-editor/README.md) | 面向模型的字符串替换编辑器 | 注册到 `ctx.tools` | -接口位于 `fs/fs/`。沙箱化、远程或限定项目作用域的文件系统后端可以替换 `fs-local`,而无需更改 seam、策略门禁或面向模型的工具 schema;`fs-sandbox` 是第一个这样的替代实现(基于共享沙箱模式的进程内路径围栏;见[跨能力族 fs 沙箱 Agent Note](../../.agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.md))。策略(`fs-policy/`)是一个只通过 `fs/*` 事件门禁参与的插件,不是工具注入的服务;因此移除它只会使策略失效,留下不受约束的裸提供方,而不会破坏工具。加载 `tool-fs/` 的部署也应加载该插件。模式围栏与编辑前读取门禁彼此正交,可以组合。发现(`tool-fs-search/`)有意不扩展提供方 seam:搜索是在 bash 执行器上运行 `rg`、基于进程的工作流,因此文件系统后端无需承担通用搜索契约;只有当执行器能找到 `rg` 时,其工具才会注册。如果 bash 工作目录与 `read` 根目录是同一工作区,其结果便可供后续读取,这也是其 README 所述的共置部署。 - -## 文件 I/O 不设超时 - -`read`/`write`/`edit` **不** 接受 `timeoutMs`,提供方 seam 也不设置截止期限。这与 bash 和 web(两者使用 [`@deepseek-ai/dsh-timeout`](../util/timeout/README.md))及基于 bash 的 `glob`/`grep` 不同(它们声明的 `timeoutMs` 由 `@deepseek-ai/dsh-timeout-policy` 强制执行):这些工作基于进程运行,截止期限可以实际终止工作。本地系统调用至多只能尽力中止:超时无法强制正在进行的 `fsync`/`rename` 停止,因此这里的截止期限会成为无法兑现承诺的配置项。在此添加截止期限还会在「显式优于隐式」明确禁止的地方引入隐式默认值。两个参考 agent(Claude Code、Codex)出于同一原因都不为文件 I/O 计时;取消仍通过工具执行信号传播,在系统调用边界尽力中止。 +后端可在 `ctx.fs` 后互相替换;策略和工具独立消费该 seam。发现功能仍由进程提供,不扩展提供方契约。子 README 负责围堵、修改、schema 和超时细节。 diff --git a/packages/fs/fs-sandbox/README.i18n.yaml b/packages/fs/fs-sandbox/README.i18n.yaml index 068b33da98..22f232b724 100644 --- a/packages/fs/fs-sandbox/README.i18n.yaml +++ b/packages/fs/fs-sandbox/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/fs/fs-sandbox/README.md README.md: d6070f4971e7531e929e659b6b5ed476a672dc5d -README.zh.md: 9304095c2d077adb657af61f4b2b54e087a252c7 +README.zh.md: 60603c8f89ed1f5251c8e927e17c7e8b71ed58dd diff --git a/packages/fs/fs-sandbox/README.zh.md b/packages/fs/fs-sandbox/README.zh.md index 9304095c2d..60603c8f89 100644 --- a/packages/fs/fs-sandbox/README.zh.md +++ b/packages/fs/fs-sandbox/README.zh.md @@ -40,4 +40,4 @@ - **策略围栏,而非内核边界**:该检查是可信代码处理模型控制的路径,因此解析到系统调用之间残留的 TOCTOU 会被原位重新规范化缩小,但不会消除;对抗性宿主进程不在范围内。不可信代码的内核级隔离仍属于 `ctx.bash`。 - **围栏与 runner 的一致性来自派生,而非断言**:可写集合来自 `writableRoots`,该函数与 Seatbelt profile 共享,并由一致性测试固定;不通过该函数更改可写集合的 runner profile 会发生漂移。 -- **要求 `ctx.sandboxPolicy`**:工具使用它解析每个会话策略,后端用它处理无 agent 调用的回退;未组合该服务时,后端不会实施约束。 +- **要求 `ctx.sandboxPolicy`**:工具使用它解析每个会话策略,后端用它处理无 agent(智能体)调用的回退;未组合该服务时,后端不会实施约束。 diff --git a/packages/fs/fs/README.i18n.yaml b/packages/fs/fs/README.i18n.yaml index 785a193a2f..ddf57a98d0 100644 --- a/packages/fs/fs/README.i18n.yaml +++ b/packages/fs/fs/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/fs/fs/README.md -README.md: 9e6c954abad124fb2b30ebc01368a55746752013 -README.zh.md: c72b1f9a83485ca9c188344ba7a345c889951c7b +README.md: 6c80cc22f6f28e792c3458df3390b241b51d8202 +README.zh.md: 4772d799221efbfff9667cbc8b9e1df6af07dcff diff --git a/packages/fs/fs/README.zh.md b/packages/fs/fs/README.zh.md index c72b1f9a83..4772d79922 100644 --- a/packages/fs/fs/README.zh.md +++ b/packages/fs/fs/README.zh.md @@ -4,16 +4,7 @@ **文件系统提供方 seam**:抽象 `FileSystem` 服务(`ctx.fs`),定义后端提供的存储原语,包括路径解析、stat 元数据、不跟随链接的路径元数据、读取/流式读取文本、列出目录、原子写入和应用字面量编辑,但不规定实现方式。两个变更操作都**可选**接收版本防护,因此 `ctx.fs` 本身就是完整且不受约束的文本存储 seam。本包还拥有由工具分派、策略插件监听的 `fs/*` 策略事件词汇。 -本包是四层文件系统栈中的提供方 seam 层;该拆分使每个关注点可以独立演进和替换(见[能力 seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)、[文件系统能力 seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-17-filesystem-capability-seam.md)、[拆分文件系统 seam Agent Note](../../../.agents/notes/implemented/simplification/2026-06-26-fsspec-style-fs-seam.md)和[文件上下文事件门禁 Agent Note](../../../.agents/notes/implemented/architecture/2026-06-26-file-context-as-event-gate.md)): - -| 层 | 包 | 角色 | -|---|---|---| -| 工具/执行器 | `@deepseek-ai/dsh-tool-fs` | 面向模型的 `read`/`write`/`edit` schema、读取窗口和文本渲染;通过 `ctx.fs` 读取/写入/编辑,并分派 `fs/*` 事件 | -| 策略 | `@deepseek-ai/dsh-fs-policy` | 已观察状态、编辑前读取和版本防护的写入/编辑,通过 `fs/*` 事件门禁贡献(无服务) | -| 提供方 seam | `@deepseek-ai/dsh-fs`(本包) | `ctx.fs`:文本 I/O 与原子变更原语(可选版本防护);拥有 `fs/*` 事件词汇 | -| 提供方 | `@deepseek-ai/dsh-fs-local` | 宿主文件系统实现 | - -未来的沙箱化、虚拟或远程后端只需实现该接口,策略层和工具层无需改变。 +本包是[文件系统家族](../README.md)中的提供方 seam 层。[工具](../tool-fs/README.md)、[策略](../fs-policy/README.md)、[本地](../fs-local/README.md)与[沙箱化](../fs-sandbox/README.md)后端分别作为消费方与实现保持独立;能力 seam 决策负责该拆分([基础](../../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)、[文件系统 seam](../../../.agents/notes/implemented/architecture/2026-06-17-filesystem-capability-seam.md)、[提供方拆分](../../../.agents/notes/implemented/simplification/2026-06-26-fsspec-style-fs-seam.md)、[事件门禁](../../../.agents/notes/implemented/architecture/2026-06-26-file-context-as-event-gate.md))。 ## 服务 API(`ctx.fs`) @@ -46,6 +37,10 @@ `FsTargetKey` / `FsVersion` 是带品牌的不透明 id(见[品牌 id Agent Note](../../../.agents/notes/implemented/architecture/2026-06-20-branded-ids.md));消费方不得解析 `targetKey` 或解释 `version`,只有 `displayPath` 用于模型/UI 输出。`FsWriteIntent` 是显式的防护写入意图(`createIfAbsent` 创建缺失目标,并以 `FS_NOT_OBSERVED` 拒绝现有目标;`replaceIfVersion` 只在观察版本上替换,否则为 `FS_STALE_VERSION`);从 `writeText` 中省略该值就是第三种无条件状态。`FsPathInfo` 是可报告 `symlink` 的不跟随链接元数据形态,区别于目标级 `FsInfo`。失败会抛出 `FsError`(继承 `HarnessError`;见[结构化错误分类 Agent Note](../../../.agents/notes/implemented/architecture/2026-06-11-structured-error-taxonomy.md)),并携带稳定的 `FsErrorCode`(`FS_NOT_FOUND`、`FS_NOT_DIRECTORY`、`FS_NOT_TEXT`、`FS_NOT_REGULAR_FILE`、`FS_PERMISSION_DENIED`、`FS_IO_ERROR`、`FS_STALE_VERSION`、`FS_NOT_OBSERVED`、`FS_AMBIGUOUS_EDIT`、`FS_EDIT_NOT_FOUND`、`FS_ABORTED`);工具注册表公开 `{ name, code }`,并将其附在 `isError` 结果上。完整契约见 `src/types.ts`。 +## 无 I/O deadline + +文件系统原语接受可选 `AbortSignal`,但不会启动 deadline。本地 I/O 只能尽力取消:超时无法强制进行中的 `fsync` 或 `rename` 停止,因此固定 deadline 会承诺后端无法提供的控制能力。基于进程的发现功能拥有独立的超时契约。 + ## 模型体验 通过 `dsh-tool-fs` 间接产生影响;该消费方把提供方文本和错误渲染为有界且保留的文件系统工具结果。 @@ -58,5 +53,5 @@ - **契约只支持文本**:后端以 `FS_NOT_TEXT` 拒绝二进制/非 UTF-8 内容;二进制安全操作是[工具 schema Agent Note](../../../.agents/notes/implemented/feature/2026-06-17-filesystem-tool-schemas.md)有意延期的工作。 - **只有八个原语**:没有删除、重命名/移动、复制或监视;`listDir` 只支持一层,递归、glob、分页和搜索不在范围内,见[目录列出 Agent Note](../../../.agents/notes/archived/architecture/2026-07-03-filesystem-directory-listing-seam.md)。 -- **没有 I/O deadline**:该 seam 不启动超时;取消只是每个原语上尽力而为的可选 `AbortSignal`(见有意采用的 [fs 能力族立场](../README.md))。 +- **没有 I/O deadline**:取消只能在原语边界尽力执行。 - **先解析后操作使远程后端每次工具调用需要两次往返**:折叠或缓存解析由这种后端自行决定。 diff --git a/packages/fs/tool-fs/README.i18n.yaml b/packages/fs/tool-fs/README.i18n.yaml index ed1f9d2ba0..292b9f395c 100644 --- a/packages/fs/tool-fs/README.i18n.yaml +++ b/packages/fs/tool-fs/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/fs/tool-fs/README.md -README.md: a695d0ba8fb1d600689d2b68763e8423d1591da5 -README.zh.md: 7b7dc26f514cc757cac97283eab52276505bfaae +README.md: 1ecfa0d013e1208b7d9058b4a254990f16f118e0 +README.zh.md: 9d4de4c219e7818c83c5ac326d2f2d385a30ece7 diff --git a/packages/fs/tool-fs/README.zh.md b/packages/fs/tool-fs/README.zh.md index 8cd48bdc29..9d4de4c219 100644 --- a/packages/fs/tool-fs/README.zh.md +++ b/packages/fs/tool-fs/README.zh.md @@ -38,7 +38,7 @@ await ctx.plugin(ToolFs) // this package — re ## 工具就是执行器;策略是事件门禁 -工具**不**注入策略服务,也不检查任何缓存。每个工具通过 `ctx.fs.resolve(path, { cwd, signal })` 解析路径;它会传入调用 agent 的会话 cwd(`exec.agent.session.header.cwd`),使相对路径以会话工作区为基准解析并与 `dsh-tool-bash` 一致,同时把工具取消转发到解析过程(见[每会话 cwd Agent Note](../../../.agents/notes/implemented/architecture/2026-07-02-fs-per-session-cwd.md))。随后执行: +工具**不**注入策略服务,也不检查任何缓存。每个工具通过 `ctx.fs.resolve(path, { cwd, signal })` 解析路径;它会传入调用 agent(智能体)的会话 cwd(`exec.agent.session.header.cwd`),使相对路径以会话工作区为基准解析并与 `dsh-tool-bash` 一致,同时把工具取消转发到解析过程(见[每会话 cwd Agent Note](../../../.agents/notes/implemented/architecture/2026-07-02-fs-per-session-cwd.md))。随后执行: - **read**:一次 `ctx.fs.stat`(用于类型、大小路由和版本),随后调用 `readText`/`streamText`,构建行窗口,再发出 `fs/observed`,使用普通 `ctx.emit`。(1 次 stat。) - **write**:调用 `ctx.waterfall('fs/write-intent', target, exec, () => undefined)` 取得可选防护,然后调用 `ctx.fs.writeText(target, content, intent)`,再发出 `fs/observed`。(0 次 stat。) diff --git a/packages/goal/command-goal/README.i18n.yaml b/packages/goal/command-goal/README.i18n.yaml index d7ee8c7505..f6f6685069 100644 --- a/packages/goal/command-goal/README.i18n.yaml +++ b/packages/goal/command-goal/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/goal/command-goal/README.md -README.md: 8e1a5b417467c8701ea935e25acfece11c5a70d4 -README.zh.md: ddb11d09d4a4d560af05b05ad7b586c1b0d365f9 +README.md: b7626f3ff14a563407c7a60dd057c9c8616fe994 +README.zh.md: f4c3c16b963ad16b1430efb25c91bd27fc75638f diff --git a/packages/goal/command-goal/README.md b/packages/goal/command-goal/README.md index 8e1a5b4174..b7626f3ff1 100644 --- a/packages/goal/command-goal/README.md +++ b/packages/goal/command-goal/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -Human-facing `/goal` control over [`ctx.goals`](../goal/README.md). The plugin registers one global command through [`ctx.commands`](../../ui/commands/README.md), so every composed command adapter discovers it; the shipped TUI executes it without a model turn. The [human goal-command Agent Note](../../../.agents/notes/implemented/feature/2026-07-19-human-goal-command.md) owns the UX and composition decisions. +Human-facing `/goal` control over [`ctx.goals`](../goal/README.md). The plugin registers one global command through [`ctx.commands`](../../ui/commands/README.md), so every composed command adapter discovers and executes it without a model turn. The [human goal-command Agent Note](../../../.agents/notes/implemented/feature/2026-07-19-human-goal-command.md) owns the UX and composition decisions. ## Command contract @@ -32,7 +32,7 @@ The producer injects `commands` and `goals`. A custom app mounts their owners pl name: '@deepseek-ai/dsh-command-goal' ``` -The TUI app enables the complete persisted-goal stack and this command by default. The ACP automation app enables the domain and model tools without mounting the command registry; `goals: false` removes that stack. The UI-less `agent-spine-demo` requires an explicit `goals: {}` so headless one-shot callers do not silently change from one physical turn to a multi-round operation. +The shipped `dsh` base enables the persisted-goal stack and this command; the Web client provides its interactive adapter. The ACP automation app enables the domain and model tools without a command adapter; `goals: false` removes that stack. The UI-less `agent-spine-demo` requires an explicit `goals: {}` so headless one-shot callers do not silently change from one physical turn to a multi-round operation. ## Model Experience @@ -55,4 +55,4 @@ Command discovery and direct output do not affect the cache. A mutation appends - **Plain-text interaction only** — the generic command registry has no modal edit form or replacement-confirmation callback; inline edit and explicit clear keep destructive intent deterministic across adapters. - **No per-command round-cap argument** — `defaultMaxGoalRounds` remains deployment config, while a direct human request may ask the model to edit `max_goal_rounds` through the separately authorized goal tool. - **No continuous status widget** — bare `/goal` is the portable observation surface; adapter-specific badges and reconnectable command output remain future UI work. -- **TUI only in the shipped apps** — the headless CLI, ACP automation, and JSON-RPC adapters do not consume `ctx.commands`. Ordinary prompts can still authorize model-facing goal tools when those are composed. +- **Web command adapter only in the shipped apps** — headless, ACP automation, and JSON-RPC adapters do not consume `ctx.commands`. Ordinary prompts can still authorize model-facing goal tools when those are composed. diff --git a/packages/goal/command-goal/README.zh.md b/packages/goal/command-goal/README.zh.md index ddb11d09d4..f4c3c16b96 100644 --- a/packages/goal/command-goal/README.zh.md +++ b/packages/goal/command-goal/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -面向用户的 `/goal` 控制,基于 [`ctx.goals`](../goal/README.md) 实现。该插件通过 [`ctx.commands`](../../ui/commands/README.md) 注册一个全局命令,因此每个已组合的命令适配器都能发现它;随附 TUI 无需模型轮次即可执行。[用户 goal 命令 Agent Note](../../../.agents/notes/implemented/feature/2026-07-19-human-goal-command.md) 负责用户体验与组合决策。 +面向用户的 `/goal` 控制,基于 [`ctx.goals`](../goal/README.md) 实现。该插件通过 [`ctx.commands`](../../ui/commands/README.md) 注册一个全局命令,因此每个已组合的命令适配器都能发现并执行它,无需模型轮次。[用户 goal 命令 Agent Note](../../../.agents/notes/implemented/feature/2026-07-19-human-goal-command.md)负责用户体验与组合决策。 ## 命令契约 @@ -32,7 +32,7 @@ name: '@deepseek-ai/dsh-command-goal' ``` -TUI 应用默认启用完整的持久 goal 栈和此命令。ACP(Agent Client Protocol)自动化应用会启用领域与模型工具,但不挂载命令注册表;`goals: false` 会移除该栈。无 UI 的 `agent-spine-demo` 必须显式配置 `goals: {}`,避免无头单次调用方在不知情时从一个物理轮次变为包含多个 Round 的操作。 +随附 `dsh` 基础配置启用持久 goal 栈和此命令;Web 客户端提供其交互适配器。ACP(Agent Client Protocol)自动化应用启用领域与模型工具,但不挂载命令适配器;`goals: false` 会移除该栈。无 UI 的 `agent-spine-demo` 必须显式配置 `goals: {}`,避免无头单次调用方在不知情时从一个物理轮次变为包含多个 Round 的操作。 ## 模型体验 @@ -55,4 +55,4 @@ TUI 应用默认启用完整的持久 goal 栈和此命令。ACP(Agent Client - **仅纯文本交互**:通用命令注册表没有模态编辑表单或替换确认回调;内联 edit 与显式 clear 能在不同适配器中保持明确且一致的破坏性意图。 - **没有逐命令 Round 上限参数**:`defaultMaxGoalRounds` 仍是部署配置;用户直接请求时,可以要求模型通过另行授权的 goal 工具编辑 `max_goal_rounds`。 - **没有持续状态组件**:裸 `/goal` 是可移植的观察接口;适配器专用徽标和重连后可恢复的命令输出仍属于未来 UI 工作。 -- **随附应用中只有 TUI 使用此命令**:无头 CLI(命令行界面)、ACP 自动化和 JSON-RPC 适配器不消费 `ctx.commands`。如果组合中包含面向模型的 goal 工具,普通提示词仍能授权它们。 +- **随附应用中只有 Web 命令适配器使用此命令**:无头、ACP 自动化和 JSON-RPC 适配器不消费 `ctx.commands`。如果组合中包含面向模型的 goal 工具,普通提示词仍能授权它们。 diff --git a/packages/goal/goal-session/README.i18n.yaml b/packages/goal/goal-session/README.i18n.yaml index 25d4f35eee..34035d6d81 100644 --- a/packages/goal/goal-session/README.i18n.yaml +++ b/packages/goal/goal-session/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/goal/goal-session/README.md README.md: 6a1c3b9455c93762c2458109c753588ce9a08d9a -README.zh.md: 7e8b04f46f0345bce026ccd8c663882f27e036ab +README.zh.md: 6101d4e3724aea99875613c96bc9c004feb38ad3 diff --git a/packages/goal/goal-session/README.zh.md b/packages/goal/goal-session/README.zh.md index 7e8b04f46f..6101d4e372 100644 --- a/packages/goal/goal-session/README.zh.md +++ b/packages/goal/goal-session/README.zh.md @@ -21,7 +21,7 @@ ## Round 契约 -当对应的活跃 agent 实例处于 idle 状态,且目标 phase 为 active、已启用续行并有剩余容量时,驱动器先为待处理 goal 变更创建检查点,再预留 `roundsStarted + 1`,对应当前 `{ goalId, revision }`。它会排入一条 `` 提示词,并携带 `GoalMessageSource`。通过 `agent/prompt-submit` 准入时,会在下游提示词钩子前后验证完整的排队记录与当前 goal;只有被接受的 `user/message` 才会增加 `roundsStarted`。因陈旧而被拒绝的预留不会消耗 Round 编号。 +当对应的活跃 agent(智能体)实例处于 idle 状态,且目标 phase 为 active、已启用续行并有剩余容量时,驱动器先为待处理 goal 变更创建检查点,再预留 `roundsStarted + 1`,对应当前 `{ goalId, revision }`。它会排入一条 `` 提示词,并携带 `GoalMessageSource`。通过 `agent/prompt-submit` 准入时,会在下游提示词钩子前后验证完整的排队记录与当前 goal;只有被接受的 `user/message` 才会增加 `roundsStarted`。因陈旧而被拒绝的预留不会消耗 Round 编号。 一个 Goal Round 对应一个普通会话轮次,该轮次可以包含多个模型/工具步骤。驱动器只会把预留与 `message` 轮次配对,且该轮次必须携带完全相同的 `GoalMessageSource`;可通过声明合并扩展的插件轮次触发器不会准入或替换该预留。用户消息仍是普通轮次,不消耗 goal 上限。如果用户工作在预留前进入 inbox,或加入预留的待处理批次,自动工作会让行,直到用户工作结算;混合批次中的待处理自动提示词会被拒绝,只有 agent 再次 idle 后才重新预留。 diff --git a/packages/guard/README.i18n.yaml b/packages/guard/README.i18n.yaml index 07234247df..637a36063e 100644 --- a/packages/guard/README.i18n.yaml +++ b/packages/guard/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/guard/README.md -README.md: b7375fd2bb12ae0cec94b13e6a1012c6f143bdad -README.zh.md: 7092f50b05516a2fb358c5d6f496b967ebdc6819 +README.md: 8da2f2ddcc1432e7963e600b35549f5e26121af2 +README.zh.md: aa8b5bd1f7d10d90d1dc84d07a7aad81bfb4259c diff --git a/packages/guard/README.zh.md b/packages/guard/README.zh.md index 7092f50b05..aa8b5bd1f7 100644 --- a/packages/guard/README.zh.md +++ b/packages/guard/README.zh.md @@ -1,11 +1,9 @@ -# guard/:循环健康 guard 家族 +# guard/ — 循环卫生 guard 家族 [English](README.md) | 中文 -这组行为 guard 插件会监视 agent loop(智能体循环)中的低效模式,并提醒模型调整方向。这里只有一个**产品**包,不设接口/实现 seam:guard 是现有核心 seam(`tools/post-execute`、`agent/prompt-submit`、`agent/status`)的自包含消费方,并非可替换能力。 +行为 guard 插件监视 agent loop(智能体循环)中的无效模式,并推动模型回到正轨。guard 是 core seam 的自包含消费方,而非可替换能力。 -| 包 | 职责 | ctx 键 | +| 包 | 职责 | ctx key | |---|---|---| -| `repeat-tool-guard/` | 当 agent 对完全相同的工具调用反复循环时给出提示 | (监听 `ctx.tools` 的 waterfall,即瀑布式事件) | - -提示以 `additionalContexts` 形式附在 `tools/post-execute` 决策中传递;agent loop 会在该步骤的工具结果之后,将其追加为有日志记录、来源为插件的 `user/message` 事件(参见[工具包](../core/tools))。因此,guard 告诉模型的所有内容都能从会话日志中重建。 +| [`repeat-tool-guard/`](repeat-tool-guard/README.md) | 针对重复工具调用的建议性提醒 | 监听工具和 agent 事件 | diff --git a/packages/hooks/README.i18n.yaml b/packages/hooks/README.i18n.yaml index 6b9e63e556..6000d1396f 100644 --- a/packages/hooks/README.i18n.yaml +++ b/packages/hooks/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/hooks/README.md -README.md: 23478fb5e9b813a3370ce465104b1f9db8b0a26a -README.zh.md: 778d5f334e5952a5b4ce08d52053144c048d4e8a +README.md: 35989292313c6cef47c02041eb671a73ff7c5786 +README.zh.md: b5187fed159b0eaf5b2bace192152e612a85bc55 diff --git a/packages/hooks/README.zh.md b/packages/hooks/README.zh.md index 0ab2202921..b5187fed15 100644 --- a/packages/hooks/README.zh.md +++ b/packages/hooks/README.zh.md @@ -1,13 +1,13 @@ -# hooks/:hook 桥接 + 共享协议 +# hooks/ — 钩子桥接与共享协议 [English](README.md) | 中文 -hooks 子系统让用户可以像使用 Claude Code 和 Codex 一样,在 agent(智能体)生命周期节点扩展 agent:把桥接插件指向现有的 `hooks.json`(或 settings),即可忠实运行这些外部 shell hook。规范的扩展表层本身是 harness 的类型化拦截 seam(见[拦截 seam Agent Note](../../.agents/notes/implemented/feature/2026-06-30-interception-seams.md));「原生 hook」只是这些 seam 上的普通 Cordis 插件。这些包是把外部 shell-hook 协议转换到同一表层的**桥接**,另含它们共同依赖的共享协议格式(wire format)库。 +hooks 子系统让用户像使用 Claude Code 和 Codex 一样,在生命周期节点扩展 agent(智能体):把桥接插件指向现有 `hooks.json`(或 settings),即可忠实运行这些外部 shell 钩子。规范扩展 surface 本身是 harness 的类型化拦截 seam(参见[拦截 seam Agent Note](../../.agents/notes/implemented/feature/2026-06-30-interception-seams.md));“原生钩子”只是这些 seam 上的普通 Cordis 插件。这些包是把外部 shell 钩子协议转换到同一 surface 的**桥接**,也包括它们共同依赖的共享协议格式库。 | 包 | 职责 | 形态 | |---|---|---| -| [`hook-protocol/`](hook-protocol/README.md) | 共享 shell hook 协议库 | 库 | -| [`hooks-claude/`](hooks-claude/README.md) | Claude Code hook 桥接 | 插件 | -| [`hooks-codex/`](hooks-codex/README.md) | Codex hook 桥接 | 插件 | +| [`hook-protocol/`](hook-protocol/README.md) | 共享 shell 钩子协议库 | 库 | +| [`hooks-claude/`](hooks-claude/README.md) | Claude Code 钩子桥接 | 插件 | +| [`hooks-codex/`](hooks-codex/README.md) | Codex 钩子桥接 | 插件 | -共享库负责通用协议行为;每个桥接负责其方言特有的事件映射。相应契约由子级 README 记录。 +共享库负责通用协议行为;各桥接负责自身方言的事件映射。子 README 记录这些契约。 diff --git a/packages/host/README.i18n.yaml b/packages/host/README.i18n.yaml index fbc4c9f42e..178db5dcef 100644 --- a/packages/host/README.i18n.yaml +++ b/packages/host/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/host/README.md -README.md: 391adb7009a01d1ec95c8dcb809e8a2065aa0b31 -README.zh.md: 94136ed165d77df5d15102d9873c88efb3780463 +README.md: 7cd331f113eeec6c0a56f0ebc60554d9647aee75 +README.zh.md: 07b0e1569e17b9f0465a43f77fa2dbddcb1bae91 diff --git a/packages/host/README.zh.md b/packages/host/README.zh.md index 94136ed165..07b0e1569e 100644 --- a/packages/host/README.zh.md +++ b/packages/host/README.zh.md @@ -1,16 +1,16 @@ -# host/ — web GUI 宿主侧 +# host/ — Web GUI 宿主侧 [English](README.md) | 中文 -dsh web GUI 的宿主侧:所有客户端形态共用的 API 网关,以及承载它的纯 HTTP 服务器。浏览器侧位于 [`client/`](../client/README.md);组合后的应用是 [`apps/cli`](../../apps/cli/config/base.cordis.yml),它负责服务 [`apps/web`](../../apps/web/)。全部为**产品**包。 +dsh Web GUI 的宿主侧:所有客户端形态共享的 API 网关,以及承载它的普通 HTTP 服务器。浏览器侧位于 [`client/`](../client/README.md);组合应用是 [`apps/cli`](../../apps/cli/config/base.cordis.yml),由它提供 [`apps/web`](../../apps/web/)。这些全是**产品**包。 -| 包 | 角色 | ctx 键 | +| 包 | 职责 | ctx key | |---|---|---| -| `apiproxy/` | 共享 API 网关:零 Node 依赖的 TS 协议契约(`src/api/`)、fetch 载体对(宿主侧 `toFetchHandler`、客户端侧 `AbstractApiClient`),以及基于 `ctx.agents`/`ctx.workspace` 的宿主实现 | `ctx.apiProxy` | -| `webserver/` | 纯 HTTP 路由注册载体:激活即监听的 `node:http` 服务器;路由以命名的 `exact`/`prefix` 处理器注册 | `ctx.httpServer` | -| `directory-picker/` | 工作区目录选择 seam:网关的 picker RPC 委托的可辨识 `native`/`browse` 能力 | `ctx.directoryPicker` | -| `directory-picker-native/` | 双面原生交互:OS 选择器后端(osascript/PowerShell/Zenity+KDialog,仅宿主屏幕可用)+ 由浏览器侧填入 ui-workspace 目录流 slot | (注册 `ctx.directoryPicker`) | -| `directory-picker-browse/` | 双面浏览交互:基于 Node 标准库的列举/创建原语(可远程)+ 由浏览器侧渲染应用内选择工作区目录对话框 | (注册 `ctx.directoryPicker`) | -| `directory-picker-auto/` | 自适应选择器:启动时一次性判定宿主处境(绑定宿主、SSH、显示),并把匹配的双面后端挂载为内存中的 Loader 条目 | (挂载一个后端行) | +| [`apiproxy/`](apiproxy/README.md) | 共享宿主 API 网关和协议契约 | `ctx.apiProxy` | +| [`webserver/`](webserver/README.md) | HTTP 路由载体 | `ctx.httpServer` | +| [`directory-picker/`](directory-picker/README.md) | workspace 目录选择 seam | `ctx.directoryPicker` | +| [`directory-picker-native/`](directory-picker-native/README.md) | 原生目录选择器后端和浏览器交互 | 注册 `ctx.directoryPicker` | +| [`directory-picker-browse/`](directory-picker-browse/README.md) | 应用内目录浏览器后端和交互 | 注册 `ctx.directoryPicker` | +| [`directory-picker-auto/`](directory-picker-auto/README.md) | 宿主自适应选择器组合 | 挂载一个后端 | -`apiproxy` 在设计上与传输方式无关——它不注册任何路由;载体自行包装 `ctx.apiProxy`。HTTP 载体路由(连同其 `/api` 浏览器信任栅栏)由 [`client/connection`](../client/connection/README.md) 的 node 半侧挂载,这正是该包住在 client 组的原因:它拥有这条协议链路的两端。 +`apiproxy` 保持传输无关;[`client/connection`](../client/connection/README.md) 提供浏览器/HTTP 载体。选择器实现可在共享 seam 后互相替换。 diff --git a/packages/host/apiproxy/README.i18n.yaml b/packages/host/apiproxy/README.i18n.yaml index e467677b97..786153c9cd 100644 --- a/packages/host/apiproxy/README.i18n.yaml +++ b/packages/host/apiproxy/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/host/apiproxy/README.md -README.md: 0ed3bd661cc489d1af64a3ee81aaf40d3427e638 -README.zh.md: 96acb16bf5a56a47648b388e154a34dfd40a78ea +README.md: b8f490b041c6f22ad371403f29942e8ebd5e70b8 +README.zh.md: f2c2389f00aca0c02793fba752334f1f1fa99f14 diff --git a/packages/host/apiproxy/README.md b/packages/host/apiproxy/README.md index 0ed3bd661c..b8f490b041 100644 --- a/packages/host/apiproxy/README.md +++ b/packages/host/apiproxy/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -The API gateway every client shape shares: the TS contract (`src/api/`, zero Node dependencies, importable from the browser), the fetch carrier pair (`src/fetch/`: `toFetchHandler` on the host side, `AbstractApiClient` plus platform subclasses on the client side), and the host-side implementation (`src/api-proxy.ts`: `createApiProxy` plus the default-exported `ApiProxyService` gateway plugin — config `{provider, model, workspaceRoot?}`, provides `ctx.apiProxy`). Transport-agnostic by design: this package registers no routes; carriers (HTTP today, IPC later) wrap `ctx.apiProxy` themselves. The shipped core composition lives in [`apps/cli/config/base.cordis.yml`](../../../apps/cli/config/base.cordis.yml). +The API gateway every client shape shares: the TS contract (`src/api/`, zero Node dependencies, importable from the browser), the fetch carrier pair (`src/fetch/`: `toFetchHandler` on the host side, `AbstractApiClient` plus platform subclasses on the client side), and the host-side implementation (`src/api-proxy.ts`: `createApiProxy` plus the default-exported `ApiProxyService` gateway plugin — config `{provider, model, workspaceRoot?}`, provides `ctx.apiProxy`). Transport-agnostic by design: this package registers no routes; carriers such as HTTP wrap `ctx.apiProxy` themselves. The shipped core composition lives in [`apps/cli/config/base.cordis.yml`](../../../apps/cli/config/base.cordis.yml). ## Contract layer (`/api`) @@ -16,7 +16,7 @@ Question responses are validated against their pending request before the first `session.history`'s tail page (`beforeSeq` absent) additionally carries an optional `projections` block — the watermark snapshot of every unit registered on `ctx.sessionProjections` (`@deepseek-ai/dsh-session-projection`), with `asOfSeq` = the last event seq the values reflect (`-1` on an empty log). The gateway also subscribes to the registry's change feed and mints a `session/projection` mux frame per changed unit (`{sessionId, key, value, seq}` — live push state, never logged; clients hold one generic per-session value store under higher-seq-wins). The carrier holds zero domain knowledge (each value passed its unit's own schema inside the registry; the wire schemas keep `values`/`value` wide); loadOlder pages never carry the block, and a composition without the registry serves histories without either surface. -Session titles ride the generic projection pair like every other domain — the history-tail `projections` block plus `session/projection` frames under the `title` key (the bespoke `session/title` frame is retired). Titles do not join `session.list`; cold sessions remain metadata-only there until an Agent-bound ordinary-session operation attaches their logs. `session.rename` accepts an explicit user title (resuming a cold session first), delegating to `ctx.sessionTitle.rename` — the accepted `session/title` event pins the title against automatic regeneration — and returns the normalized title plus its event seq so a client settles its `title` projection cell ahead of the push frame; a title that normalizes to empty returns `title-invalid`. +Session titles use the generic projection pair: the history-tail `projections` block plus `session/projection` frames under the `title` key. Titles do not join `session.list`; cold sessions remain metadata-only there until an Agent-bound ordinary-session operation attaches their logs. `session.rename` accepts an explicit user title (resuming a cold session first), delegates to `ctx.sessionTitle.rename`, and returns the normalized title plus its event seq so a client settles its `title` projection cell ahead of the push frame. The accepted `session/title` event pins the title against automatic regeneration; a title that normalizes to empty returns `title-invalid`. `session.fork` reads its source from attached state or persistence inspection without acquiring an Agent, then maps an optional event anchor to the first `turn/end` at or after it, letting a message action include that message's whole turn. An omitted or past-end anchor selects the last completed turn; an in-log anchor whose turn remains open returns `fork-unavailable` rather than clipping backward. The published ordinary child inherits the source's seeded history, cwd, latest logged provider/model/reasoning target, and lineage before joining the source Workspace, or the nearest workspace-owning ancestor when the source is a subagent. If Workspace attachment fails, `workspace-attach-failed` carries the already-published child id so clients can reconcile it. The [SessionStore fork decision](../../../.agents/notes/implemented/feature/2026-06-30-session-store-fork-api.md) owns the boundary rationale. @@ -56,8 +56,8 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work -- **`respond` routing is shipped, but pending-interaction state is host-side work** — the wire shape (POST `/api/respond`, `RpcReceipt`) is final; the pending table that makes late/duplicate answers meaningful lives in `src/api-proxy.ts` and is still minimal (questions only, no approvals). -- **Reserved seams stay out of `RpcMethodMap`** — `prompt.mode: 'inject'`, `task.list`, and a describe `hostInstanceId` are documented reservations (the former `host.listModels` reservation shipped as `llm.models`); an unknown method fails loud at envelope parse rather than getting a not-implemented code. +- **Pending-interaction state is host-side** — the wire shape is POST `/api/respond` plus `RpcReceipt`; the table in `src/api-proxy.ts` handles questions only and has no approval entries. +- **Reserved seams stay out of `RpcMethodMap`** — `prompt.mode: 'inject'`, `task.list`, and a describe `hostInstanceId` are documented reservations; model discovery uses `llm.models`. An unknown method fails loud at envelope parse rather than getting a not-implemented code. - **No protocol version field** — client and host ship together; `host.describe` gains a version negotiation field only when an independently released client exists. - **Search failures include provider diagnostics** — the gateway is a single-user local service. A carrier that exposes it to multiple users must replace internal search details with a public-safe diagnostic. - **Linux native picker requires desktop tooling** — under the `native` capability, `host.pickDirectory` reports an actionable error when neither Zenity nor KDialog is installed; the browse backend is the composition-level fallback (see the [native backend README](../directory-picker-native/README.md)). diff --git a/packages/host/apiproxy/README.zh.md b/packages/host/apiproxy/README.zh.md index 96acb16bf5..f2c2389f00 100644 --- a/packages/host/apiproxy/README.zh.md +++ b/packages/host/apiproxy/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -所有客户端形态共用的 API 网关:TS 契约(`src/api/`,不依赖 Node,可从浏览器导入)、fetch 载体对(`src/fetch/`:宿主侧的 `toFetchHandler`,以及客户端侧的 `AbstractApiClient` 与平台子类)和宿主侧实现(`src/api-proxy.ts`:`createApiProxy` 加上默认导出的 `ApiProxyService` 网关插件,其配置为 `{provider, model, workspaceRoot?}`,提供 `ctx.apiProxy`)。该包在设计上与传输方式无关,不注册任何路由;载体(目前为 HTTP,未来可以是 IPC)自行包装 `ctx.apiProxy`。已发布的核心组合位于 [`apps/cli/config/base.cordis.yml`](../../../apps/cli/config/base.cordis.yml)。 +所有客户端形态共用的 API 网关:TS 契约(`src/api/`,不依赖 Node,可从浏览器导入)、fetch 载体对(`src/fetch/`:宿主侧的 `toFetchHandler`,以及客户端侧的 `AbstractApiClient` 与平台子类)和宿主侧实现(`src/api-proxy.ts`:`createApiProxy` 加上默认导出的 `ApiProxyService` 网关插件,其配置为 `{provider, model, workspaceRoot?}`,提供 `ctx.apiProxy`)。该包在设计上与传输方式无关,不注册任何路由;HTTP 等载体自行包装 `ctx.apiProxy`。已发布的核心组合位于 [`apps/cli/config/base.cordis.yml`](../../../apps/cli/config/base.cordis.yml)。 ## 契约层(`/api`) @@ -16,7 +16,7 @@ `session.history` 的尾页(不带 `beforeSeq`)额外携带一个可选的 `projections` 块——`ctx.sessionProjections`(`@deepseek-ai/dsh-session-projection`)上每个已注册单元的水位线快照,`asOfSeq` = 这些值共同反映到的最后一个事件 seq(空日志为 `-1`)。网关还订阅注册表的变更流,为每个状态发生变化的单元生成一个 `session/projection` mux 帧(`{sessionId, key, value, seq}`——实时推送状态,绝不入日志;客户端按 seq 高者胜维护一个按会话的通用值仓)。载体不持有任何领域知识(每个值在注册表内部已过其单元自己的 schema;协议 schema 对 `values`/`value` 保持宽松);loadOlder 页永不携带该块,未装注册表的组合则两个面都不提供。 -会话标题与其他所有领域一样搭乘这对通用投影机制——历史尾页的 `projections` 块外加 `title` 键下的 `session/projection` 帧(专设的 `session/title` 帧已下线)。标题不会加入 `session.list`;冷会话在其中仍只有元数据,直到某项绑定到 Agent 的普通会话操作附加其日志。`session.rename` 接受用户显式标题(冷会话先恢复),委托给 `ctx.sessionTitle.rename`——被接受的 `session/title` 事件将标题钉住、不再被自动生成覆盖——并返回规范化后的标题及其事件 seq,让 client 在推送帧到达前就结算自己的 `title` 投影格;规范化后为空的标题返回 `title-invalid`。 +会话标题使用通用投影对:历史尾页的 `projections` 块,以及 `title` 键下的 `session/projection` 帧。标题不会加入 `session.list`;冷会话在其中仍只有元数据,直到某项绑定到 Agent 的普通会话操作附加其日志。`session.rename` 接受用户显式标题(冷会话先恢复),委托给 `ctx.sessionTitle.rename`,并返回规范化后的标题及其事件 seq,让 client 在推送帧到达前结算自己的 `title` 投影格。被接受的 `session/title` 事件将标题钉住、不再被自动生成覆盖;规范化后为空的标题返回 `title-invalid`。 `session.fork` 会从已附加状态或持久化检查中读取源会话而不获取 Agent,再将可选事件锚点映射到该锚点处或其后的首个 `turn/end`,使消息操作可包含该消息所在的完整轮次。锚点省略或超过末尾时,选择最后一个已完成轮次;若锚点已在日志中,而其所在轮次仍开放,则返回 `fork-unavailable`,不会向较早位置裁剪。发布后的普通子会话会先继承源会话的种子历史、cwd、日志中最新的提供方/模型/推理(reasoning)目标及谱系,再加入源 Workspace;若源会话是 subagent,则改为附加到最近拥有 Workspace 的祖先。如果附加到 Workspace 失败,`workspace-attach-failed` 会携带已发布的子会话 id,供客户端对账。[SessionStore fork 决策](../../../.agents/notes/implemented/feature/2026-06-30-session-store-fork-api.md)给出边界设计的理由。 @@ -56,8 +56,8 @@ Workspace 列表与 Session 列表是相互独立的重连基线。`workspace.cr ## 已知限制与暂缓事项 -- **`respond` 路由已经发布,但待处理交互状态仍属宿主侧工作**:协议形状(POST `/api/respond`、`RpcReceipt`)已经定型;使延迟或重复回答具有明确语义的待处理表位于 `src/api-proxy.ts`,目前仍很精简(只支持问题,不支持审批)。 -- **预留 seam 不进入 `RpcMethodMap`**:`prompt.mode: 'inject'`、`task.list` 和描述字段 `hostInstanceId` 都是已记录的预留项(先前预留的 `host.listModels` 已作为 `llm.models` 交付);未知方法会在信封解析时直接失败,而不会返回「尚未实现」错误码。 +- **待处理交互状态位于宿主侧**:协议形状为 POST `/api/respond` 加 `RpcReceipt`;`src/api-proxy.ts` 中的表只处理问题,不包含审批条目。 +- **预留 seam 不进入 `RpcMethodMap`**:`prompt.mode: 'inject'`、`task.list` 和描述字段 `hostInstanceId` 都是已记录的预留项;模型发现使用 `llm.models`。未知方法会在信封解析时直接失败,而不会返回「尚未实现」错误码。 - **没有协议版本字段**:客户端与宿主一同发布;只有出现独立发布的客户端后,`host.describe` 才会增加版本协商字段。 - **搜索失败会包含提供方诊断信息**:网关是单用户本地服务。将其暴露给多名用户的载体必须用可安全公开的诊断信息替代内部搜索细节。 - **Linux 原生选择器依赖桌面工具**:在 `native` 能力下,Zenity 和 KDialog 均未安装时,`host.pickDirectory` 会给出包含解决建议的错误提示;组合层面的回退是 browse 后端(见 [native 后端 README](../directory-picker-native/README.md))。 diff --git a/packages/host/directory-picker-browse/README.i18n.yaml b/packages/host/directory-picker-browse/README.i18n.yaml index 66ced876a1..60f133ae2b 100644 --- a/packages/host/directory-picker-browse/README.i18n.yaml +++ b/packages/host/directory-picker-browse/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/host/directory-picker-browse/README.md -README.md: 62384cc0b0e5756e56c1d608252721c506a0915f -README.zh.md: fd53cb69973268967e2170b74c068d87b4974cd8 +README.md: bd28be4119a542eda8c8ee2e53aa860f9c02c17d +README.zh.md: 03306f2fd1c54f9c3c5dbc808920abce944141e5 diff --git a/packages/host/directory-picker-browse/README.md b/packages/host/directory-picker-browse/README.md index 62384cc0b0..bd28be4119 100644 --- a/packages/host/directory-picker-browse/README.md +++ b/packages/host/directory-picker-browse/README.md @@ -20,4 +20,4 @@ None; this package neither assembles nor sends a provider request. - **Windows hidden attribute is not read** — Node dirents do not expose `FILE_ATTRIBUTE_HIDDEN`, so `hidden` means dot-prefixed on every platform until a native probe is worth its cost. - **No drive-root enumeration** — on Windows the ancestry stops at the drive root; crossing drives waits for the browser UI's path-entry affordance rather than an enumeration primitive here. -- **Whole-filesystem scope** — no per-deployment browse-root restriction; `workspace.create` accepts arbitrary paths today, so a root here would be UX scoping, not a boundary — deferred until a deployment needs it. +- **Whole-filesystem scope** — there is no per-deployment browse-root restriction. `workspace.create` accepts arbitrary paths, so a root here would be UX scoping rather than a security boundary. diff --git a/packages/host/directory-picker-browse/README.zh.md b/packages/host/directory-picker-browse/README.zh.md index fd53cb6997..03306f2fd1 100644 --- a/packages/host/directory-picker-browse/README.zh.md +++ b/packages/host/directory-picker-browse/README.zh.md @@ -20,4 +20,4 @@ - **不读取 Windows 隐藏属性**——Node 的 dirent 不暴露 `FILE_ATTRIBUTE_HIDDEN`,因此在所有平台上 `hidden` 都意味着点前缀,直到原生探测值得付出相应成本为止。 - **不枚举盘符根**——Windows 上祖先链止于盘符根;跨盘依赖浏览器 UI 的路径输入入口,而不是这里的枚举原语。 -- **全盘可浏览**——没有按部署限定的浏览根;`workspace.create` 今天就接受任意路径,这里的根只会是 UX 范围而非边界——等到有部署需要时再做。 +- **全盘可浏览**——没有按部署限定的浏览根。`workspace.create` 接受任意路径,因此这里的根会限定 UX 范围,而不是安全边界。 diff --git a/packages/host/directory-picker-native/README.i18n.yaml b/packages/host/directory-picker-native/README.i18n.yaml index c1b47710a7..4069bb2e53 100644 --- a/packages/host/directory-picker-native/README.i18n.yaml +++ b/packages/host/directory-picker-native/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/host/directory-picker-native/README.md -README.md: 3d270af441bd251c126c8fb3c3d2d7aec95655c9 -README.zh.md: b4a3d91b68c285aad7911ba711348e36ffc7a4c8 +README.md: 82c1e2a6adc5352362c3b4a065a29591249c8bd9 +README.zh.md: 5e69cababeea0ff13090fa6f8f4420d30446bc55 diff --git a/packages/host/directory-picker-native/README.md b/packages/host/directory-picker-native/README.md index 3d270af441..82c1e2a6ad 100644 --- a/packages/host/directory-picker-native/README.md +++ b/packages/host/directory-picker-native/README.md @@ -17,4 +17,4 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work - **Linux requires desktop tooling** — with neither Zenity nor KDialog installed, `pick` rejects with an actionable error; it does not fall back to a typed-path prompt (the browse backend is that fallback at the composition level). -- **Windows has no mechanism fallback** — the child-process picker is the only tier: koffi is a packaged dependency whose availability the install guarantees, so a failed pick (COM refusal, dialog crash) surfaces the failure instead of degrading to a PowerShell-hosted dialog (the former `pwsh` → Windows PowerShell 5.1 chain was removed). The browse backend remains the fallback at the composition level. +- **Windows has no mechanism fallback** — the child-process picker through packaged koffi is the only native tier, so a COM refusal or dialog crash surfaces the failure. The browse backend remains the fallback at the composition level. diff --git a/packages/host/directory-picker-native/README.zh.md b/packages/host/directory-picker-native/README.zh.md index b4a3d91b68..5e69cababe 100644 --- a/packages/host/directory-picker-native/README.zh.md +++ b/packages/host/directory-picker-native/README.zh.md @@ -17,4 +17,4 @@ ## 已知限制与延期工作 - **Linux 依赖桌面工具**——Zenity 与 KDialog 均未安装时,`pick` 以包含解决建议的错误拒绝;它不会回退为手输路径提示(组合层面的回退是 browse 后端)。 -- **Windows 没有机制级回退**——子进程选择器是唯一层级:koffi 是打包依赖,其可用性由安装保证,因此一次失败的 pick(COM 拒绝、对话框崩溃)直接上报失败,不会降级到 PowerShell 承载的对话框(原有的 `pwsh` → Windows PowerShell 5.1 链已删除)。组合层面的回退仍是 browse 后端。 +- **Windows 没有机制级回退**——通过打包依赖 koffi 运行的子进程选择器是唯一原生层级,因此 COM 拒绝或对话框崩溃会直接上报失败。组合层面的回退仍是 browse 后端。 diff --git a/packages/llm/README.i18n.yaml b/packages/llm/README.i18n.yaml index 009a799253..3c4ae19900 100644 --- a/packages/llm/README.i18n.yaml +++ b/packages/llm/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/llm/README.md -README.md: 66b7beabd73cc3fec7230f209a9da0da48a37c95 -README.zh.md: 08f561840d1a560cfc9bced6f8757a0b0fc5770a +README.md: 92d9fbfa2b8c8db4700562009db49229b2189ab3 +README.zh.md: 5c6e7aad1db6511bdb660b86e257652128db131f diff --git a/packages/llm/README.zh.md b/packages/llm/README.zh.md index 08f561840d..5c6e7aad1d 100644 --- a/packages/llm/README.zh.md +++ b/packages/llm/README.zh.md @@ -1,15 +1,15 @@ -# llm/:LLM(大语言模型)能力家族 +# llm/ — LLM 能力家族 [English](README.md) | 中文 -LLM seam 及其提供方适配器。接口包(`llm`)拥有抽象服务、内容块词汇和流分片组装器;适配器是在 `ctx.llm` 上注册的具体实现。这些全是**产品**包。 +LLM(大语言模型)seam 及其提供方适配器。接口包(`llm`)负责抽象服务、内容块词汇和流式分片组装器;适配器是注册到 `ctx.llm` 的具体实现。这些全是**产品**包。 | 包 | 职责 | ctx key | |---|---|---| -| `llm/` | 抽象 LLM 服务 + 内容块词汇 + 分片组装器 | `ctx.llm` | -| `token-meter/` | 感知回放的请求 token 与表层 token 测量 | `ctx.tokenMeter` | -| `llm-retry/` | 确切提供方的常规或无界请求重试策略 | (监听 `agent/request-error`) | -| `llm-deepseek/` | DeepSeek API 适配器,直接使用 fetch + eventsource-parser 和 SSE(Server-Sent Events) | (注册到 `ctx.llm`) | -| `llm-pi-ai/` | 通过 `@earendil-works/pi-ai` 实现的多提供方适配器 | (注册到 `ctx.llm`) | +| [`llm/`](llm/README.md) | LLM 服务和共享流式词汇 | `ctx.llm` | +| [`token-meter/`](token-meter/README.md) | 可感知回放的 token 测量 | `ctx.tokenMeter` | +| [`llm-retry/`](llm-retry/README.md) | 提供方作用域的重试策略 | 监听 `agent/request-error` | +| [`llm-deepseek/`](llm-deepseek/README.md) | 直接 DeepSeek 适配器 | 注册到 `ctx.llm` | +| [`llm-pi-ai/`](llm-pi-ai/README.md) | 多提供方 pi-ai 适配器 | 注册到 `ctx.llm` | -接口位于 `llm/llm/`;适配器、重试策略和可复用的 token 计量器以扁平结构并列在该分组下。请求按 `provider` 路由,而 `model` 会原样传给选中的适配器。负责该路由的适配器提供重试策略,并解析可用的确切模型身份、上下文容量和推理元数据;重试执行器与 token 计量器仍与提供方无关。新的提供方适配器只需在 `ctx.llm` 上注册一个或多个提供方路由,无需改动消费方。两个已交付实现见[双生 LLM 适配器](../../.agents/notes/implemented/architecture/2026-06-13-twin-llm-adapters.md),测量归属见[回放 token 计量器 Agent Note](../../.agents/notes/implemented/architecture/2026-07-15-replay-token-meter-service.md),容量与压缩(compaction)策略归属见[路由模型上下文 Agent Note](../../.agents/notes/implemented/architecture/2026-07-20-routed-model-context-and-compaction-policy.md)。 +适配器在 seam 上注册提供方路由;重试与 token 测量仍是独立消费方。子 README 负责路由、元数据、回放和提供方协议细节;[LLM 架构决策](../../.agents/notes/implemented/architecture/2026-06-13-twin-llm-adapters.md)负责设计原理。 diff --git a/packages/llm/token-meter/README.i18n.yaml b/packages/llm/token-meter/README.i18n.yaml index ca54dba848..6c5abe03df 100644 --- a/packages/llm/token-meter/README.i18n.yaml +++ b/packages/llm/token-meter/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/llm/token-meter/README.md -README.md: 701893b342f9a93a75bec175634b1054f3d17151 -README.zh.md: 0731e05186bec3f21d1f723c5b94ab7945e4139d +README.md: 0935a48a5f5773fbb280bc45e07faaa05c0a4f6e +README.zh.md: 83282ab47e6d406cfca30bbd8af40e0c94050504 diff --git a/packages/llm/token-meter/README.md b/packages/llm/token-meter/README.md index 701893b342..0935a48a5f 100644 --- a/packages/llm/token-meter/README.md +++ b/packages/llm/token-meter/README.md @@ -29,13 +29,13 @@ When the composition provides `ctx.sessionProjections`, token-meter registers tw `contextPressure` carries optional `pressureTokens` — the newest provider-reported prompt size, summing uncached input plus cache reads and writes — and optional `contextWindow` from the newest `request/context` record. Pressure stays absent until a provider reports usage; capacity stays absent for a route whose adapter advertises none. Output is excluded, so the numerator holds still while a turn streams and steps forward when the next request reports its usage. -Both units use the standard projection baseline, live frame, higher-seq-wins store, and JSON checkpoint paths. Unloading token-meter removes both keys. A headless or TUI composition without the projection seam keeps the measurement service's existing behavior. +Both units use the standard projection baseline, live frame, higher-seq-wins store, and JSON checkpoint paths. Unloading token-meter removes both keys. A composition without the projection seam keeps the measurement service's existing behavior. ### Context occupancy is an approximation, by design `pressureTokens` and `contextWindow` are independent last-wins fields and are **not** one atomic observation of a single request. Switching models pairs the fresh capacity with the previous route's pressure until the next request reports usage, and `pressureTokens` describes the last request rather than the surface as it stands right now. -This is deliberate. An occupancy percentage is a user-facing reference figure, not a billing record or a gating input — nothing in the harness makes decisions from it, and compaction reads `measure()` instead. The TUI status line has always computed occupancy the same way, dividing a `measure()` total by a separately-resolved capacity for the selected model. +This is deliberate. An occupancy percentage is a user-facing reference figure, not a billing record or a gating input — nothing in the harness makes decisions from it, and compaction reads `measure()` instead. A UI computes occupancy by dividing measured pressure by the separately resolved capacity for the selected model. Making the pair atomic was tried and rejected: it required a transient non-replayable wire frame, which needed lifecycle fencing against cross-stream reordering and left occupancy blank after every reconnect. The [Agent Note](../../../.agents/notes/implemented/architecture/2026-07-29-projected-token-usage-and-request-context.md) records that comparison. Consumers that need an exact same-boundary figure should call `measure()` at their own request boundary rather than read this projection. @@ -62,4 +62,3 @@ No direct invalidation; the named consumer owns any request-prefix changes. - **Every measurement clones the current surface** — coherent immutable snapshots make reads O(surface), including below-threshold pressure checks. - **Provider usage is only reusable for an identical canonical envelope** — prompt, prefix, tools, provider, model, or call-config changes deliberately fall back to full heuristic estimation. - **Legacy provenance is conservative** — assistant messages without `sourceEventSeqs` cannot distinguish provider output from listener rewrites, so the fold avoids claiming a known empty or exact chunk stream. -- **The TUI and browser fixture retain parallel folds** — `tokenUsage` owns durable session-projection semantics; the TUI keeps its live per-step map because its composition does not mount the generic projection seam, while the browser fixture mirrors the unit for standalone demo data. diff --git a/packages/llm/token-meter/README.zh.md b/packages/llm/token-meter/README.zh.md index 0731e05186..83282ab47e 100644 --- a/packages/llm/token-meter/README.zh.md +++ b/packages/llm/token-meter/README.zh.md @@ -29,13 +29,13 @@ fold 跟踪完整请求标头快照、步骤边界、表层追加与替换、成 `contextPressure` 携带可选的 `pressureTokens`(提供方报告的最新提示词规模,为未缓存输入加缓存读取与写入之和),以及来自最新一条 `request/context` 记录的可选 `contextWindow`。提供方报告用量前压力保持缺失;路由适配器未公布容量时容量也保持缺失。输出不计入其中,因此轮次流式输出期间分子保持不动,等到下一个请求报告用量时才前进。 -两个单元都使用标准的投影基线、实时帧、seq 高者胜值仓和 JSON 检查点路径。卸载 token-meter 会移除这两个键。不带投影 seam 的 headless 或 TUI 组合会保留测量服务的既有行为。 +两个单元都使用标准的投影基线、实时帧、seq 高者胜值仓和 JSON 检查点路径。卸载 token-meter 会移除这两个键。不带投影 seam 的组合会保留测量服务的既有行为。 ### 上下文占用率是刻意为之的近似值 `pressureTokens` 与 `contextWindow` 是两个各自后者胜的独立字段,**不是**对单个请求的一次原子观测。切换模型时,新容量会与上一路由的压力配对,直到下一个请求报告用量为止;而 `pressureTokens` 描述的是最后一个请求,不是此刻的表层。 -这是刻意的选择。占用率百分比是面向用户的参考数字,既不是计费记录,也不是门控输入:harness 中没有任何环节依据它做决策,压缩改为直接读取 `measure()`。TUI 状态行一直以同样的方式计算占用率,即用 `measure()` 总量除以为所选模型单独解析出的容量。 +这是刻意的选择。占用率百分比是面向用户的参考数字,既不是计费记录,也不是门控输入:harness 中没有任何环节依据它做决策,压缩改为直接读取 `measure()`。UI 用测得的压力除以为所选模型单独解析出的容量来计算占用率。 让这对值保持原子已经尝试过并被否决:它需要一个临时且不可回放的协议帧,进而需要针对跨流重排序的生命周期栅栏,还会让占用率在每次重连后变为空白。[Agent Note](../../../.agents/notes/implemented/architecture/2026-07-29-projected-token-usage-and-request-context.md)记录了这项对比。需要同一边界精确数字的消费方应在自己的请求边界调用 `measure()`,而不是读取该投影。 @@ -62,4 +62,3 @@ fold 跟踪完整请求标头快照、步骤边界、表层追加与替换、成 - **每次测量都会克隆当前表层**:一致且不可变的快照使读取成为 O(surface),包括低于阈值的压力检查。 - **提供方用量只能为完全相同的规范 envelope 复用**:提示词、前缀、工具、提供方、模型或调用配置变更都会有意回退到完整启发式估算。 - **遗留溯源采取保守策略**:没有 `sourceEventSeqs` 的 assistant 消息无法区分提供方输出与 listener 改写,因此 fold 不会声称已知空流或精确分片流。 -- **TUI 与浏览器 fixture 仍保留并行 fold**:`tokenUsage` 拥有持久会话投影语义;TUI 的组合未挂载通用投影 seam,因此继续维护实时的逐步骤 map,而浏览器 fixture 会为独立 demo 数据镜像该单元。 diff --git a/packages/llm/token-meter/src/index.ts b/packages/llm/token-meter/src/index.ts index 1300343e99..369c6e5e85 100644 --- a/packages/llm/token-meter/src/index.ts +++ b/packages/llm/token-meter/src/index.ts @@ -93,8 +93,8 @@ export class TokenMeterService extends Service { super(ctx, 'tokenMeter') validateConfigKeys(config) - // Projection registration is an optional child: headless and TUI - // compositions without the generic registry keep the meter's old shape. + // Projection registration is an optional child: compositions without the + // generic registry keep the meter's standalone read shape. ctx.inject(['sessionProjections'], (projectionCtx) => { projectionCtx.sessionProjections.register(tokenUsageProjectionDefinition) projectionCtx.sessionProjections.register(contextPressureProjectionDefinition) diff --git a/packages/llm/token-meter/src/projection.ts b/packages/llm/token-meter/src/projection.ts index 2740b4954f..080f593774 100644 --- a/packages/llm/token-meter/src/projection.ts +++ b/packages/llm/token-meter/src/projection.ts @@ -25,8 +25,7 @@ export interface TokenUsageProjection { * `contextWindow` the newest recorded route capacity. Switching models can * therefore pair a fresh capacity with the previous route's pressure until the * next request reports usage. This is an intentional trade — the value is a - * user-facing reference, not a billing or gating input — and it matches how - * the TUI status line has always computed occupancy. See the token-meter + * user-facing reference, not a billing or gating input. See the token-meter * README for the full rationale. */ export interface ContextPressureProjection { diff --git a/packages/lsp/README.i18n.yaml b/packages/lsp/README.i18n.yaml index 837c07cdb1..a41b05f148 100644 --- a/packages/lsp/README.i18n.yaml +++ b/packages/lsp/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/lsp/README.md -README.md: 7b5d9e0f50e733113539cf1ea1ed72e8651ad94c -README.zh.md: a0891a26b6cfbee6ecc2b1a758c43bff5aec1c84 +README.md: 4964d78f1096d1a4bc78fa80c6b5febaf24a5661 +README.zh.md: 93b872002cf1f53cdbb96ff42402bfeb9557575f diff --git a/packages/lsp/README.zh.md b/packages/lsp/README.zh.md index a0891a26b6..93b872002c 100644 --- a/packages/lsp/README.zh.md +++ b/packages/lsp/README.zh.md @@ -2,14 +2,12 @@ [English](README.md) | 中文 -语言服务器能力 seam:抽象 LSP 接口、通用 stdio 提供方,以及面向模型的 `lsp` 工具。这些全是**产品**包。 +语言服务器能力 seam:抽象 LSP 接口、通用 stdio 提供方和面向模型的 `lsp` 工具。这些全是**产品**包。 | 包 | 职责 | ctx key | |---|---|---| -| `lsp/` | 抽象 LSP seam(按品牌化 id + 扩展名映射组织的提供方注册表、逐查询选择、词汇、`LspError`) | `ctx.lsp` | -| `lsp-local/` | 通用多服务器本地后端(spawn、JSON-RPC、查询时临时打开文档) | (在 `ctx.lsp` 上注册提供方) | -| `tool-lsp/` | 面向模型的 `lsp` 工具(四种操作、从 1 开始的 UTF-16 光标坐标) | (注册到 `ctx.tools`) | +| [`lsp/`](lsp/README.md) | LSP 提供方 seam 和共享词汇 | `ctx.lsp` | +| [`lsp-local/`](lsp-local/README.md) | 本地 stdio 语言服务器后端 | 在 `ctx.lsp` 上注册提供方 | +| [`tool-lsp/`](tool-lsp/README.md) | 面向模型的语义导航工具 | 注册到 `ctx.tools` | -接口位于 `lsp/lsp/`。该 seam 恰好公开四种语义操作:`goToDefinition`、`findReferences`、`goToImplementation`、`hover`,且不提供通用 JSON-RPC 逃生口;因此,替换提供方不会改变模型请求导航的方式,也不会让协议载荷或未经评审的修改进入模型契约。提供方注册的是**能力**而非工具;`tool-lsp` 是面向模型名称、schema、提示词指引和呈现的唯一 owner。 - -设计原理见 [LSP 能力 seam Agent Note](../../.agents/notes/implemented/architecture/2026-07-15-lsp-capability-seam.md),其中也解释了文档为何在每次查询时临时打开、本地主机为何通过 Node API 而非 `ctx.fs` 读取,以及扩展名归属为何在同一运行时内互斥。 +提供方注册语义能力;工具负责面向模型的契约。子 README 记录操作、协议和呈现细节,[LSP 能力 seam Agent Note](../../.agents/notes/implemented/architecture/2026-07-15-lsp-capability-seam.md)负责设计原理。 diff --git a/packages/mcp/README.i18n.yaml b/packages/mcp/README.i18n.yaml index a102370f7b..3ce6f13ac3 100644 --- a/packages/mcp/README.i18n.yaml +++ b/packages/mcp/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/mcp/README.md -README.md: 3bde9023368da318ec572a72e2e86a7fa2d3ad8d -README.zh.md: 46a11c94610a7b6d54d81bc104bfc582337f0466 +README.md: cc3440f6cb2e507277e6141393ccf39fc7022d7d +README.zh.md: 0a74f3e9016be95f76104303396dc690888d645d diff --git a/packages/mcp/README.zh.md b/packages/mcp/README.zh.md index 38a5c8b1c1..0a74f3e901 100644 --- a/packages/mcp/README.zh.md +++ b/packages/mcp/README.zh.md @@ -1,9 +1,9 @@ -# MCP:Model Context Protocol +# MCP — 模型上下文协议 [English](README.md) | 中文 -连接 harness 与 MCP 生态的包。 +将 harness 与 MCP 生态系统桥接的包。 -| 包 | 角色 | +| 包 | 职责 | |---|---| -| [`mcp-client/`](mcp-client/README.md) | MCP 客户端桥接:将外部服务器工具注册到 `ctx.tools` | +| [`mcp-client/`](mcp-client/README.md) | MCP 客户端桥接,将外部服务器工具注册到 `ctx.tools` | diff --git a/packages/plan/README.i18n.yaml b/packages/plan/README.i18n.yaml index 7af5d82aa6..3ae6755be6 100644 --- a/packages/plan/README.i18n.yaml +++ b/packages/plan/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/plan/README.md -README.md: eeb58703c34acae0eb2146b87b01a56815e1362a -README.zh.md: c7bfacf4b20c252ae2e81395aca2ca43a48a677a +README.md: 598974e105aaeaf3c35418aec0655de2a5ba6888 +README.zh.md: 0f54299af5d34555d86d82e83b92d185045e770f diff --git a/packages/plan/README.zh.md b/packages/plan/README.zh.md index c7bfacf4b2..0f54299af5 100644 --- a/packages/plan/README.zh.md +++ b/packages/plan/README.zh.md @@ -2,10 +2,10 @@ [English](README.md) | 中文 -Plan mode 是一种按 agent(智能体)分开记录到日志的协作状态。它是单一**产品**包,而非通用模式注册表或能力 seam 三包组合。 +Plan mode 是按 agent(智能体)记录的协作状态,而不是通用模式注册表或能力 seam。 | 包 | 职责 | ctx 键 | |---|---|---| -| `plan-mode/` | `plan/mode` 词汇与折叠、在边界生效的状态、`plan:policy` 引导段、`/plan [message]` 进入命令与 `/plan off` 退出命令,以及面向模型的 `exit_plan_mode` 评审工具 | `ctx.planMode` | +| [`plan-mode/`](plan-mode/README.md) | 负责 plan mode 状态、指引、命令和评审流程 | `ctx.planMode` | -活跃状态是会话日志的纯函数,因此恢复和 fork 无需额外机制即可还原该状态。部署通过 Cordis 配置提供 plan 引导内容,而 `exit_plan_mode` 在 Plan mode 未激活时仍保持注册,以稳定请求工具目录。交互式适配器使用插件拥有的 `/plan` 命令;沙箱模式和审批策略仍是独立的强制执行设置。设计详见 [plan 专用协作状态](../../.agents/notes/implemented/simplification/2026-07-22-plan-specific-collaboration-state.md)。 +[plan 专用协作状态](../../.agents/notes/implemented/simplification/2026-07-22-plan-specific-collaboration-state.md)决策记录了该家族的设计。 diff --git a/packages/plan/plan-mode/README.i18n.yaml b/packages/plan/plan-mode/README.i18n.yaml index 937177785d..1330f33aa9 100644 --- a/packages/plan/plan-mode/README.i18n.yaml +++ b/packages/plan/plan-mode/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/plan/plan-mode/README.md -README.md: 6f0a9ac477b49b96ddfc2ce667e3556dec727569 -README.zh.md: 9d3d98ef387fa2f82bb87bf3e35b2a220bd643ec +README.md: 764b34ca6fc83661886c89d7d454cbe550df47e2 +README.zh.md: e43a06a8111fca39b957a90ff748800e11a37a94 diff --git a/packages/plan/plan-mode/README.md b/packages/plan/plan-mode/README.md index dfa7b7c9a7..764b34ca6f 100644 --- a/packages/plan/plan-mode/README.md +++ b/packages/plan/plan-mode/README.md @@ -18,7 +18,7 @@ The review question declares the `plan-review` presentation intent, naming `Appr When `ctx.commands` is composed, the package registers `/plan [message]` and reserves the exact argument `off` for direct exit. Bare `/plan` selects plan mode; any other non-empty argument selects it first and is then submitted through `agent.steer()`, so it becomes the next step's ordinary logged user message under plan guidance. `/plan off` selects inactive without sending model input; it also cancels a pending entry before plan mode reaches a request. -The TUI consumes the plugin-owned `/plan` command; other front doors may drive the same service directly without defining a second mode vocabulary. +The Web client consumes the plugin-owned `/plan` command; other front doors may drive the same service directly without defining a second mode vocabulary. ## Session projection @@ -94,4 +94,4 @@ Mode transitions do not change the tool catalog; plan arguments and review resul - Plan mode guides rather than enforces; deployments needing a hard boundary must combine independent sandbox and approval controls. - A pending selection made while idle is lost if the process exits before the next boundary, so the UI must reapply it. - Forked agents inherit logged plan state, while newly spawned agents begin inactive; there is no creation-time plan option. -- Only the Web UI renders the `plan-review` intent; the TUI presents the review through its generic question flow, which is answerable but does not read as a plan gate. +- Only the Web UI has a specialized `plan-review` renderer; another interaction provider may present the same request through its generic option flow. diff --git a/packages/plan/plan-mode/README.zh.md b/packages/plan/plan-mode/README.zh.md index 80876b16d0..e43a06a811 100644 --- a/packages/plan/plan-mode/README.zh.md +++ b/packages/plan/plan-mode/README.zh.md @@ -18,7 +18,7 @@ 组合 `ctx.commands` 时,该包会注册 `/plan [message]`,并将参数恰好为 `off` 的情况保留给直接退出。不带参数的 `/plan` 会启用 plan mode;任何其他非空参数都会先启用 plan mode,再通过 `agent.steer()` 提交,因此它会在 plan 引导下成为下一步骤的常规已记录用户消息。`/plan off` 会选择停用状态,不发送模型输入;它还可以在启用 plan mode 的待处理选择到达请求边界之前将其取消。 -TUI 使用该插件提供的 `/plan` 命令;其他入口可以直接驱动同一服务,无需定义第二套 mode 词汇。 +Web 客户端使用该插件提供的 `/plan` 命令;其他入口可以直接驱动同一服务,无需定义第二套 mode 词汇。 ## 会话投影 @@ -94,4 +94,4 @@ mode 转换不改变工具目录;plan 参数与评审结果按常规方式扩 - Plan mode 只进行引导,而不强制执行;需要硬边界的部署必须组合独立的沙箱与批准控制。 - 如果进程在下一个边界之前退出,空闲时作出的待生效选择会丢失,因此 UI 必须重新应用它。 - Fork 的 agent 会继承已记录的 plan 状态,新 spawn 的 agent 则从未激活状态开始;不存在创建时 plan 选项。 -- 只有 Web UI 渲染 `plan-review` 意图;TUI 通过其通用问题流程呈现该评审,可以回答,但读起来不像一个计划关口。 +- 只有 Web UI 具备专用的 `plan-review` 渲染器;其他交互提供方可以通过通用选项流程呈现同一请求。 diff --git a/packages/pty/README.i18n.yaml b/packages/pty/README.i18n.yaml index 2fe1655268..083a144b55 100644 --- a/packages/pty/README.i18n.yaml +++ b/packages/pty/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/pty/README.md -README.md: e54dcf64db083665f37b7dc19a7a92e21494442b -README.zh.md: 0518904bf047d0b03e3f59cc148ef64a039b3205 +README.md: 9c8206464d45b1be1d6ee3861c57c128e77686c5 +README.zh.md: 70d081e60a7db61443ed616b64586a93c119a640 diff --git a/packages/pty/README.zh.md b/packages/pty/README.zh.md index 0518904bf0..70d081e60a 100644 --- a/packages/pty/README.zh.md +++ b/packages/pty/README.zh.md @@ -2,13 +2,13 @@ [English](README.md) | 中文 -`PTY` 的全称是 **Pseudo-Terminal**(伪终端)。这项能力提供持久且限定所有者范围的终端会话,适用于需要跨工具调用保留状态或使用交互式 stdin 的工作流。PTY 是单次 bash 与文件系统工具的补充,不会取代后两者更严格的逐操作契约。 +本家族为交互式或有状态的终端工作提供持久且限定所有者范围的伪终端会话,是单次 bash 执行的补充。 | 包 | 职责 | ctx 键 | |---|---|---| -| [`pty`](pty/README.md)(`@deepseek-ai/dsh-pty`) | 后端注册表、品牌化 id、精确到 agent(智能体)的所有权、会话操作与等待清理完成的机制 | `ctx.pty` | -| `pty-local`(`@deepseek-ai/dsh-pty-local`) | 本地 `node-pty` 后端、就绪检测、有界终端状态、沙箱与进程会话监管 | 注册到 `ctx.pty` | -| `tool-pty`(`@deepseek-ai/dsh-tool-pty`) | 6 个面向模型的工具,以及用于后台发送的通用任务集成 | 注册到 `ctx.tools` | -| `tool-bash-persistent`(`@deepseek-ai/dsh-tool-bash-persistent`) | 一个由所有者隔离可复用 PTY shell 支撑的模型可见 `bash` | 消费 `ctx.pty`,注册到 `ctx.tools` | +| [`pty/`](pty/README.md) | 定义 PTY 服务和会话生命周期 | `ctx.pty` | +| [`pty-local/`](pty-local/README.md) | 提供本地持久终端会话 | 注册到 `ctx.pty` | +| [`tool-pty/`](tool-pty/README.md) | 向模型公开 PTY 会话操作 | 注册到 `ctx.tools` | +| [`tool-bash-persistent/`](tool-bash-persistent/README.md) | 公开可复用的 PTY 后端 bash 工具 | 注册到 `ctx.tools` | -设计与暂缓边界记录在[持久 PTY Agent Note](../../.agents/notes/implemented/feature/2026-07-16-persistent-pty-sessions.md) 中。 +[持久 PTY 决策](../../.agents/notes/implemented/feature/2026-07-16-persistent-pty-sessions.md)记录了该家族的边界。 diff --git a/packages/sandbox/README.i18n.yaml b/packages/sandbox/README.i18n.yaml index 3a88ca7986..c851d420ae 100644 --- a/packages/sandbox/README.i18n.yaml +++ b/packages/sandbox/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/sandbox/README.md -README.md: 4a651f262ee817edf5e9620d1cbf911b2d51b90b -README.zh.md: 9274ef8dccbb388170a0319410660b6ff0d52577 +README.md: 1ba56d22330c26302c9283b2319d983ebf902219 +README.zh.md: 5daf830854297fe14c6560dbcde8a380ad079a0e diff --git a/packages/sandbox/README.zh.md b/packages/sandbox/README.zh.md index 9274ef8dcc..5daf830854 100644 --- a/packages/sandbox/README.zh.md +++ b/packages/sandbox/README.zh.md @@ -2,14 +2,12 @@ [English](README.md) | 中文 -[能力 seam 拆分](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)中负责限制的一半:抽象提供方接口、平台后端和共享策略归属位置。消费方把即将 spawn 的精确 argv 交给 `ctx.sandbox`,改为 spawn 返回的已包装 argv;完整的 `SandboxExecutionPolicy`(模式 + 工作区根目录)随每次能力调用传递,其中受限制的子集成为提供方的 `SandboxPolicy`。因此,不同会话与消费方可以同时按不同策略施加限制。这些均为**产品**包。 +本家族将逐会话限制策略应用于进程执行。它覆盖与宿主共享文件系统和内核的子进程;隔离环境会替换完整的能力实现,而不是在此注册。 | 包 | 职责 | ctx key | |---|---|---| -| `sandbox/` | 抽象进程沙箱 seam(`SandboxProvider` 契约 + 模式/强制执行/策略词汇),加共享 ESCALATION 工具包(`approveEscalation`、权限逐级严格扩大的阶梯、拒绝/提示标记),以及所有强制执行方言共享的 `writableRoots` 派生 | `ctx.sandbox` | -| `sandbox-local/` | 按平台链选择的本地后端:Linux 使用 `bwrap`,否则使用 `landlock-run` launcher(通过 npm 分发的 [`node-addon-landlock-run`](https://www.npmjs.com/package/node-addon-landlock-run) 家族,在自身仓库构建发布);darwin 使用 `sandbox-exec`/Seatbelt。多候选链会执行功能探测,唯一候选项直接选择,结论缓存,失败时默认拒绝 | (注册 `ctx.sandbox`) | -| `sandbox-policy/` | 策略解析器:部署回退值,加每个会话的持久模式与不可变 cwd 根目录。两个强制执行家族都消费完整的逐调用结果,因此 bash 与 fs 不会限制到不同根目录 | `ctx.sandboxPolicy` | +| [`sandbox/`](sandbox/README.md) | 定义进程沙箱服务和共享升权词汇 | `ctx.sandbox` | +| [`sandbox-local/`](sandbox-local/README.md) | 提供本地平台限制后端 | 注册到 `ctx.sandbox` | +| [`sandbox-policy/`](sandbox-policy/README.md) | 解析持久的逐会话沙箱策略 | `ctx.sandboxPolicy` | -该 seam 只限制与宿主共享文件系统和内核的子进程。容器、microVM 和远程执行器都不是这里的后端:它们会以环境一致的分组替换整个能力实现(`ctx.bash`、`ctx.fs`);边界记录在[沙箱 Agent Note](../../.agents/notes/implemented/feature/2026-07-06-sandbox.md)中。 - -当前消费方:[`bash/bash-sandbox`](../bash/bash-sandbox/)(包装 `['bash', '-c', command]` 时会调用 `ctx.sandbox`)和 [`fs/fs-sandbox`](../fs/fs-sandbox/)(进程内路径隔离,而非 argv 包装层;读取 `ctx.sandboxPolicy`,对写入/编辑强制执行共享模式)。跨家族边界是沙箱 Agent Note 的[跨家族 fs 沙箱](../../.agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.md)阶段;共享词汇使两个家族可以向模型传授同一种拒绝标记与升权流程。 +[沙箱决策](../../.agents/notes/implemented/feature/2026-07-06-sandbox.md)记录了能力边界,[文件系统集成决策](../../.agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.md)记录了跨家族策略的使用方式。 diff --git a/packages/sandbox/sandbox-local/README.i18n.yaml b/packages/sandbox/sandbox-local/README.i18n.yaml index cb9aeeec8b..4476ca8df4 100644 --- a/packages/sandbox/sandbox-local/README.i18n.yaml +++ b/packages/sandbox/sandbox-local/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/sandbox/sandbox-local/README.md -README.md: f7dd5619fa69ae226def78415a91d51ba99e0a65 -README.zh.md: c8abe05408ac8917b2c5dc39acdbdf9d9539744d +README.md: 17ae8c98a1cf9e69427806fe6ad3ba426892c114 +README.zh.md: 9c1fd304a487f3d3f19d3f5e9985229b5c78c6e7 diff --git a/packages/sdk/README.i18n.yaml b/packages/sdk/README.i18n.yaml index 31806b800f..738a0610ab 100644 --- a/packages/sdk/README.i18n.yaml +++ b/packages/sdk/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/sdk/README.md -README.md: 0dcf3655fc4d20452981f83000f9b9b586ede17b -README.zh.md: cc5927f56654ece0caef7d329798e581b3217eeb +README.md: 3f99d6d45ddc64ac068457b0d03e533d21451a5f +README.zh.md: 003383c5f6238b37893927813959d52d96350c3e diff --git a/packages/sdk/README.zh.md b/packages/sdk/README.zh.md index cc5927f566..003383c5f6 100644 --- a/packages/sdk/README.zh.md +++ b/packages/sdk/README.zh.md @@ -2,18 +2,15 @@ [English](README.md) | 中文 -用于创建、编辑、构建和运行 DeepSeek Harness 项目的开发者工具,外加从另一进程驱动 harness 运行时的客户端 SDK 栈。 - -[功能 Agent Note](../../.agents/notes/proposed/feature/2026-07-14-sdk-developer-projects.md)负责开发者工作流;[架构 Agent Note](../../.agents/notes/proposed/architecture/2026-07-15-sdk-project-editing-architecture.md)负责包与项目编辑边界;[TypeScript SDK Agent Note](../../.agents/notes/implemented/feature/2026-07-27-typescript-sdk-and-sdk-subagent-backend.md)负责客户端 SDK 栈。 +本分组包含 Harness 项目的开发者工具,以及从另一个进程驱动 Harness 运行时的客户端栈。 | 包 | 职责 | |---|---| -| [`helper`](helper/README.md) | 项目聚合、编辑会话、内置功能、项目文档、模板、包管理器与提示词抽象 | -| [`scripts`](scripts/README.md) | `dsh-sdk` 启动器:`start`、`dev`、`build` 和交互式 `config` | -| [`create-sdk`](create-sdk/README.md) | `npm create @deepseek-ai/sdk` 初始化器 | -| [`sdk-protocol`](sdk-protocol/README.md) | 共享的 SDK 运行时通信协议:以换行符分隔的 JSON-RPC 传输 + 具名请求/通知类型 | -| [`sdk-client`](sdk-client/README.md) | TypeScript 客户端 SDK:通过 stdio JSON-RPC 驱动 harness 运行时子进程(Python SDK 的设计孪生) | +| [`helper/`](helper/README.md) | 提供共享的项目编辑领域 | +| [`scripts/`](scripts/README.md) | 提供 `dsh-sdk` 项目命令 | +| [`create-sdk/`](create-sdk/README.md) | 创建新的 SDK 项目 | +| [`sdk-protocol/`](sdk-protocol/README.md) | 定义 SDK 运行时协议格式 | +| [`sdk-client/`](sdk-client/README.md) | 通过 TypeScript 客户端 API 驱动 Harness 运行时 | +| [`telemetry/`](telemetry/README.md) | 提供启动器遥测、同意和脱敏原语 | -`@deepseek-ai/create-sdk` 是仓库 `@deepseek-ai/dsh-*` 命名规则的唯一例外:npm 的 scoped initializer 约定要求使用该名称,才能支持 `npm create @deepseek-ai/sdk`。 - -生成的项目始终以 `cordis.yml` 作为唯一运行时插件树。`dsh-sdk dev` 只是围绕同一文件增加 TypeScript 与本地工作区解析,不会创建仅供开发环境使用的配置。 +`@deepseek-ai/create-sdk` 遵循 npm 的 scoped initializer 命名约定;其他包遵循仓库的 `@deepseek-ai/dsh-*` 约定。参见[开发者项目工作流](../../.agents/notes/proposed/feature/2026-07-14-sdk-developer-projects.md)、[项目编辑架构](../../.agents/notes/proposed/architecture/2026-07-15-sdk-project-editing-architecture.md)和 [TypeScript SDK 设计](../../.agents/notes/implemented/feature/2026-07-27-typescript-sdk-and-sdk-subagent-backend.md)。 diff --git a/packages/sdk/helper/src/project/project-edit-session.ts b/packages/sdk/helper/src/project/project-edit-session.ts index e8b88f91f3..4c3d39c07c 100644 --- a/packages/sdk/helper/src/project/project-edit-session.ts +++ b/packages/sdk/helper/src/project/project-edit-session.ts @@ -90,7 +90,7 @@ export class ProjectEditSession implements FeatureProjectView { this.profile = source.profile this.documents = source.cloneDocuments() for (const feature of registry.all()) { - /* v8 ignore next -- no current builtin is interface-specific after TUI removal */ + /* v8 ignore next -- no current built-in feature is interface-specific */ if (!feature.isApplicable(this.profile)) continue const installation = feature.inspect(this) this.states.set(feature.id, { @@ -508,7 +508,7 @@ export class ProjectEditSession implements FeatureProjectView { const view = this.projectView(profile) for (const feature of this.registry.all()) { const state = this.states.get(feature.id) - /* v8 ignore next 5 -- no current builtin is interface-specific after TUI removal */ + /* v8 ignore next 5 -- no current built-in feature is interface-specific */ if (!feature.isApplicable(profile)) { if (state?.state === 'enabled') { throw new Error(`feature ${feature.id} is not available for ${profile.runInterface}`) diff --git a/packages/sdk/scripts/src/config/config-workflow.ts b/packages/sdk/scripts/src/config/config-workflow.ts index 9c578f3297..73bb07ea02 100644 --- a/packages/sdk/scripts/src/config/config-workflow.ts +++ b/packages/sdk/scripts/src/config/config-workflow.ts @@ -135,7 +135,7 @@ export class ConfigWorkflow { runInterface: targetRunInterface(project.profile.runInterface, desiredByTarget), } for (const feature of features) { - /* v8 ignore next -- no current builtin is interface-specific after TUI removal */ + /* v8 ignore next -- no current built-in feature is interface-specific */ if (!feature.isApplicable(targetProfile)) desiredByTarget.delete(featureTarget(feature)) } diff --git a/packages/sdk/sdk-client/README.i18n.yaml b/packages/sdk/sdk-client/README.i18n.yaml index d26108b4e7..3a2a16ae0c 100644 --- a/packages/sdk/sdk-client/README.i18n.yaml +++ b/packages/sdk/sdk-client/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/sdk/sdk-client/README.md -README.md: eb0387292fb0093b3ed9360e8aec087201b611f0 -README.zh.md: c264557984f5067d65e7fc60c7afd855dcf26715 +README.md: 5bce396aa36d32b0a3bd7716e1feea8ca9c9038c +README.zh.md: cf72caa669d05358c989be4ed3f084541615052f diff --git a/packages/session-persistence/README.i18n.yaml b/packages/session-persistence/README.i18n.yaml index 8893fbd132..85eb2bd27d 100644 --- a/packages/session-persistence/README.i18n.yaml +++ b/packages/session-persistence/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/session-persistence/README.md -README.md: ac4e0a8310152b9d2ba5daae61fbbf1eb0ed54ec -README.zh.md: 0903bbc55b06702f748f80643666bf6c4086a633 +README.md: 060f757b3568318b0be4b01b3a10d019d28eac3b +README.zh.md: 67022565f7dabcbbccee30d5ce60fa559088ca39 diff --git a/packages/session-persistence/README.zh.md b/packages/session-persistence/README.zh.md index 0903bbc55b..67022565f7 100644 --- a/packages/session-persistence/README.zh.md +++ b/packages/session-persistence/README.zh.md @@ -2,13 +2,13 @@ [English](README.md) | 中文 -持久会话的持久化 seam 及其存储后端。接口包负责抽象 `SessionPersistence` 服务和共享写入协调器;后端是注册到 `ctx.sessionPersistence` 的具体实现。全部都是**产品**包。 +本家族定义持久会话数据的持久化机制、语义检查点策略以及随产品交付的存储后端。 | 包 | 职责 | ctx 键 | |---|---|---| -| `session-persistence/` | 持久化 seam + 共享写入协调器 | `ctx.sessionPersistence` | -| `session-checkpoint-policy/` | agent(智能体)请求和工具执行的语义持久性屏障 | (包装 `ctx.llm` / `ctx.tools`,监听 agent 事件) | -| `session-persistence-jsonl/` | JSONL 伴随文件持久化后端 | (注册到 `ctx.sessionPersistence`) | -| `session-persistence-sqlite/` | SQLite 持久化后端 | (注册到 `ctx.sessionPersistence`) | +| [`session-persistence/`](session-persistence/README.md) | 定义持久化服务和共享写入协调机制 | `ctx.sessionPersistence` | +| [`session-checkpoint-policy/`](session-checkpoint-policy/README.md) | 应用语义持久性检查点 | 包装 `ctx.llm` 和 `ctx.tools` | +| [`session-persistence-jsonl/`](session-persistence-jsonl/README.md) | 将会话持久化到 JSONL 文件 | 注册到 `ctx.sessionPersistence` | +| [`session-persistence-sqlite/`](session-persistence-sqlite/README.md) | 将会话持久化到 SQLite | 注册到 `ctx.sessionPersistence` | -接口位于 `session-persistence/session-persistence/`;后端是同级包。新存储后端归入此处,并注册到 `ctx.sessionPersistence`。详见[会话持久化](../../.agents/notes/implemented/architecture/2026-06-14-session-persistence.md)。 +[会话持久化决策](../../.agents/notes/implemented/architecture/2026-06-14-session-persistence.md)记录了该家族的设计。 diff --git a/packages/session-projection/README.i18n.yaml b/packages/session-projection/README.i18n.yaml index 126a4fd726..5e03069b99 100644 --- a/packages/session-projection/README.i18n.yaml +++ b/packages/session-projection/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/session-projection/README.md -README.md: ae80a905705d205adb4a1ee66c72fa28d0d8b6d6 -README.zh.md: 3d4a4aa3151cc92a8b887e85c911b7f6b59bf52f +README.md: f1fc8337bbea6663915c19f39fdf02f14190370d +README.zh.md: 3323166f496fabafabcd96abeaec707a28ce552a diff --git a/packages/session-projection/README.zh.md b/packages/session-projection/README.zh.md index 3d4a4aa315..3323166f49 100644 --- a/packages/session-projection/README.zh.md +++ b/packages/session-projection/README.zh.md @@ -1,10 +1,10 @@ -# session-projection/ +# session-projection/:会话投影能力家族 [English](README.md) | 中文 -会话投影能力家族:领域 host 插件经由此 seam,把日志派生的按会话状态的当前全量值供给客户端载体。 +本家族向客户端载体提供从日志派生的当前逐会话状态。 -| 包 | ctx 键 | 职责 | +| 包 | 职责 | ctx 键 | |---|---|---| -| [`session-projection`](session-projection/README.md) | `sessionProjections` | 接口包:merge-extensible 的 `SessionProjectionMap` 类型表、`ProjectionDefinition` 单元契约,以及供载体同步读取的主动驱动注册表 | -| [`session-projection-cache`](session-projection-cache/README.md) | `sessionProjectionCache` | 持久投影缓存:基于域数据形态的按会话单元检查点持久化、带 turn/end + detach 两个必写点的节流后写,以及冷读阶梯(缓存行 + 持久化尾部回放) | +| [`session-projection/`](session-projection/README.md) | 定义并驱动会话投影单元 | `ctx.sessionProjections` | +| [`session-projection-cache/`](session-projection-cache/README.md) | 持久化并恢复投影检查点 | `ctx.sessionProjectionCache` | diff --git a/packages/session-projection/session-projection/README.i18n.yaml b/packages/session-projection/session-projection/README.i18n.yaml index 92935e92c0..34c1910b16 100644 --- a/packages/session-projection/session-projection/README.i18n.yaml +++ b/packages/session-projection/session-projection/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/session-projection/session-projection/README.md -README.md: f4898b8e567fa5998c18111c5f4e27a8a350a42e -README.zh.md: 520205e616cdc1421c8f33f00d2013a6e0947d3d +README.md: a42e88c262915fc5e53cd72205079cbc8029a8df +README.zh.md: f60a48bd41f0c33edb85a495ada9b6818ec46ee5 diff --git a/packages/session-projection/session-projection/README.md b/packages/session-projection/session-projection/README.md index f4898b8e56..a42e88c262 100644 --- a/packages/session-projection/session-projection/README.md +++ b/packages/session-projection/session-projection/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -Session-projection seam. It owns `ctx.sessionProjections`, the registry that DRIVES every registered projection unit forward over committed session events and serves finished whole values to carriers (the api-proxy history tail page and `session/projection` push frame today; TUI/ACP/headless consumers later). A domain registers pure mathematics; the framework owns the drive. Design authority: the [session-projection RFC](../../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md). +Session-projection seam. It owns `ctx.sessionProjections`, the registry that drives every registered projection unit over committed session events and serves finished whole values to carriers, currently the api-proxy history tail page and `session/projection` push frame. A domain registers pure mathematics; the framework owns the drive. The [session-projection RFC](../../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md) records the design rationale. ## Service: `SessionProjectionRegistry` (ctx key: `sessionProjections`) diff --git a/packages/session-projection/session-projection/README.zh.md b/packages/session-projection/session-projection/README.zh.md index 520205e616..f60a48bd41 100644 --- a/packages/session-projection/session-projection/README.zh.md +++ b/packages/session-projection/session-projection/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -会话投影 seam。它拥有 `ctx.sessionProjections`——该注册表驱动每个已注册的投影单元在已提交会话事件上前进,并向载体供给成品全量值(今天是 api-proxy 历史尾页与 `session/projection` 推送帧;日后是 TUI、ACP(Agent Client Protocol)、headless 消费方)。领域注册的只是纯数学;驱动权归框架。设计权威:[session-projection RFC](../../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md)。 +会话投影 seam。它拥有 `ctx.sessionProjections`:该注册表在已提交的会话事件上驱动每个已注册的投影单元,并向载体提供完整的最终值,目前包括 api-proxy 历史尾页和 `session/projection` 推送帧。领域注册的只是纯数学;驱动权归框架。[session-projection RFC](../../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md)记录了设计理由。 ## 服务:`SessionProjectionRegistry`(ctx 键:`sessionProjections`) diff --git a/packages/session-projection/session-projection/src/index.ts b/packages/session-projection/session-projection/src/index.ts index dbf0c5ee6e..90c36ed579 100644 --- a/packages/session-projection/session-projection/src/index.ts +++ b/packages/session-projection/session-projection/src/index.ts @@ -5,8 +5,8 @@ * forward eagerly over committed session events. Domain host plugins * contribute pure mathematics (init/apply/view); the framework owns the * subscription, the per-session watermark cache, and change notification; - * carriers (api-proxy today, TUI/ACP/headless later) consume the snapshot - * read face and the change feed. Neither side knows the other + * carriers consume the snapshot read face and the change feed. Neither side + * knows the other * (capability-seam three-way split). Design authority: the session-projection * RFC (.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md). * diff --git a/packages/session-query/README.i18n.yaml b/packages/session-query/README.i18n.yaml index d55821c109..3911bf76a0 100644 --- a/packages/session-query/README.i18n.yaml +++ b/packages/session-query/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/session-query/README.md -README.md: 38edbf6b0303e3d5a7bd0dc1d180cd127c60f9dc -README.zh.md: 784cb79b01f178db68e0ae8d5f4ba892da02f852 +README.md: a9c9f33e2dc67e837c2be728d2b1a937ce50f13d +README.zh.md: a29dedba09271baa5836cb2d9d57c753ee41c660 diff --git a/packages/session-query/README.zh.md b/packages/session-query/README.zh.md index 784cb79b01..a29dedba09 100644 --- a/packages/session-query/README.zh.md +++ b/packages/session-query/README.zh.md @@ -2,12 +2,10 @@ [English](README.md) | 中文 -针对实时和持久会话日志提供可信的精确读取、关系跟踪、与提供方无关的语义过滤和 SQLite 全文搜索。 +本家族提供经过授权的实时与持久会话日志检索,且独立于压缩(compaction)。 | 包 | 职责 | ctx 键 | |---|---|---| -| [`session-query/`](session-query/README.md) | 组合式服务契约:提供具体的逻辑语料库读取、跟踪和语义过滤,以及抽象全文方法 | `ctx.sessionQuery` | -| [`session-query-sqlite/`](session-query-sqlite/README.md) | 具体服务后端:使用 SQLite FTS5 持久基库和实时覆盖层 | `ctx.sessionQuery` | -| [`tool-session-query/`](tool-session-query/README.md) | 工作区授权的面向模型搜索、血缘、关系和精确事件工具 | 无 | - -查询服务与压缩(compaction)无关:它读取规范血缘、接口操作、已记录来源信息和语义事件文本,但不参与压缩策略或执行。一个抽象服务组合全部查询操作;一个具体后端负责全文生命周期,无需提供方注册表或协调器;消费方将过大的纯文本结果交给通用执行后 spill 策略。 +| [`session-query/`](session-query/README.md) | 定义可信读取、关系查询和搜索操作 | `ctx.sessionQuery` | +| [`session-query-sqlite/`](session-query-sqlite/README.md) | 使用 SQLite 全文搜索实现会话查询 | `ctx.sessionQuery` | +| [`tool-session-query/`](tool-session-query/README.md) | 向模型公开经过工作区授权的会话查询 | 注册到 `ctx.tools` | diff --git a/packages/settings/settings-local/README.i18n.yaml b/packages/settings/settings-local/README.i18n.yaml index 0b1c7e9064..ec26e4241f 100644 --- a/packages/settings/settings-local/README.i18n.yaml +++ b/packages/settings/settings-local/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/settings/settings-local/README.md README.md: d1f3d755f9073acdf6fcfc5d1de883d74cc023c4 -README.zh.md: 3a290c2795e6d1944c5bce6edb99aab53f2728ee +README.zh.md: 03c7ef7c35312d9e1e4a86b0df71412875af7d6c diff --git a/packages/settings/settings-local/README.zh.md b/packages/settings/settings-local/README.zh.md index 3a290c2795..03c7ef7c35 100644 --- a/packages/settings/settings-local/README.zh.md +++ b/packages/settings/settings-local/README.zh.md @@ -28,15 +28,15 @@ - **按内容抑制自写。** provider 缓存最后可用文本;watcher 事件内容与缓存相同(含自己的写入)即为 no-op。 - **Host 配置适配器会收到解析后的路径。** `ctx.settings.documentPath` 是 `resolveSpec()` 得出的绝对文件名,包括自定义 YAML/JSON 路径;`prepareDocument()` 会保留现有文件,或在 Host 打开文档前,以仅属主可访问的权限独占创建缺失的空文件。浏览器只收到可用性标志,绝不重建 `$DSH_HOME`,也绝不提交文件系统目标。 -## Model Experience +## 模型体验 间接生效:本 provider 只存储并发布 namespace 分节,模型效果经由 `ctx.settings` 的消费插件产生,由各消费者自己的文档描述。 -#### KV Cache effect +#### KV Cache 影响 无直接失效;请求前缀的变更由消费插件拥有。 -## Known Limitations and Deferred Work +## 已知限制与暂缓事项 - **同 namespace 冲突仍是后写胜出** — 写锁加读-改-写让并发写入者不会丢掉彼此的 namespace,但两个写入者编辑同一个 namespace 时仍以较后的写入为准;没有按值合并,也没有修订检查。 - **漏掉的 watcher 事件在下一个信号前保持不可见** — 读取从不重新 stat 文件,因此 watcher 漏报的变更只会在下一个事件、下一次写入或重启时被并入。 diff --git a/packages/settings/settings/README.i18n.yaml b/packages/settings/settings/README.i18n.yaml index d0cfacf720..b2549e6be1 100644 --- a/packages/settings/settings/README.i18n.yaml +++ b/packages/settings/settings/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/settings/settings/README.md README.md: 0841624e553364fa03f7a1f1209aa72ab13d97e4 -README.zh.md: fa84ca198ba7383f8fbd7b29a53d74f2c375bd5e +README.zh.md: e8fe4485c2cf6cb6b8710347ebb8dcd1e8424b38 diff --git a/packages/settings/settings/README.zh.md b/packages/settings/settings/README.zh.md index fa84ca198b..e8fe4485c2 100644 --- a/packages/settings/settings/README.zh.md +++ b/packages/settings/settings/README.zh.md @@ -18,7 +18,7 @@ - 解析值是深冻结快照。每次提交后观察者收到 `(next, prev)`:同一回调的调用异步、逐次、按提交顺序执行(慢的旧调用绝不会覆盖更新的结果),异常——同步抛出与异步拒绝——均被隔离。watch 的 disposer 返回后不再启动新的调用(已排队的那一次会被跳过);已启动的调用仍会结算。`settings/updated` 事件逐 listener 扇出,一个抛错的 listener 不会饿死其余 listener;异步 listener 的拒绝会被隔离并记入日志,这正是 `INVARIANT` 编码的失败只从同步 listener 重新抛出的原因。 - 服务卸载先拒绝新写入与观察者调用的启动,再排干全部排队写入与已启动的观察者调用后才完成;registrant fiber 在写入途中被 dispose 时,该写入仍到达存储,但不向任何人提交或通知。 -## Provider 契约 +## 提供方契约 子类实现 `writable`、`load()`、`persist(ns, section)`,可选择为一个本地用户可编辑文件重写 `documentPath` 与 `prepareDocument()`,并通过受保护的 `publish(doc)` 推入外部观察到的文档。基类 service init 在服务可注入前加载并发布一次文档;自有 init(watcher、连接)的 provider 先经 `yield* super[Service.init]()` 委托。publish 时每个已注册 namespace 独立重解析:非法分节保留该 namespace 的最后可用值并告警——热重载绝不拖垮进程;启动期与注册期校验则立即报错。 @@ -28,15 +28,15 @@ `settings/document-updated (ns, revision)` 在**原始**用户分节发生变化时触发,无论解析值是否随之改变。配置界面需要的是这一个:存入一个与组合 `base` 相同的覆盖值不会改变解析值,却改变了文档的说法(该字段从继承变成了覆盖),也推进了每个已打开编辑器所持有的 revision。监听器的收容方式与 `settings/updated` 相同。 -## Model Experience +## 模型体验 间接生效:消费插件从各自 namespace 解析影响模型的值(例如默认模型路由);效果由各消费者自己的文档描述。 -#### KV Cache effect +#### KV Cache 影响 无直接失效;把设置值折叠进请求前缀的消费者拥有该变更。 -## Known Limitations and Deferred Work +## 已知限制与暂缓事项 - **单一用户层** — 解析只认识 schema 默认值、一个组合 `base` 与一个用户文档;尚无 project/managed 分层或按值溯源。 - **`redactSecrets` 并非一条可被证明的协议边界**:walker 只跟随 `object`/`dict`/`array`,因此只能经由 union、intersection 或 transform 抵达的 `role('secret')` 会被**原样**返回,且 `secrets` 列表为空;而 `schema.toJSON()` 会把 secret 字段的 `.default(...)` 一并带给每个客户端。这两种情况都不会被拒绝;机密无法经由被遍历的容器抵达的 schema,绝不可注册到暴露于协议的 namespace 上。真正的答案是一个 fail-closed 的 `describeForWire()`——它拒绝自己无法证明安全的 schema,并对序列化信封与错误文本做净化——此项暂缓。 diff --git a/packages/skill/README.i18n.yaml b/packages/skill/README.i18n.yaml index 88c51eca62..2d424c61dd 100644 --- a/packages/skill/README.i18n.yaml +++ b/packages/skill/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/skill/README.md -README.md: 4fb41dda5d9f001f5d7c47a29f7743291c0b0822 -README.zh.md: ebe9ca40a5967d3167bee9f30ba3d3e9c417f636 +README.md: d10049ac3e741350fddb42f430b70f063da1d12d +README.zh.md: 67f2da6f75edecd180ff861122c6392684ed9bdb diff --git a/packages/skill/README.zh.md b/packages/skill/README.zh.md index ebe9ca40a5..67f2da6f75 100644 --- a/packages/skill/README.zh.md +++ b/packages/skill/README.zh.md @@ -1,13 +1,13 @@ -# skill/ - skill(技能)能力家族 +# skill/:skill(技能)能力家族 [English](README.md) | 中文 -可复用 agent(智能体)指令的规范能力 seam 由三个包组成:提供方注册表、本地实现,以及面向模型的目录/loader 消费方。全部均为**产品**包。 +本家族发现可复用的 agent(智能体)指令,并通过与提供方无关的目录和 loader 将其公开给模型。 | 包 | 职责 | ctx 键 | |---|---|---| -| `skill/` | 提供方注册表、优先级解析、完整/不完整目录快照和完整定义查找 | `ctx.skills` | -| `skill-local/` | 带目录成员关系监视的项目/自定义/用户文件系统提供方 | (注册到 `ctx.skills`) | -| `tool-skill/` | 初始目录和替换目录,以及面向模型的 `skill` loader | (注册到 `ctx.tools`) | +| [`skill/`](skill/README.md) | 定义 skill 提供方注册和查找 | `ctx.skills` | +| [`skill-local/`](skill-local/README.md) | 从本地文件系统发现 skill | 注册到 `ctx.skills` | +| [`tool-skill/`](tool-skill/README.md) | 发布 skill 目录和面向模型的 loader | 注册到 `ctx.tools` | -接口位于 `skill/skill/`。提供方同步注册,并通过 `ctx.skills` 执行异步发现;`tool-skill` 只消费该接口,因此嵌入式或远程提供方可替换或补充 `skill-local`,无需改变面向模型的契约。`agent-core` 默认加载该家族,但它仍然是核心控制主干之外的能力,与 [`bash/`](../bash/README.md)、[`fs/`](../fs/README.md)、[`web/`](../web/README.md) 和 [`subagent/`](../subagent/README.md) 并列。 +该能力位于核心控制主干之外,可以使用本地、嵌入式或远程提供方,而无需更改面向模型的契约。 diff --git a/packages/spill/README.i18n.yaml b/packages/spill/README.i18n.yaml index 78759f933d..95800ac03e 100644 --- a/packages/spill/README.i18n.yaml +++ b/packages/spill/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/spill/README.md -README.md: cb6f1e9af94ee5b0d0c06a8c4293e37e2d1eb856 -README.zh.md: e43d30e1038877467a8c42c12c878de19cfc24e2 +README.md: b96375b1a332cd6e3e4da9c8b1c98011aa97c8bc +README.zh.md: c8495068edf1035937c99198dfdb5db65c9a9e59 diff --git a/packages/spill/README.zh.md b/packages/spill/README.zh.md index e43d30e103..c8495068ed 100644 --- a/packages/spill/README.zh.md +++ b/packages/spill/README.zh.md @@ -1,15 +1,13 @@ -# spill/ - spill 存储能力家族 +# spill/:工具输出 spill 能力家族 [English](README.md) | 中文 -工具输出 spill 的能力 seam:一个抽象存储接口、一个本地文件系统实现,以及一个使用该实现的工具结果策略。全部均为**产品**包。 +本家族持久化过大的工具输出,并以有界预览和取回定位信息替换内联结果。 | 包 | 职责 | ctx 键 | |---|---|---| -| `spill/` | 抽象 spill 存储 seam(`saveText`:持久化过大的工具文本,返回定位信息与取回指引) | `ctx.spillStore` | -| `spill-local/` | 本地文件系统后端:名称可防止路径遍历的私有会话级文件 | (注册到 `ctx.spillStore`) | -| `spill-policy/` | `tools/post-execute` 策略:将过大的纯文本结果替换为预览和 spill 定位信息 | (无服务接口) | +| [`spill/`](spill/README.md) | 定义 spill 存储 | `ctx.spillStore` | +| [`spill-local/`](spill-local/README.md) | 在会话范围的本地文件中存储 spill 文本 | 注册到 `ctx.spillStore` | +| [`spill-policy/`](spill-policy/README.md) | 应用执行后 spill 策略 | 监听 `ctx.tools` | -接口位于 `spill/spill/`。这种拆分方式与 bash/fs 相同:seam 只负责存储,`spill-local` 负责文件系统机制,`spill-policy` 负责决定何时 spill 以及面向模型的通知。预览机制位于 [`util/retention`](../util/README.md);策略只组合两者,不会让任何一方承担对方的职责。 - -设计原理见[工具输出 spill Agent Note](../../.agents/notes/implemented/architecture/2026-07-08-tool-output-spill-files.md),其中说明了为什么最终结果 spill 要与工具自行提前 spill(bash 流、subagent rollout)分离,以及为什么创建操作应由运行时 spill seam 而非面向模型的 `write` 工具承担。 +参见[工具输出 spill 决策](../../.agents/notes/implemented/architecture/2026-07-08-tool-output-spill-files.md),其中记录了存储、保留和工具自有输出处理之间的边界。 diff --git a/packages/storage/README.i18n.yaml b/packages/storage/README.i18n.yaml index 49c1028a10..58fc19a4f8 100644 --- a/packages/storage/README.i18n.yaml +++ b/packages/storage/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/storage/README.md -README.md: c9eca7355fd63e1023e9723486c86673e4c3574b -README.zh.md: 43449f0a7a72e48dbd8a6b5d731f130f04f2d780 +README.md: a5f9d0204b93445699d28b1285a4a18f0408930e +README.zh.md: 0710dfb57f96011755b253ad95a0f9d2c0923a70 diff --git a/packages/storage/README.zh.md b/packages/storage/README.zh.md index 43449f0a7a..0710dfb57f 100644 --- a/packages/storage/README.zh.md +++ b/packages/storage/README.zh.md @@ -2,13 +2,13 @@ [English](README.md) | 中文 -存储家族持久化会话事件日志之外的一切数据:命名后端与类型化数据形式在一个中心相接。设计记录:[领域 KV 存储 Agent Note](../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.zh.md)。 +本家族通过具名后端和类型化数据形式,持久化会话事件日志以外的应用数据。 | 包 | 职责 | ctx key | |---|---|---| -| `storage/` | 中心:命名后端注册表 + 可通过合并扩展的数据形式挂载、后端分面词汇、共享一致性测试套件 | `ctx.storage` | -| `storage-json/` | JSON 后端:每个单元一个人类可读文件,以原子方式重写整个文件 | 注册后端 `json` | -| `storage-sqlite/` | SQLite 后端:一个数据库承载所有已路由单元,每行一个文档 | 注册后端 `sqlite` | -| `domain/` | 领域数据形式:经 zod 验证的记录、逐领域写入链、`domain/changed` 事件、按配置路由后端 | `ctx.storageDomain` + `ctx.storage.domain` | +| [`storage/`](storage/README.md) | 将已注册后端与类型化数据形式连接起来 | `ctx.storage` | +| [`storage-json/`](storage-json/README.md) | 在 JSON 文件中存储数据 | 注册后端 `json` | +| [`storage-sqlite/`](storage-sqlite/README.md) | 在 SQLite 中存储数据 | 注册后端 `sqlite` | +| [`storage-domain/`](storage-domain/README.md) | 提供经过验证的领域记录存储 | `ctx.storageDomain` | -每个后端拥有一种介质,并公开数据形状**分面**(目前为 `kv`;为未来的会话后端迁移预留追加日志分面)。每个后端插件都会在注册后发布内部生命周期服务;领域插件在公开自身服务前注入每个已配置的后端 key,因此配置树中的条目顺序不影响启动顺序。消费方绝不直接接触后端,而是注入 `storageDomain` 并通过它打开已声明的领域。 +消费方使用数据形式,而不是直接访问后端。[领域存储决策](../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.md)记录了该家族的设计。 diff --git a/packages/storage/storage/README.i18n.yaml b/packages/storage/storage/README.i18n.yaml index 44493d3f84..4378ae55ad 100644 --- a/packages/storage/storage/README.i18n.yaml +++ b/packages/storage/storage/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/storage/storage/README.md -README.md: 994285842925539e73f8f11780f19495f71d0280 -README.zh.md: 9e03b5cf09350d3af134e2c7b099f9379fa917f1 +README.md: bf827220afe0d8b1cbc53b3a12e6d0034004e1d2 +README.zh.md: 6c1a5b4c05f665cdedd16fa67b47c87a04737e7b diff --git a/packages/storage/storage/README.md b/packages/storage/storage/README.md index 9942858429..bf827220af 100644 --- a/packages/storage/storage/README.md +++ b/packages/storage/storage/README.md @@ -2,22 +2,13 @@ English | [中文](README.zh.md) -Storage hub (`ctx.storage`) for non-session data: a named backend registry plus mounted data-form facilities. The hub performs no IO itself — backends own media, data forms own semantics. Design and trade-offs: [domain KV storage Agent Note](../../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.zh.md). +Storage hub (`ctx.storage`) for non-session data: a named backend registry plus mounted data-form facilities. The hub performs no IO itself — backends own media, and data forms own semantics. The [storage family overview](../README.md) maps those packages; the [domain KV storage Agent Note](../../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.md) records the design rationale. ## Shape - `ctx.storage.backend` — name → backend table. Multiple backends stay mounted side by side (`json`, `sqlite`); which backend serves a consumer is that consumer's configuration (the domain layer's route table), never a hub-global choice. `register()` returns the disposer; duplicate names and unknown lookups fail loud. - `ctx.storage.mount(form, facility)` / `ctx.storage.form(form)` — data-form mounting. `StorageForms` is merge-extensible; the domain layer merges `domain` and is reached as `ctx.storage.domain`. -- A backend owns one medium (file-tree root, database file) and exposes optional data-shape **facets** — `kv` today; an append-log facet is reserved for the future session-backend migration. `src/backend.ts` is the normative contract text; `tests/contract.ts` exports the shared conformance suite every backend runs. - -## Packages in this group - -| Package | Role | -| --- | --- | -| `dsh-storage` | The hub service + backend vocabulary + shared conformance suite | -| `dsh-storage-json` | JSON backend: one unit per human-readable file, atomic whole-file rewrite | -| `dsh-storage-sqlite` | SQLite backend: one database hosting all routed units, document-per-row | -| `dsh-storage-domain` | Domain data form (`ctx.storage.domain`): typed schemas, write chain, change events | +- A backend owns one medium and exposes the data-shape facets it supports. `kv` is the current facet; `src/backend.ts` owns its exact contract. ## Model Experience @@ -37,5 +28,5 @@ Independent of live requests: the hub never touches a request prefix, so it cann ## Known Limitations and Deferred Work -- **`kv` is the only data shape** — the append-log facet the future session-backend migration needs is reserved in the design note but not yet defined; backends currently have exactly one facet to implement. +- **`kv` is the only data shape** — backends currently have one facet to implement. - **Forms resolve lazily** — reading `ctx.storage.domain` before the domain plugin mounts throws `form-not-mounted`; assemblies order plugins accordingly (misconfiguration fails loud rather than silently deferring). diff --git a/packages/storage/storage/README.zh.md b/packages/storage/storage/README.zh.md index 9e03b5cf09..6c1a5b4c05 100644 --- a/packages/storage/storage/README.zh.md +++ b/packages/storage/storage/README.zh.md @@ -2,22 +2,13 @@ [English](README.md) | 中文 -非会话数据的存储中心(`ctx.storage`):命名后端注册表加已挂载的数据形式设施。中心自身不执行 IO:后端拥有介质,数据形式拥有语义。设计与取舍见[领域 KV 存储 Agent Note](../../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.zh.md)。 +非会话数据的存储中心(`ctx.storage`):具名后端注册表加已挂载的数据形式设施。中心自身不执行 IO:后端拥有介质,数据形式拥有语义。[存储家族概述](../README.md)列出了这些包;[领域 KV 存储 Agent Note](../../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.md)记录了设计理由。 ## 结构 - `ctx.storage.backend`:名称 → 后端表。多个后端并排保持挂载(`json`、`sqlite`);为消费方提供服务的后端由该消费方自身的配置决定(领域层的路由表),绝非中心的全局选择。`register()` 返回资源释放函数;注册重复名称或查找未知名称时都会明确报错。 - `ctx.storage.mount(form, facility)`/`ctx.storage.form(form)`:数据形式挂载。`StorageForms` 可通过合并扩展;领域层合并 `domain`,并通过 `ctx.storage.domain` 访问。 -- 后端拥有一种介质(文件树根、数据库文件),并公开可选的数据形状**分面**:目前为 `kv`;为未来的会话后端迁移预留追加日志分面。`src/backend.ts` 是规范契约文本;`tests/contract.ts` 导出每个后端都会运行的共享一致性测试套件。 - -## 该分组中的包 - -| 包 | 职责 | -| --- | --- | -| `dsh-storage` | 中心服务 + 后端词汇 + 共享一致性测试套件 | -| `dsh-storage-json` | JSON 后端:每个单元一个人类可读文件,以原子方式重写整个文件 | -| `dsh-storage-sqlite` | SQLite 后端:一个数据库承载所有已路由单元,每行一个文档 | -| `dsh-storage-domain` | 领域数据形式(`ctx.storage.domain`):类型化 schema、写入链、变更事件 | +- 后端拥有一种介质,并公开其支持的数据形状**分面**。当前分面为 `kv`;其确切契约由 `src/backend.ts` 负责。 ## 模型体验 @@ -37,5 +28,5 @@ ## 已知限制与暂缓事项 -- **`kv` 是唯一的数据形状**:设计记录为未来的会话后端迁移预留了 append-log facet,但尚未定义;后端目前恰好只有一个 facet 需要实现。 +- **`kv` 是唯一的数据形状**:后端目前只有一个分面需要实现。 - **数据形式按需解析**:在领域插件挂载前读取 `ctx.storage.domain` 会抛出 `form-not-mounted`;组装会按相应顺序排列插件(错误配置会明确报错,而不是静默推迟处理)。 diff --git a/packages/subagent/README.i18n.yaml b/packages/subagent/README.i18n.yaml index e5dde20e9d..9cde7517ab 100644 --- a/packages/subagent/README.i18n.yaml +++ b/packages/subagent/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/subagent/README.md -README.md: f9b04b4aa80b6feacf5d0d1fa4cf6b3b2aebc211 -README.zh.md: cbac36517e31573a19f3e5631f0068df430ea761 +README.md: 6aeb7fb1eaa9341dd72df614ca11d114f321fb83 +README.zh.md: a78cb365a8e96ad44c0c930c072372f88930906c diff --git a/packages/subagent/README.zh.md b/packages/subagent/README.zh.md index cbac36517e..a78cb365a8 100644 --- a/packages/subagent/README.zh.md +++ b/packages/subagent/README.zh.md @@ -2,20 +2,18 @@ [English](README.md) | 中文 -subagent seam 允许 agent(智能体)把工作委派给子 agent。与 [bash](../bash/README.md) 和 [llm](../llm/README.md) 能力家族一样,这也是一种能力 seam(见[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)),但有一个关键差异:**多个提供方实现在同一上下文中共存,并按名称注册**,而不是采用 bash 的单实现形态。该注册表仿照 LLM(大语言模型)适配器注册表。 +本家族允许一个 agent(智能体)将工作委派给子 agent。多个具名提供方可在同一上下文中共存。 -| 包 | 角色 | ctx 键 | +| 包 | 职责 | ctx 键 | |---|---|---| -| `subagent/` | subagent 服务:具名提供方注册表、词汇、持久化描述符与可继续子 agent 编排 | `ctx.subagents` | -| `subagent-inprocess/` | 共享进程内运行驱动器(不含提供方;每次运行使用一个清理 effect) | 无 | -| `subagent-spawn/` | 进程内后端:支持冷恢复的全新子 agent | (注册到 `ctx.subagents`) | -| `subagent-fork/` | 进程内后端:以父 agent 已完成轮次的前缀作为初始内容、支持冷恢复的子 agent | (注册到 `ctx.subagents`) | -| `subagent-acp/` | 进程外后端:在 spawn 出的子进程中运行并通过 ACP(Agent Client Protocol)驱动的一次性子 agent | (注册到 `ctx.subagents`) | -| `subagent-dsh-sdk/` | 进程外后端:在 spawn 出的子进程中运行的子 harness 运行时,经 TypeScript SDK 客户端走 stdio JSON-RPC 驱动 | (注册到 `ctx.subagents`) | -| `tool-subagent/` | 面向模型的 `subagent` 委派工具,基于 `ctx.subagents` | (注册到 `ctx.tools`) | -| `tool-subagent-control/` | 基于 `ctx.subagents`、可选且全局名称唯一的 `send_message` 与 `list_agents` 工具 | (注册到 `ctx.tools`) | -| `tool-subagent-report/` | 子级作用域的 `report` 返回通道,用于可继续的进程内子级 | (注册到每个子级作用域) | +| [`subagent/`](subagent/README.md) | 定义提供方注册、委派和继续执行 | `ctx.subagents` | +| [`subagent-inprocess/`](subagent-inprocess/README.md) | 提供共享的进程内运行驱动器 | 无 | +| [`subagent-spawn/`](subagent-spawn/README.md) | 启动全新的进程内子 agent | 注册到 `ctx.subagents` | +| [`subagent-fork/`](subagent-fork/README.md) | 从父 agent 已完成的历史记录启动进程内子 agent | 注册到 `ctx.subagents` | +| [`subagent-acp/`](subagent-acp/README.md) | 通过 ACP(Agent Client Protocol)启动进程外子 agent | 注册到 `ctx.subagents` | +| [`subagent-dsh-sdk/`](subagent-dsh-sdk/README.md) | 通过 TypeScript SDK 启动进程外 Harness 子 agent | 注册到 `ctx.subagents` | +| [`tool-subagent/`](tool-subagent/README.md) | 向模型公开委派操作 | 注册到 `ctx.tools` | +| [`tool-subagent-control/`](tool-subagent-control/README.md) | 向模型公开子级消息发送和列举操作 | 注册到 `ctx.tools` | +| [`tool-subagent-report/`](tool-subagent-report/README.md) | 提供从子级到父级的报告通道 | 注册到子级作用域 | -接口和继续执行编排位于 `subagent/subagent/`。一次性提供方的 `start` 分发不依赖持久化;内部继续执行管理器以一个 Session 和至多一个进程内 Activation 的形式拥有每个持久的可继续子 agent。该管理器不绑定任何 Task,仅在 Agent 服务存在期间存在,并为每次继续执行操作分别解析持久化服务。进程内 `subagent-spawn` / `subagent-fork` 后端共享 `subagent-inprocess` 驱动器(一个自身不含提供方的库:两者都依赖它,彼此不依赖),进程外 `subagent-acp` / `subagent-dsh-sdk` 后端则经由 [`subprocess/`](../subprocess/README.md) seam spawn 其子进程(共享的凭据清除、以进程树为范围的拆卸、dispose(资源释放)阶梯)。测试只用包内 fixture(测试前置数据)替换子 agent 边界。 - -设计理由见 [.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md](../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md)、[.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md](../../.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md) 和 [.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md](../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md)。 +参见[能力家族](../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md)、[可继续执行的子级](../../.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md)和[控制工具](../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md)决策。 diff --git a/packages/subagent/subagent-acp/README.i18n.yaml b/packages/subagent/subagent-acp/README.i18n.yaml index 4e20ab3cc0..2f5532e9f4 100644 --- a/packages/subagent/subagent-acp/README.i18n.yaml +++ b/packages/subagent/subagent-acp/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/subagent/subagent-acp/README.md -README.md: efcc77c442714a83631d009efb712fa7b8f5dfa0 -README.zh.md: baeecb623d000778a6beb3fdf90dc7ef3ba365bc +README.md: 83a5f60414528bdb768ffccd29f3091793f44b6b +README.zh.md: 4ea8daef9341897463f3dbedca86fd2c83b45514 diff --git a/packages/subagent/subagent-dsh-sdk/README.i18n.yaml b/packages/subagent/subagent-dsh-sdk/README.i18n.yaml index 311cd0c12a..297c86d78d 100644 --- a/packages/subagent/subagent-dsh-sdk/README.i18n.yaml +++ b/packages/subagent/subagent-dsh-sdk/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/subagent/subagent-dsh-sdk/README.md -README.md: a0c0811b585b69a634e49a2c838137655f316585 -README.zh.md: 2d699dec90f3098b799da912ed184ae2429df0bd +README.md: 266825daaa951f3207a3c1b4a21f525fc5fb451a +README.zh.md: d68d3ea7a9bac5e658ddd52a9e00fa674ac2c45d diff --git a/packages/subagent/subagent/README.i18n.yaml b/packages/subagent/subagent/README.i18n.yaml index 9a455a6ce8..c8aefc7533 100644 --- a/packages/subagent/subagent/README.i18n.yaml +++ b/packages/subagent/subagent/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/subagent/subagent/README.md -README.md: e54f0b98ec3649cec428a47026e6657a9749608b -README.zh.md: 074117cdbad52d754a449a0b6adc10daffa0c2f1 +README.md: a21aa6ae2822d68d513fd9409d77b3f3bf74a7a3 +README.zh.md: 3caa612aefcdac4f1dcdbcf4a3c1b81adc52c3d3 diff --git a/packages/subagent/subagent/README.md b/packages/subagent/subagent/README.md index e54f0b98ec..a21aa6ae28 100644 --- a/packages/subagent/subagent/README.md +++ b/packages/subagent/subagent/README.md @@ -4,21 +4,7 @@ English | [中文](README.zh.md) The subagent seam lets one agent delegate work to a child through a named provider. Callers use one service API (`ctx.subagents`); providers decide whether the child runs in this process, in another process, or through a future transport. -## Package roles - -The family separates the stable interface from implementations and model-facing tools: - -| Package | Role | -|---|---| -| `@deepseek-ai/dsh-subagent` | Provider registry, request/result/descriptor types, lifecycle events, and continuable-child orchestration. | -| `@deepseek-ai/dsh-subagent-spawn` | Fresh in-process child; supports continuable children. | -| `@deepseek-ai/dsh-subagent-fork` | In-process child seeded with completed parent turns; supports continuable children. | -| `@deepseek-ai/dsh-subagent-acp` | Fresh out-of-process ACP child (one-shot). | -| `@deepseek-ai/dsh-tool-subagent` | Model-facing delegation tool over one configured provider. | -| `@deepseek-ai/dsh-tool-subagent-control` | The globally named `send_message` follow-up tool. | -| `@deepseek-ai/dsh-tool-subagent-report` | Child-scoped return channel to the direct parent. | - -Multiple providers may coexist under different names. This lets a deployment expose, for example, a cheap in-process child and an isolated ACP child without changing the service contract. +The [subagent family overview](../README.md) maps implementations and model-facing consumers. This package owns the provider registry, shared request and result contracts, durable descriptors, and continuable-child orchestration. Multiple named providers may coexist behind that contract. ## Service API diff --git a/packages/subagent/subagent/README.zh.md b/packages/subagent/subagent/README.zh.md index 074117cdba..3caa612aef 100644 --- a/packages/subagent/subagent/README.zh.md +++ b/packages/subagent/subagent/README.zh.md @@ -4,21 +4,7 @@ subagent seam 允许一个 agent(智能体)通过具名提供方把工作委派给子 agent。调用方使用统一的服务 API(`ctx.subagents`);提供方决定子 agent 在当前进程、另一进程还是未来的传输之上运行。 -## 包角色 - -该系列包把稳定接口与实现、面向模型的工具分开: - -| 包 | 角色 | -|---|---| -| `@deepseek-ai/dsh-subagent` | 提供方注册表、请求/结果/描述符类型、生命周期事件和可继续子 agent 编排。 | -| `@deepseek-ai/dsh-subagent-spawn` | 全新的进程内子 agent;支持可继续子 agent。 | -| `@deepseek-ai/dsh-subagent-fork` | 以父 agent 已完成轮次作为初始内容的进程内子 agent;支持可继续子 agent。 | -| `@deepseek-ai/dsh-subagent-acp` | 全新的进程外 ACP(Agent Client Protocol)子 agent(一次性)。 | -| `@deepseek-ai/dsh-tool-subagent` | 基于一个已配置提供方、面向模型的委派工具。 | -| `@deepseek-ai/dsh-tool-subagent-control` | 全局具名 `send_message` 后续操作工具。 | -| `@deepseek-ai/dsh-tool-subagent-report` | 子级作用域的返回通道,指向直接父级。 | - -多个提供方可以使用不同名称共存。因此,部署可以同时公开低成本的进程内子 agent 和隔离的 ACP 子 agent,而无需改变服务契约。 +[subagent 家族概述](../README.md)列出了实现和面向模型的消费方。本包负责提供方注册表、共享请求和结果契约、持久描述符以及可继续子级编排。多个具名提供方可以在该契约背后共存。 ## 服务 API @@ -62,7 +48,7 @@ subagent seam 允许一个 agent(智能体)通过具名提供方把工作委 该 seam 拥有实现和消费方共享的深度词汇:`AgentOptions.subagentDepth` 声明、`assertSubagentMaxDepth` 和 `delegationDepthOf(agent)`。持久化的 `SessionHeader.delegationDepth` 具有权威性且单调:运行时选项可以加深计数,但绝不能降低它,因此恢复后的子 agent 不会被重新计为顶层。 -`inheritsParentContext` 只用于描述,不能强制执行。它仅说明子 agent 是否能看到父级已完成的对话历史(`fork` 可以;`spawn` 和 ACP 不可以),不表示是否继承工具、服务或权限。 +`inheritsParentContext` 只用于描述,不能强制执行。它仅说明子 agent 是否能看到父级已完成的对话历史(`fork` 可以;`spawn` 和 ACP(Agent Client Protocol)不可以),不表示是否继承工具、服务或权限。 ## 一次性所有权与生命周期 diff --git a/packages/subprocess/README.i18n.yaml b/packages/subprocess/README.i18n.yaml index 83491c5d82..9c0cb0c25d 100644 --- a/packages/subprocess/README.i18n.yaml +++ b/packages/subprocess/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/subprocess/README.md -README.md: 64e4740c7ac2706e45bb3517891504bf31a6109b -README.zh.md: e614441268d34e1e58960cc6ed86bb4495810743 +README.md: 187ea5b778a4bc1f9c3c9121adb18bda11cc7b58 +README.zh.md: dd3a975daec014131877d7b1523810bd932619d8 diff --git a/packages/subprocess/README.zh.md b/packages/subprocess/README.zh.md index e614441268..dd3a975dae 100644 --- a/packages/subprocess/README.zh.md +++ b/packages/subprocess/README.zh.md @@ -2,11 +2,11 @@ [English](README.md) | 中文 -这里集中提供受管子进程树的 spawn 能力:完整指定的 spawn spec,采用 Node 风格、按流划分的 stdio 处置方式(disposition),包括原始管道、inherit、附带 spill 文件的有界尾部保留收集;harness 中所有 spawn 调用方共用的凭据清除机制;基于偏移量的增量读取;以进程树为范围、带 SIGTERM→宽限期→SIGKILL 升级的信号发送;以及协作式 dispose(资源释放)阶梯。命令默认值补全、shell 语义、时限、协议分帧与呈现留在消费方:[bash 执行器](../bash/README.md)、[LSP 主机](../lsp/README.md)与 [ACP(Agent Client Protocol)subagent 后端](../subagent/README.md)。参见[subprocess seam Agent Note](../../.agents/notes/implemented/architecture/2026-07-26-subprocess-seam.md)。 +本家族通过显式的进程生命周期服务运行宿主子进程。 -| 包 | ctx 键 | 角色 | +| 包 | 职责 | ctx 键 | |---|---|---| -| [`subprocess`](subprocess/README.md)(`@deepseek-ai/dsh-subprocess`) | `ctx.subprocess` | seam 本体:抽象的 `SubprocessService.spawn(spec)`、完全显式且带按流划分 stdio 处置方式的 `SubprocessSpawnSpec`、`SubprocessHandle`(流、基于偏移量的读取器、terminate/waitForExit/dispose),以及共享的凭据清除 + `DSH_*`/`CollectedOutput` 词汇 | -| [`subprocess-local`](subprocess-local/README.md)(`@deepseek-ai/dsh-subprocess-local`) | 无 | 本地实现:detached 进程树、按处置方式接线的流、附带有界私有 spill 文件的尾部保留截断、`DSH_*` 合并次序、带升级的进程树信号发送、dispose 阶梯,以及先终止再等待退出的 dispose | +| [`subprocess/`](subprocess/README.md) | 定义子进程启动、流、终止和 dispose(资源释放)契约 | `ctx.subprocess` | +| [`subprocess-local/`](subprocess-local/README.md) | 实现本地进程树执行 | 注册到 `ctx.subprocess` | -即使消费方重载,进程生命周期仍由服务负责管理;消费方负责定义进程的含义(一条 bash 命令、未来的非 shell 运行器),以及决定塑造该进程的每一项默认值。 +服务负责进程生命周期;每个消费方负责进程执行的工作以及所应用的默认值。 diff --git a/packages/support/README.i18n.yaml b/packages/support/README.i18n.yaml index f6cacbfe54..1b392a1729 100644 --- a/packages/support/README.i18n.yaml +++ b/packages/support/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/support/README.md -README.md: b9550fd54feb36448227faae8485fe8b6dbf4fb0 -README.zh.md: 1b7464e0cab4460e587f2b5c8afa58d28116bab1 +README.md: 15cb82d3d76a1241af8b40e1c8536292618409a9 +README.zh.md: ff8595b47f1a1220912dc77a072463adf74f7bf9 diff --git a/packages/support/README.zh.md b/packages/support/README.zh.md index 1b7464e0ca..ff8595b47f 100644 --- a/packages/support/README.zh.md +++ b/packages/support/README.zh.md @@ -1,16 +1,16 @@ -# support/:开发/测试/示例基础设施 +# support/:开发和测试基础设施 [English](README.md) | 中文 -这些包用于开发、测试和示例,而非作为产品 API 发布。它们是实际的工作区包(具备类型、经过测试,并受覆盖率门禁约束),但具有**较低的兼容性预期**:当其背后的开发需求变化时,它们可以改变或被移除,无需像产品包那样谨慎执行弃用流程。 +这些包为仓库开发、测试和示例提供支持,而不是产品 API。其兼容性取决于所服务的开发需求。 -| 包 | 职责 | ctx 键 | -|---|---|---| -| `acp-snapshot/` | ACP(Agent Client Protocol)测试工具包:共享子进程/客户端启动器、快照 harness、规范化器和套件工厂 | (库:由 ACP e2e 和 `*.snapshot.ts` 套件导入) | -| `agent-loop-testkit/` | 为验证具体 agent loop(智能体循环)的测试挂载共享先决条件 | (库:由 AgentLoop 集成测试导入) | -| `invariants/` | 用于开发诊断的运行时事件契约断言 | (监听 `session/*`、`agent/*`) | -| `loader-smoke/` | 共享的真实 Loader 子进程 harness,用于无密钥示例冒烟测试 | (库:由示例 e2e 套件导入) | -| `llm-mock-server/` | 可编程的 OpenAI 兼容 HTTP/SSE(Server-Sent Events)故障服务器与 CLI(命令行界面),用于 LLM(大语言模型)恢复测试 | (独立服务器和测试库) | -| `llm-replay/` | 录制/回放适配器:通过已记录的会话 JSONL 对 `llm/stream` 进行短路处理(无密钥快照测试) | (监听 `llm/stream`) | +| 包 | 职责 | +|---|---| +| [`acp-snapshot/`](acp-snapshot/README.md) | 提供 ACP(Agent Client Protocol)快照测试工具包 | +| [`agent-loop-testkit/`](agent-loop-testkit/README.md) | 为 AgentLoop 测试挂载共享先决条件 | +| [`invariants/`](invariants/README.md) | 运行开发期运行时契约断言 | +| [`loader-smoke/`](loader-smoke/README.md) | 启动由 Loader 组合的应用以执行冒烟测试 | +| [`llm-mock-server/`](llm-mock-server/README.md) | 提供确定性的 OpenAI 兼容故障服务器 | +| [`llm-replay/`](llm-replay/README.md) | 为无密钥测试和演示回放已记录的模型响应 | -`invariants` 是开发支持,但没有环境条件限制:无论在何处注册,它都会运行;默认 `dsh-agent-spine-demo` bundle 无条件挂载它。`agent-loop-testkit` 为手工构建的 AgentLoop 测试集中管理必需服务主干,而不负责其 agent loop 或场景。`llm-replay` 支撑演示和受逐文件覆盖率门禁约束的快照测试层,`llm-mock-server` 则通过确定性 HTTP/SSE 故障驱动真实提供方适配器。`acp-snapshot` 包含 ACP 子进程/客户端边界以及快照 harness、规范化器和套件机制,`loader-smoke` 负责无密钥示例 e2e 套件使用的并列真实 Loader 启动边界。只有当某个包获得文档记载的产品消费方时,它才会从 `support/` 转入产品分组。 +当一个包获得产品契约和产品消费方时,它会移出 `support/`。 diff --git a/packages/support/acp-snapshot/README.i18n.yaml b/packages/support/acp-snapshot/README.i18n.yaml index 362de9fbd1..9f70a479ca 100644 --- a/packages/support/acp-snapshot/README.i18n.yaml +++ b/packages/support/acp-snapshot/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/support/acp-snapshot/README.md -README.md: c8fe6907848a7b661c4bfb60c871f077753fda8b -README.zh.md: 0aa28f4b2c3df1a30e5fd33a80401712f81d6614 +README.md: ff8b89437703e0d63542f2a004f9b0929171010e +README.zh.md: 582285363f6556fcc68d783e2ceedc7eb007dafd diff --git a/packages/support/acp-snapshot/README.md b/packages/support/acp-snapshot/README.md index c8fe690784..ff8b894377 100644 --- a/packages/support/acp-snapshot/README.md +++ b/packages/support/acp-snapshot/README.md @@ -59,7 +59,7 @@ Every scenario compares `stdout.expected.jsonl` with cwd-rooted separators canon The example also ships a `cordis.snapshot.yml` replay overlay next to its `cordis.yml` (the bin swaps them under `DSH_SNAPSHOT=replay` — [single-source replay config Agent Note](../../../.agents/notes/archived/testing/2026-07-04-single-source-acp-replay-config.md)); replay fixtures are served by [`dsh-llm-replay`](../llm-replay/README.md), which this package points at via the `DSH_SNAPSHOT_*` env vars it sets on the child. `pnpm run test:snapshot:record` calls the live LLM and rewrites the recorded scenarios' model fixtures; `pnpm run test:snapshot:refresh` stays keyless, runs the replay overlay, and rewrites stdout, comparable session-log expected outputs, and owned prompt and tool-schema sidecars from the committed model scripts. Fixture roles, record/replay/refresh semantics, and scenario-table fields are documented on `Scenario` and in the [snapshot Agent Note](../../../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md). -Constraints: `suite.ts` and `harness.ts` import vitest (the harness polls its durable-boundary waits through `vi.waitFor`), so the package entry is importable only inside a vitest run (the launcher and normalizers have no such dependency but ship from the same entry). The launcher and suite factory are ACP-specific by design — the launcher speaks the SDK's `ClientSideConnection` — while the normalizers are transport-neutral session-log/text helpers also consumed by the TUI snapshot suite and the web browser e2e lane. Input scripts cover initialization, fresh-session creation, text prompting, cancellation, expected RPC failures, and durable turn-boundary waits. Permission round-trips are a FIFO queue of option-kind selections (`allow_once`, `reject_once`, …) mapped to the agent-issued `optionId`; an absent or exhausted queue answers `cancelled`, and an unoffered kind rejects the run. +Constraints: `suite.ts` and `harness.ts` import vitest (the harness polls its durable-boundary waits through `vi.waitFor`), so the package entry is importable only inside a vitest run (the launcher and normalizers have no such dependency but ship from the same entry). The launcher and suite factory are ACP-specific by design — the launcher speaks the SDK's `ClientSideConnection` — while the normalizers are transport-neutral session-log/text helpers also consumed by the Web browser e2e lane. Input scripts cover initialization, fresh-session creation, text prompting, cancellation, expected RPC failures, and durable turn-boundary waits. Permission round-trips are a FIFO queue of option-kind selections (`allow_once`, `reject_once`, …) mapped to the agent-issued `optionId`; an absent or exhausted queue answers `cancelled`, and an unoffered kind rejects the run. ## Model Experience diff --git a/packages/support/acp-snapshot/README.zh.md b/packages/support/acp-snapshot/README.zh.md index 0aa28f4b2c..582285363f 100644 --- a/packages/support/acp-snapshot/README.zh.md +++ b/packages/support/acp-snapshot/README.zh.md @@ -59,7 +59,7 @@ defineAcpSnapshotSuite({ 示例还发布 `cordis.snapshot.yml` 回放 overlay,位于 `cordis.yml` 旁边(bin 在 `DSH_SNAPSHOT=replay` 下交换它们,见[单源回放配置 Agent Note](../../../.agents/notes/archived/testing/2026-07-04-single-source-acp-replay-config.md));回放 fixture 由 [`dsh-llm-replay`](../llm-replay/README.md) 提供,该包通过对子级设置的 `DSH_SNAPSHOT_*` env var 指向它。`pnpm run test:snapshot:record` 调用实时 LLM,并重写已记录场景的模型 fixture;`pnpm run test:snapshot:refresh` 保持无密钥,运行回放 overlay,并从已提交模型脚本重写 stdout、可比较会话日志预期输出,以及各 pin 自有的提示词与工具 schema sidecar。Fixture 角色、录制/回放/刷新语义和场景表字段记录在 `Scenario` 以及[快照 Agent Note](../../../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md) 中。 -约束:`suite.ts` 与 `harness.ts` 导入 vitest(harness 通过 `vi.waitFor` 轮询其持久边界等待),因此包入口只能在 vitest 运行中导入(启动器和规范化器没有此依赖,但从同一入口发布)。启动器和套件工厂按设计专用于 ACP,启动器使用 SDK 的 `ClientSideConnection`;规范化器是与传输无关的会话日志/文本辅助工具,还由 TUI 快照套件和 web 浏览器 e2e lane 消费。输入脚本覆盖初始化、新建会话、文本提示、取消、预期 RPC 失败和持久轮次边界等待。权限往返是选项类别选择(`allow_once`、`reject_once`等)的 FIFO 队列,映射到 agent 发出的 `optionId`;缺少或耗尽的队列回答 `cancelled`,未提供类别会拒绝运行。 +约束:`suite.ts` 与 `harness.ts` 导入 vitest(harness 通过 `vi.waitFor` 轮询其持久边界等待),因此包入口只能在 vitest 运行中导入(启动器和规范化器没有此依赖,但从同一入口发布)。启动器和套件工厂按设计专用于 ACP,启动器使用 SDK 的 `ClientSideConnection`;规范化器是与传输无关的会话日志/文本辅助工具,还由 Web 浏览器 e2e lane 消费。输入脚本覆盖初始化、新建会话、文本提示、取消、预期 RPC 失败和持久轮次边界等待。权限往返是选项类别选择(`allow_once`、`reject_once` 等)的 FIFO 队列,映射到 agent 发出的 `optionId`;缺少或耗尽的队列回答 `cancelled`,未提供类别会拒绝运行。 ## 模型体验 diff --git a/packages/support/llm-replay/README.i18n.yaml b/packages/support/llm-replay/README.i18n.yaml index 9382698c66..a4729b2e69 100644 --- a/packages/support/llm-replay/README.i18n.yaml +++ b/packages/support/llm-replay/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/support/llm-replay/README.md -README.md: 85aa56705929e7630e4cfb6c2a3c9cbbd0d843a6 -README.zh.md: dfa43cf78c2b584fe4868603feb3fbcfb909e099 +README.md: ee062d0c2804905f33f1ff476d12bb6dd57666e5 +README.zh.md: ab3420d9500a6ca77f04a2ad96095f8883aeb874 diff --git a/packages/support/llm-replay/README.md b/packages/support/llm-replay/README.md index 85aa567059..ee062d0c28 100644 --- a/packages/support/llm-replay/README.md +++ b/packages/support/llm-replay/README.md @@ -4,7 +4,7 @@ English | [中文](README.zh.md) A replay LLM plugin for keyless snapshot tests. It yields model streams reconstructed from a recorded **session JSONL** fixture, so a test can boot the real agent against a fixed model transcript with no API key. With `providers` configured it registers a replay-only adapter whose catalog is available to scenarios that exercise model discovery; without `providers` it installs the catch-all `llm/stream` waterfall used by tests that do not need discovery. -Its consumers are the ACP, headless `stream-json`, and TUI snapshot suites plus the web browser e2e lane. Loader-driven suites mount this plugin in place of a real LLM adapter; the web lane installs it directly to retain the teardown consumption handle. Keeping derivation and replay here places that logic under the per-file 100% coverage gate on `packages/*/src`. +Its consumers are the ACP and headless `stream-json` snapshot suites plus the Web browser e2e lane. Loader-driven suites mount this plugin in place of a real LLM adapter; the Web lane installs it directly to retain the teardown consumption handle. ## How the fixture works diff --git a/packages/support/llm-replay/README.zh.md b/packages/support/llm-replay/README.zh.md index dfa43cf78c..ab3420d950 100644 --- a/packages/support/llm-replay/README.zh.md +++ b/packages/support/llm-replay/README.zh.md @@ -4,7 +4,7 @@ 用于无密钥快照测试的 LLM(大语言模型)回放插件。它根据已记录的**会话 JSONL** fixture(测试前置数据)重建模型流,使测试无需 API 密钥即可针对固定的模型 transcript(文本记录)启动真实 agent(智能体)。配置 `providers` 后,它会注册仅用于回放的适配器,其模型目录可供测试模型发现功能的场景使用;未配置 `providers` 时,它会安装无需模型发现功能的测试所用 catch-all `llm/stream` waterfall(瀑布式事件)。 -其消费方包括 ACP(Agent Client Protocol)、headless `stream-json` 和 TUI 快照套件,以及 Web 浏览器 e2e 流水线。Loader 驱动的套件使用此插件替代真实 LLM 适配器;Web 流水线直接安装它,以保留清理阶段的消费检查句柄。将派生和回放逻辑放在此处,可使其受 `packages/*/src` 的逐文件 100% 覆盖率门禁约束。 +其消费方包括 ACP(Agent Client Protocol)与 headless `stream-json` 快照套件,以及 Web 浏览器 e2e 流水线。Loader 驱动的套件使用此插件替代真实 LLM 适配器;Web 流水线直接安装它,以保留清理阶段的消费检查句柄。 ## fixture 的工作方式 diff --git a/packages/tasks/README.i18n.yaml b/packages/tasks/README.i18n.yaml index a8de1542ee..38cd47299c 100644 --- a/packages/tasks/README.i18n.yaml +++ b/packages/tasks/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/tasks/README.md -README.md: 9bafe5633bb7e57a5404ffb41fad04b621832b6d -README.zh.md: 37b0aeeddcaafd872f906eb2c35e75345d904b99 +README.md: 05ed9c439684337c45abe11f03c861a1b795b2ac +README.zh.md: 16fe6c0a2b183b719cb6aa8ac068e34d51832a2b diff --git a/packages/tasks/README.zh.md b/packages/tasks/README.zh.md index 37b0aeeddc..16fe6c0a2b 100644 --- a/packages/tasks/README.zh.md +++ b/packages/tasks/README.zh.md @@ -2,12 +2,12 @@ [English](README.md) | 中文 -这是后台任务 id、所有者隔离、读取、取消、等待和完成通知的共用归属位置。Bash、subagent(子 agent)及未来的长时间运行工具共用一套面向模型的协议。参见[后台任务运行时 Agent Note](../../.agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.md)和[任务注册表 seam Agent Note](../../.agents/notes/implemented/architecture/2026-07-26-task-registry-seam.md)。 +本家族为长时间运行的工具提供一套按所有者隔离的后台任务协议,用于观察、取消、等待和完成通知。 -| 包 | ctx 键 | 角色 | +| 包 | 职责 | ctx 键 | |---|---|---| -| [`tasks`](tasks/README.md)(`@deepseek-ai/dsh-tasks`) | `ctx.tasks` | 注册表 seam:带品牌类型的 `-N` id、按所有者隔离的读取/终止/等待/列出契约、快照词汇、`attachSurface` 配置错误防线,以及快照不变式配套项 | -| [`tasks-local`](tasks-local/README.md)(`@deepseek-ai/dsh-tasks-local`) | 无 | 进程内注册表实现:内存记录、以首次结算为准的簿记,以及会等待执行完毕的所有者清理路径和销毁路径 | -| [`tool-tasks`](tool-tasks/README.md)(`@deepseek-ai/dsh-tool-tasks`) | 无 | 面向模型的控制接口:`task_output`、`task_list`、`task_kill`、完成通知注入和后台工作习惯提示词段落 | +| [`tasks/`](tasks/README.md) | 定义任务注册表和生命周期契约 | `ctx.tasks` | +| [`tasks-local/`](tasks-local/README.md) | 实现进程本地任务注册表 | 注册到 `ctx.tasks` | +| [`tool-tasks/`](tool-tasks/README.md) | 向模型公开任务控制和完成通知 | 注册到 `ctx.tools` | -生产方或接口重载时,状态仍由注册表持有;工具包负责呈现。生产方通过 `ctx.tasks.start` 注册执行钩子,并自行决定其配置是否公开 `run_in_background`。 +参见[后台任务运行时](../../.agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.md)和[任务注册表](../../.agents/notes/implemented/architecture/2026-07-26-task-registry-seam.md)决策。 diff --git a/packages/telemetry/README.i18n.yaml b/packages/telemetry/README.i18n.yaml index 1ca80fbd0c..0d2a11ab23 100644 --- a/packages/telemetry/README.i18n.yaml +++ b/packages/telemetry/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/telemetry/README.md -README.md: 944cb3f9bac6169feddf8b49bc481cfbe7c6fa9d -README.zh.md: dc764799db6398d8e56d2635414c036c8cae8a6a +README.md: c390493d4053f9f532c30c3c2291d833554d6a99 +README.zh.md: 846a3e276aeaeda7e3456f4e4d4bea577d224a89 diff --git a/packages/telemetry/README.zh.md b/packages/telemetry/README.zh.md index dc764799db..846a3e276a 100644 --- a/packages/telemetry/README.zh.md +++ b/packages/telemetry/README.zh.md @@ -1,10 +1,12 @@ -# telemetry/ +# telemetry/:会话遥测能力家族 [English](README.md) | 中文 -面向外部的会话上报:遥测(telemetry)seam 及其 OpenTelemetry 后端。整套设计固定在[复活 Agent Note](../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.md)中:边界公理(harness 的职责止于 `emit()`,投递由上报 SDK 负责)、`telemetry/record` waterfall(瀑布式事件;脱敏规则由部署方挂载,seam 自身不带任何规则)、固定分片投影、handoff 游标,以及运维记录通道。 +本家族将会话活动投影为外发遥测,并将投递委派给配置的上报后端。 | 包 | 职责 | |---|---| -| [`@deepseek-ai/dsh-session-telemetry`](session-telemetry/) | seam 本体:捕获点、投影、脱敏、handoff 游标、运维信号,以及最小后端契约(`emit`/`flush?`/`shutdown`)。 | -| [`@deepseek-ai/dsh-session-telemetry-otel`](session-telemetry-otel/) | 部署方要加载的后端:OTel JS SDK 的日志流水线(`LoggerProvider` + `BatchLogRecordProcessor` + OTLP/HTTP 导出器),经透传(passthrough)原样配置。 | +| [`session-telemetry/`](session-telemetry/README.md) | 定义捕获、脱敏、投影和后端投递 | +| [`session-telemetry-otel/`](session-telemetry-otel/README.md) | 通过 OpenTelemetry 日志投递遥测 | + +[遥测决策](../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.md)记录了上报边界。 diff --git a/packages/timeout/README.i18n.yaml b/packages/timeout/README.i18n.yaml index 45143c1014..33a7a5b26d 100644 --- a/packages/timeout/README.i18n.yaml +++ b/packages/timeout/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/timeout/README.md -README.md: 2a75e4d54aa1f518858d97e2f4c7d09a23a80a70 -README.zh.md: 40704d9c9c9ef659fe3922876b1de2cf020fe4be +README.md: d9d8e8a829d8d466d1cc3e42a6c32ac4b3db428b +README.zh.md: c5920258ade7022fb5b28a351ba060eb56e8bc3c diff --git a/packages/timeout/README.zh.md b/packages/timeout/README.zh.md index 40704d9c9c..c5920258ad 100644 --- a/packages/timeout/README.zh.md +++ b/packages/timeout/README.zh.md @@ -2,10 +2,10 @@ [English](README.md) | 中文 -工具调用超时策略插件。它是单一 **产品**包:它是 `tools/execute` 环绕分发 seam(由 [`dsh-tools`](../core/tools) 拥有)和纯 [`dsh-timeout`](../util/timeout) 库的部署策略消费方,而非带接口/实现拆分的可替换能力,因此无需 seam 三包组合。 +本分组将部署配置的截止时间应用于面向模型的工具调用。各项能力仍负责终止自身工作。 -| 包 | 职责 | ctx 键 | -|---|---|---| -| `timeout-policy/` | `tools/execute` 包装层:对每个已配置工具,它都在 `exec.signal` 上设置单次调用截止时间,并在截止时间先到时返回结构化 `TOOL_TIMEOUT` 结果 | (注册 `tools/execute` 监听器;不注入任何内容) | +| 包 | 职责 | +|---|---| +| [`timeout-policy/`](timeout-policy/README.md) | 强制执行配置的逐工具调用截止时间 | -超时被拆分为三层:[`dsh-timeout`](../util/timeout) 拥有纯计时/分类原语(`deadline`/`timeoutOf`);每种能力拥有终止操作(bash 终止其进程组,fetch 提供方关闭其 socket);本包则拥有 *作为部署策略的面向模型工具调用预算*:没有面向模型的超时参数,也没有全局默认值。它是[超时库 Agent Note](../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md) 所预见的中间件。`bash` 和钩子命令执行保留各自的 `BASH_TIMEOUT` 后端超时,不经过此策略。 +纯计时原语位于 [`util/timeout`](../util/timeout/README.md)。参见[超时库决策](../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md)。 diff --git a/packages/todo/README.i18n.yaml b/packages/todo/README.i18n.yaml index 859de5e095..c75e09350a 100644 --- a/packages/todo/README.i18n.yaml +++ b/packages/todo/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/todo/README.md -README.md: da85a5573507cd8bb1ac52f9614225819a479baf -README.zh.md: 5b83fe4d2d59ff6401b6a455d8d5977d6a5e5122 +README.md: 66abf18131ee53d87e933757c40117841e94b07f +README.zh.md: 38a6b3c22653b8e22cc0b693eb666620513a3d8c diff --git a/packages/todo/README.md b/packages/todo/README.md index da85a55735..66abf18131 100644 --- a/packages/todo/README.md +++ b/packages/todo/README.md @@ -2,10 +2,10 @@ English | [中文](README.zh.md) -The model-facing todo tool. A single **product** package — there is no interface/implementation seam here, because the list is single-owner session state (one agent session owns its own list), not a swappable capability. +The model-facing todo capability. It is a single **product** package because one agent session owns the list; there is no replaceable provider seam. | Package | Role | ctx key | |---|---|---| -| `tool-todo/` | Model-facing `todo_write` tool; writes the whole list to the session log (`todo/write`) | (registers on `ctx.tools`) | +| [`tool-todo/`](tool-todo/README.md) | Stores and exposes the session's todo list. | (registers on `ctx.tools`) | -The list lives on the event-sourced session log (`SessionEventMap['todo/write']`, owned by [`dsh-session`](../core/session)); this package is the thin consumer that appends the snapshot. Host/client runtimes render the durable list from session events. +The child README owns the tool, persistence, and rendering contract. diff --git a/packages/todo/README.zh.md b/packages/todo/README.zh.md index 5b83fe4d2d..38a6b3c226 100644 --- a/packages/todo/README.zh.md +++ b/packages/todo/README.zh.md @@ -2,10 +2,10 @@ [English](README.md) | 中文 -面向模型的 todo 工具。它是单一 **产品**包(package):这里没有接口/实现 seam,因为该列表是由单一所有者管理的会话状态(每个 agent(智能体)会话拥有自己的列表),而非可替换能力。 +面向模型的 todo 能力。它是单一**产品**包,因为一个 agent(智能体)会话拥有该列表;不存在可替换的提供方 seam。 | 包 | 职责 | ctx 键 | |---|---|---| -| `tool-todo/` | 面向模型的 `todo_write` 工具;将完整列表写入会话日志(`todo/write`) | (注册到 `ctx.tools`) | +| [`tool-todo/`](tool-todo/README.md) | 存储并公开会话的 todo 列表。 | (注册到 `ctx.tools`) | -列表存在于事件溯源会话日志中(`SessionEventMap['todo/write']`,由 [`dsh-session`](../core/session) 拥有);本包是追加快照的轻量消费方。宿主/客户端运行时会根据会话事件渲染该持久化列表。 +子级 README 负责工具、持久化和渲染契约。 diff --git a/packages/typert/README.i18n.yaml b/packages/typert/README.i18n.yaml index 0f3581331e..cf44d9aace 100644 --- a/packages/typert/README.i18n.yaml +++ b/packages/typert/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/typert/README.md -README.md: d11fd8f57245379d67d2a1cdcca334f0032db469 -README.zh.md: 696f572985f6cfe09c8855455b6b45ddb1efd4fe +README.md: ad9f843e48be0e3be85921ed8fd3ca4e2c327160 +README.zh.md: 0d4be016b766178b54f7e269583fa4200e3eb24b diff --git a/packages/typert/README.zh.md b/packages/typert/README.zh.md index 696f572985..0d4be016b7 100644 --- a/packages/typert/README.zh.md +++ b/packages/typert/README.zh.md @@ -2,10 +2,10 @@ [English](README.md) | 中文 -Typert 将源代码分析、运行时存储和 Loader 发现机制拆分为彼此独立的包。 +Typert 将源代码分析、运行时存储和 Loader 发现机制分离。 | 包 | 职责 | Cordis 键 | |---|---|---| -| [`registry/`](registry/README.md) | 运行时包反射和实时 Zod schema 注册表 | `ctx.typert` | -| [`loader/`](loader/README.md) | 发现 Loader 条目并注册所生成的宿主产物 | 使用 `ctx.loader`、`ctx.typert` | -| [`generator/`](generator/README.md) | 与编译器无关的类型分析和产物生成 | 构建时库 | +| [`registry/`](registry/README.md) | 存储运行时包反射和 schema | `ctx.typert` | +| [`loader/`](loader/README.md) | 发现 Loader 条目并注册生成的宿主产物 | 消费 `ctx.loader` 和 `ctx.typert` | +| [`generator/`](generator/README.md) | 从源代码类型生成运行时产物 | 构建时库 | diff --git a/packages/ui/README.i18n.yaml b/packages/ui/README.i18n.yaml index 541de5b199..fba1f95c52 100644 --- a/packages/ui/README.i18n.yaml +++ b/packages/ui/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/ui/README.md -README.md: 76ca80e5685f70e73a6c46fe8d980f951b965ed3 -README.zh.md: 3958b0bdfb5d8cbab82f9fecfe54d12d462738ea +README.md: 15754410a4a81eb3fc898dd55269ddd1637e1dab +README.zh.md: 4023b80085998f57ed321bfda3a0abdd08b70a28 diff --git a/packages/ui/README.md b/packages/ui/README.md index 76ca80e568..15754410a4 100644 --- a/packages/ui/README.md +++ b/packages/ui/README.md @@ -6,16 +6,12 @@ Human-facing channels and the out-of-process SDK server. These are **product** p | Package | Role | ctx key | |---|---|---| -| `commands/` | Human-command registry: shared discovery metadata, scoped shadowing, cancellation, and direct UI dispatch | `ctx.commands` | -| `user-approval/` | One-shot user-approval mechanism, closed outcome vocabulary, audit events, and per-session approval policy | `ctx.approval` | -| `permission/` | User-facing permission presets (`workspace-write`/`danger-full-access`): one product-level select bundling the sandbox-mode and approval-policy knobs, written through to their session events | `ctx.permission` | -| `user-interaction/` | Abstract human question/answer seam used by UI-backed confirmation tools | `ctx.userInteraction` | -| `tool-ask-user/` | Model-facing `ask_user_question` tool over `ctx.userInteraction` | (registers on `ctx.tools`) | -| `jsonrpc/` | Stdio JSON-RPC server for out-of-process SDK clients | (drives `ctx.agents`) | -| `app-boot/` | Shared boot glue for the app bins: `.env` loading, fail-loud Loader guards, snapshot-aware config resolution, the settle-the-tree boot sequence | (library for the bins) | +| [`commands/`](commands/README.md) | Registers and dispatches human commands for interactive adapters. | `ctx.commands` | +| [`user-approval/`](user-approval/README.md) | Coordinates one-shot approval decisions. | `ctx.approval` | +| [`permission/`](permission/README.md) | Presents and persists user-facing permission presets. | `ctx.permission` | +| [`user-interaction/`](user-interaction/README.md) | Defines the provider-neutral human question/answer seam. | `ctx.userInteraction` | +| [`tool-ask-user/`](tool-ask-user/README.md) | Exposes human questions to the model. | (registers on `ctx.tools`) | +| [`jsonrpc/`](jsonrpc/README.md) | Serves out-of-process SDK clients over stdio JSON-RPC. | (drives `ctx.agents`) | +| [`app-boot/`](app-boot/README.md) | Provides shared boot support for application launchers. | (library for the bins) | -A UI integration is a client-driver plugin, not a loop change: it consumes the existing `agent/*` event taxonomy and the `dsh-agent` factory. [`jsonrpc`](jsonrpc/README.md) serves out-of-process SDK clients, while non-interactive one-shot tasks use `cli-demo`. [`commands`](commands/README.md) is the human-only discovery and dispatch plane for interactive adapters; command input and output do not become model messages. - -`user-approval`, `user-interaction`, and `tool-ask-user` live here because asking a human is a UI-backed product affordance, not part of the providerless core spine. `user-approval` owns the one-shot `ctx.approval` decision mechanism and its policy tier; answerers remain with the channel or automation transport that owns the agent. `user-interaction` remains provider-neutral (`ctx.userInteraction`), while `tool-ask-user` is its model-facing consumer and interactive app packages provide concrete providers. - -The runnable app bundles composed over [`agent-spine-demo`](../examples/agent-spine-demo/README.md) live in [`examples/`](../examples/README.md) (`cli-demo`, `acp-demo`, `jsonrpc-demo`), each with its own entry contract. The product [`dsh`](../../apps/cli/README.md) CLI uses no demo bundle. `ui/` keeps the reusable human/SDK channel plugins and shared `app-boot` glue; the automation-only ACP transport lives in [`acp/`](../acp/README.md). Each front door owns its stdout policy, and a leaf `cordis.yml` supplies backends and optional tools. +These packages integrate through existing agent and session contracts rather than changing the loop. Interactive applications provide the concrete command, approval, and question adapters; automation uses [`acp/`](../acp/README.md), and runnable demo bundles live under [`examples/`](../examples/README.md). The product [`dsh`](../../apps/cli/README.md) CLI composes these packages directly. diff --git a/packages/ui/README.zh.md b/packages/ui/README.zh.md index 3958b0bdfb..4023b80085 100644 --- a/packages/ui/README.zh.md +++ b/packages/ui/README.zh.md @@ -2,20 +2,16 @@ [English](README.md) | 中文 -面向用户的交互通道和进程外 SDK 服务器。这些是**产品**包(package):由用户或 SDK 客户端直接操作的真实接口。 +面向用户的通道和进程外 SDK 服务器。这些是**产品**包:由用户或 SDK 客户端直接操作的真实接口。 | 包 | 职责 | ctx 键 | |---|---|---| -| `commands/` | 用户命令注册表:共享发现元数据、作用域遮蔽、取消以及 UI 直接分派 | `ctx.commands` | -| `user-approval/` | 一次性用户审批机制、封闭的结果词汇、审计事件和逐会话审批策略 | `ctx.approval` | -| `permission/` | 面向用户的权限预设(`workspace-write`/`danger-full-access`):通过一项产品级选择组合沙箱模式与审批策略两个可调参数,并写入各自的会话事件 | `ctx.permission` | -| `user-interaction/` | UI 支持的确认工具所使用的抽象用户问答 seam | `ctx.userInteraction` | -| `tool-ask-user/` | 模型侧 `ask_user_question` 工具,基于 `ctx.userInteraction` 实现 | (注册到 `ctx.tools`) | -| `jsonrpc/` | 面向进程外 SDK 客户端的 stdio JSON-RPC 服务器 | (驱动 `ctx.agents`) | -| `app-boot/` | app bin 的共享启动粘合层:加载 `.env`、会明确报错的 Loader 保护机制、感知快照的配置解析,以及等待整棵树停稳的启动序列 | (供各 bin 使用的库) | +| [`commands/`](commands/README.md) | 为交互式适配器注册并分派用户命令。 | `ctx.commands` | +| [`user-approval/`](user-approval/README.md) | 协调一次性审批决策。 | `ctx.approval` | +| [`permission/`](permission/README.md) | 呈现并持久化面向用户的权限预设。 | `ctx.permission` | +| [`user-interaction/`](user-interaction/README.md) | 定义与提供方无关的用户问答 seam。 | `ctx.userInteraction` | +| [`tool-ask-user/`](tool-ask-user/README.md) | 向模型公开用户问题。 | (注册到 `ctx.tools`) | +| [`jsonrpc/`](jsonrpc/README.md) | 通过 stdio JSON-RPC 为进程外 SDK 客户端提供服务。 | (驱动 `ctx.agents`) | +| [`app-boot/`](app-boot/README.md) | 为应用启动器提供共享启动支持。 | (供各 bin 使用的库) | -UI 集成属于由客户端驱动的插件,而非对循环的修改:它使用现有的 `agent/*` 事件分类和 `dsh-agent` 工厂。[`jsonrpc`](jsonrpc/README.md) 为进程外 SDK 客户端提供服务,非交互式的一次性任务则使用 `cli-demo`。[`commands`](commands/README.md) 是面向交互式适配器的仅面向用户的发现与分派通道;命令输入和输出不会成为模型消息。 - -`user-approval`、`user-interaction` 和 `tool-ask-user` 位于此处,因为向用户提问是由 UI 支持的产品功能,并不属于无提供方的核心主干。`user-approval` 负责一次性的 `ctx.approval` 决策机制及其策略层级;应答逻辑仍由负责 agent(智能体)的通道或自动化传输层提供。`user-interaction` 保持提供方无关(`ctx.userInteraction`),`tool-ask-user` 是其模型侧消费方,而交互式 app 包提供具体的提供方。 - -基于 [`agent-spine-demo`](../examples/agent-spine-demo/README.md) 组合的可运行 app bundle 位于 [`examples/`](../examples/README.md)(`cli-demo`、`acp-demo`、`jsonrpc-demo`),各自拥有入口契约。产品 [`dsh`](../../apps/cli/README.md) CLI(命令行界面)不使用 demo bundle。`ui/` 保留可复用的用户/SDK 通道插件和共享 `app-boot` 粘合层;仅供自动化使用的 ACP(Agent Client Protocol)传输层位于 [`acp/`](../acp/README.md)。每个入口都负责自己的 stdout 策略,叶子 `cordis.yml` 则提供后端与可选工具。 +这些包通过现有的 agent(智能体)和会话契约集成,而不改变循环。交互式应用提供具体的命令、审批和提问适配器;自动化使用 [`acp/`](../acp/README.md),可运行的演示组合包位于 [`examples/`](../examples/README.md)。产品 [`dsh`](../../apps/cli/README.md) CLI(命令行界面)直接组合这些包。 diff --git a/packages/ui/app-boot/src/index.ts b/packages/ui/app-boot/src/index.ts index 7f3579cda1..2e5a133f00 100644 --- a/packages/ui/app-boot/src/index.ts +++ b/packages/ui/app-boot/src/index.ts @@ -340,10 +340,10 @@ export async function watchPersonalPatches( try { return await register } catch (error) { - // A surface can dispose the whole tree while the watcher is still opening - // (a TUI `/exit` typed during startup): the HMR effect registration then - // fails with INACTIVE_EFFECT. That is the app exiting exactly as asked, - // not a watch failure — return a no-op disposer instead of crashing. + // A surface can dispose the whole tree while the watcher is still opening; + // the HMR effect registration then fails with INACTIVE_EFFECT. That is the + // app exiting exactly as asked, not a watch failure, so return a no-op + // disposer instead of crashing. if ((error as { code?: string } | null)?.code === 'INACTIVE_EFFECT') return async () => {} throw error } @@ -625,11 +625,10 @@ export async function boot( stage = 'plugin tree failed to load' await mountRootInclude(ctx, absoluteConfigPath, patches) // A surface can finish and dispose the whole tree while startup is still - // in flight: the TUI renders as soon as its own fiber starts, so an `/exit` - // typed before the last entry settles tears the context down under us. The - // Loader service goes with it, and the activation audit describes a live - // tree — reading `ctx.loader` past this point would throw a TypeError over - // an app that exited exactly as asked. Transactional group updates settle + // in flight, before the last entry settles. The Loader service goes with + // it, and the activation audit describes a live tree — reading `ctx.loader` + // past this point would throw a TypeError over an app that exited exactly + // as asked. Transactional group updates settle // lifecycle inside the mount, so the teardown can land before it returns; // re-check after every await. await ctx.get('loader')?.await() diff --git a/packages/ui/app-boot/tests/app-boot.spec.ts b/packages/ui/app-boot/tests/app-boot.spec.ts index 96cad31ea3..24a24b4c58 100644 --- a/packages/ui/app-boot/tests/app-boot.spec.ts +++ b/packages/ui/app-boot/tests/app-boot.spec.ts @@ -435,11 +435,10 @@ describe('boot', () => { }) it('returns instead of asserting over a tree a surface disposed mid-startup', async () => { - // What a TUI `/exit` does (ui-tui's disposeRootAndExit): dispose the root - // fiber, which lands while boot() is still awaiting the Loader whenever the - // surface renders before the last entry settles. The Loader service goes - // with the tree, so reading it for the post-boot assertions would crash an - // app that exited exactly as the user asked. + // A surface can dispose the root fiber while boot() is still awaiting the + // Loader, before the last entry settles. The Loader service goes with the + // tree, so reading it for the post-boot assertions would crash an app that + // exited exactly as the user asked. const dir = tmp() writeFileSync(join(dir, 'exiting.mjs'), [ 'export const name = "exiting"', diff --git a/packages/ui/app-boot/tests/config-reload.spec.ts b/packages/ui/app-boot/tests/config-reload.spec.ts index d9f4ffa830..1e88954f69 100644 --- a/packages/ui/app-boot/tests/config-reload.spec.ts +++ b/packages/ui/app-boot/tests/config-reload.spec.ts @@ -344,9 +344,8 @@ describe('include patches layered over one base', () => { // The surface/`--config`/personal composition: `dsh` includes one shared // base and applies each source as its own patch list at the SAME include // level, because patches never cross an include boundary. A later layer - // must therefore be able to reach a row an earlier layer inserted — - // otherwise every surface-only row (the whole TUI front door) would be - // invisible to the user's `~/.dsh/config.yaml`. + // must therefore be able to reach a row an earlier layer inserted, or + // surface-only rows would be invisible to the user's personal config. const dir = mkdtempSync(join(tmpdir(), 'dsh-config-layered-')) writeFileSync(join(dir, 'noop.mjs'), NOOP_PLUGIN) writeFileSync(join(dir, 'base.yml'), '- id: shared\n name: ./noop.mjs\n config:\n value: base\n') diff --git a/packages/ui/app-boot/tests/personal-config.spec.ts b/packages/ui/app-boot/tests/personal-config.spec.ts index 53df1d84b7..7c92d53e56 100644 --- a/packages/ui/app-boot/tests/personal-config.spec.ts +++ b/packages/ui/app-boot/tests/personal-config.spec.ts @@ -235,11 +235,11 @@ describe('boot with personal patches', () => { }) it('returns a no-op disposer when the tree is disposed while the watcher opens', async () => { - // A TUI `/exit` typed during startup disposes the whole tree while - // registerConfig's effect registration is still in flight (the HMR effect - // then fails with INACTIVE_EFFECT); the app is exiting exactly as asked, - // so the watcher must not crash the process. The stub makes the race - // deterministic — the live-teardown ordering itself is not stageable. + // A surface can dispose the whole tree while registerConfig's effect + // registration is still in flight (the HMR effect then fails with + // INACTIVE_EFFECT); the app is exiting exactly as asked, so the watcher + // must not crash the process. The stub makes the race deterministic — the + // live-teardown ordering itself is not stageable. const dir = tmp() const ctx = await boot(NAME, writeTree(dir)) try { diff --git a/packages/ui/commands/README.i18n.yaml b/packages/ui/commands/README.i18n.yaml index 4b4254481a..83cb7e4883 100644 --- a/packages/ui/commands/README.i18n.yaml +++ b/packages/ui/commands/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/ui/commands/README.md -README.md: 4ad72cf9e232c8d41e525f42eecde5637032a391 -README.zh.md: ba214cb7a4c0370128b188db0a08fcd0bc4fa127 +README.md: a931628df28cb11ffb8e91068f7602a4a30cff94 +README.zh.md: e1ef7288ca7dca2945ec93d0ba23a3d1d6edf90c diff --git a/packages/ui/commands/README.md b/packages/ui/commands/README.md index 4ad72cf9e2..a931628df2 100644 --- a/packages/ui/commands/README.md +++ b/packages/ui/commands/README.md @@ -16,7 +16,7 @@ Handlers return `success` or `error` plus optional UI text. Results are rendered ## Composition -The terminal app bundle mounts this service with `dsh-tui`; the UI-less agent spine and ACP automation app do not. Custom interactive compositions and command producers mount `@deepseek-ai/dsh-commands` explicitly. +The shipped `dsh` base mounts this service and the Web client dispatches through it. UI-less demo spines and ACP automation do not provide a command adapter. Custom interactive compositions and command producers mount `@deepseek-ai/dsh-commands` explicitly. ## Model Experience diff --git a/packages/ui/commands/README.zh.md b/packages/ui/commands/README.zh.md index ba214cb7a4..e1ef7288ca 100644 --- a/packages/ui/commands/README.zh.md +++ b/packages/ui/commands/README.zh.md @@ -16,7 +16,7 @@ ## 组合 -终端应用组合包会将此服务与 `dsh-tui` 一起挂载;无 UI 的 agent 主干和 ACP(Agent Client Protocol)自动化应用不会挂载它。自定义交互式组合与命令生产方会显式挂载 `@deepseek-ai/dsh-commands`。 +随产品交付的 `dsh` 基础组合会挂载此服务,Web 客户端通过它分派命令。无 UI 的演示主干和 ACP(Agent Client Protocol)自动化不提供命令适配器。自定义交互式组合与命令生产方会显式挂载 `@deepseek-ai/dsh-commands`。 ## 模型体验 diff --git a/packages/ui/user-interaction/README.i18n.yaml b/packages/ui/user-interaction/README.i18n.yaml index dabfab02f3..2fd2733c62 100644 --- a/packages/ui/user-interaction/README.i18n.yaml +++ b/packages/ui/user-interaction/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/ui/user-interaction/README.md -README.md: c7fec590d6e44a13b94cc682f5e069b2d3c5e416 -README.zh.md: 094cd2bf81eafc7d4f24385cfb30e97478cbb58d +README.md: 7d3c4e87c018e42794d319b540fd0a94abc9b43c +README.zh.md: 0e5d15a673c124abab4b13e869623df1a5c63acd diff --git a/packages/ui/user-interaction/README.md b/packages/ui/user-interaction/README.md index c7fec590d6..7d3c4e87c0 100644 --- a/packages/ui/user-interaction/README.md +++ b/packages/ui/user-interaction/README.md @@ -28,7 +28,7 @@ For a single-select question, `custom` overrides the selected choice and `select ## Role -This is the interface package. Model-facing consumers such as `@deepseek-ai/dsh-tool-ask-user` depend on this seam; `dsh-tui` and the host runtime provide interactive implementations. The loop stays unchanged: a tool call awaits a promise, and the tool result resumes the normal agent loop. +This is the interface package. Model-facing consumers such as `@deepseek-ai/dsh-tool-ask-user` depend on this seam; the Web host runtime provides the shipped interactive implementation. The loop stays unchanged: a tool call awaits a promise, and the tool result resumes the normal agent loop. ## Model Experience diff --git a/packages/ui/user-interaction/README.zh.md b/packages/ui/user-interaction/README.zh.md index 094cd2bf81..0e5d15a673 100644 --- a/packages/ui/user-interaction/README.zh.md +++ b/packages/ui/user-interaction/README.zh.md @@ -28,7 +28,7 @@ ## 职责 -这是接口包。`@deepseek-ai/dsh-tool-ask-user` 等面向模型的消费方依赖此 seam;`dsh-tui` 和宿主运行时提供交互式实现。循环保持不变:工具调用等待 Promise,工具结果随后恢复正常的 agent loop(智能体循环)。 +这是接口包。`@deepseek-ai/dsh-tool-ask-user` 等面向模型的消费方依赖此 seam;Web 宿主运行时提供随产品交付的交互式实现。循环保持不变:工具调用等待 Promise,工具结果随后恢复正常的 agent loop(智能体循环)。 ## 模型体验 diff --git a/packages/util/README.i18n.yaml b/packages/util/README.i18n.yaml index c1ab4a3b74..82e5f323d8 100644 --- a/packages/util/README.i18n.yaml +++ b/packages/util/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/util/README.md -README.md: 46904aba70c7cf0f98bb75cce79d97bb12b950a9 -README.zh.md: 78a4c3038ef31bb1ec5fb10751b7c148ea20b716 +README.md: b84f2cbb88981aa2edbde65d8a711b28cc5b48e9 +README.zh.md: 44834041aad8d891b5dcaf63e02be5641e878cdc diff --git a/packages/util/README.zh.md b/packages/util/README.zh.md index 78a4c3038e..44834041aa 100644 --- a/packages/util/README.zh.md +++ b/packages/util/README.zh.md @@ -2,21 +2,13 @@ [English](README.md) | 中文 -其他分组共享的零依赖原语。当某个包拥有某个微小的基础类型或辅助工具,而该类型或辅助工具为多个功能家族所需,却不属于其中任何一个家族时,该包就归入此处。这样可避免一个功能包仅为使用共享原语而依赖不相关的功能包。这些都是**支持** 包:规模小、稳定,且不依赖 harness。 +这些零依赖包提供由多个能力家族共享的小型原语。业务语义仍归各个消费这些原语的能力所有。 | 包 | 职责 | |---|---| -| `brand/` | 仅包含类型的 `Branded` 名义类型原语(无运行时代码,无 harness 依赖) | -| `paths/` | 规范的单根 `DSH_HOME` 解析,以及 harness 用户数据的共享文件系统路径常量和辅助工具(无 harness 依赖) | -| `timeout/` | 超时的时序/分类部分:`clampTimeout`/`deadline`/`timeoutOf`/`TimeoutReason`(纯函数,无 harness 依赖);终止机制保留在各个功能中 | -| `retention/` | 有界的面向模型输出:`ItemRetainer`/`TextRetainer` 加上中性通知辅助工具(纯工具,无 harness 依赖);业务语义保留在各个工具中 | -| `atomic-write/` | 原子文件替换:`writeFileAtomic`(独占创建临时文件 + 携带调用方所声明 mode 的 rename);由设置与凭据存储共用 | -| `native-command/` | 宿主原生 OS 集成的免 shell `execFile` 运行器——utf8 捕获、abort 传播、Windows 窗口隐藏(无 harness 依赖);命令选择保留在各调用方 | - -`dsh-brand` 是规范示例:它只负责 `Branded` 辅助工具,因此功能包可以为自己拥有的 id 添加品牌(`dsh-tasks` 的 `TaskId`、`dsh-session` 的 `SessionId` 等),而只需依赖 `dsh-brand`,无需仅为使用 `Branded` 而引入不相关的包。 - -`dsh-paths` 为每个包提供同一个可配置的 Harness 主目录,而不将这项横切事实归属给 bash、skill(技能)、telemetry 或组合包。它优先解析显式值,其次是 `$DSH_HOME`,最后回退到 `~/.dsh`;返回绝对路径,但不缓存、创建或修改任何内容。harness 将所有用户数据保存在同一根目录下。 - -`dsh-timeout` 对超时家族采用相同结构:`dsh-bash` 和 `dsh-web-fetch-local` 都只依赖 `dsh-timeout`,便可将调用方的取消与 deadline 融合,然后区分「已超时」和「已取消」。它刻意只负责时序/分类部分,*终止*机制(对进程组发送 SIGKILL、关闭 fetch socket)保留在各个功能中,因为没有任何共享层可以负责每个功能的终止操作(见[超时库 Agent Note](../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md))。 - -`dsh-retention` 对有界工具输出采用同样的拆分方式:工具(`glob`/`grep`/`bash`/`web_fetch`/`web_search`)将项或文本送入 retainer,取回保留的内容以及精确的省略量;分组、退出码、提供方错误和恢复文案则仍由工具负责。它刻意只负责保留机制;`truncated` 只表示因预算而发生了省略,绝不表示「检查不完整」状态(见[保留库 Agent Note](../../.agents/notes/implemented/architecture/2026-07-06-tool-result-retention-library.md))。 +| [`brand/`](brand/README.md) | 提供带名义品牌的类型 | +| [`paths/`](paths/README.md) | 解析 Harness 数据根目录和共享路径 | +| [`timeout/`](timeout/README.md) | 提供截止时间和超时分类原语 | +| [`retention/`](retention/README.md) | 限制保留文本和项目集合的大小 | +| [`atomic-write/`](atomic-write/README.md) | 以原子方式替换文件 | +| [`native-command/`](native-command/README.md) | 不经 shell 运行宿主原生命令 | diff --git a/packages/util/atomic-write/README.i18n.yaml b/packages/util/atomic-write/README.i18n.yaml index 822a0952ca..33e1f6014b 100644 --- a/packages/util/atomic-write/README.i18n.yaml +++ b/packages/util/atomic-write/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/util/atomic-write/README.md README.md: 2ff4abb6ac10d8b592ccd2056b4f1f92cc8518b0 -README.zh.md: bd5d3f1f2583ff1b97a80ce2c1c6ca7989e13d15 +README.zh.md: 4284d06422564268bb9e31d1a1ed06ab5271e562 diff --git a/packages/util/atomic-write/README.zh.md b/packages/util/atomic-write/README.zh.md index bd5d3f1f25..4284d06422 100644 --- a/packages/util/atomic-write/README.zh.md +++ b/packages/util/atomic-write/README.zh.md @@ -30,15 +30,15 @@ await withFileLock('/home/u/.dsh/settings.yaml', async () => { `withFileLock` 跨进程串行化同一文件的写入方,服务于单靠原子提交无法保证安全的读-渲染-提交循环。锁是以 `wx` 创建的同目录 `.lock`,因此读取方从不参与竞争;等待方按指数退避,超时即失败而非无限阻塞。竞争者绝不移除现有锁:锁龄无法区分已经崩溃的所有者与被暂停但仍存活的写入方。 -## Model Experience +## 模型体验 无:本包是纯文件系统原语,此处没有任何内容会到达模型请求。 -#### KV Cache effect +#### KV Cache 影响 无;此处没有任何内容会进入请求前缀。 -## Known Limitations and Deferred Work +## 已知限制与暂缓事项 - **原子但不保证持久**——不对文件或其所在目录做 `fsync`,因此崩溃后可能观察到 rename 被回退。此处的文件型存储在启动时重新读取并重新发布,把持久性留作调用方的策略。 - **仅支持字符串内容**——在有消费方需要之前,不提供 `Buffer` 或流式形态。 diff --git a/packages/util/native-command/README.i18n.yaml b/packages/util/native-command/README.i18n.yaml index efffaa41cb..7c984a67ad 100644 --- a/packages/util/native-command/README.i18n.yaml +++ b/packages/util/native-command/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/util/native-command/README.md README.md: 7fc8b1f4640ef87ada62b6656854feb37080e4e6 -README.zh.md: 94db732afbb77eb90faf3c5710a8a9c4a8630f95 +README.zh.md: b91af6fe4787c35c56fbd4523d5a7e10673e32cb diff --git a/packages/util/native-command/README.zh.md b/packages/util/native-command/README.zh.md index 94db732afb..b91af6fe47 100644 --- a/packages/util/native-command/README.zh.md +++ b/packages/util/native-command/README.zh.md @@ -8,7 +8,7 @@ 它是**库,不是服务或插件**:没有 `ctx`、不注册任何东西、不持有状态、不发事件。 -## Surface +## 接口面 ```ts import { runNativeCommand, type NativeCommandRunner } from '@deepseek-ai/dsh-native-command' diff --git a/packages/util/retention/README.i18n.yaml b/packages/util/retention/README.i18n.yaml index aa87f67e37..b9ba96e765 100644 --- a/packages/util/retention/README.i18n.yaml +++ b/packages/util/retention/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/util/retention/README.md -README.md: d257075a67b35e92bce53a88fc6d002f4f4d5d9b -README.zh.md: 1a0761b4744b7f090cfbf94f847bb99fc2d41a96 +README.md: 45c88e9fc2d8df98f935fd1c3b43c4622802bd2a +README.zh.md: 0fcb76a8ae381d3e3da54e90640a38cba9de12e8 diff --git a/packages/util/retention/README.md b/packages/util/retention/README.md index d257075a67..45c88e9fc2 100644 --- a/packages/util/retention/README.md +++ b/packages/util/retention/README.md @@ -47,7 +47,7 @@ Text caps and `omittedBytes` count **bytes**, for process/body safety (a child's ## Tool mappings -Every current retention consumer maps to the library below. A broad migration is out of scope for the library's first landing — these are the intended shapes. +Current retention consumers use these mappings: | Tool | Retainer & strategy | Notes | |---|---|---| @@ -57,7 +57,7 @@ Every current retention consumer maps to the library below. A broad migration is | `web_fetch` | `TextRetainer`, `head` or `headTail` | Provider/resource caps stay provider facts; the retainer supplies only retained text and omission metadata. | | `web_search` | `ItemRetainer`, `head` | Standardizes the "sources capped" notice when providers return more sources than the model-facing result should include. | -`read` is **intentionally out of scope for v1.** Its `read-render` helper owns a file-specific pagination contract — `offset`/`limit`, line numbers, `totalLines`, offset-out-of-range errors, per-line preview truncation, a byte cap over the selected window — which is a line-window renderer, not generic retention. A single `Omitted` count cannot represent both sides of a line window. +`read` remains outside this generic library. Its `read-render` helper owns a file-specific pagination contract — `offset`/`limit`, line numbers, `totalLines`, offset-out-of-range errors, per-line preview truncation, and a byte cap over the selected window — which is a line-window renderer. A single `Omitted` count cannot represent both sides of that window. ## Usage shape diff --git a/packages/util/retention/README.zh.md b/packages/util/retention/README.zh.md index 1a0761b474..0fcb76a8ae 100644 --- a/packages/util/retention/README.zh.md +++ b/packages/util/retention/README.zh.md @@ -47,7 +47,7 @@ import type { ## 工具映射 -当前每个保留消费方都按下表映射到该库。广泛迁移不属于该库首次落地的范围;下表是预期形态。 +当前的保留机制消费方采用以下映射: | 工具 | Retainer 与策略 | 说明 | |---|---|---| @@ -57,7 +57,7 @@ import type { | `web_fetch` | `TextRetainer`,`head` 或 `headTail` | 提供方/资源上限保留为提供方事实;retainer 只提供保留文本和省略元数据。 | | `web_search` | `ItemRetainer`,`head` | 当提供方返回的来源超过面向模型的结果应包含的数量时,标准化「来源已达上限」通知。 | -`read` **刻意不在 v1 范围内**。其 `read-render` 辅助工具负责文件专用的分页契约:`offset`/`limit`、行号、`totalLines`、偏移越界错误、每行预览截断、针对已选窗口的字节上限。这是行窗口渲染器,而非通用保留机制。单个 `Omitted` 数量无法表示行窗口两侧。 +`read` 仍不属于这个通用库。其 `read-render` 辅助工具负责文件专用的分页契约:`offset`/`limit`、行号、`totalLines`、偏移越界错误、每行预览截断,以及所选窗口的字节上限。这是行窗口渲染器。单个 `Omitted` 数量无法表示该窗口两侧。 ## 使用形态 diff --git a/packages/util/timeout/README.i18n.yaml b/packages/util/timeout/README.i18n.yaml index 45df2b407f..de4a85e9c9 100644 --- a/packages/util/timeout/README.i18n.yaml +++ b/packages/util/timeout/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/util/timeout/README.md -README.md: 11c55a45a1255e14fb551e42ba3965453dbd94ae -README.zh.md: 078c36a20b4ba7d08f67cc394b2f178f6c0a9ab8 +README.md: 0ff5550ef7ea6b8315a6a529a4b8b503162a8f12 +README.zh.md: 79d7ee674209b0324ae9428c1a04fda8a2547db5 diff --git a/packages/util/timeout/README.md b/packages/util/timeout/README.md index 11c55a45a1..0ff5550ef7 100644 --- a/packages/util/timeout/README.md +++ b/packages/util/timeout/README.md @@ -46,7 +46,7 @@ export async function runWithDeadline(upstream: AbortSignal | undefined, timeout The signal only *notifies* — the caller MUST attach its own termination (`d.signal.addEventListener('abort', kill)`, or hand `d.signal` to `fetch`). Racing a promise against a timer would resolve the tool-call while the child process or socket leaks on; handing out a signal forces a real termination path to exist. -Pass your own `code` to `timeoutOf` so classification composes under nesting: when the `upstream` you were handed is *itself* a deadline signal (a future `tools/execute` middleware arming a per-call deadline), `AbortSignal.any` preserves the outer `TimeoutReason` if the outer timer fires first. Scoping to your `code` makes a foreign timeout read as an ordinary upstream cancel — the correct classification from your capability's view — instead of your own timeout firing when your local timer never expired. +Pass your own `code` to `timeoutOf` so classification composes under nesting. When `upstream` is itself a deadline signal, `AbortSignal.any` preserves its `TimeoutReason` if that timer fires first. Scoping to your code makes a foreign timeout read as an ordinary upstream cancel instead of claiming that the local timer expired. For a streamed transport, create one `idleWatchdog`, pass its stable `signal` into the transport, and call `watchdog.next(iterator)` for each provider read. The interval must be positive, finite, and no greater than `MAX_TIMER_DELAY_MS`; Node otherwise clamps it to one millisecond. It measures only outstanding demand, so no timer runs while downstream code renders or otherwise waits before asking for the next chunk. The primitive still only notifies, so the transport must observe the stable signal; the DeepSeek and pi-ai adapters prove that timeout closes their real response body or SDK request. diff --git a/packages/util/timeout/README.zh.md b/packages/util/timeout/README.zh.md index 078c36a20b..79d7ee6742 100644 --- a/packages/util/timeout/README.zh.md +++ b/packages/util/timeout/README.zh.md @@ -46,7 +46,7 @@ export async function runWithDeadline(upstream: AbortSignal | undefined, timeout 该信号只会*通知*;调用方必须接入自己的终止机制(`d.signal.addEventListener('abort', kill)`,或将 `d.signal` 传给 `fetch`)。让 promise 与 timer 竞速,会在子进程或套接字仍在泄漏时就让工具调用完成;发出信号则会强制要求存在真正的终止路径。 -将你自己的 `code` 传给 `timeoutOf`,以便分类可在嵌套中组合:当你收到的 `upstream` *本身*就是 deadline 信号时(未来启动每次调用 deadline 的 `tools/execute` 中间件),如果外层 timer 首先触发,`AbortSignal.any` 会保留外层 `TimeoutReason`。将范围限定为你的 `code`,可将外部超时视为普通 upstream 取消,这才是你所属功能视角下的正确分类,而不会在本地 timer 尚未到期时就声称自己超时。 +将你自己的 `code` 传给 `timeoutOf`,使分类可在嵌套场景中正确组合。当 `upstream` 本身是 deadline 信号时,如果该 timer 先触发,`AbortSignal.any` 会保留它的 `TimeoutReason`。将匹配范围限定为你的 code,会把外部超时视为普通的 upstream 取消,而不会声称本地 timer 已到期。 对于流式传输,创建一个 `idleWatchdog`,将其稳定的 `signal` 传给传输层,并为提供方的每次读取调用 `watchdog.next(iterator)`。间隔必须为正有限数,且不得超过 `MAX_TIMER_DELAY_MS`;否则 Node 会将其限制为 1 毫秒。它只对尚未完成的读取请求计时,因此当下游代码进行渲染或在请求下一个分片前以其他方式等待时,timer 不会运行。该原语仍然只会通知,因此传输层必须观察稳定信号;DeepSeek 和 pi-ai 适配器证明,超时会关闭它们的真实响应正文或 SDK 请求。 diff --git a/packages/web/README.i18n.yaml b/packages/web/README.i18n.yaml index 612114305c..cd412d1a99 100644 --- a/packages/web/README.i18n.yaml +++ b/packages/web/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/web/README.md -README.md: 8cd173b922ead32219ffab2b2d6b6428b3ce375c -README.zh.md: 0b07514ace8cfc9bd92126274c68363b3e942f4d +README.md: 65b3dde1e2f9c35c308a10ca80245064f7a0361e +README.zh.md: 43b8b7d9c53336e984d9c61fa9b612fc1733e31f diff --git a/packages/web/README.zh.md b/packages/web/README.zh.md index 0b07514ace..43b8b7d9c5 100644 --- a/packages/web/README.zh.md +++ b/packages/web/README.zh.md @@ -1,18 +1,16 @@ -# web/ - web 能力家族 +# web/:web 能力家族 [English](README.md) | 中文 -web 访问能力 seam:抽象 web 接口、搜索/抓取提供方实现,以及面向模型的 web 工具。这些全是**产品**包。 +本家族提供与提供方无关的 web 搜索和抓取操作,以及消费这些操作的面向模型工具。 | 包 | 职责 | ctx key | |---|---|---| -| `web/` | 抽象 web seam(搜索/抓取提供方注册表 + 选择 + 词汇 + `WebError`) | `ctx.web` | -| `web-search-exa/` | Exa 支持的 `WebSearchProvider` | (注册到 `ctx.web`) | -| `web-search-perplexity/` | Perplexity 支持的 `WebSearchProvider` | (注册到 `ctx.web`) | -| `web-search-deepseek/` | DeepSeek 支持的 `WebSearchProvider`,通过 Anthropic 兼容 API 使用原生 `web_search` | (注册到 `ctx.web`) | -| `web-fetch-local/` | 用于匿名访问公共 HTTP(S) 的 `WebFetchProvider` | (注册到 `ctx.web`) | -| `tool-web/` | 面向模型的 `web_search`/`web_fetch` 工具 schema | (注册到 `ctx.tools`) | +| [`web/`](web/README.md) | 定义 web 提供方注册、选择和共享错误 | `ctx.web` | +| [`web-search-exa/`](web-search-exa/README.md) | 通过 Exa 提供 web 搜索 | 注册到 `ctx.web` | +| [`web-search-perplexity/`](web-search-perplexity/README.md) | 通过 Perplexity 提供 web 搜索 | 注册到 `ctx.web` | +| [`web-search-deepseek/`](web-search-deepseek/README.md) | 提供 DeepSeek 原生 web 搜索 | 注册到 `ctx.web` | +| [`web-fetch-local/`](web-fetch-local/README.md) | 抓取公共 HTTP 和 HTTPS 资源 | 注册到 `ctx.web` | +| [`tool-web/`](tool-web/README.md) | 向模型公开 web 搜索和抓取 | 注册到 `ctx.tools` | -接口位于 `web/web/`。与 bash/fs 不同,该 seam 跨越**两种能力**(搜索和抓取),每种能力都可能有多个提供方:`ctx.web` 是单一的 web 访问中间层,拥有一项提供方选择策略、一套中止/错误词汇,以及一个面向产品的「该 harness 如何访问 web」配置接口。提供方注册的是**能力**而非工具;`tool-web` 是面向模型名称、schema、提示词指引和呈现的唯一负责方。替换搜索提供方不会改变模型提出查询的方式,替换抓取实现也不会改变模型请求 URL 的方式。 - -设计原理见 [web 能力 seam Agent Note](../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md),其中也解释了搜索与抓取为何有意合并为一个 seam,以及为何暂缓实现 `web_fetch` 的 SSRF 防护。 +[web 能力决策](../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md)记录了搜索和抓取共用一项提供方选择服务的原因。 diff --git a/packages/workflow/README.i18n.yaml b/packages/workflow/README.i18n.yaml index 450461c540..f02915a31d 100644 --- a/packages/workflow/README.i18n.yaml +++ b/packages/workflow/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/workflow/README.md -README.md: 17da3397e4fa64837b271d89e9062e3621434338 -README.zh.md: 1609d1e961f9b21f859eda66ed84d7f8bdbbe978 +README.md: 2416e26e73340a6d8624b8e77681dfc9f55fe7c5 +README.zh.md: a5c94a0a9799d78e5cca72394385b38d2fb2ba70 diff --git a/packages/workflow/README.zh.md b/packages/workflow/README.zh.md index 1609d1e961..a5c94a0a97 100644 --- a/packages/workflow/README.zh.md +++ b/packages/workflow/README.zh.md @@ -1,16 +1,14 @@ -# workflow/:动态工作流能力族 +# workflow/:动态工作流能力家族 [English](README.md) | 中文 -工作流 seam:由模型编写 JavaScript 编排脚本,大规模扇出 subagent(分阶段、每个 agent(智能体)的结构化结果、并发上限),其设计参考 Claude Code 动态工作流。这是 bash 形态的能力 seam(见[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)):每个上下文只有一个引擎实现注册为 `ctx.workflows`;面向模型的工具使用它。 +本家族通过 subagent 运行由模型编写的编排工作流,并将通用工具与固定策略工具公开给模型。 -| 包 | 角色 | ctx 键 | +| 包 | 职责 | ctx 键 | |---|---|---| -| `workflow/` | 抽象工作流 seam:服务基类、运行词汇和 `workflow/*` 事件 | `ctx.workflows` | -| `workflow-workerthread/` | `node:worker_threads` 引擎:每次运行使用一个 worker;脚本的 vm 上下文位于 worker 内,`agent()` 通过消息端口桥接到 `ctx.subagents` | (提供 `ctx.workflows`) | -| `tool-workflow/` | 面向模型的 `workflow` 工具,基于 `ctx.workflows` | (注册到 `ctx.tools`) | -| `tool-ralph/` | 基于 `ctx.workflows` 和全新结构化输出 subagent 提供方的固定全新 agent Ralph 策略 | (注册到 `ctx.tools`) | +| [`workflow/`](workflow/README.md) | 定义工作流执行和生命周期事件 | `ctx.workflows` | +| [`workflow-workerthread/`](workflow-workerthread/README.md) | 在线程中运行工作流脚本 | 注册到 `ctx.workflows` | +| [`tool-workflow/`](tool-workflow/README.md) | 向模型公开通用工作流执行 | 注册到 `ctx.tools` | +| [`tool-ralph/`](tool-ralph/README.md) | 公开使用全新 agent(智能体)的固定 Ralph 工作流 | 注册到 `ctx.tools` | -接口位于 `workflow/workflow/`。引擎的 `agent()` 钩子使用 [subagent seam](../subagent/README.md)(任何已注册提供方;随产品交付的示例使用 `spawn`),`agent({ schema })` 则使用进程内后端实现的结构化输出支持。worker thread 隔离的是脚本:宿主绝不会被它阻塞,已取消运行经过宽限时间后的终止也会实际生效;但它不是安全边界。如果将来确有需要,可以在同一接口背后换用 isolated-vm/独立进程引擎,以实现真正的沙箱隔离。 - -通用脚本引擎的决策和暂缓事项见[动态工作流 Agent Note](../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md)。独立的 [Ralph 消费方](../../.agents/notes/implemented/feature/2026-07-19-fresh-agent-ralph-workflow-tool.md)会固定脚本和全新提供方策略,而不是再添加一个引擎或 agent loop(智能体循环)模式。 +worker thread 将工作流执行与宿主事件循环隔离,但不构成安全边界。参见[动态工作流](../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md)和 [Ralph 工具](../../.agents/notes/implemented/feature/2026-07-19-fresh-agent-ralph-workflow-tool.md)决策。 diff --git a/packages/workspace/README.i18n.yaml b/packages/workspace/README.i18n.yaml index ba76f98d80..93dea4b3f0 100644 --- a/packages/workspace/README.i18n.yaml +++ b/packages/workspace/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/workspace/README.md -README.md: ba92e95d3cde0a95eaaeae5a9b4384c3b8c9c4b8 -README.zh.md: 96fd4b9790dcb5cc69e068aa14e2348dfa86a31e +README.md: 9f6ead776c1e5d36af5b6917ad8f27725b11e6a0 +README.zh.md: aa691ec681112f4c25a6391c33eabd2711c7abfc diff --git a/packages/workspace/README.md b/packages/workspace/README.md index 4b3b19b282..9f6ead776c 100644 --- a/packages/workspace/README.md +++ b/packages/workspace/README.md @@ -8,4 +8,4 @@ This family owns persistent workspaces: user directories with titles and ordered |---|---|---| | [`workspace/`](workspace/README.md) | Registers workspaces and accounts for their sessions | `ctx.workspace` | -Workspace deletion removes the registry record, not user files or session logs. See the [workspace lifecycle decision](../../.agents/notes/implemented/feature/2026-07-27-workspace-registration-deletion.md) and [storage design](../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.md). +The [workspace package reference](workspace/README.md) owns lifecycle, persistence, and deletion semantics. diff --git a/packages/workspace/README.zh.md b/packages/workspace/README.zh.md index 96fd4b9790..aa691ec681 100644 --- a/packages/workspace/README.zh.md +++ b/packages/workspace/README.zh.md @@ -1,11 +1,11 @@ -# workspace/:Workspace 实体 +# workspace/:workspace 实体家族 [English](README.md) | 中文 -Workspace 系列负责持久 workspace 概念:用户工作所在的目录,包含标题以及属于它的有序会话列表。设计记录:[领域 KV 存储 Agent Note](../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.md)。 +本家族拥有持久 workspace:带标题和有序会话成员关系的用户目录。 | 包 | 职责 | ctx 键 | |---|---|---| -| `workspace/` | 基于存储领域数据形式的 `WorkspaceRegistry` 服务:按 realpath 去重的路径、会话归属记账、实体缓存 | `ctx.workspace` | +| [`workspace/`](workspace/README.md) | 注册 workspace 并记录其会话归属 | `ctx.workspace` | -所有权信息以 workspace 记录中的 `sessionIds`(有序)为准,绝不从会话 cwd 派生;`attachSession` 会验证会话头的 cwd 解析到 workspace 路径,因此一个会话在结构上最多属于一个 workspace。删除 Workspace 只会移除该注册表记录及会话归属记录:目录、用户文件和会话日志都会保留,相关会话则进入 Ungrouped(参见[决策记录](../../.agents/notes/implemented/feature/2026-07-27-workspace-registration-deletion.md))。 +[workspace 包参考](workspace/README.md)负责生命周期、持久化和删除语义。 diff --git a/python/README.i18n.yaml b/python/README.i18n.yaml index 29806c7c41..8dadf07570 100644 --- a/python/README.i18n.yaml +++ b/python/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write python/README.md -README.md: 010d922b52eae0be184baec063e307b5c8e55819 -README.zh.md: ebf773d943e3ad3248fb41c58ca7e44950bb9d7f +README.md: 27637edb9d4d5e8714fe379a00b6aae3af1a541f +README.zh.md: dd020aa507de965815fc05f8423b87e766a2fb42 diff --git a/python/README.md b/python/README.md index 010d922b52..27637edb9d 100644 --- a/python/README.md +++ b/python/README.md @@ -17,4 +17,4 @@ The SDK starts the matching bundled runtime unless the caller selects an explici ## Contributor workflows -The [Python contributor reference](development.md) covers building runtime artifacts, validating the packages, source-mode development, and distribution. +The [Python contributor workflows](development.md) cover building runtime artifacts, validating the packages, source-mode development, and distribution. diff --git a/python/README.zh.md b/python/README.zh.md index ebf773d943..dd020aa507 100644 --- a/python/README.zh.md +++ b/python/README.zh.md @@ -17,4 +17,4 @@ ## 贡献者工作流 -[Python 贡献者参考](development.md)介绍运行时产物构建、包验证、源码模式开发和分发。 +[Python 贡献者工作流](development.md)介绍运行时产物构建、包验证、源码模式开发和分发。 diff --git a/python/development.i18n.yaml b/python/development.i18n.yaml index ec6e970c2c..b7792acf27 100644 --- a/python/development.i18n.yaml +++ b/python/development.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write python/development.md -development.md: f7cf69f798c08f8e2b558bb68431bc77ab220378 -development.zh.md: 53e31bc1455367f95260dccf5a2bcc7d2ed6e55d +development.md: b0d4875f0d5b7c8fd2b4b480ac67793741640710 +development.zh.md: 4fda7efbeaa0359bd4999aca148ca21a63c4fd70 diff --git a/python/development.md b/python/development.md index f7cf69f798..b0d4875f0d 100644 --- a/python/development.md +++ b/python/development.md @@ -2,7 +2,7 @@ English | [中文](development.zh.md) -Reference for building, validating, and distributing the Python SDK and bundled runtime. Package behavior belongs in the [SDK reference](sdk/README.md) and [runtime carrier reference](sdk-runtime/README.md). +Follow the workflow for the contributor outcome you need: build runtime artifacts, validate the SDK, run against source, or build distributions. Package behavior belongs in the [SDK reference](sdk/README.md) and [runtime carrier reference](sdk-runtime/README.md). ## Build runtime artifacts diff --git a/python/development.zh.md b/python/development.zh.md index 53e31bc145..4fda7efbea 100644 --- a/python/development.zh.md +++ b/python/development.zh.md @@ -2,7 +2,7 @@ [English](development.md) | 中文 -构建、验证和分发 Python SDK 与内置运行时的参考。包行为分别见 [SDK 参考](sdk/README.md)和[运行时载体参考](sdk-runtime/README.md)。 +根据所需的贡献者成果选择工作流:构建运行时产物、验证 SDK、从源码运行或构建分发包。包行为分别见 [SDK 参考](sdk/README.md)和[运行时载体参考](sdk-runtime/README.md)。 ## 构建运行时产物 diff --git a/python/sdk/README.i18n.yaml b/python/sdk/README.i18n.yaml index a96ccd30b8..a57fee928d 100644 --- a/python/sdk/README.i18n.yaml +++ b/python/sdk/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write python/sdk/README.md README.md: bb3420f1a1bd461facbd0eb1312a255df1da4412 -README.zh.md: adc8e377ca9f70838b6164301a9052e2247c8eff +README.zh.md: 2cb48ac9aaae115bbcce5943449bca9e42f2592c diff --git a/python/sdk/README.zh.md b/python/sdk/README.zh.md index adc8e377ca..2cb48ac9aa 100644 --- a/python/sdk/README.zh.md +++ b/python/sdk/README.zh.md @@ -29,7 +29,7 @@ with DeepSeekHarness( result = harness.run("Make the requested code change.") ``` -`provider` 用于选择当前 Cordis 组合已注册的提供方路由;`model` 是该适配器解析的模型 ID。`max_tokens` 是可选的正整数,用于限制根 agent 及其进程内后代每次请求的输出 token;省略时由提供方默认值控制。压缩摘要继续使用压缩插件单独配置的上限。内置默认组合注册 `deepseek-official`。自定义组合可以挂载 `llm-pi-ai`,在其中配置各提供方的凭据与端点,再选择 pi-ai 已安装目录中的任意提供方/模型组合。 +`provider` 用于选择当前 Cordis 组合已注册的提供方路由;`model` 是该适配器解析的模型 ID。`max_tokens` 是可选的正整数,用于限制根 agent(智能体)及其进程内后代每次请求的输出 token;省略时由提供方默认值控制。压缩摘要继续使用压缩插件单独配置的上限。内置默认组合注册 `deepseek-official`。自定义组合可以挂载 `llm-pi-ai`,在其中配置各提供方的凭据与端点,再选择 pi-ai 已安装目录中的任意提供方/模型组合。 `HarnessClient` 会在运行时进程的生命周期内保留已发现的 subagent(子 agent)祖先关系。每次执行 `Session.run()` 时,`TurnResult.notifications` 与 `on_notification` 会按协议传输顺序收到根会话及所有已知后代的通知,其中包括嵌套 subagent 的生命周期事件与会话事件。`TurnResult.events` 仍只保存根会话的完整事件流,`TurnResult.final_response` 则取该会话最后一个 `assistant/message` 的文本内容,因此后代消息不会覆盖根会话回复。 diff --git a/scripts/gen-third-party-notices.ts b/scripts/gen-third-party-notices.ts index 6b790829d5..0d41953e4b 100644 --- a/scripts/gen-third-party-notices.ts +++ b/scripts/gen-third-party-notices.ts @@ -562,7 +562,7 @@ ${vendored.map(row => `| \`${row.npmName}\` | [${row.upstream.replace('https://' ## Runtime npm dependencies -External packages that a workspace package resolves at runtime. \`scripts/install.sh\` installs this repository itself, so the tier covers every plugin a user can mount from \`cordis.yml\` — not only what the \`dsh\` CLI/TUI, the Web UI, and the Python SDK runtime load by default. +External packages that a workspace package resolves at runtime. \`scripts/install.sh\` installs this repository itself, so the tier covers every plugin a user can mount from \`cordis.yml\` — not only what the \`dsh\` CLI, Web UI, and Python SDK runtime load by default. ${renderNpmTable(runtimeDeps)} diff --git a/skills/dsh-customize/SKILL.md b/skills/dsh-customize/SKILL.md index bde4c2eb00..5d67af1e51 100644 --- a/skills/dsh-customize/SKILL.md +++ b/skills/dsh-customize/SKILL.md @@ -22,7 +22,7 @@ Do not assume a path or branch name. DSH is usually installed from source with a 1. Create a fresh task branch and worktree from the recorded staging tip, using the repository-required worktree location — default to `.worktrees/` under the repository root unless the repository requires otherwise. Never implement or commit directly on staging. 2. Implement the change, then select and run the repository-required review and checks. If a check fails, fix the cause and rerun it before integration. -3. Test assembled TUI behavior interactively in a dedicated tmux session. Test other interactive UI behavior in a browser; unit tests and snapshots alone are insufficient. +3. Test assembled interactive behavior in the Web UI; unit tests and snapshots alone are insufficient. 4. Record the task tip and confirm the task worktree is clean before integration. ## Integrate under the lock diff --git a/skills/dsh-upstream-customization/SKILL.md b/skills/dsh-upstream-customization/SKILL.md index 944f4b083c..7ec1618db3 100644 --- a/skills/dsh-upstream-customization/SKILL.md +++ b/skills/dsh-upstream-customization/SKILL.md @@ -23,5 +23,5 @@ Classification and a recommendation are not publishing approval. Obtain explicit 3. Review the outgoing commits and diff against upstream. Confirm they contain only the approved feature, no credentials or personal data, and a clean worktree. 4. Reconfirm the approved feature name and publishing target before the first push. Do not infer authorization from earlier local work. 5. Push only that branch and open only a draft PR. Keep its description synchronized with later changes. -6. For a TUI feature, preferably attach a screenshot from the assembled application after removing credentials and personal data. +6. For a Web UI feature, attach a screenshot or GIF from the assembled application after removing credentials and personal data. 7. Report the upstream base and branch commits, commands and checks run, pushed branch, and draft PR URL. From 82218e0c364c74322d269e33ba74dac0ef73819d Mon Sep 17 00:00:00 2001 From: Turtle Date: Wed, 5 Aug 2026 12:54:42 +0800 Subject: [PATCH 6/7] test: refresh translation prompt snapshot --- .../request-response.expected.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/snapshots/translation-prompt-v4/request-response.expected.json b/scripts/snapshots/translation-prompt-v4/request-response.expected.json index f7be2898e9..7ca884f2f5 100644 --- a/scripts/snapshots/translation-prompt-v4/request-response.expected.json +++ b/scripts/snapshots/translation-prompt-v4/request-response.expected.json @@ -8,27 +8,27 @@ }, { "role": "user", - "content": "# DeepSeek Harness\n\nEnglish | [中文](README.zh.md)\n\nDeepSeek Harness (`dsh`) is an open-source coding agent built on the DeepSeek Harness SDK.\n\nIt uses an architecture where **everything is a plugin**.\n\n## Internal testing notice\n\n感谢您愿意拨冗试用 DeepSeek Harness。当前版本仍处于内部测试阶段,功能仍待完善,体验难免有些粗糙。\n\n“如切如磋,如琢如磨。” 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中发现的问题,也可能促使我们重新审视,甚至推翻已有的设计。\n\n为了帮助我们更准确地还原您真实使用中的问题,内测版本默认会上传所有 Session Log;如需关闭,可以设置环境变量 `DSH_TELEMETRY_DISABLED=1`。另外,如果您有任何反馈与建议,请在企业微信群中留言告诉我们。每一条反馈,都会帮助我们把它打磨得更好。\n\n## Install\n\nClone the repository, then run the installer:\n\n```sh\ngit clone \ncd deepseek-harness\nscripts/install.sh\n```\n\nThe installer requires `git` and Node `^22.19 || >=24`, offers to install `pnpm` when it is missing, prompts for a DeepSeek API key, builds the required repository artifacts, and launches the Web UI.\n\nThe installer keeps every checkout under `~/.dsh/source`: the master clone at `~/.dsh/source/master` and each install's staging checkout as a git worktree `~/.dsh/source/staging-`. The stable symlink `~/.dsh/source/current` points at the active staging worktree, and `dsh` in `~/.local/bin` links to `current/bin/dsh`, so an upgrade repoints one symlink and the `dsh` on PATH never moves. Re-running the command adds a fresh staging worktree from an updated master and repoints `current` at it. See [`scripts/install.sh`](scripts/install.sh) for alternate install locations and other options.\n\n## Use DeepSeek Harness\n\n### Web UI\n\nFor the recommended local interface, choose Web UI when the installer finishes. To start it later, or after updating the active checkout, build the repository and run:\n\n```sh\n(cd ~/.dsh/source/current && pnpm run build)\ndsh web\n```\n\nThe path above is the installer's default. If you set `DSH_SOURCE` or `DSH_CURRENT`, or reused an existing checkout, replace `~/.dsh/source/current` with that checkout path; see [`scripts/install.sh`](scripts/install.sh) for details. The Web UI is served at `http://127.0.0.1:3080` by default.\n\n### Configured runtime\n\nRaw `dsh` requires a patch-list configuration applied over the shipped base:\n\n```sh\ndsh --config ./app.cordis.yml\n```\n\nThe [CLI contract](apps/cli/README.md#raw-config) describes the base, overlay semantics, and config dump commands.\n\n### Headless\n\nRun one task, print the final answer, and exit:\n\n```sh\ndsh -p \"summarize this workspace\"\n```\n\n### Automation and SDKs\n\nFrom a source checkout with `DEEPSEEK_API_KEY` in the environment or its root `.env`, start the ACP automation server:\n\n```sh\npnpm run demo:acp\n```\n\nThe [Python SDK](python/README.md) drives a bundled JSON-RPC runtime. The [examples](examples/README.md) cover the runnable headless, ACP, JSON-RPC, Code Mode, and self-referential compositions.\n\n## Why DeepSeek Harness\n\nBuilt-in capabilities cover file reading, editing, and search; shell and persistent PTY execution; reusable skills; task tracking, goals, plans, todos, and background tasks; subagents and workflows; sandboxing and approvals; settings and credentials; persistent, resumable, forkable, and queryable sessions; LSP and web access; context compaction; and telemetry. Each composition selects the subset appropriate to its surface. The Web UI includes Plan Mode.\n\n- **Everything is a plugin.** Models, tools, policies, storage, context management, and interfaces are composable [Cordis plugins](docs/user/develop/basic/index.md), so deployments can extend or replace behavior without forking the agent loop. See the [architecture](docs/architecture.md) for the underlying design.\n- **Runs are reconstructable.** Anything visible to the model is logged in the authoritative session stream; persistence, resume/fork/query, replay, telemetry, and UIs derive from the same events. See the [session-log architecture](docs/architecture.md#session-log).\n- **Code Mode (opt-in).** It exposes a `run_code` tool and a generated TypeScript SDK; only program output re-enters model context. See [Code Mode](packages/core/tools/README.md#code-mode).\n- **Self-referential Cordis tools are opt-in.** They let the agent inspect its live runtime and mount or unmount plugins while it runs. See the [Cordis tools](packages/cordis/tool-cordis/README.md).\n\n## Community\n\nFollow DeepSeek Harness on Twitter for project updates.\n\n## Development\n\n```sh\npnpm install\npnpm run test:coverage\n```\n\nStart with the [development guide](docs/development.md) and read the [architecture](docs/architecture.md) before changing packages.\n\nFor agents, follow [AGENTS.md](AGENTS.md).\n\nDeepSeek Harness is currently in internal testing.\n\n## License\n\n[BSD 3-Clause](LICENSE)\n\nThird-party dependencies and their licenses are disclosed in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).\n" + "content": "# DeepSeek Harness\n\nEnglish | [中文](README.zh.md)\n\nDeepSeek Harness (`dsh`) is an open-source coding agent built on the DeepSeek Harness SDK.\n\nIt uses an architecture where **everything is a plugin**.\n\n## Internal testing notice\n\nDeepSeek Harness is under internal testing. Features and interfaces may change.\n\nThe internal build uploads all Session Logs by default to help diagnose reported problems. Set `DSH_TELEMETRY_DISABLED=1` to disable telemetry. Send feedback through the internal WeChat group.\n\n## Install\n\nClone the repository, then run the installer:\n\n```sh\ngit clone \ncd deepseek-harness\nscripts/install.sh\n```\n\nThe installer requires `git` and Node `^22.19 || >=24`, offers to install `pnpm` when it is missing, prompts for a DeepSeek API key, builds the required repository artifacts, and launches the Web UI.\n\nThe default active checkout is `~/.dsh/source/current`, and the launcher is linked into `~/.local/bin`. Re-run the installer to update. [`scripts/install.sh`](scripts/install.sh) owns alternate locations, update mechanics, and recovery options.\n\n## Use DeepSeek Harness\n\n### Web UI\n\nFor the recommended local interface, choose Web UI when the installer finishes. To start it later, or after updating the active checkout, build the repository and run:\n\n```sh\n(cd ~/.dsh/source/current && pnpm run build)\ndsh web\n```\n\nThe path above is the installer's default. If you set `DSH_SOURCE` or `DSH_CURRENT`, or reused an existing checkout, replace `~/.dsh/source/current` with that checkout path; see [`scripts/install.sh`](scripts/install.sh) for details. The Web UI is served at `http://127.0.0.1:3080` by default.\n\n### Configured runtime\n\nRaw `dsh` requires a patch-list configuration applied over the shipped base:\n\n```sh\ndsh --config ./app.cordis.yml\n```\n\nThe [CLI contract](apps/cli/README.md#raw-config) describes the base, overlay semantics, and config dump commands.\n\n### Headless\n\nRun one task, print the final answer, and exit:\n\n```sh\ndsh -p \"summarize this workspace\"\n```\n\n### Automation and SDKs\n\nFrom a source checkout with `DEEPSEEK_API_KEY` in the environment or its root `.env`, start the ACP automation server:\n\n```sh\npnpm run demo:acp\n```\n\nThe [Python SDK](python/README.md) drives a bundled JSON-RPC runtime. The [examples](examples/README.md) cover the runnable headless, ACP, JSON-RPC, Code Mode, and self-referential compositions.\n\n## Why DeepSeek Harness\n\nBuilt-in capabilities cover file reading, editing, and search; shell and persistent PTY execution; reusable skills; task tracking, goals, plans, todos, and background tasks; subagents and workflows; sandboxing and approvals; settings and credentials; persistent, resumable, forkable, and queryable sessions; LSP and web access; context compaction; and telemetry. Each composition selects the subset appropriate to its surface. The Web UI includes Plan Mode.\n\n- **Everything is a plugin.** Models, tools, policies, storage, context management, and interfaces are composable [Cordis plugins](docs/user/develop/basic/index.md), so deployments can extend or replace behavior without forking the agent loop. See the [architecture](docs/architecture.md) for the underlying design.\n- **Runs are reconstructable.** Anything visible to the model is logged in the authoritative session stream; persistence, resume/fork/query, replay, telemetry, and UIs derive from the same events. See the [session-log architecture](docs/architecture.md#session-log).\n- **Code Mode (opt-in).** It exposes a `run_code` tool and a generated TypeScript SDK; only program output re-enters model context. See [Code Mode](packages/core/tools/README.md#code-mode).\n- **Self-referential Cordis tools are opt-in.** They let the agent inspect its live runtime and mount or unmount plugins while it runs. See the [Cordis tools](packages/cordis/tool-cordis/README.md).\n\n## Community\n\nFollow DeepSeek Harness on Twitter for project updates.\n\n## Development\n\n```sh\npnpm install\npnpm run test:coverage\n```\n\nStart with the [development guide](docs/development.md) and read the [architecture](docs/architecture.md) before changing packages.\n\nFor agents, follow [AGENTS.md](AGENTS.md).\n\nDeepSeek Harness is currently in internal testing.\n\n## License\n\n[BSD 3-Clause](LICENSE)\n\nThird-party dependencies and their licenses are disclosed in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).\n" }, { "role": "assistant", - "content": "# DeepSeek Harness\n\n[English](README.md) | 中文\n\nDeepSeek Harness(`dsh`)是一款基于 DeepSeek Harness SDK 构建的开源 coding agent(编程智能体)。\n\n它采用了**一切皆插件**的架构。\n\n## 内测声明\n\n感谢您愿意拨冗试用 DeepSeek Harness。当前版本仍处于内部测试阶段,功能仍待完善,体验难免有些粗糙。\n\n“如切如磋,如琢如磨。” 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中发现的问题,也可能促使我们重新审视,甚至推翻已有的设计。\n\n为了帮助我们更准确地还原您真实使用中的问题,内测版本默认会上传所有 Session Log;如需关闭,可以设置环境变量 `DSH_TELEMETRY_DISABLED=1`。另外,如果您有任何反馈与建议,请在企业微信群中留言告诉我们。每一条反馈,都会帮助我们把它打磨得更好。\n\n## 安装\n\n克隆仓库,然后运行安装器:\n\n```sh\ngit clone \ncd deepseek-harness\nscripts/install.sh\n```\n\n安装器要求系统已安装 `git` 和 Node `^22.19 || >=24`,缺少 `pnpm` 时可代为安装,并会提示输入 DeepSeek API 密钥,然后构建所需的仓库产物并启动 Web UI。\n\n安装器会把所有检出都放在 `~/.dsh/source` 下:master 克隆位于 `~/.dsh/source/master`,每次安装的 staging 检出是一个 git worktree `~/.dsh/source/staging-<时间戳>`。稳定符号链接 `~/.dsh/source/current` 指向当前生效的 staging worktree,`~/.local/bin` 中的 `dsh` 链接到 `current/bin/dsh`,因此升级只需重指一个符号链接,PATH 上的 `dsh` 从不移动。再次运行该命令会基于更新后的 master 新增一个 staging worktree,并把 `current` 重指到它。其他安装位置和选项见 [`scripts/install.sh`](scripts/install.sh)。\n\n## 使用 DeepSeek Harness\n\n### Web UI\n\n推荐在本地使用 Web UI;安装结束时,选择 Web UI 即可。以后需要启动时,或更新当前生效的检出后,请构建仓库并运行:\n\n```sh\n(cd ~/.dsh/source/current && pnpm run build)\ndsh web\n```\n\n上述路径是安装器的默认位置。如果你设置过 `DSH_SOURCE` 或 `DSH_CURRENT`,或者复用了已有检出,请把 `~/.dsh/source/current` 换成该检出路径;详情见 [`scripts/install.sh`](scripts/install.sh)。Web UI 默认通过 `http://127.0.0.1:3080` 提供服务。\n\n### 自定义运行时\n\n原始 `dsh` 要求传入一份 patch 列表配置,并将其叠加在随附 base 之上:\n\n```sh\ndsh --config ./app.cordis.yml\n```\n\nbase、overlay 语义与配置输出命令详见 [CLI(命令行界面)契约](apps/cli/README.md#raw-config)。\n\n### Headless\n\n运行一项任务,打印最终答案后退出:\n\n```sh\ndsh -p \"summarize this workspace\"\n```\n\n### 自动化与 SDK\n\n在源码检出中通过环境变量或根目录 `.env` 设置 `DEEPSEEK_API_KEY`,然后启动 ACP(Agent Client Protocol)自动化服务器:\n\n```sh\npnpm run demo:acp\n```\n\n[Python SDK](python/README.md) 驱动随附的 JSON-RPC 运行时。[示例](examples/README.md)涵盖可运行的 headless、ACP、JSON-RPC、Code Mode 和自指组合。\n\n## 为什么选择 DeepSeek Harness\n\n内置功能涵盖文件读取、编辑与搜索、shell 和持久 PTY 执行、可复用 skill(技能)、任务跟踪、目标、计划、待办事项与后台任务、subagent 与工作流、沙箱与审批、设置与凭据、可持久化、恢复、fork 与查询的会话、LSP 与 Web 访问、上下文压缩(context compaction),以及遥测。每个组合只选用适合其使用方式的能力子集。Web UI 包含 Plan Mode。\n\n- **一切皆插件。** 模型、工具、策略、存储、上下文管理和界面均可组合为 [Cordis 插件](docs/user/develop/basic/index.md),部署方无需 fork agent loop(智能体循环)即可扩展或替换行为。底层设计见[架构文档](docs/architecture.md)。\n- **运行可重建。** 凡是模型可见的内容,都会记录在权威会话流中;持久化、恢复/fork/查询、回放、遥测和 UI 均从同一组事件派生。参见[会话日志架构](docs/architecture.md#session-log)。\n- **Code Mode(需显式启用)。** 它会提供 `run_code` 工具和生成的 TypeScript SDK,只有程序输出会重新进入模型上下文。参见 [Code Mode](packages/core/tools/README.md#code-mode)。\n- **自指 Cordis 工具需显式启用。** 这些工具可让 agent 检查自身的实时运行时,并在运行中挂载或卸载插件。参见 [Cordis 工具](packages/cordis/tool-cordis/README.md)。\n\n## 社区\n\n扫描二维码,或打开 DeepSeek Harness 微信社区申请页面 申请加入。\n\n

\n \"DeepSeek\n

\n\n## 开发\n\n```sh\npnpm install\npnpm run test:coverage\n```\n\n请先阅读[开发指南](docs/development.md);修改包之前,请阅读[架构文档](docs/architecture.md)。\n\n面向 agent:遵循 [AGENTS.md](AGENTS.md)。\n\nDeepSeek Harness 目前处于内测阶段。\n\n## 许可证\n\n[BSD 3-Clause](LICENSE)\n\n第三方依赖及其许可证在 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) 中披露。\n" + "content": "# DeepSeek Harness\n\n[English](README.md) | 中文\n\nDeepSeek Harness(`dsh`)是一款基于 DeepSeek Harness SDK 构建的开源 coding agent(编程智能体)。\n\n它采用了**一切皆插件**的架构。\n\n## 内测声明\n\nDeepSeek Harness 正处于内部测试阶段,功能和接口可能发生变化。\n\n为帮助诊断上报的问题,内测版本默认上传所有会话日志。设置 `DSH_TELEMETRY_DISABLED=1` 可关闭遥测。请通过内部企业微信群反馈问题和建议。\n\n## 安装\n\n克隆仓库,然后运行安装器:\n\n```sh\ngit clone \ncd deepseek-harness\nscripts/install.sh\n```\n\n安装器要求系统已安装 `git` 和 Node `^22.19 || >=24`,缺少 `pnpm` 时可代为安装,并会提示输入 DeepSeek API 密钥,然后构建所需的仓库产物并启动 Web UI。\n\n默认生效的检出位于 `~/.dsh/source/current`,启动器链接到 `~/.local/bin`。再次运行安装器即可更新。其他位置、更新机制和恢复选项由 [`scripts/install.sh`](scripts/install.sh) 负责。\n\n## 使用 DeepSeek Harness\n\n### Web UI\n\n推荐在本地使用 Web UI;安装结束时,选择 Web UI 即可。以后需要启动时,或更新当前生效的检出后,请构建仓库并运行:\n\n```sh\n(cd ~/.dsh/source/current && pnpm run build)\ndsh web\n```\n\n上述路径是安装器的默认位置。如果你设置过 `DSH_SOURCE` 或 `DSH_CURRENT`,或者复用了已有检出,请把 `~/.dsh/source/current` 换成该检出路径;详情见 [`scripts/install.sh`](scripts/install.sh)。Web UI 默认通过 `http://127.0.0.1:3080` 提供服务。\n\n### 自定义运行时\n\n原始 `dsh` 要求传入一份 patch 列表配置,并将其叠加在随附 base 之上:\n\n```sh\ndsh --config ./app.cordis.yml\n```\n\nbase、overlay 语义与配置输出命令详见 [CLI(命令行界面)契约](apps/cli/README.md#raw-config)。\n\n### Headless\n\n运行一项任务,打印最终答案后退出:\n\n```sh\ndsh -p \"summarize this workspace\"\n```\n\n### 自动化与 SDK\n\n在源码检出中通过环境变量或根目录 `.env` 设置 `DEEPSEEK_API_KEY`,然后启动 ACP(Agent Client Protocol)自动化服务器:\n\n```sh\npnpm run demo:acp\n```\n\n[Python SDK](python/README.md) 驱动随附的 JSON-RPC 运行时。[示例](examples/README.md)涵盖可运行的 headless、ACP、JSON-RPC、Code Mode 和自指组合。\n\n## 为什么选择 DeepSeek Harness\n\n内置功能涵盖文件读取、编辑与搜索、shell 和持久 PTY 执行、可复用 skill(技能)、任务跟踪、目标、计划、待办事项与后台任务、subagent 与工作流、沙箱与审批、设置与凭据、可持久化、恢复、fork 与查询的会话、LSP 与 Web 访问、上下文压缩(context compaction),以及遥测。每个组合只选用适合其使用方式的能力子集。Web UI 包含 Plan Mode。\n\n- **一切皆插件。** 模型、工具、策略、存储、上下文管理和界面均可组合为 [Cordis 插件](docs/user/develop/basic/index.md),部署方无需 fork agent loop(智能体循环)即可扩展或替换行为。底层设计见[架构文档](docs/architecture.md)。\n- **运行可重建。** 凡是模型可见的内容,都会记录在权威会话流中;持久化、恢复/fork/查询、回放、遥测和 UI 均从同一组事件派生。参见[会话日志架构](docs/architecture.md#session-log)。\n- **Code Mode(需显式启用)。** 它会提供 `run_code` 工具和生成的 TypeScript SDK,只有程序输出会重新进入模型上下文。参见 [Code Mode](packages/core/tools/README.md#code-mode)。\n- **自指 Cordis 工具需显式启用。** 这些工具可让 agent 检查自身的实时运行时,并在运行中挂载或卸载插件。参见 [Cordis 工具](packages/cordis/tool-cordis/README.md)。\n\n## 社区\n\n扫描二维码,或打开 DeepSeek Harness 微信社区申请页面 申请加入。\n\n

\n \"DeepSeek\n

\n\n## 开发\n\n```sh\npnpm install\npnpm run test:coverage\n```\n\n请先阅读[开发指南](docs/development.md);修改包之前,请阅读[架构文档](docs/architecture.md)。\n\n面向 agent:遵循 [AGENTS.md](AGENTS.md)。\n\nDeepSeek Harness 目前处于内测阶段。\n\n## 许可证\n\n[BSD 3-Clause](LICENSE)\n\n第三方依赖及其许可证在 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) 中披露。\n" }, { "role": "user", - "content": "# Development guide\n\nEnglish | [中文](development.zh.md)\n\nThis onboarding guide helps project contributors get started with the local environment, daily workflow, and CI flow; see the Agent Notes for design rationale and technical trade-offs.\n\n## Prerequisites\n\n- Node.js supports 22.19+ and 24+. CI covers 22.19, 24, and 26; see the [Node engine floor Agent Note](../.agents/notes/implemented/process/2026-07-06-node-engine-floor.md).\n- Corepack-enabled pnpm. The repo pins `pnpm@11.7.0` in `package.json`; run `corepack enable` if `pnpm --version` does not resolve through Corepack.\n- Git 2.26 or newer; hook setup enables Git's worktree-specific configuration extension.\n- Optional: a DeepSeek API key for the Web, headless, and ACP automation demos and real-API e2e tests.\n\n## First-time setup\n\nInstall dependencies from the repo root:\n\n```sh\npnpm install\n```\n\nThe install also runs the root `postinstall` script, which installs lefthook from the repo dev dependency through `scripts/install-lefthook.mjs`. With `CI=true` or `GITHUB_ACTIONS=true`, the wrapper returns before Git discovery because automated jobs do not consume contributor hooks. Otherwise, it requires Git 2.26 or newer and gives the current worktree an explicit hook directory under its own Git directory; linked worktrees therefore use their own lefthook binary and configuration instead of rewriting common hooks. The first install enables Git's worktree-specific configuration extension and repository format 1; see the [worktree-local hooks Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md).\n\nIf hooks are missing because dependencies were restored from cache or `postinstall` was skipped, install them manually:\n\n```sh\nnode scripts/install-lefthook.mjs\n```\n\nThe wrapper refuses user-owned `core.hooksPath` values. An inherited system, global, or common-repository path requires `DSH_LEFTHOOK_ALLOW_HOOKS_PATH_OVERRIDE=1`. When Git seeds a new worktree with another registered worktree's marker-backed hook path, the wrapper replaces that copied value with the new worktree's own path; command-scoped and other worktree-scoped paths must be integrated or removed explicitly.\n\nBefore enabling worktree config, migrate direct `extensions.*` in a format-0 common config, direct `core.worktree` or `core.bare=true`, and any non-empty dormant `config.worktree`. The common config and every worktree config must be regular files, while the owned hook directory may contain only unaliased regular files.\n\nAfter moving a checkout, rerun the wrapper to relocate its owned path and regenerate hooks. For a stale or invalid installer lock, first confirm no installer is running, then remove the reported lock and retry. If installation and hook-path rollback both fail, inspect the reported worktree config before retrying. The [worktree-local hooks Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md) owns the full safety contract.\n\nRun typecheck once after a fresh clone:\n\n```sh\npnpm run typecheck\n```\n\nThat first typecheck runs the whole-repo `tsc -b` graph: it emits every package/vendor `lib/types` and checks examples, tests, and scripts through the two no-emit aggregates described below.\n\n## TypeScript project layout\n\nThe repository's TypeScript configuration has exactly three roles; every tsconfig file plays one of them.\n\n| File | Role | Forms a program? |\n|---|---|---|\n| `tsconfig.json` | Solution root: `extends` base, `files: []`, references to the two aggregates. The whole-repo `tsc -b tsconfig.json` graph, the tsserver discovery entry, and — through the inherited `paths` — the resolution config for tsx running `examples/` and `scripts/` (their nearest tsconfig is this file). | No |\n| `tsconfig.host.json` | Host aggregate: host-side packages (via references), examples, tests, scripts, website. Excludes `packages/client`. | Yes |\n| `tsconfig.client.json` | Client aggregate: `packages/client/*` packages and their tests, `apps/web`. | Yes |\n| `tsconfig.base.json` | Shared compilerOptions and the source `paths` map. Also the resolution facade the vitest configs point vite-tsconfig-paths at: it has no `include`, so its `paths` apply to every importer. | No |\n| `tsconfig.base.client.json` | Browser compiler shape (`jsx`, DOM libs, `types: []`) extended by the client aggregate and every `packages/client/*` package. | No |\n\nHost and client stay two aggregate programs because both sides declaration-merge the cordis `Context` interface under the same keys with different services; one program seeing both merges reports a collision. The collision exists only inside a `ts.Program` — module resolution never triggers it — which is why the solution may reference both aggregates and one paths facade may span both sides. Two disciplines follow:\n\n- `tsconfig.base.json` never gains `include` or `files`: they would leak into every extending package project and narrow the facade's match-all scope.\n- A script that builds a repo-wide `ts.Program` seeds `tsconfig.host.json` or `tsconfig.client.json` explicitly — never the root solution, because flattening both aggregates into one program collides the `Context` merges. Program-backed generators and gates (`scripts/ts-project.ts` consumers, doc-typecheck standalone mode) are host-only by decision; the client side gains program-backed tooling only with a concrete need.\n\nStatic analysis and tests resolve workspace imports through the base `paths` map to `src` and must pass on a clean tree; gates that consume built `lib/` output declare that dependency explicitly. Decision record: [solution-root note](../.agents/notes/implemented/process/2026-07-22-tsconfig-solution-root-two-aggregates.md); the tsc-first emit pipeline is the [ts-build-config note](../.agents/notes/implemented/process/2026-06-17-ts-build-config.md).\n\nIf a relevant local check consumes built package output, build once first:\n\n```sh\npnpm run build\n```\n\n`pnpm run hygiene` includes `publint`, which validates package entrypoints against the built `lib/*.js` files, and `verify-node-next-types`, which validates built declarations against a temporary NodeNext consumer. A fresh worktree has no bundled JS or declarations until `pnpm run build` runs; ordinary commits and pushes do not require that build unless their selected checks consume it.\n\n## Environment variables\n\nThe real DeepSeek adapter and key-backed agent demos read credentials from the environment or from a gitignored `.env` at the repo root:\n\n```sh\nDEEPSEEK_API_KEY=sk-...\nDEEPSEEK_BASE_URL=https://... # optional\n```\n\n`DEEPSEEK_BASE_URL` is optional and defaults to the public API. Never commit real credentials. The real-API e2e suites self-skip when `DEEPSEEK_API_KEY` is not set.\n\n## Git hooks\n\nlefthook is configured in `lefthook.yml` as a fast local checkpoint:\n\n- `pre-commit` applies formatting-only ESLint fixes, validates the staged files with Oxlint and applies its native fixes, regenerates `THIRD_PARTY_NOTICES.md` when a staged file is one of its inputs, checks the staged diff for whitespace errors, and runs the vendor manifest guard.\n- `pre-push` runs only the incremental repository typecheck (`tsc -b` over the root solution, covering both the host and client aggregates).\n\nThe vendor manifest guard checks that changes under `vendor/*/src` are staged with the matching `vendor/README.md` manifest update. See `vendor/README.md` before editing vendored code.\n\nThe hooks intentionally do not run tests, snapshots, documentation checks, builds, or hygiene. Contributors run the [checks relevant to the changed behavior](../AGENTS.md#run-relevant-checks-locally) once; CI owns exhaustive coverage, built-artifact smokes, and the Node 22.19, 24, and 26 compatibility matrix.\n\nContributors can opt into the comprehensive local gate set with `pnpm run check:all`. The command is independent of both Git hooks and is not an agent instruction.\n\n## CI gates\n\nThe keyless [CI workflow](../.github/workflows/ci.yml) groups independent gates into broad lanes and runs a smaller compatibility signal across supported Node versions. Artifact consumers wait for one build within their lane. The separate real-API workflow runs `pnpm run test:e2e` with its configured worker bound. See [scripts/run-gates.ts](../scripts/run-gates.ts) and the workflow files for the current gate and job inventory.\n\n## Daily commands\n\nUse these from the repo root:\n\n```sh\npnpm run test # unit tests\npnpm run test:coverage # unit tests with per-file coverage gates\npnpm run test:e2e # real-API tests; self-skips without DEEPSEEK_API_KEY\npnpm run check:all # comprehensive opt-in gate set; not wired to Git hooks\npnpm run typecheck # tsc -b over the root solution: emits package/vendor lib/types, checks both aggregates\npnpm run lint # oxlint .\npnpm run lint:fix # formatting-only ESLint, then oxlint . --fix\npnpm run doc-typecheck # compile checked TypeScript snippets in Markdown docs\npnpm run gen-cordis-catalog # regenerate docs/cordis-catalog/events.md + services.md from source\npnpm run verify-cordis-catalog # fail if either cordis catalog is stale\npnpm run verify-export-jsdoc # fail if a module-level package export lacks complete JSDoc\npnpm run gen-doc-graphs # regenerate generated relationship docs from source and curated graph definitions\npnpm run verify-doc-graphs # fail if generated relationship docs are stale\npnpm run verify-md-wrap # fail on hard-wrapped prose paragraphs in docs/README markdown\npnpm run verify-mermaid # fail if a ```mermaid diagram has invalid Mermaid syntax\npnpm run verify-type-equiv # fail if a ```ts type-equiv doc block drifts from its source type\npnpm run verify-doc-budgets # fail if a budgeted standing doc exceeds its word ceiling\npnpm run gen-translation-brief # print the minimal-update briefing for out-of-sync translation pairs (--apply splices code-only edits)\npnpm run doc-sync # all Markdown/doc gates, scheduled concurrently; the doc-sync leaf list in scripts/run-gates.ts is the full list\npnpm run gen-module-graph # regenerate docs/module-graph.md from package peerDeps\npnpm run verify-module-graph # fail if docs/module-graph.md is stale\npnpm run build # emit lib/types intermediates, then bundle lib/index.* runtime files\npnpm run verify-node-next-types # fail if built declarations are not NodeNext-consumable\npnpm run hygiene # knip, publint, workspace constraints, and NodeNext declaration check\n```\n\nWhen changing package public behavior, update the relevant README or JSDoc in the same change. `pnpm run doc-sync` catches checked TypeScript snippets, generated doc freshness, markdown wrap/link drift, type equivalence, translation pairing, Mermaid syntax, and doc budgets, but broader prose/API sync still needs review.\n\n## Demos\n\nThe one-shot Headless coding agent needs `DEEPSEEK_API_KEY` in the environment or repo-root `.env`:\n\n```sh\npnpm run demo:headless \"summarize this workspace\"\n```\n\nThe self-referential cordis demo can inspect and modify its live plugin runtime and needs the same credentials (`web` by default, or `acp`):\n\n```sh\npnpm run demo:cordis\n```\n\nThe ACP automation server exposes fresh agent sessions over JSON-RPC stdio and also needs `DEEPSEEK_API_KEY`:\n\n```sh\npnpm run demo:acp\n```\n\n## TODO markers\n\nUse one of three comment tags to flag known issues in the code, ordered by urgency:\n\n- `FIXME` — an issue that should block a new release. A release should not ship with an open `FIXME` unless reviewers explicitly agree the change can be merged anyway.\n- `TODO` — an issue that should be fixed soon, once we have the resources.\n- `XXX` — an issue that we may fix someday; lowest priority, no commitment.\n\nPick the tag that matches the urgency so anyone scanning the code can tell a release blocker from a someday-maybe.\n\n## Documenting types verbatim (`ts type-equiv`)\n\nThe [core data structures](core-data-structures/core.md) docs paste source-equivalent declarations together with their original JSDoc so a reader sees the exact shape and source contract. To keep a paste from drifting when source changes, fence it as ` ```ts type-equiv ` (instead of ` ```ts `) and register it in `scripts/type-equiv.manifest.json` with the source file and symbol it mirrors:\n\n```json\n{ \"doc\": \"docs/core-data-structures/session.md\", \"symbol\": \"SessionEvent\", \"source\": \"packages/core/session/src/types.ts\" }\n```\n\n`pnpm run verify-type-equiv` (part of `doc-sync`) then extracts that symbol's declaration and attached JSDoc from source via the TypeScript parser and asserts the block matches both. For a class whose implementation bodies do not belong in the catalog, use ` ```ts public-api ` and set `\"projection\": \"public-api\"`; the checked projection retains the public fields, constructor, accessors, methods, and original class/member JSDoc while omitting bodies and private or protected members. Comparison ignores whitespace and non-JSDoc comments but requires every original JSDoc comment, including member documentation, so readers see the source contract beside the exact shape. The gate enforces a 1:1 correspondence by document, symbol, and projection between primary blocks and manifest entries; a paired `.zh.md` block reuses its unsuffixed sibling's entry only when the whole tracked fence sequence is byte-identical and ordered identically. `doc-typecheck` applies the same derivative rule to compilable fences, while skipping both source-equivalence fence kinds from compilation and its opt-out ratio. When you change a documented declaration or its JSDoc, the gate fails until you update the paste; when you add or remove a primary block, update the manifest in the same change.\n\n## Architecture context\n\nRead `docs/architecture.md` before changing anything under `packages/`. The codebase is built around Cordis plugins, event-sourced sessions, typed service seams, and explicit extension points.\n" + "content": "# Development guide\n\nEnglish | [中文](development.zh.md)\n\nThe setup tutorial takes a new contributor from prerequisites to a checked checkout. The contributor reference that follows covers repository layout, daily workflow, and CI shape. Design rationale and implementation details belong to the linked Agent Notes and scripts.\n\n## Setup tutorial\n\n### Prerequisites\n\n- Node.js supports 22.19+ and 24+. CI covers 22.19, 24, and 26; see the [Node engine floor Agent Note](../.agents/notes/implemented/process/2026-07-06-node-engine-floor.md).\n- Corepack-enabled pnpm. The repo pins `pnpm@11.7.0` in `package.json`; run `corepack enable` if `pnpm --version` does not resolve through Corepack.\n- Git 2.26 or newer; hook setup enables Git's worktree-specific configuration extension.\n- Optional: a DeepSeek API key for the Web, headless, and ACP automation demos and real-API e2e tests.\n\n### First-time setup\n\nInstall dependencies from the repo root:\n\n```sh\npnpm install\n```\n\nThe install also configures worktree-local lefthook hooks through `scripts/install-lefthook.mjs`. The [worktree-local hooks Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md) owns the safety and migration contract.\n\nIf hooks are missing because dependencies were restored from cache or `postinstall` was skipped, install them manually:\n\n```sh\nnode scripts/install-lefthook.mjs\n```\n\nIf the wrapper rejects existing Git configuration or reports a stale lock, follow its diagnostic and the linked Agent Note rather than editing worktree metadata speculatively. After moving a checkout, rerun the wrapper to regenerate the owned path.\n\nRun typecheck once after a fresh clone:\n\n```sh\npnpm run typecheck\n```\n\nSetup is complete when `pnpm run typecheck` exits successfully.\n\n## Contributor reference\n\n### TypeScript project layout\n\nThe repository typecheck runs the whole-repo `tsc -b` graph: it emits every package/vendor `lib/types` and checks examples, tests, and scripts through two no-emit aggregates.\n\nThe repository's TypeScript configuration has exactly three roles; every tsconfig file plays one of them.\n\n| File | Role | Forms a program? |\n|---|---|---|\n| `tsconfig.json` | Solution root: `extends` base, `files: []`, references to the two aggregates. The whole-repo `tsc -b tsconfig.json` graph, the tsserver discovery entry, and — through the inherited `paths` — the resolution config for tsx running `examples/` and `scripts/` (their nearest tsconfig is this file). | No |\n| `tsconfig.host.json` | Host aggregate: host-side packages (via references), examples, tests, scripts, website. Excludes `packages/client`. | Yes |\n| `tsconfig.client.json` | Client aggregate: `packages/client/*` packages and their tests, `apps/web`. | Yes |\n| `tsconfig.base.json` | Shared compilerOptions and the source `paths` map. Also the resolution facade the vitest configs point vite-tsconfig-paths at: it has no `include`, so its `paths` apply to every importer. | No |\n| `tsconfig.base.client.json` | Browser compiler shape (`jsx`, DOM libs, `types: []`) extended by the client aggregate and every `packages/client/*` package. | No |\n\nHost and client stay two aggregate programs because both sides declaration-merge the cordis `Context` interface under the same keys with different services; one program seeing both merges reports a collision. The collision exists only inside a `ts.Program` — module resolution never triggers it — which is why the solution may reference both aggregates and one paths facade may span both sides. Two disciplines follow:\n\n- `tsconfig.base.json` never gains `include` or `files`: they would leak into every extending package project and narrow the facade's match-all scope.\n- A script that builds a repo-wide `ts.Program` seeds `tsconfig.host.json` or `tsconfig.client.json` explicitly — never the root solution, because flattening both aggregates into one program collides the `Context` merges. Program-backed generators and gates (`scripts/ts-project.ts` consumers, doc-typecheck standalone mode) are host-only by decision; the client side gains program-backed tooling only with a concrete need.\n\nStatic analysis and tests resolve workspace imports through the base `paths` map to `src` and must pass on a clean tree; gates that consume built `lib/` output declare that dependency explicitly. Decision record: [solution-root note](../.agents/notes/implemented/process/2026-07-22-tsconfig-solution-root-two-aggregates.md); the tsc-first emit pipeline is the [ts-build-config note](../.agents/notes/implemented/process/2026-06-17-ts-build-config.md).\n\nIf a relevant local check consumes built package output, build once first:\n\n```sh\npnpm run build\n```\n\n`pnpm run hygiene` includes `publint`, which validates package entrypoints against the built `lib/*.js` files, and `verify-node-next-types`, which validates built declarations against a temporary NodeNext consumer. A fresh worktree has no bundled JS or declarations until `pnpm run build` runs; ordinary commits and pushes do not require that build unless their selected checks consume it.\n\n### Environment variables\n\nThe real DeepSeek adapter and key-backed agent demos read credentials from the environment or from a gitignored `.env` at the repo root:\n\n```sh\nDEEPSEEK_API_KEY=sk-...\nDEEPSEEK_BASE_URL=https://... # optional\n```\n\n`DEEPSEEK_BASE_URL` is optional and defaults to the public API. Never commit real credentials. The real-API e2e suites self-skip when `DEEPSEEK_API_KEY` is not set.\n\n### Git hooks\n\nlefthook is configured in `lefthook.yml` as a fast local checkpoint:\n\n- `pre-commit` applies formatting-only ESLint fixes, validates the staged files with Oxlint and applies its native fixes, regenerates `THIRD_PARTY_NOTICES.md` when a staged file is one of its inputs, checks the staged diff for whitespace errors, and runs the vendor manifest guard.\n- `pre-push` runs only the incremental repository typecheck (`tsc -b` over the root solution, covering both the host and client aggregates).\n\nThe vendor manifest guard checks that changes under `vendor/*/src` are staged with the matching `vendor/README.md` manifest update. See `vendor/README.md` before editing vendored code.\n\nThe hooks intentionally do not run tests, snapshots, documentation checks, builds, or hygiene. Contributors run the [checks relevant to the changed behavior](../AGENTS.md#run-relevant-checks-locally) once; CI owns exhaustive coverage, built-artifact smokes, and the Node 22.19, 24, and 26 compatibility matrix.\n\nContributors can opt into the comprehensive local gate set with `pnpm run check:all`. The command is independent of both Git hooks and is not an agent instruction.\n\n### CI gates\n\nThe keyless [CI workflow](../.github/workflows/ci.yml) groups independent gates into broad lanes and runs a smaller compatibility signal across supported Node versions. Artifact consumers wait for one build within their lane. The separate real-API workflow runs `pnpm run test:e2e` with its configured worker bound. See [scripts/run-gates.ts](../scripts/run-gates.ts) and the workflow files for the current gate and job inventory.\n\n### Daily commands\n\nThe root [contributor instructions](../AGENTS.md#commands) summarize common commands, while [`package.json`](../package.json) and [scripts/run-gates.ts](../scripts/run-gates.ts) own the current script and gate inventories. Select the smallest checks that cover the changed surface. Documentation changes use `pnpm run doc-sync`; package-public behavior changes also update the owning README or JSDoc, and built-artifact checks require `pnpm run build` first.\n\n### Demos\n\nThe one-shot Headless coding agent needs `DEEPSEEK_API_KEY` in the environment or repo-root `.env`:\n\n```sh\npnpm run demo:headless \"summarize this workspace\"\n```\n\nThe self-referential cordis demo can inspect and modify its live plugin runtime and needs the same credentials (`web` by default, or `acp`):\n\n```sh\npnpm run demo:cordis\n```\n\nThe ACP automation server exposes fresh agent sessions over JSON-RPC stdio and also needs `DEEPSEEK_API_KEY`:\n\n```sh\npnpm run demo:acp\n```\n\n### TODO markers\n\nUse one of three comment tags to flag known issues in the code, ordered by urgency:\n\n- `FIXME` — an issue that should block a new release. A release should not ship with an open `FIXME` unless reviewers explicitly agree the change can be merged anyway.\n- `TODO` — an issue that should be fixed soon, once we have the resources.\n- `XXX` — an issue that we may fix someday; lowest priority, no commitment.\n\nPick the tag that matches the urgency so anyone scanning the code can tell a release blocker from a someday-maybe.\n\n### Documenting types verbatim (`ts type-equiv`)\n\nThe [core data structures](core-data-structures/core.md) docs paste source-equivalent declarations together with their original JSDoc so a reader sees the exact shape and source contract. To keep a paste from drifting when source changes, fence it as ` ```ts type-equiv ` (instead of ` ```ts `) and register it in `scripts/type-equiv.manifest.json` with the source file and symbol it mirrors:\n\n```json\n{ \"doc\": \"docs/core-data-structures/session.md\", \"symbol\": \"SessionEvent\", \"source\": \"packages/core/session/src/types.ts\" }\n```\n\n`pnpm run verify-type-equiv` (part of `doc-sync`) then extracts that symbol's declaration and attached JSDoc from source via the TypeScript parser and asserts the block matches both. For a class whose implementation bodies do not belong in the catalog, use ` ```ts public-api ` and set `\"projection\": \"public-api\"`; the checked projection retains the public fields, constructor, accessors, methods, and original class/member JSDoc while omitting bodies and private or protected members. Comparison ignores whitespace and non-JSDoc comments but requires every original JSDoc comment, including member documentation, so readers see the source contract beside the exact shape. The gate enforces a 1:1 correspondence by document, symbol, and projection between primary blocks and manifest entries; a paired `.zh.md` block reuses its unsuffixed sibling's entry only when the whole tracked fence sequence is byte-identical and ordered identically. `doc-typecheck` applies the same derivative rule to compilable fences, while skipping both source-equivalence fence kinds from compilation and its opt-out ratio. When you change a documented declaration or its JSDoc, the gate fails until you update the paste; when you add or remove a primary block, update the manifest in the same change.\n" }, { "role": "assistant", - "content": "# 开发指南\n\n[English](development.md) | 中文\n\n本指南覆盖参与 DeepSeek Harness 开发所需的本地环境搭建、日常工作流与 CI 流程;设计动机与技术权衡请查阅相应 Agent Note。\n\n## 前置条件\n\n- Node.js 支持 22.19+ 与 24+。CI 覆盖 22.19、24 和 26;见 [Node 引擎下限 Agent Note](../.agents/notes/implemented/process/2026-07-06-node-engine-floor.md)。\n- 启用了 Corepack 的 pnpm。仓库在 `package.json` 中固定使用 `pnpm@11.7.0`;如果 `pnpm --version` 无法通过 Corepack 解析,请先运行 `corepack enable`。\n- Git 2.26 或更高版本;钩子设置会启用 Git 的 worktree 专属配置扩展。\n- 可选:一个 DeepSeek API key,用于 Web、headless 和 ACP(Agent Client Protocol)自动化 agent(智能体)演示以及真实 API 的 e2e 测试。\n\n## 首次搭建\n\n在仓库根目录安装依赖:\n\n```sh\npnpm install\n```\n\n安装过程同时会运行根目录的 `postinstall` 脚本,该脚本通过 `scripts/install-lefthook.mjs` 从仓库 dev 依赖安装 lefthook。当 `CI=true` 或 `GITHUB_ACTIONS=true` 时,该脚本会在探测 Git 前返回,因为自动化任务不会使用贡献者钩子。否则,包装脚本要求使用 Git 2.26 或更高版本,并会为当前 worktree 在其自身的 Git 目录下设置显式钩子目录;因此,关联 worktree 会使用各自的 lefthook 二进制文件和配置,而不会改写共用钩子。首次安装会启用 Git 的 worktree 专属配置扩展和仓库格式 1;见 [worktree 本地钩子 Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md)。\n\n如果依赖是从缓存恢复或 `postinstall` 被跳过而导致缺少钩子,请手动安装:\n\n```sh\nnode scripts/install-lefthook.mjs\n```\n\n包装层会拒绝用户自有的 `core.hooksPath` 值。继承自系统、全局或共用仓库配置的路径必须设置 `DSH_LEFTHOOK_ALLOW_HOOKS_PATH_OVERRIDE=1`。当 Git 使用另一个已注册 worktree 中由所有权标记佐证的钩子路径初始化新 worktree 时,包装层会将这个复制值替换为新 worktree 自有的路径;命令作用域和其他 worktree 作用域的路径必须显式集成或移除。\n\n启用 worktree 配置之前,请迁移格式 0 共用配置中直接设置的 `extensions.*`,并迁移直接设置的 `core.worktree` 或 `core.bare=true`,以及任何非空且尚未生效的 `config.worktree`。共用配置和每个 worktree 配置都必须是常规文件,而自有钩子目录只能包含不带别名的常规文件。\n\n检出目录移动后,请重新运行包装层,使其重新定位自有路径并重新生成钩子。对于陈旧或无效的安装程序锁,请先确认没有安装程序正在运行,再移除报告的锁并重试。若安装和钩子路径回滚都失败,请在重试前检查报告的 worktree 配置。完整安全契约由 [worktree 本地钩子 Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md) 统一定义。\n\n新克隆后请先运行一次类型检查:\n\n```sh\npnpm run typecheck\n```\n\n首次类型检查会执行全仓 `tsc -b tsconfig.json` 图:发射每个 package/vendor 的 `lib/types`,并通过下述两个 no-emit 聚合检查示例、测试和脚本。\n\n## TypeScript 项目布局\n\n仓库的 TypeScript 配置只有三种角色;每个 tsconfig 文件恰好扮演其中一种。\n\n| 文件 | 角色 | 是否构成 program? |\n|---|---|---|\n| `tsconfig.json` | solution 根:`extends` base、`files: []`、引用两个聚合。全仓 `tsc -b tsconfig.json` 图、tsserver 发现入口,并经继承的 `paths` 充当 tsx 运行 `examples/` 与 `scripts/` 时的解析配置(它们最近的 tsconfig 就是此文件)。 | 否 |\n| `tsconfig.host.json` | host 聚合:host 侧各包(经 references)、示例、测试、脚本、website。排除 `packages/client`。 | 是 |\n| `tsconfig.client.json` | client 聚合:`packages/client/*` 各包及其测试、`apps/web`。 | 是 |\n| `tsconfig.base.json` | 共享 compilerOptions 与源码 `paths` 映射。同时是各 vitest 配置让 vite-tsconfig-paths 指向的解析门面:它没有 `include`,因此其 `paths` 适用于任何 importer。 | 否 |\n| `tsconfig.base.client.json` | 浏览器编译形状(`jsx`、DOM lib、`types: []`),由 client 聚合和每个 `packages/client/*` 包 extends。 | 否 |\n\nhost 与 client 保持两个聚合 program,是因为两侧在相同键下以不同服务对 cordis `Context` 接口做声明合并;单一 program 同时看到两份合并会报冲突。这种冲突只存在于 `ts.Program` 内部——模块解析永远不会触发它——所以 solution 可以同时引用两个聚合,一个 paths 门面也可以横跨两侧。由此推出两条纪律:\n\n- `tsconfig.base.json` 永不添加 `include` 或 `files`:它们会泄漏进每个 extends 它的包项目,并收窄门面的全匹配范围。\n- 构造全仓 `ts.Program` 的脚本显式种子 `tsconfig.host.json` 或 `tsconfig.client.json`——永不种子根 solution,因为把两个聚合展平进一个 program 会撞上 `Context` 合并冲突。基于 program 的生成器与门禁(`scripts/ts-project.ts` 的消费者、doc-typecheck standalone 模式)按决策仅覆盖 host 侧;client 侧只在出现真实需求时再获得基于 program 的工具。\n\n静态分析和测试通过 base 的 `paths` 映射把工作区 import 解析到 `src`,且必须在干净树上通过;消费构建产物 `lib/` 的门禁显式声明该依赖。决策记录:[solution-root note](../.agents/notes/implemented/process/2026-07-22-tsconfig-solution-root-two-aggregates.md);tsc-first 发射管线见 [ts-build-config note](../.agents/notes/implemented/process/2026-06-17-ts-build-config.md)。\n\n如果相关的本地检查需要使用构建后的包产物,请先构建一次:\n\n```sh\npnpm run build\n```\n\n`pnpm run hygiene` 包含 `publint`(用构建出的 `lib/*.js` 文件校验 package 入口点)和 `verify-node-next-types`(用一个临时的 NodeNext 消费方校验构建出的声明文件)。新 worktree 在 `pnpm run build` 运行之前没有打包的 JS 和声明文件;普通提交和推送无需构建,除非所选检查会使用这些产物。\n\n## 环境变量\n\n真实的 DeepSeek 适配器和需要密钥的 agent 演示从环境变量或仓库根目录一个被 gitignore 的 `.env` 文件读取凭证:\n\n```sh\nDEEPSEEK_API_KEY=sk-...\nDEEPSEEK_BASE_URL=https://... # optional\n```\n\n`DEEPSEEK_BASE_URL` 可选,默认为公开 API。请勿提交真实凭证。未设置 `DEEPSEEK_API_KEY` 时,真实 API 的 e2e 套件会自动跳过。\n\n## Git 钩子\n\nlefthook 在 `lefthook.yml` 中配置,作为快速的本地检查点:\n\n- `pre-commit` 应用仅用于格式化的 ESLint 修复,使用 Oxlint 验证暂存文件并应用其原生修复,在暂存文件属于 `THIRD_PARTY_NOTICES.md` 的输入时重新生成该文件,然后检查暂存 diff 中的空白错误,并运行 vendor manifest(元数据清单)守卫;\n- `pre-push` 只运行仓库增量类型检查(对根 solution 执行 `tsc -b`,覆盖 host 与 client 两个聚合)。\n\nvendor manifest 守卫检查 `vendor/*/src` 下的改动是否连同对应的 `vendor/README.md` manifest 更新一起暂存。请在编辑 vendor 代码前先阅读 `vendor/README.md`。\n\n这些钩子有意不运行测试、快照、文档检查、构建或 `hygiene`。贡献者只运行一次[与改动行为相关的检查](../AGENTS.md#run-relevant-checks-locally);CI 负责全量覆盖率门禁、构建产物冒烟测试,以及 Node 22.19、24 和 26 兼容性矩阵。\n\n贡献者可以选择运行 `pnpm run check:all`,执行全面的本地门禁集。该命令独立于两个 Git 钩子,也不是对 agent 的指令。\n\n## CI 门禁\n\nkeyless [CI 工作流](../.github/workflows/ci.yml) 将独立门禁分组到若干宽粒度 lane,并在受支持的 Node 版本上运行一组较小的兼容性检查。产物消费方在各自 lane 内等待一次 build。单独的真实 API 工作流按其配置的 worker 上限运行 `pnpm run test:e2e`。当前门禁和 job 清单以 [scripts/run-gates.ts](../scripts/run-gates.ts) 和工作流文件为准。\n\n## 日常命令\n\n在仓库根目录使用:\n\n```sh\npnpm run test # unit tests\npnpm run test:coverage # unit tests with per-file coverage gates\npnpm run test:e2e # real-API tests; self-skips without DEEPSEEK_API_KEY\npnpm run check:all # comprehensive opt-in gate set; not wired to Git hooks\npnpm run typecheck # tsc -b over the root solution: emits package/vendor lib/types, checks both aggregates\npnpm run lint # oxlint .\npnpm run lint:fix # formatting-only ESLint, then oxlint . --fix\npnpm run doc-typecheck # compile checked TypeScript snippets in Markdown docs\npnpm run gen-cordis-catalog # regenerate docs/cordis-catalog/events.md + services.md from source\npnpm run verify-cordis-catalog # fail if either cordis catalog is stale\npnpm run verify-export-jsdoc # fail if a module-level package export lacks complete JSDoc\npnpm run gen-doc-graphs # regenerate generated relationship docs from source and curated graph definitions\npnpm run verify-doc-graphs # fail if generated relationship docs are stale\npnpm run verify-md-wrap # fail on hard-wrapped prose paragraphs in docs/README markdown\npnpm run verify-mermaid # fail if a ```mermaid diagram has invalid Mermaid syntax\npnpm run verify-type-equiv # fail if a ```ts type-equiv doc block drifts from its source type\npnpm run verify-doc-budgets # fail if a budgeted standing doc exceeds its word ceiling\npnpm run gen-translation-brief # print the minimal-update briefing for out-of-sync translation pairs (--apply splices code-only edits)\npnpm run doc-sync # all Markdown/doc gates, scheduled concurrently; the doc-sync leaf list in scripts/run-gates.ts is the full list\npnpm run gen-module-graph # regenerate docs/module-graph.md from package peerDeps\npnpm run verify-module-graph # fail if docs/module-graph.md is stale\npnpm run build # emit lib/types intermediates, then bundle lib/index.* runtime files\npnpm run verify-node-next-types # fail if built declarations are not NodeNext-consumable\npnpm run hygiene # knip, publint, workspace constraints, and NodeNext declaration check\n```\n\n修改 package 的公开行为时,请在同一个变更中更新相关 README 或 JSDoc。`pnpm run doc-sync` 能检测到被检查的 TypeScript 片段、生成文档的新鲜度、Markdown 换行/链接漂移、type-equiv、翻译配对、Mermaid 语法和文档预算,但更广泛的行文/API 同步仍需评审把关。\n\n## 演示\n\n单次运行的 Headless coding agent 需要环境变量或仓库根目录 `.env` 中的 `DEEPSEEK_API_KEY`:\n\n```sh\npnpm run demo:headless \"summarize this workspace\"\n```\n\n自指的 cordis 演示可以检查并修改其实时插件运行时,并需要相同的凭证(默认 `web`,也可用 `acp`):\n\n```sh\npnpm run demo:cordis\n```\n\nACP 自动化服务器通过 JSON-RPC stdio 提供全新 agent 会话,同样需要 `DEEPSEEK_API_KEY`:\n\n```sh\npnpm run demo:acp\n```\n\n## TODO 标记\n\n请使用以下三种注释标签之一标记代码中的已知问题,按紧急程度排序:\n\n- `FIXME`:应当阻塞新版本发布的问题。除非评审者明确同意该更改可以合并,否则发布版本不应包含未解决的 `FIXME`;\n- `TODO`:应当尽快修复的问题,等资源到位即可处理;\n- `XXX`:也许某天会修复的问题,优先级最低,不作承诺。\n\n请选择与紧急程度匹配的标签,让浏览代码的人一眼分清「发布阻塞」和「有空再说」。\n\n## 逐字记录类型(`ts type-equiv`)\n\n[核心数据结构](core-data-structures/core.md)文档会把与源码等价的声明及其原始 JSDoc 一并粘贴,让读者看到确切形状和源码契约。为防止粘贴内容在源码变化时漂移,请将其围栏为 ` ```ts type-equiv `(而不是 ` ```ts `),并在 `scripts/type-equiv.manifest.json` 中登记它镜像的源文件和符号:\n\n```json\n{ \"doc\": \"docs/core-data-structures/session.md\", \"symbol\": \"SessionEvent\", \"source\": \"packages/core/session/src/types.ts\" }\n```\n\n`pnpm run verify-type-equiv`(`doc-sync` 的一环)随后通过 TypeScript 解析器从源码提取该符号的声明及其附带的 JSDoc,并断言代码块同时匹配两者。对于不应把实现体写进目录的类,请使用 ` ```ts public-api ` 并设置 `\"projection\": \"public-api\"`;门禁检查的投影会保留公共字段、构造函数、访问器、方法以及类和成员的原始 JSDoc,同时省略实现体和私有或受保护成员。比对会忽略空白和非 JSDoc 注释,但要求保留每条原始 JSDoc(包括成员文档),让读者同时看到源码契约和确切形状。该门禁按文档、符号和投影,在主块与 manifest 条目之间强制 1:1 对应;只有当配对 `.zh.md` 块的完整受跟踪围栏序列与其无后缀兄弟文件按字节一致且顺序相同时,才会复用后者的条目。`doc-typecheck` 对可编译围栏应用同一派生规则,同时跳过两种源码等价围栏的编译,并将其排除在 opt-out 比例之外。当你改动一个已记录的类型声明或其 JSDoc 时,门禁会失败直到你更新粘贴内容;当你增删一个主块时,请在同一个变更里更新 manifest。\n\n## 架构上下文\n\n在修改 `packages/` 目录下的任何内容之前,请先阅读 `docs/architecture.md`。这套代码围绕 Cordis 插件、事件溯源的会话、类型化的服务 seam 与显式扩展点构建。\n" + "content": "# 开发指南\n\n[English](development.md) | 中文\n\n搭建教程引导新贡献者从准备前置条件开始,直到检出通过检查。后面的贡献者参考介绍仓库布局、日常工作流和 CI 形态。设计依据与实现细节属于链接的 Agent Note 和脚本。\n\n## 搭建教程\n\n### 前置条件\n\n- Node.js 支持 22.19+ 与 24+。CI 覆盖 22.19、24 和 26;见 [Node 引擎下限 Agent Note](../.agents/notes/implemented/process/2026-07-06-node-engine-floor.md)。\n- 启用了 Corepack 的 pnpm。仓库在 `package.json` 中固定使用 `pnpm@11.7.0`;如果 `pnpm --version` 无法通过 Corepack 解析,请先运行 `corepack enable`。\n- Git 2.26 或更高版本;钩子设置会启用 Git 的 worktree 专属配置扩展。\n- 可选:一个 DeepSeek API key,用于 Web、headless 和 ACP(Agent Client Protocol)自动化 agent(智能体)演示以及真实 API 的 e2e 测试。\n\n### 首次搭建\n\n在仓库根目录安装依赖:\n\n```sh\npnpm install\n```\n\n安装过程还会通过 `scripts/install-lefthook.mjs` 配置 worktree 本地的 lefthook 钩子。其安全与迁移契约由 [worktree 本地钩子 Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md) 负责。\n\n如果依赖是从缓存恢复或 `postinstall` 被跳过而导致缺少钩子,请手动安装:\n\n```sh\nnode scripts/install-lefthook.mjs\n```\n\n如果包装脚本拒绝现有 Git 配置或报告陈旧锁,请遵循其诊断和所链接的 Agent Note,不要凭猜测编辑 worktree 元数据。移动检出目录后,请重新运行包装脚本以重新生成自有路径。\n\n新克隆后请先运行一次类型检查:\n\n```sh\npnpm run typecheck\n```\n\n`pnpm run typecheck` 成功退出即表示搭建完成。\n\n## 贡献者参考\n\n### TypeScript 项目布局\n\n仓库类型检查会执行全仓 `tsc -b` 图:它会发射每个 package/vendor 的 `lib/types`,并通过两个 no-emit 聚合检查示例、测试和脚本。\n\n仓库的 TypeScript 配置只有三种角色;每个 tsconfig 文件恰好扮演其中一种。\n\n| 文件 | 角色 | 是否构成 program? |\n|---|---|---|\n| `tsconfig.json` | solution 根:`extends` base、`files: []`、引用两个聚合。全仓 `tsc -b tsconfig.json` 图、tsserver 发现入口,并经继承的 `paths` 充当 tsx 运行 `examples/` 与 `scripts/` 时的解析配置(它们最近的 tsconfig 就是此文件)。 | 否 |\n| `tsconfig.host.json` | host 聚合:host 侧各包(经 references)、示例、测试、脚本、website。排除 `packages/client`。 | 是 |\n| `tsconfig.client.json` | client 聚合:`packages/client/*` 各包及其测试、`apps/web`。 | 是 |\n| `tsconfig.base.json` | 共享 compilerOptions 与源码 `paths` 映射。同时是各 vitest 配置让 vite-tsconfig-paths 指向的解析门面:它没有 `include`,因此其 `paths` 适用于任何 importer。 | 否 |\n| `tsconfig.base.client.json` | 浏览器编译形状(`jsx`、DOM lib、`types: []`),由 client 聚合和每个 `packages/client/*` 包 extends。 | 否 |\n\nhost 与 client 保持两个聚合 program,是因为两侧在相同键下以不同服务对 cordis `Context` 接口做声明合并;单一 program 同时看到两份合并会报冲突。这种冲突只存在于 `ts.Program` 内部——模块解析永远不会触发它——所以 solution 可以同时引用两个聚合,一个 paths 门面也可以横跨两侧。由此推出两条纪律:\n\n- `tsconfig.base.json` 永不添加 `include` 或 `files`:它们会泄漏进每个 extends 它的包项目,并收窄门面的全匹配范围。\n- 构造全仓 `ts.Program` 的脚本显式种子 `tsconfig.host.json` 或 `tsconfig.client.json`——永不种子根 solution,因为把两个聚合展平进一个 program 会撞上 `Context` 合并冲突。基于 program 的生成器与门禁(`scripts/ts-project.ts` 的消费者、doc-typecheck standalone 模式)按决策仅覆盖 host 侧;client 侧只在出现真实需求时再获得基于 program 的工具。\n\n静态分析和测试通过 base 的 `paths` 映射把工作区 import 解析到 `src`,且必须在干净树上通过;消费构建产物 `lib/` 的门禁显式声明该依赖。决策记录:[solution-root note](../.agents/notes/implemented/process/2026-07-22-tsconfig-solution-root-two-aggregates.md);tsc-first 发射管线见 [ts-build-config note](../.agents/notes/implemented/process/2026-06-17-ts-build-config.md)。\n\n如果相关的本地检查需要使用构建后的包产物,请先构建一次:\n\n```sh\npnpm run build\n```\n\n`pnpm run hygiene` 包含 `publint`(用构建出的 `lib/*.js` 文件校验 package 入口点)和 `verify-node-next-types`(用一个临时的 NodeNext 消费方校验构建出的声明文件)。新 worktree 在 `pnpm run build` 运行之前没有打包的 JS 和声明文件;普通提交和推送无需构建,除非所选检查会使用这些产物。\n\n### 环境变量\n\n真实的 DeepSeek 适配器和需要密钥的 agent 演示从环境变量或仓库根目录一个被 gitignore 的 `.env` 文件读取凭证:\n\n```sh\nDEEPSEEK_API_KEY=sk-...\nDEEPSEEK_BASE_URL=https://... # optional\n```\n\n`DEEPSEEK_BASE_URL` 可选,默认为公开 API。请勿提交真实凭证。未设置 `DEEPSEEK_API_KEY` 时,真实 API 的 e2e 套件会自动跳过。\n\n### Git 钩子\n\nlefthook 在 `lefthook.yml` 中配置,作为快速的本地检查点:\n\n- `pre-commit` 应用仅用于格式化的 ESLint 修复,使用 Oxlint 验证暂存文件并应用其原生修复,在暂存文件属于 `THIRD_PARTY_NOTICES.md` 的输入时重新生成该文件,然后检查暂存 diff 中的空白错误,并运行 vendor manifest(元数据清单)守卫;\n- `pre-push` 只运行仓库增量类型检查(对根 solution 执行 `tsc -b`,覆盖 host 与 client 两个聚合)。\n\nvendor manifest 守卫检查 `vendor/*/src` 下的改动是否连同对应的 `vendor/README.md` manifest 更新一起暂存。请在编辑 vendor 代码前先阅读 `vendor/README.md`。\n\n这些钩子有意不运行测试、快照、文档检查、构建或 `hygiene`。贡献者只运行一次[与改动行为相关的检查](../AGENTS.md#run-relevant-checks-locally);CI 负责全量覆盖率门禁、构建产物冒烟测试,以及 Node 22.19、24 和 26 兼容性矩阵。\n\n贡献者可以选择运行 `pnpm run check:all`,执行全面的本地门禁集。该命令独立于两个 Git 钩子,也不是对 agent 的指令。\n\n### CI 门禁\n\nkeyless [CI 工作流](../.github/workflows/ci.yml) 将独立门禁分组到若干宽粒度 lane,并在受支持的 Node 版本上运行一组较小的兼容性检查。产物消费方在各自 lane 内等待一次 build。单独的真实 API 工作流按其配置的 worker 上限运行 `pnpm run test:e2e`。当前门禁和 job 清单以 [scripts/run-gates.ts](../scripts/run-gates.ts) 和工作流文件为准。\n\n### 日常命令\n\n根目录的[贡献者说明](../AGENTS.md#commands)概述常用命令,[`package.json`](../package.json) 与 [scripts/run-gates.ts](../scripts/run-gates.ts) 则负责当前脚本和门禁清单。请选择覆盖变更表面的最小检查集。文档变更使用 `pnpm run doc-sync`;package 公开行为变更还需更新所属 README 或 JSDoc,而基于构建产物的检查需要先运行 `pnpm run build`。\n\n### 演示\n\n单次运行的 Headless coding agent 需要环境变量或仓库根目录 `.env` 中的 `DEEPSEEK_API_KEY`:\n\n```sh\npnpm run demo:headless \"summarize this workspace\"\n```\n\n自指的 cordis 演示可以检查并修改其实时插件运行时,并需要相同的凭证(默认 `web`,也可用 `acp`):\n\n```sh\npnpm run demo:cordis\n```\n\nACP 自动化服务器通过 JSON-RPC stdio 提供全新 agent 会话,同样需要 `DEEPSEEK_API_KEY`:\n\n```sh\npnpm run demo:acp\n```\n\n### TODO 标记\n\n请使用以下三种注释标签之一标记代码中的已知问题,按紧急程度排序:\n\n- `FIXME`:应当阻塞新版本发布的问题。除非评审者明确同意该更改可以合并,否则发布版本不应包含未解决的 `FIXME`;\n- `TODO`:应当尽快修复的问题,等资源到位即可处理;\n- `XXX`:也许某天会修复的问题,优先级最低,不作承诺。\n\n请选择与紧急程度匹配的标签,让浏览代码的人一眼分清「发布阻塞」和「有空再说」。\n\n### 逐字记录类型(`ts type-equiv`)\n\n[核心数据结构](core-data-structures/core.md)文档会把与源码等价的声明及其原始 JSDoc 一并粘贴,让读者看到确切形状和源码契约。为防止粘贴内容在源码变化时漂移,请将其围栏为 ` ```ts type-equiv `(而不是 ` ```ts `),并在 `scripts/type-equiv.manifest.json` 中登记它镜像的源文件和符号:\n\n```json\n{ \"doc\": \"docs/core-data-structures/session.md\", \"symbol\": \"SessionEvent\", \"source\": \"packages/core/session/src/types.ts\" }\n```\n\n`pnpm run verify-type-equiv`(`doc-sync` 的一环)随后通过 TypeScript 解析器从源码提取该符号的声明及其附带的 JSDoc,并断言代码块同时匹配两者。对于不应把实现体写进目录的类,请使用 ` ```ts public-api ` 并设置 `\"projection\": \"public-api\"`;门禁检查的投影会保留公共字段、构造函数、访问器、方法以及类和成员的原始 JSDoc,同时省略实现体和私有或受保护成员。比对会忽略空白和非 JSDoc 注释,但要求保留每条原始 JSDoc(包括成员文档),让读者同时看到源码契约和确切形状。该门禁按文档、符号和投影,在主块与 manifest 条目之间强制 1:1 对应;只有当配对 `.zh.md` 块的完整受跟踪围栏序列与其无后缀兄弟文件按字节一致且顺序相同时,才会复用后者的条目。`doc-typecheck` 对可编译围栏应用同一派生规则,同时跳过两种源码等价围栏的编译,并将其排除在 opt-out 比例之外。当你改动一个已记录的类型声明或其 JSDoc 时,门禁会失败直到你更新粘贴内容;当你增删一个主块时,请在同一个变更里更新 manifest。\n" }, { "role": "user", - "content": "# Bilingual documentation\n\nEnglish | [中文](README.zh.md)\n\nThis repo's documentation is read by people and agents both inside and outside the company, so every document in scope is maintained in English and Simplified Chinese. This page defines the pairing contract, enforcement gate, scope, and exclusions; [translation-rules.md](translation-rules.md) defines how to translate; [terminology.md](terminology.md) is the terminology source of truth. The committed agent workflow lives in [.agents/skills/dsh-translate-docs](../../.agents/skills/dsh-translate-docs/SKILL.md).\n\n## The pairing contract\n\n- **Both languages carry equal authority.** A document may be authored and reviewed in either language first — a Chinese-first Agent Note is as legitimate as an English-first one — and the counterpart is translated from it. Neither file outranks the other; what binds them is that they must say the same thing.\n- **A pair is three sibling files.** The English `foo.md`, the Chinese `foo.zh.md`, and a consistency record `foo.i18n.yaml`, all in the same directory. No locale directories, no separate translation repo, no interleaved bilingual files. Pairs merge whole: a PR never lands one language without the other two files.\n- **The consistency record.** `foo.i18n.yaml` holds the full git blob hash of each side as of the last time the two were confirmed to say the same thing:\n\n ```yaml\n foo.md: 3f786850e387550fdab836ed7e6dc881de23001b\n foo.zh.md: 89e6c98d92887913cadf06b2adb97f26cde4849b\n ```\n\n Blob hashes, not commit hashes, so the record is computable for files edited in the same PR (`git hash-object foo.md`) and consistency is a pure content comparison. `--write` stores those snapshots in the local Git object database before recording them, including uncommitted working-tree contents, and pins every distinct stored blob under a content-addressed `refs/dsh/translation-pairing/snapshots/` ref so garbage collection cannot invalidate a recorded recovery pointer. The recorded hashes therefore recover the exact last-confirmed text of either side, so an out-of-sync pair is updated by patching the counterpart minimally against the edited side's diff — never by re-translating whole files. `pnpm run gen-translation-brief ` assembles that update's working set mechanically at the narrowest safely aligned granularity — changed Markdown units, then heading sections, then whole document — with the edited side's diff since last confirmation, each changed span's three-way text, the terminology rows the change touches, and the binding update rules; a change confined to the pair's byte-identical code fences is computed outright, and `--apply` splices it into the counterpart after structural validation ([briefed-updates Agent Note](../../.agents/notes/implemented/process/2026-07-26-briefed-minimal-translation-updates.md)). After bringing the pair back in line, `pnpm run verify-translation-pairing --write ` re-records both hashes; that yaml diff is the reviewable act of confirming consistency, which is why `--write` requires naming the pairs you confirmed (`--write --all` is the explicit corpus-wide form).\n- **Language switcher.** Both files link to each other immediately after their H1 heading: the English file carries `English | [中文](foo.zh.md)` and the Chinese file carries `[English](foo.md) | 中文`.\n- **Structure mirrors the counterpart.** Heading depths and order, list kinds, ordered-list starts, list item counts, table row and column counts, link targets, and verbatim code blocks match one to one across the pair — see [translation-rules.md](translation-rules.md) for the full preservation rules. Existing Markdown gates apply to `.zh.md` files unchanged (`verify-md-wrap`, `verify-md-links`).\n\n## The gate: verify-translation-pairing\n\n`pnpm run verify-translation-pairing` (part of `doc-sync`, which contributors run locally for documentation changes and CI runs exhaustively) enforces the contract mechanically:\n\n1. Every document in scope has a complete pair. README discovery is case-insensitive on the basename, so `missions/readme.md` is in scope alongside the other documentation roots.\n2. Every pair artifact that exists at all is complete and consistent: all three files present, each side's current blob hash equals the recorded one (editing either side without re-confirming the pair goes red), both sides carry the language switcher, and the structural signatures match in order — heading depths, verbatim code blocks (info string and content), table row and column counts, list kinds, ordered-list starts, item counts, and every link target apart from the switcher.\n3. Files listed as `excluded` have no `.zh.md` and no `.i18n.yaml` at all. Frozen Agent Notes under `.agents/notes/archived/` are outside this evolving gate; their dedicated verifier requires and seals the complete existing triplet instead.\n\nSource-oriented code gates consume an exact `.zh.md` fence sequence as a derivative of its unsuffixed sibling instead of compiling or manifesting the same code twice. The sequence must match in length, order, fence kind, and byte-exact body; otherwise both copies remain independently checked and the pairing gate reports the structural mismatch.\n\n`pnpm run verify-translation-pairing --list` prints the current pairing state of every document in scope — missing, out-of-sync, or ok. It never fails; `missing` and `out-of-sync` rows identify violations that the normal check rejects.\n\n`pnpm run verify-translation-pairing ` checks just the named pairs — any of a pair's three files (or its bare stem) names it — so an update loop verifies its own pair in seconds instead of re-scanning the corpus. The no-argument corpus-wide form is what `doc-sync` and CI run; a scoped green never substitutes for it at PR level.\n\nThe practical rule this gate creates: **when a PR edits either side of a paired document, the same PR updates the counterpart and re-records the pair** (run the [dsh-translate-docs](../../.agents/skills/dsh-translate-docs/SKILL.md) skill, then `--write `), exactly like the repo's existing doc-sync rule for code and READMEs. A PR that leaves a pair out of sync goes red in CI.\n\nThe gate's limit, stated plainly: **a green gate means the pair was confirmed consistent at these exact contents, not that the confirmation was sound.** It checks hashes and shape; it cannot judge whether the two sides actually say the same thing, or whether the wording is accurate, well-termed, and natural — that is the reviewer's half of the contract, per [translation-rules.md](translation-rules.md). A re-recorded pair with a sloppy counterpart passes the gate; it must not pass review.\n\n## Scope and exclusions\n\n**Scope**: every non-vendor README, plus every active document under `.agents/notes/**`, `docs/**`, and `python/**`. README matching is case-insensitive on the basename and covers future directories without another manifest edit. Dependency and ignored build-output trees and the frozen `.agents/notes/archived/` tree are discovery exclusions, not evolving translation source.\n\n**Excluded** (never paired, and the gate rejects a `.zh.md` or `.i18n.yaml` for them):\n\n- `docs/cordis-catalog/`, `docs/tool-catalog/`, `docs/config-catalog.md`, `docs/persistence-catalog.md`, `docs/module-graph.md`, `docs/agent-lifecycle.md`, `docs/capability-seams.md`, `docs/event-producer-consumer.md`, `docs/graph-atlas.md`, and `docs/tool-execution-pipeline.md` — generated files; their generators emit English only today, so a hand-written translation would go stale on every regeneration. The planned follow-up is to teach the generators to emit Chinese alongside English, at which point these leave the exclusion list.\n- `docs/AGENTS.md`, `.agents/notes/**/AGENTS.md`, and their `CLAUDE.md` instruction symlinks — agent instructions, maintained in English only like the root `AGENTS.md`.\n- `docs/i18n/terminology.md` and [style-samples.md](style-samples.md) — both are bilingual by construction.\n- [translation-prompt.md](translation-prompt.md) — the automated pipeline's prompt template; its body is machine-consumed verbatim, so a paired translation would change pipeline behavior.\n- `.agents/notes/archived/` — frozen historical triplets. [`verify-archived-agent-notes`](../../scripts/verify-archived-agent-notes.ts) validates their completeness and content seals; translation maintenance must never rewrite them.\n\n**Universal requirement**: every current or future document in scope must merge as a complete bilingual pair. [scripts/translation-pairing.manifest.json](../../scripts/translation-pairing.manifest.json) contains only explicit exclusions; there is no per-file rollout list, date cutoff, or README-specific policy class.\n\n## Division of labor\n\nCounterparts here are produced by an agent running [dsh-translate-docs](../../.agents/skills/dsh-translate-docs/SKILL.md) and reviewed by a human — inference is cheap here, review attention is the scarce resource. The gate checks pair completeness, recorded hashes, switchers, and its documented structural signature. Review still owns translation quality, terminology, and structural requirements that the signature does not encode. The prompt contract is executable: [scripts/translation-prompt.ts](../../scripts/translation-prompt.ts) renders the committed template (terminology injected; the template carries its own calibrated rules) into either direction and parses the three-section response, while `verify-translation-prompt` exercises both render directions and the checked-in example in `doc-sync`.\n" + "content": "# Bilingual documentation\n\nEnglish | [中文](README.zh.md)\n\nThis repo's documentation is read by people and agents both inside and outside the company, so every document in scope is maintained in English and Simplified Chinese. This page defines the pairing contract, enforcement gate, scope, and exclusions; [translation-rules.md](translation-rules.md) defines how to translate; [terminology.md](terminology.md) is the terminology source of truth. The committed agent workflow lives in [.agents/skills/dsh-translate-docs](../../.agents/skills/dsh-translate-docs/SKILL.md).\n\n## The pairing contract\n\n- **Both languages carry equal authority.** A document may be authored and reviewed in either language first — a Chinese-first Agent Note is as legitimate as an English-first one — and the counterpart is translated from it. Neither file outranks the other; what binds them is that they must say the same thing.\n- **A pair is three sibling files.** The English `foo.md`, the Chinese `foo.zh.md`, and a consistency record `foo.i18n.yaml`, all in the same directory. No locale directories, no separate translation repo, no interleaved bilingual files. Pairs merge whole: a PR never lands one language without the other two files.\n- **The consistency record.** `foo.i18n.yaml` holds the full git blob hash of each side as of the last time the two were confirmed to say the same thing:\n\n ```yaml\n foo.md: 3f786850e387550fdab836ed7e6dc881de23001b\n foo.zh.md: 89e6c98d92887913cadf06b2adb97f26cde4849b\n ```\n\n Blob hashes, not commit hashes, so the record is computable for files edited in the same PR (`git hash-object foo.md`) and consistency is a pure content comparison. `--write` stores those snapshots in the local Git object database before recording them, including uncommitted working-tree contents, and pins every distinct stored blob under a content-addressed `refs/dsh/translation-pairing/snapshots/` ref so garbage collection cannot invalidate a recorded recovery pointer. The recorded hashes therefore recover the exact last-confirmed text of either side, so an out-of-sync pair is updated by patching the counterpart minimally against the edited side's diff — never by re-translating whole files. `pnpm run gen-translation-brief ` assembles that update's working set mechanically at the narrowest safely aligned granularity — changed Markdown units, then heading sections, then whole document — with the edited side's diff since last confirmation, each changed span's three-way text, the terminology rows the change touches, and the binding update rules; a change confined to the pair's byte-identical code fences is computed outright, and `--apply` splices it into the counterpart after structural validation ([briefed-updates Agent Note](../../.agents/notes/implemented/process/2026-07-26-briefed-minimal-translation-updates.md)). After bringing the pair back in line, `pnpm run verify-translation-pairing --write ` re-records both hashes; that yaml diff is the reviewable act of confirming consistency, which is why `--write` requires naming the pairs you confirmed (`--write --all` is the explicit corpus-wide form).\n- **Language switcher.** Both files link to each other immediately after their H1 heading: the English file carries `English | [中文](foo.zh.md)` and the Chinese file carries `[English](foo.md) | 中文`.\n- **Structure mirrors the counterpart.** Heading depths and order, list kinds, ordered-list starts, list item counts, table row and column counts, link targets, and verbatim code blocks match one to one across the pair — see [translation-rules.md](translation-rules.md) for the full preservation rules. Existing Markdown gates apply to `.zh.md` files unchanged (`verify-md-wrap`, `verify-md-links`).\n\n## The gate: verify-translation-pairing\n\n`pnpm run verify-translation-pairing` (part of `doc-sync`, which contributors run locally for documentation changes and CI runs exhaustively) enforces the contract mechanically:\n\n1. Every document in scope has a complete pair. README discovery is case-insensitive on the basename, so `missions/readme.md` is in scope alongside the other documentation roots.\n2. Every pair artifact that exists at all is complete and consistent: all three files present, each side's current blob hash equals the recorded one (editing either side without re-confirming the pair goes red), both sides carry the language switcher, and the structural signatures match in order — heading depths, verbatim code blocks (info string and content), table row and column counts, list kinds, ordered-list starts, item counts, and every link target apart from the switcher.\n3. Files listed as `excluded` have no `.zh.md` and no `.i18n.yaml` at all. Frozen Agent Notes under `.agents/notes/archived/` are outside this evolving gate; their dedicated verifier requires and seals the complete existing triplet instead.\n\nSource-oriented code gates consume an exact `.zh.md` fence sequence as a derivative of its unsuffixed sibling instead of compiling or manifesting the same code twice. The sequence must match in length, order, fence kind, and byte-exact body; otherwise both copies remain independently checked and the pairing gate reports the structural mismatch.\n\n`pnpm run verify-translation-pairing --list` prints the current pairing state of every document in scope — missing, out-of-sync, or ok. It never fails; `missing` and `out-of-sync` rows identify violations that the normal check rejects.\n\n`pnpm run verify-translation-pairing ` checks just the named pairs — any of a pair's three files (or its bare stem) names it — so an update loop verifies its own pair in seconds instead of re-scanning the corpus. The no-argument corpus-wide form is what `doc-sync` and CI run; a scoped green never substitutes for it at PR level.\n\nThe practical rule this gate creates: **when a PR edits either side of a paired document, the same PR updates the counterpart and re-records the pair** (run the [dsh-translate-docs](../../.agents/skills/dsh-translate-docs/SKILL.md) skill, then `--write `), exactly like the repo's existing doc-sync rule for code and READMEs. A PR that leaves a pair out of sync goes red in CI.\n\nThe gate's limit, stated plainly: **a green gate means the pair was confirmed consistent at these exact contents, not that the confirmation was sound.** It checks hashes and shape; it cannot judge whether the two sides actually say the same thing, or whether the wording is accurate, well-termed, and natural — that is the reviewer's half of the contract, per [translation-rules.md](translation-rules.md). A re-recorded pair with a sloppy counterpart passes the gate; it must not pass review.\n\n## Scope and exclusions\n\n**Scope**: every non-vendor README, plus every active document under `.agents/notes/**`, `docs/**`, and `python/**`. README matching is case-insensitive on the basename and covers future directories without another manifest edit. Dependency and ignored build-output trees and the frozen `.agents/notes/archived/` tree are discovery exclusions, not evolving translation source.\n\n**Excluded** (never paired, and the gate rejects a `.zh.md` or `.i18n.yaml` for them):\n\n- `docs/cordis-catalog/`, `docs/tool-catalog/`, `docs/config-catalog.md`, `docs/persistence-catalog.md`, `docs/module-graph.md`, `docs/agent-lifecycle.md`, `docs/capability-seams.md`, `docs/event-producer-consumer.md`, `docs/graph-atlas.md`, and `docs/tool-execution-pipeline.md` — generated files whose generators emit English only; a hand-written translation would go stale on regeneration.\n- `docs/AGENTS.md`, `.agents/notes/**/AGENTS.md`, and their `CLAUDE.md` instruction symlinks — agent instructions, maintained in English only like the root `AGENTS.md`.\n- `docs/i18n/terminology.md` and [style-samples.md](style-samples.md) — both are bilingual by construction.\n- [translation-prompt.md](translation-prompt.md) — the automated pipeline's prompt template; its body is machine-consumed verbatim, so a paired translation would change pipeline behavior.\n- `.agents/notes/archived/` — frozen historical triplets. [`verify-archived-agent-notes`](../../scripts/verify-archived-agent-notes.ts) validates their completeness and content seals; translation maintenance must never rewrite them.\n\n**Universal requirement**: every current or future document in scope must merge as a complete bilingual pair. [scripts/translation-pairing.manifest.json](../../scripts/translation-pairing.manifest.json) contains only explicit exclusions; there is no per-file rollout list, date cutoff, or README-specific policy class.\n\n## Division of labor\n\nCounterparts here are produced by an agent running [dsh-translate-docs](../../.agents/skills/dsh-translate-docs/SKILL.md) and reviewed by a human — inference is cheap here, review attention is the scarce resource. The gate checks pair completeness, recorded hashes, switchers, and its documented structural signature. Review still owns translation quality, terminology, and structural requirements that the signature does not encode. The prompt contract is executable: [scripts/translation-prompt.ts](../../scripts/translation-prompt.ts) renders the committed template (terminology injected; the template carries its own calibrated rules) into either direction and parses the three-section response, while `verify-translation-prompt` exercises both render directions and the checked-in example in `doc-sync`.\n" }, { "role": "assistant", - "content": "# 双语文档\n\n[English](README.md) | 中文\n\n本仓库的文档会被公司内外的人和 agent(智能体)阅读,因此范围内的每篇文档都以英文和简体中文维护。本页定义配对契约、强制门禁、范围与排除规则;[translation-rules.md](translation-rules.md) 定义如何翻译;[terminology.md](terminology.md) 是术语真源。仓库内置的 agent 工作流见 [.agents/skills/dsh-translate-docs](../../.agents/skills/dsh-translate-docs/SKILL.md)。\n\n## 配对契约\n\n- **两种语言同权。** 一篇文档可以先用任一语言撰写和评审(先写中文的 Agent Note 与先写英文的一样正当),另一侧由它翻译而来。两个文件谁也不高于谁;约束它们的是二者必须说同样的话。\n- **一对文档是三个同目录文件。** 英文 `foo.md`、中文 `foo.zh.md`,加一份一致性记录 `foo.i18n.yaml`,都在同一目录。不用语言目录,不用独立翻译仓库,不用中英混排的单文件。配对必须整体合并:PR(Pull Request)永远不会只带一种语言而缺其余两个文件。\n- **一致性记录。**`foo.i18n.yaml` 保存两侧文件在上一次被确认「说同样的话」时各自的完整 git blob hash:\n\n ```yaml\n foo.md: 3f786850e387550fdab836ed7e6dc881de23001b\n foo.zh.md: 89e6c98d92887913cadf06b2adb97f26cde4849b\n ```\n\n 用 blob hash 而不是 commit hash,这样同一个 PR 里改动的文件也能算出记录(`git hash-object foo.md`),一致性是纯内容比较。`--write` 会先把这些快照存入本地 Git 对象库再写下记录,未提交的工作树内容也不例外;它还会在内容寻址的 `refs/dsh/translation-pairing/snapshots/` ref 下固定每个不同的已存 blob,使垃圾回收无法让已记录的恢复指针失效。因此记录的 hash 能还原任一侧上次确认时的确切文本,所以失去同步的配对是「按被改一侧的 diff 最小化地修补另一侧」,从不整篇重译。`pnpm run gen-translation-brief ` 会以能安全对齐的最窄粒度——先是有改动的 Markdown 单元,再是标题小节,最后是整篇文档——机械地汇集这次更新的工作集:被改一侧自上次确认以来的 diff、每个改动块的三方文本、改动触及的术语表行,以及有约束力的更新规则;仅落在配对中逐字节一致的围栏代码块内的改动可以直接算出,`--apply` 则经结构签名校验后把它拼接进对侧文件([briefed-updates Agent Note](../../.agents/notes/implemented/process/2026-07-26-briefed-minimal-translation-updates.md))。两侧对齐后,`pnpm run verify-translation-pairing --write ` 重新记录两个 hash;那份 yaml diff 就是「确认一致」这个动作本身,可以被评审,也正因如此,`--write` 要求点名你确认过的配对(`--write --all` 是显式的全语料形式)。\n- **语言切换行。** 两个文件在各自 H1 标题之后立即互链:英文文件带 `English | [中文](foo.zh.md)`,中文文件带 `[English](foo.md) | 中文`。\n- **结构与另一侧一一对应。** 标题深度与顺序、列表类型、有序列表起始编号、列表项数量、表格行列数、链接目标与逐字节一致的代码块在配对两侧一一对应;完整保持规则见 [translation-rules.md](translation-rules.md)。既有 Markdown 门禁对 `.zh.md` 文件原样生效(`verify-md-wrap`、`verify-md-links`)。\n\n## 门禁:verify-translation-pairing\n\n`pnpm run verify-translation-pairing`(`doc-sync`(文档同步门禁)的一环,贡献者会针对文档变更在本地运行,CI 则会完整运行)机械地强制执行这份契约:\n\n1. 范围内的每篇文档都有完整配对。发现 README 时,basename 不区分大小写,因此 `missions/readme.md` 与其他文档根一样属于范围。\n2. 任何已存在的配对产物都完整且一致:三个文件齐全、每一侧的当前 blob hash 等于记录值(改了任一侧而没重新确认配对就变红)、双方都带语言切换行、结构签名按序一致:标题深度、逐字节一致的代码块(信息字符串与内容)、表格行列数、列表类型、有序列表起始编号、列表项数量,以及除切换行之外的每个链接目标。\n3. 列为 `excluded` 的文件完全没有 `.zh.md`,也没有 `.i18n.yaml`。`.agents/notes/archived/` 下冻结的 Agent Note 不受这个持续演进的门禁约束;专用校验器会要求其现有的三个配对文件完整,并将其封存。\n\n面向源码的代码门禁会把精确的 `.zh.md` 围栏序列视为其无后缀兄弟文件的派生内容,而不会再次编译相同代码或在 manifest 中重复登记。该序列必须在长度、顺序、围栏类型和按字节精确的正文上一致;否则两份副本仍会独立受检,配对门禁也会报告结构不匹配。\n\n`pnpm run verify-translation-pairing --list` 打印范围内每篇文档的当前配对状态(missing、out-of-sync 或 ok)。它从不失败;其中 missing 与 out-of-sync 行指出普通检查会拒绝的违规。\n\n`pnpm run verify-translation-pairing ` 只检查被点名的配对——配对的三个文件中的任意一个(或其裸词干)都能点名它——因此更新循环几秒内就能验证自己的配对,而不必重新扫描全语料。`doc-sync` 与 CI 运行的是无参数的全语料形式;限定范围的绿灯在 PR 层面永远不能替代它。\n\n这个门禁带来的实际规则是:**当一个 PR 修改了已配对文档的任一侧时,同一个 PR 更新另一侧并重新记录配对**(运行 [dsh-translate-docs](../../.agents/skills/dsh-translate-docs/SKILL.md) skill(技能),再 `--write `),与本仓库既有的代码与 README 的 doc-sync 规则完全一致。留下失去同步的配对的 PR 会在 CI 变红。\n\n把门禁的边界说白:**门禁通过意味着这组文档在当前内容上的一致性得到了确认,不代表确认本身正确可靠。** 它检查记录的 hash 与结构签名;它无法判断两侧是否真的在说同样的话,也无法判断措辞是否准确、术语是否得当、行文是否自然;这部分契约由评审者把关,见 [translation-rules.md](translation-rules.md)。重新记录了 hash 但另一侧翻得潦草的配对能通过门禁;它不得通过评审。\n\n## 范围与排除\n\n**范围**:除 vendor 源码外的全部 README,以及 `.agents/notes/**`、`docs/**` 与 `python/**` 下的全部活跃文档。匹配 README 时只看文件名且不区分大小写,因此今后新增的目录无需再修改 manifest。依赖目录、被忽略的构建产物目录以及冻结的 `.agents/notes/archived/` 目录树只在发现阶段排除,不属于持续演进的翻译源文档。\n\n**排除**(永不配对,门禁拒绝为它们建 `.zh.md` 或 `.i18n.yaml`):\n\n- `docs/cordis-catalog/`、`docs/tool-catalog/`、`docs/config-catalog.md`、`docs/persistence-catalog.md`、`docs/module-graph.md`、`docs/agent-lifecycle.md`、`docs/capability-seams.md`、`docs/event-producer-consumer.md`、`docs/graph-atlas.md` 与 `docs/tool-execution-pipeline.md`:生成文件;生成器目前只输出英文,手写译文在每次重新生成时必然陈旧。计划中的后续工作是让生成器同时输出中文,届时这些文件移出排除清单。\n- `docs/AGENTS.md`、`.agents/notes/**/AGENTS.md` 以及指向它们的 `CLAUDE.md` 指令符号链接:agent 指令,与根 `AGENTS.md` 一样只以英文维护。\n- `docs/i18n/terminology.md` 与 [style-samples.md](style-samples.md):二者本身即为中英对照文档。\n- [translation-prompt.md](translation-prompt.md):自动翻译流水线的提示词模板;正文逐字进入模型请求,配对翻译会改变流水线行为。\n- `.agents/notes/archived/`:冻结的历史三文件配对。[`verify-archived-agent-notes`](../../scripts/verify-archived-agent-notes.ts) 校验其完整性和内容封存记录;翻译维护绝不能重写这些文件。\n\n**统一要求**:当前及今后纳入范围的每篇文档,合并时都必须构成完整的双语配对。[scripts/translation-pairing.manifest.json](../../scripts/translation-pairing.manifest.json) 只包含显式排除项;不存在逐文件推进清单、日期分界或 README 专用政策类别。\n\n## 分工\n\n这里的对侧文件由运行 [dsh-translate-docs](../../.agents/skills/dsh-translate-docs/SKILL.md) 的 agent 生成,再由人评审:在这里推理(inference)很便宜,评审注意力才是稀缺资源。门禁负责检查配对是否完整、记录的 hash、语言切换行以及本文列出的结构签名;翻译质量、术语和签名未涵盖的结构要求仍由评审把关。提示词契约也有可执行实现:[scripts/translation-prompt.ts](../../scripts/translation-prompt.ts) 会把仓库内置的模板(注入术语表;模板自带经人工校准的规则)渲染为英译中或中译英两个方向的提示词,并解析三段式响应;`doc-sync` 中的 `verify-translation-prompt` 会检查两个渲染方向与仓库内示例。\n" + "content": "# 双语文档\n\n[English](README.md) | 中文\n\n本仓库的文档会被公司内外的人和 agent(智能体)阅读,因此范围内的每篇文档都以英文和简体中文维护。本页定义配对契约、强制门禁、范围与排除规则;[translation-rules.md](translation-rules.md) 定义如何翻译;[terminology.md](terminology.md) 是术语真源。仓库内置的 agent 工作流见 [.agents/skills/dsh-translate-docs](../../.agents/skills/dsh-translate-docs/SKILL.md)。\n\n## 配对契约\n\n- **两种语言同权。** 一篇文档可以先用任一语言撰写和评审(先写中文的 Agent Note 与先写英文的一样正当),另一侧由它翻译而来。两个文件谁也不高于谁;约束它们的是二者必须说同样的话。\n- **一对文档是三个同目录文件。** 英文 `foo.md`、中文 `foo.zh.md`,加一份一致性记录 `foo.i18n.yaml`,都在同一目录。不用语言目录,不用独立翻译仓库,不用中英混排的单文件。配对必须整体合并:PR(Pull Request)永远不会只带一种语言而缺其余两个文件。\n- **一致性记录。**`foo.i18n.yaml` 保存两侧文件在上一次被确认「说同样的话」时各自的完整 git blob hash:\n\n ```yaml\n foo.md: 3f786850e387550fdab836ed7e6dc881de23001b\n foo.zh.md: 89e6c98d92887913cadf06b2adb97f26cde4849b\n ```\n\n 用 blob hash 而不是 commit hash,这样同一个 PR 里改动的文件也能算出记录(`git hash-object foo.md`),一致性是纯内容比较。`--write` 会先把这些快照存入本地 Git 对象库再写下记录,未提交的工作树内容也不例外;它还会在内容寻址的 `refs/dsh/translation-pairing/snapshots/` ref 下固定每个不同的已存 blob,使垃圾回收无法让已记录的恢复指针失效。因此记录的 hash 能还原任一侧上次确认时的确切文本,所以失去同步的配对是「按被改一侧的 diff 最小化地修补另一侧」,从不整篇重译。`pnpm run gen-translation-brief ` 会以能安全对齐的最窄粒度——先是有改动的 Markdown 单元,再是标题小节,最后是整篇文档——机械地汇集这次更新的工作集:被改一侧自上次确认以来的 diff、每个改动块的三方文本、改动触及的术语表行,以及有约束力的更新规则;仅落在配对中逐字节一致的围栏代码块内的改动可以直接算出,`--apply` 则经结构签名校验后把它拼接进对侧文件([briefed-updates Agent Note](../../.agents/notes/implemented/process/2026-07-26-briefed-minimal-translation-updates.md))。两侧对齐后,`pnpm run verify-translation-pairing --write ` 重新记录两个 hash;那份 yaml diff 就是「确认一致」这个动作本身,可以被评审,也正因如此,`--write` 要求点名你确认过的配对(`--write --all` 是显式的全语料形式)。\n- **语言切换行。** 两个文件在各自 H1 标题之后立即互链:英文文件带 `English | [中文](foo.zh.md)`,中文文件带 `[English](foo.md) | 中文`。\n- **结构与另一侧一一对应。** 标题深度与顺序、列表类型、有序列表起始编号、列表项数量、表格行列数、链接目标与逐字节一致的代码块在配对两侧一一对应;完整保持规则见 [translation-rules.md](translation-rules.md)。既有 Markdown 门禁对 `.zh.md` 文件原样生效(`verify-md-wrap`、`verify-md-links`)。\n\n## 门禁:verify-translation-pairing\n\n`pnpm run verify-translation-pairing`(`doc-sync`(文档同步门禁)的一环,贡献者会针对文档变更在本地运行,CI 则会完整运行)机械地强制执行这份契约:\n\n1. 范围内的每篇文档都有完整配对。发现 README 时,basename 不区分大小写,因此 `missions/readme.md` 与其他文档根一样属于范围。\n2. 任何已存在的配对产物都完整且一致:三个文件齐全、每一侧的当前 blob hash 等于记录值(改了任一侧而没重新确认配对就变红)、双方都带语言切换行、结构签名按序一致:标题深度、逐字节一致的代码块(信息字符串与内容)、表格行列数、列表类型、有序列表起始编号、列表项数量,以及除切换行之外的每个链接目标。\n3. 列为 `excluded` 的文件完全没有 `.zh.md`,也没有 `.i18n.yaml`。`.agents/notes/archived/` 下冻结的 Agent Note 不受这个持续演进的门禁约束;专用校验器会要求其现有的三个配对文件完整,并将其封存。\n\n面向源码的代码门禁会把精确的 `.zh.md` 围栏序列视为其无后缀兄弟文件的派生内容,而不会再次编译相同代码或在 manifest 中重复登记。该序列必须在长度、顺序、围栏类型和按字节精确的正文上一致;否则两份副本仍会独立受检,配对门禁也会报告结构不匹配。\n\n`pnpm run verify-translation-pairing --list` 打印范围内每篇文档的当前配对状态(missing、out-of-sync 或 ok)。它从不失败;其中 missing 与 out-of-sync 行指出普通检查会拒绝的违规。\n\n`pnpm run verify-translation-pairing ` 只检查被点名的配对——配对的三个文件中的任意一个(或其裸词干)都能点名它——因此更新循环几秒内就能验证自己的配对,而不必重新扫描全语料。`doc-sync` 与 CI 运行的是无参数的全语料形式;限定范围的绿灯在 PR 层面永远不能替代它。\n\n这个门禁带来的实际规则是:**当一个 PR 修改了已配对文档的任一侧时,同一个 PR 更新另一侧并重新记录配对**(运行 [dsh-translate-docs](../../.agents/skills/dsh-translate-docs/SKILL.md) skill(技能),再 `--write `),与本仓库既有的代码与 README 的 doc-sync 规则完全一致。留下失去同步的配对的 PR 会在 CI 变红。\n\n把门禁的边界说白:**门禁通过意味着这组文档在当前内容上的一致性得到了确认,不代表确认本身正确可靠。** 它检查记录的 hash 与结构签名;它无法判断两侧是否真的在说同样的话,也无法判断措辞是否准确、术语是否得当、行文是否自然;这部分契约由评审者把关,见 [translation-rules.md](translation-rules.md)。重新记录了 hash 但另一侧翻得潦草的配对能通过门禁;它不得通过评审。\n\n## 范围与排除\n\n**范围**:除 vendor 源码外的全部 README,以及 `.agents/notes/**`、`docs/**` 与 `python/**` 下的全部活跃文档。匹配 README 时只看文件名且不区分大小写,因此今后新增的目录无需再修改 manifest。依赖目录、被忽略的构建产物目录以及冻结的 `.agents/notes/archived/` 目录树只在发现阶段排除,不属于持续演进的翻译源文档。\n\n**排除**(永不配对,门禁拒绝为它们建 `.zh.md` 或 `.i18n.yaml`):\n\n- `docs/cordis-catalog/`、`docs/tool-catalog/`、`docs/config-catalog.md`、`docs/persistence-catalog.md`、`docs/module-graph.md`、`docs/agent-lifecycle.md`、`docs/capability-seams.md`、`docs/event-producer-consumer.md`、`docs/graph-atlas.md` 与 `docs/tool-execution-pipeline.md`:生成文件,其生成器只输出英文;手写译文会在重新生成时变得陈旧。\n- `docs/AGENTS.md`、`.agents/notes/**/AGENTS.md` 以及指向它们的 `CLAUDE.md` 指令符号链接:agent 指令,与根 `AGENTS.md` 一样只以英文维护。\n- `docs/i18n/terminology.md` 与 [style-samples.md](style-samples.md):二者本身即为中英对照文档。\n- [translation-prompt.md](translation-prompt.md):自动翻译流水线的提示词模板;正文逐字进入模型请求,配对翻译会改变流水线行为。\n- `.agents/notes/archived/`:冻结的历史三文件配对。[`verify-archived-agent-notes`](../../scripts/verify-archived-agent-notes.ts) 校验其完整性和内容封存记录;翻译维护绝不能重写这些文件。\n\n**统一要求**:当前及今后纳入范围的每篇文档,合并时都必须构成完整的双语配对。[scripts/translation-pairing.manifest.json](../../scripts/translation-pairing.manifest.json) 只包含显式排除项;不存在逐文件推进清单、日期分界或 README 专用政策类别。\n\n## 分工\n\n这里的对侧文件由运行 [dsh-translate-docs](../../.agents/skills/dsh-translate-docs/SKILL.md) 的 agent 生成,再由人评审:在这里推理(inference)很便宜,评审注意力才是稀缺资源。门禁负责检查配对是否完整、记录的 hash、语言切换行以及本文列出的结构签名;翻译质量、术语和签名未涵盖的结构要求仍由评审把关。提示词契约也有可执行实现:[scripts/translation-prompt.ts](../../scripts/translation-prompt.ts) 会把仓库内置的模板(注入术语表;模板自带经人工校准的规则)渲染为英译中或中译英两个方向的提示词,并解析三段式响应;`doc-sync` 中的 `verify-translation-prompt` 会检查两个渲染方向与仓库内示例。\n" }, { "role": "user", From c991a7cc79de48592cee5a25abf0788fb0eaacb9 Mon Sep 17 00:00:00 2001 From: Turtle Date: Wed, 5 Aug 2026 15:50:31 +0800 Subject: [PATCH 7/7] docs: finish hierarchy rescan after rebase --- README.i18n.yaml | 4 ++-- README.md | 5 ----- README.zh.md | 5 ----- docs/cookbook/adding-a-package.i18n.yaml | 4 ++-- docs/cookbook/adding-a-package.md | 8 +++----- docs/cookbook/adding-a-package.zh.md | 8 +++----- docs/cookbook/adding-a-vendored-package.i18n.yaml | 4 ++-- docs/cookbook/adding-a-vendored-package.md | 4 ++-- docs/cookbook/adding-a-vendored-package.zh.md | 4 ++-- docs/cookbook/adding-an-llm-adapter.i18n.yaml | 4 ++-- docs/cookbook/adding-an-llm-adapter.md | 11 ++++------- docs/cookbook/adding-an-llm-adapter.zh.md | 11 ++++------- docs/core-data-structures/sandbox.i18n.yaml | 4 ++-- docs/core-data-structures/sandbox.md | 6 +++--- docs/core-data-structures/sandbox.zh.md | 6 +++--- docs/core-data-structures/session.i18n.yaml | 4 ++-- docs/core-data-structures/session.md | 2 +- docs/core-data-structures/session.zh.md | 2 +- docs/defensive-patterns.i18n.yaml | 4 ++-- docs/defensive-patterns.md | 2 +- docs/defensive-patterns.zh.md | 2 +- ...tial-notice-misclassified-child-failures.i18n.yaml | 4 ++-- ...ock-partial-notice-misclassified-child-failures.md | 10 +++++----- ...-partial-notice-misclassified-child-failures.zh.md | 10 +++++----- packages/bash/bash-sandbox/README.i18n.yaml | 4 ++-- packages/bash/bash-sandbox/README.md | 2 -- packages/bash/bash-sandbox/README.zh.md | 2 -- packages/bash/bash/README.i18n.yaml | 4 ++-- packages/bash/bash/README.md | 2 +- packages/bash/bash/README.zh.md | 2 +- packages/bash/tool-bash/README.i18n.yaml | 4 ++-- packages/bash/tool-bash/README.md | 2 +- packages/bash/tool-bash/README.zh.md | 2 +- packages/client/connection/README.i18n.yaml | 4 ++-- packages/client/connection/README.md | 6 +----- packages/client/connection/README.zh.md | 6 +----- packages/client/runtime/README.i18n.yaml | 4 ++-- packages/client/runtime/README.md | 2 +- packages/client/runtime/README.zh.md | 2 +- .../runtime/src/client/sessions/transcript-adapter.ts | 1 - packages/client/ui-command/README.i18n.yaml | 4 ++-- packages/client/ui-command/README.md | 1 - packages/client/ui-command/README.zh.md | 1 - packages/client/ui-layout/README.i18n.yaml | 4 ++-- packages/client/ui-layout/README.md | 2 +- packages/client/ui-layout/README.zh.md | 2 +- packages/client/ui-model/README.i18n.yaml | 4 ++-- packages/client/ui-model/README.md | 6 +++++- packages/client/ui-model/README.zh.md | 6 +++++- packages/client/ui-sidebar/README.i18n.yaml | 4 ++-- packages/client/ui-sidebar/README.md | 2 +- packages/client/ui-sidebar/README.zh.md | 2 +- packages/client/ui-workspace/README.i18n.yaml | 4 ++-- packages/client/ui-workspace/README.md | 2 +- packages/client/ui-workspace/README.zh.md | 2 +- packages/client/web/README.i18n.yaml | 4 ++-- packages/client/web/README.md | 2 +- packages/client/web/README.zh.md | 2 +- .../code-runtime/code-runtime-worker/README.i18n.yaml | 4 ++-- packages/code-runtime/code-runtime-worker/README.md | 4 ++-- .../code-runtime/code-runtime-worker/README.zh.md | 4 ++-- packages/compact/compact-basic/README.i18n.yaml | 4 ++-- packages/compact/compact-basic/README.md | 1 - packages/compact/compact-basic/README.zh.md | 1 - packages/core/session/README.i18n.yaml | 4 ++-- packages/core/session/README.md | 11 +++-------- packages/core/session/README.zh.md | 11 +++-------- packages/core/session/src/index.ts | 2 +- packages/fs/fs-sandbox/README.i18n.yaml | 4 ++-- packages/fs/fs-sandbox/README.md | 2 +- packages/fs/fs-sandbox/README.zh.md | 2 +- packages/guard/repeat-tool-guard/README.i18n.yaml | 4 ++-- packages/guard/repeat-tool-guard/README.md | 4 ---- packages/guard/repeat-tool-guard/README.zh.md | 4 ---- packages/host/apiproxy/README.i18n.yaml | 4 ++-- packages/host/apiproxy/README.md | 2 +- packages/host/apiproxy/README.zh.md | 2 +- packages/host/directory-picker-auto/README.i18n.yaml | 4 ++-- packages/host/directory-picker-auto/README.md | 2 +- packages/host/directory-picker-auto/README.zh.md | 2 +- .../host/directory-picker-native/README.i18n.yaml | 4 ++-- packages/host/directory-picker-native/README.md | 2 +- packages/host/directory-picker-native/README.zh.md | 2 +- packages/llm/llm-deepseek/README.i18n.yaml | 4 ++-- packages/llm/llm-deepseek/README.md | 6 +----- packages/llm/llm-deepseek/README.zh.md | 6 +----- packages/llm/llm-pi-ai/README.i18n.yaml | 4 ++-- packages/llm/llm-pi-ai/README.md | 4 ---- packages/llm/llm-pi-ai/README.zh.md | 4 ---- packages/llm/llm-retry/README.i18n.yaml | 4 ++-- packages/llm/llm-retry/README.md | 2 +- packages/llm/llm-retry/README.zh.md | 2 +- packages/lsp/lsp-local/README.i18n.yaml | 4 ++-- packages/lsp/lsp-local/README.md | 2 +- packages/lsp/lsp-local/README.zh.md | 2 +- packages/sandbox/sandbox-local/README.i18n.yaml | 4 ++-- packages/sandbox/sandbox-local/README.md | 2 +- packages/sandbox/sandbox-local/README.zh.md | 2 +- packages/sdk/sdk-client/README.i18n.yaml | 4 ++-- packages/sdk/sdk-client/README.md | 2 +- packages/sdk/sdk-client/README.zh.md | 2 +- .../session-persistence-jsonl/README.i18n.yaml | 4 ++-- .../session-persistence-jsonl/README.md | 2 +- .../session-persistence-jsonl/README.zh.md | 2 +- .../session-persistence/README.i18n.yaml | 4 ++-- .../session-persistence/session-persistence/README.md | 6 ------ .../session-persistence/README.zh.md | 6 ------ packages/subprocess/subprocess-local/README.i18n.yaml | 4 ++-- packages/subprocess/subprocess-local/README.md | 4 ++-- packages/subprocess/subprocess-local/README.zh.md | 4 ++-- .../telemetry/session-telemetry-otel/README.i18n.yaml | 4 ++-- packages/telemetry/session-telemetry-otel/README.md | 2 +- .../telemetry/session-telemetry-otel/README.zh.md | 2 +- packages/ui/app-boot/README.i18n.yaml | 4 ++-- packages/ui/app-boot/README.md | 8 +++----- packages/ui/app-boot/README.zh.md | 8 +++----- packages/util/native-command/README.i18n.yaml | 4 ++-- packages/util/native-command/README.md | 2 +- packages/util/native-command/README.zh.md | 2 +- .../request-response.expected.json | 4 ++-- 120 files changed, 189 insertions(+), 268 deletions(-) diff --git a/README.i18n.yaml b/README.i18n.yaml index 8b5cea2324..e705f07877 100644 --- a/README.i18n.yaml +++ b/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write README.md -README.md: 7f393077201a974335f9f72f5924d8b1fec585c3 -README.zh.md: ecdb2057fafc4678220b8ccedfe39cb7f387b36a +README.md: b8e46044fb8857730b32d9fbbb9ed4de964d6017 +README.zh.md: e289d523bf61a577f1dd2335b3b4567736d9100d diff --git a/README.md b/README.md index 7f39307720..b8e46044fb 100644 --- a/README.md +++ b/README.md @@ -82,11 +82,6 @@ Follow DeepSeek Harness on Twitter f ## Development -```sh -pnpm install -pnpm run test:coverage -``` - Start with the [development guide](docs/development.md) and read the [architecture](docs/architecture.md) before changing packages. For agents, follow [AGENTS.md](AGENTS.md). diff --git a/README.zh.md b/README.zh.md index ecdb2057fa..e289d523bf 100644 --- a/README.zh.md +++ b/README.zh.md @@ -86,11 +86,6 @@ pnpm run demo:acp ## 开发 -```sh -pnpm install -pnpm run test:coverage -``` - 请先阅读[开发指南](docs/development.md);修改包之前,请阅读[架构文档](docs/architecture.md)。 面向 agent:遵循 [AGENTS.md](AGENTS.md)。 diff --git a/docs/cookbook/adding-a-package.i18n.yaml b/docs/cookbook/adding-a-package.i18n.yaml index 6232e2155d..85c1af757b 100644 --- a/docs/cookbook/adding-a-package.i18n.yaml +++ b/docs/cookbook/adding-a-package.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/cookbook/adding-a-package.md -adding-a-package.md: 5df22b23a69e2a33a5d963970c771088ed7f3b5d -adding-a-package.zh.md: e573657988bd67f5ff9b9191550c10bc654ef116 +adding-a-package.md: a45b222f6aed905a18ef9b480c989e6045029afe +adding-a-package.zh.md: af0e4d0779fa99ce43ebccba00c33eab16c4d362 diff --git a/docs/cookbook/adding-a-package.md b/docs/cookbook/adding-a-package.md index 5df22b23a6..a45b222f6a 100644 --- a/docs/cookbook/adding-a-package.md +++ b/docs/cookbook/adding-a-package.md @@ -14,7 +14,6 @@ packages/// # ../../../vendor/cordis (+ ../../../vendor/schemastery if # you use Config, + ../..// for each dsh dep) src/index.ts # service default export or plugin (name/inject/apply/Config) - tests/.spec.ts README.md # service API, events, extension points, design notes, # + gated Model Experience context blocks or short form # + the gated "Known Limitations and Deferred Work" section @@ -33,11 +32,11 @@ In-package relative imports use explicit `.ts` specifiers in source (for example |---|---| | `tsconfig.base.json` | no edit for an existing group; for a new group, add a `./packages//*/src` candidate to the `@deepseek-ai/dsh-*` wildcard | | `tsconfig.host.json` (host-side package) or `tsconfig.client.json` (client-side package) | add `{ "path": "./packages//" }` to `references` — exactly one aggregate, never both ([layout](../development.md#typescript-project-layout)) | -| `knip.json` | only if the package has non-`*.spec.ts` entries (e.g. `*.e2e.ts` → add a per-workspace override like `packages/llm/llm-deepseek`) | +| `knip.json` | only if the package has entrypoints that repository discovery does not already cover | A `packages/client/*` package additionally extends `tsconfig.base.client.json` instead of `tsconfig.base.json`, and a client plugin package declares `dshClient` in package.json, exports `./client`, and calls the shared tsdown preset (`packages/client/tsdown.client.ts`) — see [packages/client/AGENTS.md](../../packages/client/AGENTS.md) for the client-side contract. -Covered automatically by globs or package-manifest discovery — no edits needed: root `package.json` workspaces, `scripts/publint-all.ts`, `tsdown.config.ts`, `vitest.config.ts`, `.oxlintrc.json`, `scripts/check-workspace-constraints.ts`. +Covered automatically by globs or package-manifest discovery — no edits needed: root `package.json` workspaces, `scripts/publint-all.ts`, `tsdown.config.ts`, `.oxlintrc.json`, `scripts/check-workspace-constraints.ts`. ## 3. Decide the package topology @@ -85,8 +84,7 @@ A package with no context effect or one consumer-owned path uses the audited `No pnpm install # registers the workspace pnpm run doc-sync pnpm run constraints && pnpm run typecheck && pnpm run lint -pnpm run test:coverage # 100% per-file over src (types.ts exempt) pnpm run build && pnpm run hygiene ``` -Test expectations: every registry/registration needs an HMR-safety test (register from a child fiber, dispose it, assert cleanup). Excessive tests are welcome — see [docs/testing.md](../testing.md). +Follow the [repository testing policy](../testing.md) for the behavior-specific checks and coverage required by the new package. diff --git a/docs/cookbook/adding-a-package.zh.md b/docs/cookbook/adding-a-package.zh.md index e573657988..af0e4d0779 100644 --- a/docs/cookbook/adding-a-package.zh.md +++ b/docs/cookbook/adding-a-package.zh.md @@ -14,7 +14,6 @@ packages/// # ../../../vendor/cordis (+ ../../../vendor/schemastery if # you use Config, + ../..// for each dsh dep) src/index.ts # service default export or plugin (name/inject/apply/Config) - tests/.spec.ts README.md # service API, events, extension points, design notes, # + gated Model Experience context blocks or short form # + the gated "Known Limitations and Deferred Work" section @@ -33,11 +32,11 @@ package.json 不变式(由 `pnpm run constraints` / `scripts/check-workspace-c |---|---| | `tsconfig.base.json` | 已有分组无需编辑;新分组需为 `@deepseek-ai/dsh-*` 通配符添加 `./packages//*/src` 候选路径 | | `tsconfig.host.json`(host 侧包)或 `tsconfig.client.json`(client 侧包) | 在 `references` 中添加 `{ "path": "./packages//" }`——恰好一个聚合,绝不两个都加([布局](../development.md#typescript-project-layout)) | -| `knip.json` | 仅当包有非 `*.spec.ts` 入口时需要(如 `*.e2e.ts` → 添加 per-workspace override,参照 `packages/llm/llm-deepseek`) | +| `knip.json` | 仅当包有仓库发现机制尚未覆盖的入口时需要 | `packages/client/*` 包改为 extends `tsconfig.base.client.json`(而非 `tsconfig.base.json`);client 插件包还需在 package.json 声明 `dshClient`、导出 `./client`、调用共享 tsdown preset(`packages/client/tsdown.client.ts`)——client 侧见 [packages/client/AGENTS.md](../../packages/client/AGENTS.md)。 -以下内容由 glob 或包 manifest 发现机制自动覆盖,无需手动编辑:根 `package.json` workspaces、`scripts/publint-all.ts`、`tsdown.config.ts`、`vitest.config.ts`、`.oxlintrc.json`、`scripts/check-workspace-constraints.ts`。 +以下内容由 glob 或包 manifest 发现机制自动覆盖,无需手动编辑:根 `package.json` workspaces、`scripts/publint-all.ts`、`tsdown.config.ts`、`.oxlintrc.json`、`scripts/check-workspace-constraints.ts`。 ## 3. 确定包拓扑 @@ -85,8 +84,7 @@ Append-only, prefix-stable, replacing, or independent behavior, including the ex pnpm install # registers the workspace pnpm run doc-sync pnpm run constraints && pnpm run typecheck && pnpm run lint -pnpm run test:coverage # 100% per-file over src (types.ts exempt) pnpm run build && pnpm run hygiene ``` -测试要求:每个注册表/注册操作都需要一个 HMR(热模块替换)安全测试(从子 fiber 注册,dispose(资源释放)它,断言清理完成)。鼓励编写充分的测试——见 [docs/testing.md](../testing.md)。 +请遵循[仓库测试政策](../testing.md),为新包运行行为所需的专项检查并达到相应覆盖率。 diff --git a/docs/cookbook/adding-a-vendored-package.i18n.yaml b/docs/cookbook/adding-a-vendored-package.i18n.yaml index 3ddff28713..b8416c384f 100644 --- a/docs/cookbook/adding-a-vendored-package.i18n.yaml +++ b/docs/cookbook/adding-a-vendored-package.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/cookbook/adding-a-vendored-package.md -adding-a-vendored-package.md: a951a96f62d2ea3aa693a24d83bf46a1a12070cd -adding-a-vendored-package.zh.md: 66b71d9ac7901d40074a21213c4962d1db96db8f +adding-a-vendored-package.md: b85d74a3a09b27254883b88cb8e6587e32ed811c +adding-a-vendored-package.zh.md: 2927837a28d1e7b593090d581522f69f84504806 diff --git a/docs/cookbook/adding-a-vendored-package.md b/docs/cookbook/adding-a-vendored-package.md index a951a96f62..b85d74a3a0 100644 --- a/docs/cookbook/adding-a-vendored-package.md +++ b/docs/cookbook/adding-a-vendored-package.md @@ -53,7 +53,7 @@ Covered automatically by globs — no edits needed: root `package.json` workspac ```sh pnpm install # registers the workspace pnpm run typecheck -pnpm run build && pnpm run test && pnpm run constraints +pnpm run build && pnpm run constraints ``` -The source `paths` map lives once in `tsconfig.base.json` and serves every graph. The important isolation boundary is the project-reference graph: vendored source must be referenced through its own `vendor//tsconfig.json`, not pulled into an aggregate's strict program ([layout](../development.md#typescript-project-layout)). +Run the behavior checks selected by the [testing policy](../testing.md). The source `paths` map lives once in `tsconfig.base.json` and serves every graph. The important isolation boundary is the project-reference graph: vendored source must be referenced through its own `vendor//tsconfig.json`, not pulled into an aggregate's strict program ([layout](../development.md#typescript-project-layout)). diff --git a/docs/cookbook/adding-a-vendored-package.zh.md b/docs/cookbook/adding-a-vendored-package.zh.md index 66b71d9ac7..2927837a28 100644 --- a/docs/cookbook/adding-a-vendored-package.zh.md +++ b/docs/cookbook/adding-a-vendored-package.zh.md @@ -53,7 +53,7 @@ vendored TypeScript 源码中的本地相对导入/导出在复制后使用显 ```sh pnpm install # registers the workspace pnpm run typecheck -pnpm run build && pnpm run test && pnpm run constraints +pnpm run build && pnpm run constraints ``` -源码 `paths` 映射只在 `tsconfig.base.json` 存在一份,服务所有图。重要的隔离边界是 project-reference 图:vendored 源码必须通过其自身的 `vendor//tsconfig.json` 被引用,而非被拉入某个聚合的严格程序中([布局](../development.md#typescript-project-layout))。 +请运行[测试政策](../testing.md)所选择的行为检查。源码 `paths` 映射只在 `tsconfig.base.json` 存在一份,服务所有图。重要的隔离边界是 project-reference 图:vendored 源码必须通过其自身的 `vendor//tsconfig.json` 被引用,而非被拉入某个聚合的严格程序中([布局](../development.md#typescript-project-layout))。 diff --git a/docs/cookbook/adding-an-llm-adapter.i18n.yaml b/docs/cookbook/adding-an-llm-adapter.i18n.yaml index 9641e984ea..13827c2b60 100644 --- a/docs/cookbook/adding-an-llm-adapter.i18n.yaml +++ b/docs/cookbook/adding-an-llm-adapter.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/cookbook/adding-an-llm-adapter.md -adding-an-llm-adapter.md: a7f9dced70041653a0cb815147a07b6386d79e3e -adding-an-llm-adapter.zh.md: 494115bdd5cc2365feb0964f2bef4d6dc070d3f6 +adding-an-llm-adapter.md: 4fcc646ed2eea8a6170027d01761887aa28b0045 +adding-an-llm-adapter.zh.md: 35a671416f8160a6187a06f3dbd614dfe4faa778 diff --git a/docs/cookbook/adding-an-llm-adapter.md b/docs/cookbook/adding-an-llm-adapter.md index a7f9dced70..4fcc646ed2 100644 --- a/docs/cookbook/adding-an-llm-adapter.md +++ b/docs/cookbook/adding-an-llm-adapter.md @@ -34,13 +34,10 @@ Registration is effect-based (HMR-safe); one adapter per provider route — dupl Provider-specific thinking-mode toggles remain in the adapter's Config. Exact model metadata uses one provider-neutral capability seam: implement `resolveModel()` with provider/model identity and optional `context` and `reasoning` fields, declare a configured `defaultEffort` only when one exists, and honor the resolver's optional `AbortSignal`. Reasoning efforts are ordered opaque ids mapped to provider requests by the adapter. Preserve the adapter's authoritative selectable list, including an adapter-defined `off` when supported, without exposing final wire spellings or clamping unsupported values; an id need not equal its wire representation. -## Structure that worked +## Implementation structure -Split the adapter into testable stages (llm-deepseek's layout): wire types (`types.ts`, coverage-exempt) → request serializer → SSE/transport parser → chunk-translation state machine → a thin adapter class wiring them. Each stage gets its own unit suite. +Keep wire types, request serialization, transport parsing, chunk translation, and the adapter class as separate responsibilities; [`llm-deepseek`](../../packages/llm/llm-deepseek/README.md) is the reference layout. -## Testing +## Verification -- **Unit: mock the provider, not the harness.** A scripted `node:http` server speaking the provider's wire format covers happy paths, every error status, malformed payloads, premature closes, and aborts — no network, and it drives the 100% per-file coverage gate. Works for SDK-backed adapters too (point the SDK's baseURL at the mock). -- **Hostile framing tests.** Split stream payloads at arbitrary byte positions (including mid-UTF-8) — real networks do. -- **E2E: `tests/*.e2e.ts`** under `pnpm run test:e2e`, gated with `describe.skipIf(!process.env.MY_KEY)` so CI (no secrets) stays green. Cover representative model/provider/API families and every provider mode you map, a tool-call round trip INCLUDING the follow-up turn with results in history, and loose assertions only (substring/structure, bounded maxTokens — real models are nondeterministic). -- Register the e2e file pattern in `knip.json` (per-workspace `entry` override) or knip flags it unused. +Follow the [repository testing policy](../testing.md), which owns adapter coverage, real-provider checks, and published-entry requirements. diff --git a/docs/cookbook/adding-an-llm-adapter.zh.md b/docs/cookbook/adding-an-llm-adapter.zh.md index 494115bdd5..35a671416f 100644 --- a/docs/cookbook/adding-an-llm-adapter.zh.md +++ b/docs/cookbook/adding-an-llm-adapter.zh.md @@ -34,13 +34,10 @@ export function apply(ctx: Context, config: Config) { 提供方特有的思考模式开关仍放在适配器的 Config 中。确切模型元数据使用一处提供方无关的能力 seam:实现 `resolveModel()`,返回提供方/模型身份以及可选的 `context` 和 `reasoning` 字段;仅当存在配置指定的默认值时才声明 `defaultEffort`;遵守解析模型时传入的可选 `AbortSignal`。推理(reasoning)强度是由适配器映射到提供方请求的有序不透明 ID。请保留适配器给出的权威可选列表,包括适配器在支持时定义的 `off`;不得暴露最终协议值的具体拼写,也不得自动调整不支持的值。ID 无需与其协议表示相同。 -## 经验证有效的结构 +## 实现结构 -将适配器拆分为可测试的阶段(llm-deepseek 的布局):协议格式(wire format)类型(`types.ts`,豁免覆盖率)→ 请求序列化器 → SSE/传输解析器 → 分片转换状态机 → 一个将它们串联的薄适配器类。每个阶段配备独立的单元测试套件。 +让协议类型、请求序列化、传输解析、分片转换和适配器类分别承担独立职责;[`llm-deepseek`](../../packages/llm/llm-deepseek/README.md) 是参考布局。 -## 测试 +## 验证 -- **单元测试:mock 提供方,而非 harness。** 用脚本化的 `node:http` 服务器模拟提供方的协议格式,覆盖正常路径、所有错误状态码、畸形载荷、连接提前关闭和中止——无需网络,且能满足 100% 逐文件覆盖率门禁。对基于 SDK 的适配器同样适用(将 SDK 的 baseURL 指向 mock 服务器)。 -- **恶意分帧测试。** 在任意字节位置(包括 UTF-8 字符中间)切割流载荷——真实网络环境正是如此。 -- **e2e:`tests/*.e2e.ts`**,通过 `pnpm run test:e2e` 运行,以 `describe.skipIf(!process.env.MY_KEY)` 守卫,确保无密钥的 CI 保持绿色。覆盖具有代表性的模型/提供方/API 系列以及你映射的每种提供方模式、一次务必包含后续轮次(历史中带工具结果)的工具调用往返,以及仅做宽松断言(子串/结构匹配、有界的 maxTokens——真实模型是非确定性的)。 -- 在 `knip.json` 中注册 e2e 文件模式(per-workspace `entry` 覆盖),否则 knip 会将其标记为未使用。 +遵循[仓库测试策略](../testing.md),该策略负责适配器覆盖、真实提供方检查和已发布入口要求。 diff --git a/docs/core-data-structures/sandbox.i18n.yaml b/docs/core-data-structures/sandbox.i18n.yaml index 292d24d818..34691ad25b 100644 --- a/docs/core-data-structures/sandbox.i18n.yaml +++ b/docs/core-data-structures/sandbox.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/core-data-structures/sandbox.md -sandbox.md: b931dca54afd1fdd1dcba5b8e7778feeedb7d15d -sandbox.zh.md: a2334148e0237db2309c77437cf0681586e2fb5a +sandbox.md: 9e5feafe046f18dad49aeaf281793f0b8e03c240 +sandbox.zh.md: a1314d1b78eb0d46ea4c8ca5aa330ee83bf89132 diff --git a/docs/core-data-structures/sandbox.md b/docs/core-data-structures/sandbox.md index b931dca54a..9e5feafe04 100644 --- a/docs/core-data-structures/sandbox.md +++ b/docs/core-data-structures/sandbox.md @@ -139,10 +139,10 @@ interface ConfinedArgv { } ``` -The operator-facing local-provider key remains `runnerFailureSignatures`: an operator-configured runner must supply at least one non-empty, single-line, case-insensitive substring for its own pre-exec refusal dialect. The provider maps those entries into one rule. Consumers directly spawn `ConfinedArgv.argv`, so a missing runner, a non-executable runner, or an executable script whose shebang interpreter is unavailable rejects through the spawn channel rather than a stderr rule when Node supplies attributable `ENOENT`/`EACCES` evidence; after a process starts, child exits such as 126 or 127 remain ordinary unless the selected runner's documented fatal signature matches. +The [local provider](../../packages/sandbox/sandbox-local/README.md) owns operator configuration and maps its runner dialect into these rules. The [sandboxed bash consumer](../../packages/bash/bash-sandbox/README.md) owns spawn and result attribution. ## Provider and fail-closed errors -`ctx.sandbox.confine(argv, policy)` returns a `ConfinedArgv` or throws `SandboxUnavailableError` with code `SANDBOX_UNAVAILABLE` when no usable backend exists. Any direct spawn rejection of the returned argv proves the confined launch never started, but only `ENOENT` or `EACCES` with positive Node provenance for provider argv[0] after the caller-owned workdir is independently verified usable carries infrastructure meaning and the original error as detail. A bare `syscall: 'spawn'` without an exact error path, any other code, an invalid or unusable workdir, a resource failure, an unrelated syscall, or an unstructured rejection retains the consumer's ordinary command-start semantics. After a process starts, a matching structured rule identifies a runner refusal. Silent unconfined passthrough is never legal for a confined policy. +`ctx.sandbox.confine(argv, policy)` returns a `ConfinedArgv` or throws `SandboxUnavailableError` with code `SANDBOX_UNAVAILABLE` when no usable backend exists. Consumers may also classify a failure while spawning or observing the returned argv; that attribution belongs to the consumer contract. Silent unconfined passthrough is never legal for a confined policy. -Provider probing arbitrates between multiple candidates and is cached for the provider lifetime. A platform with one candidate may select it directly; execution-time refusal retains the safety property. The local provider reports bwrap and Seatbelt as full and preserves the Landlock launcher's full/partial kernel verdict. +Provider selection, probing, caching, and backend-specific enforcement reports belong to the [local provider](../../packages/sandbox/sandbox-local/README.md). diff --git a/docs/core-data-structures/sandbox.zh.md b/docs/core-data-structures/sandbox.zh.md index a2334148e0..a1314d1b78 100644 --- a/docs/core-data-structures/sandbox.zh.md +++ b/docs/core-data-structures/sandbox.zh.md @@ -139,10 +139,10 @@ interface ConfinedArgv { } ``` -面向运维人员的本地提供方配置键仍为 `runnerFailureSignatures`:运维人员配置的 runner 必须为自身的 pre-exec 拒绝方言提供至少一个非空、单行、不区分大小写的子串。提供方会将这些条目映射到一条规则。消费方直接 spawn `ConfinedArgv.argv`,因此当 Node 提供可归因的 `ENOENT`/`EACCES` 证据时,缺失的 runner、不可执行的 runner,或 shebang 解释器不可用的可执行脚本会在 spawn 通道遭拒,而不是由 stderr 规则判定;进程启动后,126 或 127 等子进程退出码仍按普通结果处理,除非匹配所选 runner 文档所定义的致命签名。 +[本地提供方](../../packages/sandbox/sandbox-local/README.md)拥有运维配置,并将其 runner 方言映射到这些规则。[沙箱化 bash 消费方](../../packages/bash/bash-sandbox/README.md)拥有 spawn 与结果归因。 ## 提供方与 fail-closed 错误 -`ctx.sandbox.confine(argv, policy)` 返回一个 `ConfinedArgv`,或在没有可用后端时抛出 `SandboxUnavailableError`(错误码 `SANDBOX_UNAVAILABLE`)。直接 spawn 所返回的 argv 时,任何拒绝都能证明受限启动从未开始;但只有在调用方拥有的 workdir 经独立验证可用,且 `ENOENT` 或 `EACCES` 带有明确指向提供方 argv[0] 的 Node 来源信息时,该拒绝才具有基础设施含义,并以原始错误作为详细信息。没有精确错误路径的裸 `syscall: 'spawn'`、任何其他错误码、无效或不可用的 workdir、资源失败、无关 syscall 或无结构拒绝仍保留消费方的普通命令启动语义。进程启动后,匹配到的结构化规则标识 runner 拒绝。对于受限策略,静默的无隔离透传永远不合法。 +`ctx.sandbox.confine(argv, policy)` 返回一个 `ConfinedArgv`,或在没有可用后端时抛出 `SandboxUnavailableError`(错误码 `SANDBOX_UNAVAILABLE`)。消费方也可以在 spawn 或观察所返回的 argv 时对失败进行分类;该归因属于消费方契约。对于受限策略,静默的无隔离透传永远不合法。 -提供方探测在多个候选后端之间仲裁,结果在提供方生命周期内缓存。只有一个候选后端的平台可以直接选定它;执行时拒绝仍保留安全属性。本地提供方将 bwrap 和 Seatbelt 报告为 full,并保留 Landlock 启动器的 full/partial 内核裁定。 +提供方选择、探测、缓存和后端专有的强制执行报告归[本地提供方](../../packages/sandbox/sandbox-local/README.md)所有。 diff --git a/docs/core-data-structures/session.i18n.yaml b/docs/core-data-structures/session.i18n.yaml index db1e649ecd..e66b9dcd8c 100644 --- a/docs/core-data-structures/session.i18n.yaml +++ b/docs/core-data-structures/session.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/core-data-structures/session.md -session.md: 1045915da0b3b6b527d3d859e3566ffc77b501d3 -session.zh.md: 53e22ca8e9dc17a758c5fd00e6818250e4889680 +session.md: c5491b8d6b44c0a86ce5804320533925a0e6e287 +session.zh.md: 3a713c77e972096cf95223701e8aff4cb9d0ff87 diff --git a/docs/core-data-structures/session.md b/docs/core-data-structures/session.md index 1045915da0..c5491b8d6b 100644 --- a/docs/core-data-structures/session.md +++ b/docs/core-data-structures/session.md @@ -368,7 +368,7 @@ declare class Session { /** * Detached, deep-frozen creation metadata (format version, cwd, lineage, * seed boundary). Supplied by the store via `ctx.sessions.create()`. When a - * `Session` is constructed bare (tests, ad-hoc replay), a minimal header is + * `Session` is created without a store-owned header, a minimal header is * synthesized (stamped with the current {@link SESSION_FORMAT_VERSION}) so * `session.header` is always present. Kept out of the event log — it is a * storage concern, not replayable conversation state. diff --git a/docs/core-data-structures/session.zh.md b/docs/core-data-structures/session.zh.md index 53e22ca8e9..3a713c77e9 100644 --- a/docs/core-data-structures/session.zh.md +++ b/docs/core-data-structures/session.zh.md @@ -370,7 +370,7 @@ declare class Session { /** * Detached, deep-frozen creation metadata (format version, cwd, lineage, * seed boundary). Supplied by the store via `ctx.sessions.create()`. When a - * `Session` is constructed bare (tests, ad-hoc replay), a minimal header is + * `Session` is created without a store-owned header, a minimal header is * synthesized (stamped with the current {@link SESSION_FORMAT_VERSION}) so * `session.header` is always present. Kept out of the event log — it is a * storage concern, not replayable conversation state. diff --git a/docs/defensive-patterns.i18n.yaml b/docs/defensive-patterns.i18n.yaml index 5071652c3e..f6539dc883 100644 --- a/docs/defensive-patterns.i18n.yaml +++ b/docs/defensive-patterns.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/defensive-patterns.md -defensive-patterns.md: cc34877fb0d6a2e1740d8fa138f879363c8e69a3 -defensive-patterns.zh.md: 277e4904d033e26d379e0495cb2b22370ac159d0 +defensive-patterns.md: f9737cd6ba8d2bf1f926c962b3842885778f9af4 +defensive-patterns.zh.md: 26a8933401cef39287bab8a48a07527e0c86dc1f diff --git a/docs/defensive-patterns.md b/docs/defensive-patterns.md index cc34877fb0..f9737cd6ba 100644 --- a/docs/defensive-patterns.md +++ b/docs/defensive-patterns.md @@ -18,7 +18,7 @@ When an interface documents two valid ways to signal something — an adapter ma ## Dispose must reach quiescence, not just request it -A teardown that issues kills/aborts but returns before the work stops leaves orphans. Make cleanup async and await the children's exit (kill → await `done`), and close listener/notification registries BEFORE killing so late completions stay silent. Tests prove disposal waited (pid gone right after `await fiber.dispose()`), not merely that the process eventually dies. +A teardown that issues kills/aborts but returns before the work stops leaves orphans. Make cleanup async and await the children's exit (kill → await `done`), and close listener/notification registries BEFORE killing so late completions stay silent. ## Contain callback exceptions at the boundary diff --git a/docs/defensive-patterns.zh.md b/docs/defensive-patterns.zh.md index 277e4904d0..26a8933401 100644 --- a/docs/defensive-patterns.zh.md +++ b/docs/defensive-patterns.zh.md @@ -18,7 +18,7 @@ ## Dispose 必须达到完全停稳,而不仅仅是请求停止 -如果清理流程只发出终止或中止信号便返回,而不等待工作真正停止,就会留下孤儿进程。清理逻辑应采用异步流程,并等待子进程退出(发出终止信号后等待 `done`);还应在终止进程前关闭监听器和通知注册表,使迟到的完成事件保持静默。测试必须证明 dispose 确实等待了,例如 `await fiber.dispose()` 返回后进程 ID 已不存在,而不能只证明该进程最终会退出。 +如果清理流程只发出终止或中止信号便返回,而不等待工作真正停止,就会留下孤儿进程。清理逻辑应采用异步流程,并等待子进程退出(发出终止信号后等待 `done`);还应在终止进程前关闭监听器和通知注册表,使迟到的完成事件保持静默。 ## 在边界处隔离回调异常 diff --git a/docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.i18n.yaml b/docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.i18n.yaml index 75c3218f4b..47a2e3fdd7 100644 --- a/docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.i18n.yaml +++ b/docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md -0004-landlock-partial-notice-misclassified-child-failures.md: b3be42d221623c70796105a593127971660ebc2b -0004-landlock-partial-notice-misclassified-child-failures.zh.md: 7a057d4405fcc6733e9c86cbbbbfff9bf8012641 +0004-landlock-partial-notice-misclassified-child-failures.md: db810fdc896f9734d1b581617838d72166f4efc9 +0004-landlock-partial-notice-misclassified-child-failures.zh.md: 4a31fb038c44b036e6a183040947295a47221967 diff --git a/docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md b/docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md index b3be42d221..db810fdc89 100644 --- a/docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +++ b/docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md @@ -6,7 +6,7 @@ Status: resolved ## Executive summary -On kernels with an older Landlock ABI, the launcher prints a benign partial-enforcement notice before executing every child. The harness treated that shared `landlock-run:` prefix plus any nonzero child exit as launcher failure, so ordinary outcomes such as ripgrep's exit 1 for no matches surfaced as `SANDBOX_UNAVAILABLE`; the then-bash-backed filesystem search also hid that structured error behind `SEARCH_FAILED`. Broad signature rules and missing partial-ABI composition coverage let the defect through. Runner classification now requires status-gated fatal evidence after exact informational exclusions, and an assembled keyless scenario pins the surviving bash path. Filesystem search has since moved to packaged ripgrep through the subprocess seam and no longer crosses sandboxed bash. +On kernels with an older Landlock ABI, the launcher prints a benign partial-enforcement notice before executing every child. The harness treated that shared `landlock-run:` prefix plus any nonzero child exit as launcher failure, so ordinary outcomes such as ripgrep's exit 1 for no matches surfaced as `SANDBOX_UNAVAILABLE`; the then-bash-backed filesystem search also hid that structured error behind `SEARCH_FAILED`. Broad signature rules and missing partial-ABI composition coverage let the defect through. Runner classification now requires status-gated fatal evidence after exact informational exclusions, and an assembled keyless scenario pins the surviving bash path. Filesystem search uses packaged ripgrep through the subprocess seam and does not cross sandboxed bash. ## Summary @@ -28,7 +28,7 @@ The defect did not weaken confinement or run a command unconfined. Its security - The sandbox provider reduced that contract to `runnerFailureSignatures: ['landlock-run: ']`; the bash consumer combined the prefix with any nonzero exit and reported stderr's first line. - Unit tests covered clean success, denial diagnostics, and fatal runner prefixes. Real-runner tests self-skipped without a usable kernel and did not force partial enforcement followed by a nonzero child. - A minimal POSIX wrapper that prints the notice and `exec`s its payload reproduced the failure with `false` and ripgrep no-match. -- Structured rules plus shared foreground/background classification and assembled replay coverage closed the surviving sandbox attribution gap. Before this fix was reconciled with current `master`, filesystem search moved to packaged ripgrep through `ctx.subprocess`; the obsolete bash-adapter patch and tests were dropped instead of reintroducing the old architecture. +- Structured rules plus shared foreground/background classification and assembled replay coverage closed the surviving sandbox attribution gap. Filesystem search uses packaged ripgrep through `ctx.subprocess`; the fix leaves that path outside sandboxed bash. ## Root cause @@ -43,9 +43,9 @@ Stderr remains an in-band attribution channel. A confined child can deliberately - [`RunnerFailureRule`](../core-data-structures/sandbox.md#wrapped-argv-and-classification-dialects) carries optional allowed exit codes, case-insensitive per-line fatal signatures, and case-insensitive exact informational-line exclusions. - [`dsh-sandbox-local`](../../packages/sandbox/sandbox-local/) maps Landlock to exit 125 plus a non-notice `landlock-run:` line while bwrap, Seatbelt, and custom runners remain signature-only. - [`dsh-bash-sandbox`](../../packages/bash/bash-sandbox/) directly spawns the provider argv, so a pre-start rejection uses the spawn-error channel instead of localized shell diagnostics. Settled foreground and background execution share one evidence-returning classifier; fatal evidence outranks denial, and foreground errors report the matched fatal line without changing captured stderr. -- Current [`dsh-tool-fs-search`](../../packages/fs/tool-fs-search/) uses packaged ripgrep through `ctx.subprocess` and no longer consumes the sandboxed bash seam; the base reconciliation keeps that architecture unchanged. -- Deterministic tests use a POSIX fake partial-Landlock launcher to cover notice-only child exits 1, 2, and 125, ordinary child exits 126 and 127, gated fatal diagnostics, permission denial, and foreground/background parity. -- The `examples/acp-agent` keyless snapshot runs direct bash `false` through a test-only partial-Landlock provider, keeping the product regression pinned independently of filesystem-search implementation choices. +- [`dsh-tool-fs-search`](../../packages/fs/tool-fs-search/) uses packaged ripgrep through `ctx.subprocess` and remains outside the sandboxed bash seam. +- The native-boundary regression cases live in [`partial-landlock.spec.ts`](../../packages/bash/bash-sandbox/tests/partial-landlock.spec.ts), including informational notices, fatal evidence, and foreground/background classification. +- The assembled product path is pinned by the [`partial-landlock` snapshot composition](../../examples/acp-agent/partial-landlock.cordis.snapshot.yml), independently of filesystem-search implementation choices. ## Lessons diff --git a/docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md b/docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md index 7a057d4405..4a31fb038c 100644 --- a/docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +++ b/docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md @@ -6,7 +6,7 @@ Status: resolved ## 摘要 -在 Landlock ABI 较旧的内核上,launcher 会在执行每个子进程前打印一条无害的部分强制执行通知。harness 把共享的 `landlock-run:` 前缀与任意非零子进程退出组合起来,判定为 launcher 失败,因此 ripgrep 在没有匹配项时以 1 退出等普通结果会呈现为 `SANDBOX_UNAVAILABLE`;当时仍由 bash 支撑的文件系统搜索还会用 `SEARCH_FAILED` 遮蔽这个结构化错误。过于宽泛的签名规则,以及缺少较旧 ABI 下部分强制执行的组合测试覆盖,让该缺陷得以流入。runner 分类现在会先精确排除信息性行,再要求由退出状态门控的致命证据,并由一个组装后的无密钥场景固定仍然存在的 bash 路径。文件系统搜索后来已改为通过 subprocess seam 运行打包的 ripgrep,不再经过沙箱化 bash。 +在 Landlock ABI 较旧的内核上,launcher 会在执行每个子进程前打印一条无害的部分强制执行通知。harness 把共享的 `landlock-run:` 前缀与任意非零子进程退出组合起来,判定为 launcher 失败,因此 ripgrep 在没有匹配项时以 1 退出等普通结果会呈现为 `SANDBOX_UNAVAILABLE`;当时仍由 bash 支撑的文件系统搜索还会用 `SEARCH_FAILED` 遮蔽这个结构化错误。过于宽泛的签名规则,以及缺少较旧 ABI 下部分强制执行的组合测试覆盖,让该缺陷得以流入。runner 分类现在会先精确排除信息性行,再要求由退出状态门控的致命证据,并由一个组装后的无密钥场景固定仍然存在的 bash 路径。文件系统搜索通过 subprocess seam 运行打包的 ripgrep,不经过沙箱化 bash。 ## 概述 @@ -28,7 +28,7 @@ harness 用一个不区分大小写的 `landlock-run: ` 子串表示这两种情 - 沙箱提供方把该契约简化为 `runnerFailureSignatures: ['landlock-run: ']`;bash 消费方将此前缀与任意非零退出组合,并报告 stderr 的第一行。 - 单元测试覆盖了无诊断的成功、拒绝诊断和致命 runner 前缀。真实 runner 测试在没有可用内核时会自行跳过,也没有强制构造「部分强制执行通知后跟非零子进程退出」的情况。 - 一个最小 POSIX 包装脚本会打印该通知并 `exec` 其负载;它通过 `false` 与 ripgrep 无匹配场景复现了故障。 -- 结构化规则、前台与后台共享的分类逻辑和组装后的回放覆盖共同弥补了仍然存在的沙箱归因缺口。本修复与当前 `master` 对齐前,文件系统搜索已改为通过 `ctx.subprocess` 运行打包的 ripgrep;合并时删除了过时的 bash 适配器补丁与测试,而没有把旧架构重新引入。 +- 结构化规则、前台与后台共享的分类逻辑和组装后的回放覆盖共同弥补了仍然存在的沙箱归因缺口。文件系统搜索通过 `ctx.subprocess` 运行打包的 ripgrep;本修复让该路径继续位于沙箱化 bash 之外。 ## 根因 @@ -43,9 +43,9 @@ stderr 仍是带内归因通道。受限子进程可以故意复现 runner 的 - [`RunnerFailureRule`](../core-data-structures/sandbox.md#wrapped-argv-and-classification-dialects) 携带可选的允许退出码、不区分大小写的逐行致命签名,以及按不区分大小写的整行精确匹配排除的信息性行。 - [`dsh-sandbox-local`](../../packages/sandbox/sandbox-local/) 把 Landlock 映射为退出码 125 加一行非通知的 `landlock-run:` 诊断,而 bwrap、Seatbelt 和自定义 runner 仍仅依据签名。 - [`dsh-bash-sandbox`](../../packages/bash/bash-sandbox/) 直接 spawn 提供方 argv,因此启动前遭拒时使用 spawn 错误通道,而非本地化的 shell 诊断。已结算的前台与后台执行共用一个返回证据的分类器;致命证据优先于拒绝,前台错误会报告匹配到的致命行,同时保持捕获的 stderr 不变。 -- 当前 [`dsh-tool-fs-search`](../../packages/fs/tool-fs-search/) 通过 `ctx.subprocess` 运行打包的 ripgrep,不再消费沙箱化 bash seam;与新基线对齐时保持该架构不变。 -- 确定性测试使用一个模拟 Landlock 部分强制执行行为的 POSIX launcher,覆盖仅带通知的子进程退出码 1、2、125,普通子进程退出码 126、127,带门控的致命诊断、权限拒绝,以及前台/后台一致性。 -- `examples/acp-agent` 的无密钥快照会通过仅用于测试的部分 Landlock 提供方直接运行 bash `false`,从而独立于文件系统搜索的实现选择固定产品层回归。 +- [`dsh-tool-fs-search`](../../packages/fs/tool-fs-search/) 通过 `ctx.subprocess` 运行打包的 ripgrep,并继续位于沙箱化 bash seam 之外。 +- 原生边界回归用例位于 [`partial-landlock.spec.ts`](../../packages/bash/bash-sandbox/tests/partial-landlock.spec.ts),包括信息性通知、致命证据和前台/后台分类。 +- 组装后的产品路径由 [`partial-landlock` 快照组合](../../examples/acp-agent/partial-landlock.cordis.snapshot.yml)固定,独立于文件系统搜索的实现选择。 ## 教训 diff --git a/packages/bash/bash-sandbox/README.i18n.yaml b/packages/bash/bash-sandbox/README.i18n.yaml index ce02372bdc..2de1c468b8 100644 --- a/packages/bash/bash-sandbox/README.i18n.yaml +++ b/packages/bash/bash-sandbox/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/bash/bash-sandbox/README.md -README.md: 2f69ea66251f00c74779f1decc69abc6003a4398 -README.zh.md: fc4afb554442dfaf806292f30ddf9c495c427831 +README.md: 74c1e28f76db35603bb9f72e0522e16ece5589f7 +README.zh.md: 2593049ce09c7bc1ae0a996321bb27cbac449977 diff --git a/packages/bash/bash-sandbox/README.md b/packages/bash/bash-sandbox/README.md index 2f69ea6625..74c1e28f76 100644 --- a/packages/bash/bash-sandbox/README.md +++ b/packages/bash/bash-sandbox/README.md @@ -36,8 +36,6 @@ Deny-only at the seam: a denial is a reported fact, and this executor never nego name: '@deepseek-ai/dsh-bash-sandbox' ``` -The keyless consumer-integration proofs are `tests/bwrap.e2e.ts`, `tests/landlock.e2e.ts`, and `tests/seatbelt.e2e.ts` (the real provider + real runner driven through `ctx.bash`, world-verified, each self-skipping where its runner is absent). The agent-spine e2e additionally drives two concurrent sessions in one Cordis context and proves each real bash tool call can write only its own project. See [the acp-agent example's default composition](../../../examples/acp-agent/) for the runnable demo. - ## Model Experience ### Bash tool schema, indirectly diff --git a/packages/bash/bash-sandbox/README.zh.md b/packages/bash/bash-sandbox/README.zh.md index fc4afb5544..2593049ce0 100644 --- a/packages/bash/bash-sandbox/README.zh.md +++ b/packages/bash/bash-sandbox/README.zh.md @@ -36,8 +36,6 @@ name: '@deepseek-ai/dsh-bash-sandbox' ``` -无密钥消费方集成证明是 `tests/bwrap.e2e.ts`、`tests/landlock.e2e.ts` 和 `tests/seatbelt.e2e.ts`(通过 `ctx.bash` 驱动真实提供方 + 真实 runner,从外部验证实际文件效果,并在相应 runner 缺失时各自自行跳过)。agent-spine e2e 还会在一个 Cordis 上下文中驱动两个并发会话,并证明每个真实 bash 工具调用只能写入自身项目。可运行 demo 见 [acp-agent 示例的默认组合](../../../examples/acp-agent/)。 - ## 模型体验 ### 间接的 Bash 工具 schema diff --git a/packages/bash/bash/README.i18n.yaml b/packages/bash/bash/README.i18n.yaml index 10cf39dbaf..468e4d83ed 100644 --- a/packages/bash/bash/README.i18n.yaml +++ b/packages/bash/bash/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/bash/bash/README.md -README.md: d7bf746969f52000fe298b65b995b7c631d8001c -README.zh.md: 4eef38e436cb0a4bcb3505aeb228d18c4a8c170b +README.md: e459037205730455cc9bd3afb248d3a5541ce241 +README.zh.md: d5acab202d81b72d8524b291a0b6550ce45eae2f diff --git a/packages/bash/bash/README.md b/packages/bash/bash/README.md index d7bf746969..e459037205 100644 --- a/packages/bash/bash/README.md +++ b/packages/bash/bash/README.md @@ -25,7 +25,7 @@ The split mirrors the LLM seam (`LlmService`/`LlmAdapter`) and the agent-tool su | `BashProcess.readOutput()` | **Incremental** output read — consecutive reads never re-deliver. Reads that lost data to buffer bounds flag `lossy` and point at full-stream spill files. | | `BashProcess.kill()` | Kill the process group. Returns `false` when it already finished. | -Implementations subclass `BashExecutor` and implement the abstract methods. Disposal must kill every running process and await its exit — see the HMR-safety tests. +Implementations subclass `BashExecutor` and implement the abstract methods. Disposal must kill every running process and await its exit. ## Vocabulary diff --git a/packages/bash/bash/README.zh.md b/packages/bash/bash/README.zh.md index 4eef38e436..d5acab202d 100644 --- a/packages/bash/bash/README.zh.md +++ b/packages/bash/bash/README.zh.md @@ -25,7 +25,7 @@ | `BashProcess.readOutput()` | **增量** 读取输出:连续读取绝不会重复交付。因缓冲区边界丢失数据的读取会标记 `lossy`,并指向完整流 spill 文件。 | | `BashProcess.kill()` | 终止进程组。如果进程已结束,返回 `false`。 | -实现会继承 `BashExecutor` 并实现抽象方法。dispose(资源释放)必须终止每个运行中的进程并等待其退出,详见 HMR(热模块替换)安全测试。 +实现会继承 `BashExecutor` 并实现抽象方法。dispose(资源释放)必须终止每个运行中的进程并等待其退出。 ## 词汇 diff --git a/packages/bash/tool-bash/README.i18n.yaml b/packages/bash/tool-bash/README.i18n.yaml index e37f3a2859..43ff188d8d 100644 --- a/packages/bash/tool-bash/README.i18n.yaml +++ b/packages/bash/tool-bash/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/bash/tool-bash/README.md -README.md: 35a5647365dab6daa903c14cba8b83702b50305d -README.zh.md: 0c7b1c1ec83c5d30dd97bf5cdae1f626f4065ab7 +README.md: 47101e1198d13518c3d82877df8726c1fbf26b82 +README.zh.md: d60ac4b3826838e875f7d43bc314f62e1453c1d9 diff --git a/packages/bash/tool-bash/README.md b/packages/bash/tool-bash/README.md index 35a5647365..47101e1198 100644 --- a/packages/bash/tool-bash/README.md +++ b/packages/bash/tool-bash/README.md @@ -6,7 +6,7 @@ The model-facing `bash` tool registered over the `ctx.bash` executor seam. Foreg Requires a loaded executor implementation (e.g. `@deepseek-ai/dsh-bash-local`) and the [`@deepseek-ai/dsh-bash-env`](../bash-env/README.md) registry; the plugin stays pending until every injected service exists (`inject: ['tools', 'bash', 'systemPrompt', 'bashEnv']`). The tool contract is bash-dialect — mount a bash-parsing executor. -The package root exposes only the Cordis plugin contract (`name`, `inject`, `Config`, `apply`); result rendering and background-process adaptation remain implementation details covered by same-package tests. +The package root exposes only the Cordis plugin contract (`name`, `inject`, `Config`, `apply`); result rendering and background-process adaptation remain package-internal. The plugin also contributes the `tool:bash` prompt section (order 105): check the `[exit code: N]` marker on every result and investigate failures before moving on. diff --git a/packages/bash/tool-bash/README.zh.md b/packages/bash/tool-bash/README.zh.md index 0c7b1c1ec8..d60ac4b382 100644 --- a/packages/bash/tool-bash/README.zh.md +++ b/packages/bash/tool-bash/README.zh.md @@ -6,7 +6,7 @@ 需要加载执行器实现(例如 `@deepseek-ai/dsh-bash-local`)与 [`@deepseek-ai/dsh-bash-env`](../bash-env/README.md) 注册表;在每个注入服务就绪之前,插件会保持等待状态(`inject: ['tools', 'bash', 'systemPrompt', 'bashEnv']`)。工具契约是 bash 方言——请挂载能解析 bash 的执行器。 -包(package)根只公开 Cordis 插件契约(`name`、`inject`、`Config`、`apply`);结果渲染和后台进程适配仍是实现细节,由同包测试覆盖。 +包(package)根只公开 Cordis 插件契约(`name`、`inject`、`Config`、`apply`);结果渲染和后台进程适配仍保留在包内部。 插件还会提供 `tool:bash` 提示词段落(顺序 105):检查每个结果中的 `[exit code: N]` 标记,发现失败时先调查原因再继续。 diff --git a/packages/client/connection/README.i18n.yaml b/packages/client/connection/README.i18n.yaml index 79102ffddc..05b9bb4141 100644 --- a/packages/client/connection/README.i18n.yaml +++ b/packages/client/connection/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/connection/README.md -README.md: a2ca8a1d2cdebaeb7c4223aae33e5cd9e1a60884 -README.zh.md: 6b031e58425c53a8adc4153a70a04ec1c1486b97 +README.md: 1393e79aacecbbf7b186f19e4c42269595854b0e +README.zh.md: 70380ceba1b16b2970e947fb6cd9b2af9085ae51 diff --git a/packages/client/connection/README.md b/packages/client/connection/README.md index a2ca8a1d2c..1393e79aac 100644 --- a/packages/client/connection/README.md +++ b/packages/client/connection/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -Wire consumer layer: the client plugin's apply mounts `ctx.connection` (shared api client + current-page loopback state + single-consumer stream-loop starter); the export face carries the wire contract types, the `AbstractApiClient` seam, and the loop's sink/config types. The real browser carrier uses HTTP POST for unary and respond operations and opens one downlink-only WebSocket each for `events.mux` and `events.host`; the fixture and in-process carriers continue to satisfy the same two-stream abstraction. Loopback hostname classification stays package-internal: the `/api` Host fence and WebSocket upgrades use it directly, while other client plugins consume the derived `ctx.connection.isLoopback` state. The node half's `/api` route pins the privileged method set (`host.pickDirectory`, `host.openPath`, and the whole configuration plane — `settings.describe`, `settings.openDocument`, `settings.update`, `settings.replace`, `settings.mutate`, `credentials.describe`, `credentials.set`, and `credentials.unset`; reads and native actions included, since describing returns the exposed configuration, opening acts on the Host desktop, and probing an arbitrary reference reports where a credential comes from) to loopback by passing the trust fence with an empty trust list — a declared `trustedHosts` authority reaches every other method, while these stay loopback-local until a real authentication layer exists. The platform subclasses (WebApiClient/FixtureApiClient), the ConnectionController loop, and the fixture data source are package-internal — apply selects and drives them; tests reach them via src. The downlink boundary is documented in the [WebSocket downlink carrier Agent Note](../../../.agents/notes/implemented/architecture/2026-08-04-websocket-downlink-carrier.md); the protocol contract is api-contracts v3 §3. +Wire consumer layer: the client plugin's apply mounts `ctx.connection` (shared api client + current-page loopback state + single-consumer stream-loop starter); the export face carries the wire contract types, the `AbstractApiClient` seam, and the loop's sink/config types. The browser carrier uses HTTP POST for unary and respond operations and opens one downlink-only WebSocket each for `events.mux` and `events.host`; the in-process carrier satisfies the same two-stream abstraction. Loopback hostname classification stays package-internal: the `/api` Host fence and WebSocket upgrades use it directly, while other client plugins consume the derived `ctx.connection.isLoopback` state. The node half's `/api` route pins the privileged method set (`host.pickDirectory`, `host.openPath`, and the whole configuration plane — `settings.describe`/`openDocument`/`update`/`replace`/`mutate` and `credentials.describe`/`set`/`unset`; reads and native actions included, since describing returns the exposed configuration, opening acts on the Host desktop, and probing an arbitrary reference reports where a credential comes from) to loopback by passing the trust fence with an empty trust list — a declared `trustedHosts` authority reaches every other method, while these stay loopback-local until a real authentication layer exists. The platform carriers and ConnectionController loop are package-internal; apply selects and drives them. The downlink boundary is documented in the [WebSocket downlink carrier Agent Note](../../../.agents/notes/implemented/architecture/2026-08-04-websocket-downlink-carrier.md); the protocol contract is api-contracts v3 §3. ## /api browser-trust fence @@ -12,10 +12,6 @@ The node half guards every entry under `/api` before bridging or upgrading (`src `/api/events.mux` and `/api/events.host` each accept a WebSocket upgrade and send only the corresponding `ServerRequest` text messages to the browser; the client sends no application data over these sockets. If either socket ends, the current connection generation fails and rebuilds both streams; readiness still requires both sockets to be open and the `host.describe` HTTP call to succeed. Host teardown terminates both sockets, aborts their sources, and waits for source cleanup before returning. Ordinary network GETs to these paths return 426 with no SSE fallback; `toFetchHandler`'s SSE codec serves only the isomorphic in-process carrier. -## Keyless fixture - -Any `fixture` query parameter selects the in-memory carrier. `fixture=empty` starts with no Workspace or Session; `fixturePrompt=reject` rejects prompts before acceptance; `fixtureAttach=fail` publishes a Session but rejects its Workspace attachment; `fixtureSessionCreate=drop-response` publishes and frames a Session before dropping the create response; and `fixtureFrames=workspace-first` reverses the default session-first create-frame order. Workspace creation by name/path and caller-preallocated SessionIds remain deterministic enough for assembled Web tests to reconcile list and frame arrival. Fixture content search preserves the production-facing `unicode61`-style case, diacritic, and token-phrase behavior and returns a match-centered snippet of at most 120 Unicode code points. - ## Model Experience None, as the wire consumer layer moves already-composed messages between browser and host; nothing here reaches a model request. diff --git a/packages/client/connection/README.zh.md b/packages/client/connection/README.zh.md index 6b031e5842..70380ceba1 100644 --- a/packages/client/connection/README.zh.md +++ b/packages/client/connection/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -协议消费层:客户端插件的 apply 会挂载 `ctx.connection`(共享 API 客户端 + 当前页面的 loopback 状态 + 单消费方流循环启动器);导出表层携带协议契约类型、`AbstractApiClient` seam,以及循环的 sink/配置类型。真实浏览器载体以 HTTP POST 发送 unary/respond,并为 `events.mux` 与 `events.host` 各开一条只下行的 WebSocket;fixture 与进程内载体继续满足同一双流抽象。Loopback hostname 判定逻辑留在包内部:`/api` Host fence 与 WebSocket upgrade 会直接使用它,其他客户端插件则消费派生的 `ctx.connection.isLoopback` 状态。node 半侧的 `/api` 路由让特权方法集(`host.pickDirectory`、`host.openPath`,以及整个配置面——`settings.describe`、`settings.openDocument`、`settings.update`、`settings.replace`、`settings.mutate`、`credentials.describe`、`credentials.set` 和 `credentials.unset`;读取与原生操作也在内,因为 describe 会返回已暴露的配置、打开操作会作用于 Host 桌面,而探测任意引用会报出某条凭据来自何处)以空信任表过信任 fence,从而钉在回环——已声明的 `trustedHosts` 授权可达其余全部方法,而这些方法在真正的认证层出现之前仍只限回环本机。平台子类(WebApiClient/FixtureApiClient)、ConnectionController 循环和 fixture 数据源都属于包内部:apply 负责选择并驱动它们,测试则通过 src 访问。下行边界见 [WebSocket 下行载体 Agent Note](../../../.agents/notes/implemented/architecture/2026-08-04-websocket-downlink-carrier.md);协议契约见 api-contracts v3 §3。 +协议消费层:客户端插件的 apply 会挂载 `ctx.connection`(共享 API 客户端 + 当前页面的 loopback 状态 + 单消费方流循环启动器);导出表层携带协议契约类型、`AbstractApiClient` seam,以及循环的 sink/配置类型。浏览器载体以 HTTP POST 发送 unary/respond,并为 `events.mux` 与 `events.host` 各开一条只下行的 WebSocket;进程内载体满足同一双流抽象。Loopback hostname 判定逻辑留在包内部:`/api` Host fence 与 WebSocket upgrade 会直接使用它,其他客户端插件则消费派生的 `ctx.connection.isLoopback` 状态。node 半侧的 `/api` 路由让特权方法集(`host.pickDirectory`、`host.openPath`,以及整个配置面——`settings.describe`/`openDocument`/`update`/`replace`/`mutate` 与 `credentials.describe`/`set`/`unset`;读取与原生操作也在内,因为 describe 会返回已暴露的配置、打开操作会作用于 Host 桌面,而探测任意引用会报出某条凭据来自何处)以空信任表过信任 fence,从而钉在回环——已声明的 `trustedHosts` 授权可达其余全部方法,而这些方法在真正的认证层出现之前仍只限回环本机。平台载体与 ConnectionController 循环属于包内部;apply 负责选择并驱动它们。下行边界见 [WebSocket 下行载体 Agent Note](../../../.agents/notes/implemented/architecture/2026-08-04-websocket-downlink-carrier.md);协议契约见 api-contracts v3 §3。 ## /api 浏览器信任栅栏 @@ -12,10 +12,6 @@ node 半侧在桥接或 upgrade 前守卫 `/api` 下的每个入口(`src/api-r `/api/events.mux` 与 `/api/events.host` 各接受一条 WebSocket upgrade,并只向浏览器发送对应的 `ServerRequest` text message;客户端不会在这些 socket 上发送业务数据。任一 socket 结束都会使当前 connection generation 失败并重建两条流,连接就绪仍要求两条 socket open 且 `host.describe` HTTP 调用成功。Host teardown 会终止两条 socket、中止各自的 source,并等待 source 清理完成后再返回。普通网络 GET 这些路径会返回 426,不保留 SSE 回退;`toFetchHandler` 的 SSE 编解码只服务进程内同构载体。 -## 无密钥 fixture - -任何 `fixture` 查询参数都会选择内存载体。`fixture=empty` 启动时不含 Workspace 或 Session;`fixturePrompt=reject` 在接受前拒绝提示词;`fixtureAttach=fail` 发布 Session 但拒绝将其附加到 Workspace;`fixtureSessionCreate=drop-response` 在丢弃创建响应前发布 Session 并为其发出帧;`fixtureFrames=workspace-first` 则反转默认的 Session 优先创建帧顺序。按名称/路径创建 Workspace 以及由调用方预先分配 SessionId,均具有足够的确定性,组装后的 Web 测试可以据此协调列表与帧的到达。fixture 内容搜索会保留面向生产环境的 `unicode61` 式大小写、变音符号和 token/短语行为,并返回以匹配位置为中心、最多包含 120 个 Unicode 码点的 snippet。 - ## 模型体验 无。协议消费层只在浏览器与主机之间搬运已经组合好的消息;这里没有任何内容进入模型请求。 diff --git a/packages/client/runtime/README.i18n.yaml b/packages/client/runtime/README.i18n.yaml index ec7183a3dc..7acfba7d09 100644 --- a/packages/client/runtime/README.i18n.yaml +++ b/packages/client/runtime/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/runtime/README.md -README.md: e921b86379173df59575c7f4a43eaedacedb9585 -README.zh.md: b3ed3fbf86a7003e552ef82f78f8cc34d613ae17 +README.md: db046202b63562a9282c7f4dd1f220d356a21dad +README.zh.md: 07b80eff9f3dc992cc57ba2f963dd76759a65787 diff --git a/packages/client/runtime/README.md b/packages/client/runtime/README.md index e921b86379..db046202b6 100644 --- a/packages/client/runtime/README.md +++ b/packages/client/runtime/README.md @@ -28,7 +28,7 @@ SlotsService gives the renderer separate bare observables for `useSessions` and ## The human transcript -`ConversationSnapshot.nodes` is the human transcript, not the model surface. `TranscriptAdapter` projects the raw window in log order — every append-origin surface event (`isAppendSurfaceEvent`) at its own log position, plus one `CompactionSummaryNode` marker per landed compaction checkpoint — and never consults surface order. `ConversationSnapshot.turnEnds` maps each completed turn in that window to its `turn/end` seq, retaining turn completion independently from the transcript so presentation can require a real boundary before enabling an action. A landed compaction therefore keeps the conversation it shadowed on the model side: the marker reports where the model stopped seeing that history instead of erasing it. Model-only replacement copies stay out: a pruned `tool/result` and a regenerated `assistant/message` rewrite one node for the model and mark no boundary. A checkpoint is a `user/message` carrying the compaction seam's plugin source that **replaced** a surface range; an appending plugin-sourced `user/message` is injected context, not a compaction. The adapter's plugin literal is pinned to the seam's own declaration by a type-only import of the cordis-free [`dsh-compact/checkpoint`](../../compact/compact/README.md) leaf, so renaming it there fails `tsc` here; a **value** import of the package would fail the client purity gate, and the package **root** is unreachable even as a type (it reaches `dsh-session`'s root, whose `Context` merge collides the host `sessions` with this program's). `tests/compact-checkpoint-pin.spec.ts` covers the same drift behaviorally. +`ConversationSnapshot.nodes` is the human transcript, not the model surface. `TranscriptAdapter` projects the raw window in log order — every append-origin surface event (`isAppendSurfaceEvent`) at its own log position, plus one `CompactionSummaryNode` marker per landed compaction checkpoint — and never consults surface order. `ConversationSnapshot.turnEnds` maps each completed turn in that window to its `turn/end` seq, retaining turn completion independently from the transcript so presentation can require a real boundary before enabling an action. A landed compaction therefore keeps the conversation it shadowed on the model side: the marker reports where the model stopped seeing that history instead of erasing it. Model-only replacement copies stay out: a pruned `tool/result` and a regenerated `assistant/message` rewrite one node for the model and mark no boundary. A checkpoint is a `user/message` carrying the compaction seam's plugin source that **replaced** a surface range; an appending plugin-sourced `user/message` is injected context, not a compaction. The adapter's plugin literal is pinned to the seam's own declaration by a type-only import of the cordis-free [`dsh-compact/checkpoint`](../../compact/compact/README.md) leaf, so renaming it there fails `tsc` here; a **value** import of the package would fail the client purity gate, and the package **root** is unreachable even as a type (it reaches `dsh-session`'s root, whose `Context` merge collides the host `sessions` with this program's). Because the projection is log-ordered, the node array is seq-monotonic by construction: log-only `command/run` / `command/done` nodes splice in by seq, `Session` merges interrupted frozen nodes by their fractional seqs, and a window whose checkpoint cites a shadowed range outside it renders the marker with nothing logged. The marker's summary text comes from the checkpoint's `compact/summary` provenance; a window cut that left the provenance outside makes the row non-expandable rather than empty, and a later page that supplies it resolves the text. Performance contract: one append materializes at most one node and copies the projection only when it adds that node; an event that changes no node keeps the previous array reference (a chunk storm costs nothing), and unchanged nodes keep their object identity. diff --git a/packages/client/runtime/README.zh.md b/packages/client/runtime/README.zh.md index b3ed3fbf86..07b80eff9f 100644 --- a/packages/client/runtime/README.zh.md +++ b/packages/client/runtime/README.zh.md @@ -28,7 +28,7 @@ SlotsService 分别为 renderer 提供 `useSessions` 与 `useWorkspaces` 的裸 ## 面向人的 transcript(文本记录) -`ConversationSnapshot.nodes` 是面向人的 transcript,不是模型 surface。`TranscriptAdapter` 按日志顺序投影原始窗口——每个 append 来源的 surface 事件(`isAppendSurfaceEvent`)落在它自己的日志位置上,外加每次落地的压缩(compaction)检查点贡献一个 `CompactionSummaryNode` 标记——且从不查询 surface 顺序。`ConversationSnapshot.turnEnds` 把该窗口中的每个已完成轮次映射到其 `turn/end` seq;它独立于 transcript 保留轮次完成状态,使呈现层能够在启用操作前要求存在真实边界。于是一次落地的压缩会保留它在模型侧遮蔽掉的对话:标记报告模型从哪里开始看不见那段历史,而不是把它抹掉。仅模型可见的 replacement 副本不进入记录:被裁剪的 `tool/result` 和重新生成的 `assistant/message` 只为模型重写一个节点,不标记任何边界。检查点是携带压缩 seam 插件来源、且**替换**了一段 surface 范围的 `user/message`;一条 append 的插件来源 `user/message` 是注入上下文,不是压缩。适配器的插件字面量通过对无 cordis 的 [`dsh-compact/checkpoint`](../../compact/compact/README.md) 叶子做仅类型导入,钉在压缩 seam 自己的声明上:在那里改名会让此处 `tsc` 失败;而对该包(package)做**值**导入会被客户端纯度门禁拒绝,包的**根**即便作为类型也无法到达(它会到达 `dsh-session` 的根,其 `Context` 合并会让 host 的 `sessions` 与本程序的冲突)。`tests/compact-checkpoint-pin.spec.ts` 从行为侧覆盖同一漂移。 +`ConversationSnapshot.nodes` 是面向人的 transcript,不是模型 surface。`TranscriptAdapter` 按日志顺序投影原始窗口——每个 append 来源的 surface 事件(`isAppendSurfaceEvent`)落在它自己的日志位置上,外加每次落地的压缩(compaction)检查点贡献一个 `CompactionSummaryNode` 标记——且从不查询 surface 顺序。`ConversationSnapshot.turnEnds` 把该窗口中的每个已完成轮次映射到其 `turn/end` seq;它独立于 transcript 保留轮次完成状态,使呈现层能够在启用操作前要求存在真实边界。于是一次落地的压缩会保留它在模型侧遮蔽掉的对话:标记报告模型从哪里开始看不见那段历史,而不是把它抹掉。仅模型可见的 replacement 副本不进入记录:被裁剪的 `tool/result` 和重新生成的 `assistant/message` 只为模型重写一个节点,不标记任何边界。检查点是携带压缩 seam 插件来源、且**替换**了一段 surface 范围的 `user/message`;一条 append 的插件来源 `user/message` 是注入上下文,不是压缩。适配器的插件字面量通过对无 cordis 的 [`dsh-compact/checkpoint`](../../compact/compact/README.md) 叶子做仅类型导入,钉在压缩 seam 自己的声明上:在那里改名会让此处 `tsc` 失败;而对该包(package)做**值**导入会被客户端纯度门禁拒绝,包的**根**即便作为类型也无法到达(它会到达 `dsh-session` 的根,其 `Context` 合并会让 host 的 `sessions` 与本程序的冲突)。 由于投影按日志顺序,节点数组天然按 seq 单调:仅日志的 `command/run` / `command/done` 节点按 seq 插入,`Session` 按分数 seq 归并被打断的冻结节点,而检查点所引范围落在窗口之外的窗口会渲染出标记且不打印任何日志。标记的摘要文本来自检查点的 `compact/summary` 溯源;窗口切分把溯源留在窗口外时该行不可展开而非空白,后续补上溯源的分页会解析出文本。性能契约:一次追加最多物化一个节点,并且仅在加入该节点时复制投影;不改变任何节点的事件保持上一次的数组引用(分片风暴零成本),未变化的节点保持其对象标识。 diff --git a/packages/client/runtime/src/client/sessions/transcript-adapter.ts b/packages/client/runtime/src/client/sessions/transcript-adapter.ts index 05ac3cb067..8dbaa4832e 100644 --- a/packages/client/runtime/src/client/sessions/transcript-adapter.ts +++ b/packages/client/runtime/src/client/sessions/transcript-adapter.ts @@ -29,7 +29,6 @@ import { toAssistantBlocks } from './conversation.ts' * import stays type-only because a value import would fail the client purity * gate (`packages/client/tsdown.client.ts`) — cross-plugin value imports are * forbidden in a browser bundle — while an erased type never reaches it. - * `tests/compact-checkpoint-pin.spec.ts` covers the same drift behaviorally. */ const COMPACT_PLUGIN: typeof COMPACT_CHECKPOINT_SOURCE.plugin = 'compact' diff --git a/packages/client/ui-command/README.i18n.yaml b/packages/client/ui-command/README.i18n.yaml index 4c29ba83d2..e8bce8520f 100644 --- a/packages/client/ui-command/README.i18n.yaml +++ b/packages/client/ui-command/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-command/README.md -README.md: 7846af7789f20cc8e19bf2b2f25d867063ce6936 -README.zh.md: fa703c8e94d906ede8108ae73b9a885bcf40cc9c +README.md: a19bfe7135acc5408448dc73d04813ed4104dd48 +README.zh.md: 79d903b916728c1200ab1311055f2be190008787 diff --git a/packages/client/ui-command/README.md b/packages/client/ui-command/README.md index 7846af7789..a19bfe7135 100644 --- a/packages/client/ui-command/README.md +++ b/packages/client/ui-command/README.md @@ -22,5 +22,4 @@ None directly; this package neither assembles nor sends a provider request. Comm ## Known Limitations and Deferred Work -- **The popupSelect shell has no product consumer** — package tests exercise the shell directly. - **Detached-result notices fall back to the console off-session** — the fire-and-forget paths route results to the triggering session's composer via `SessionInput.notify`; after session teardown the console line is the only remaining surface. diff --git a/packages/client/ui-command/README.zh.md b/packages/client/ui-command/README.zh.md index fa703c8e94..79d903b916 100644 --- a/packages/client/ui-command/README.zh.md +++ b/packages/client/ui-command/README.zh.md @@ -22,5 +22,4 @@ ## 已知限制与暂缓事项 -- **popupSelect 壳没有产品消费方**:包测试会直接演练该壳。 - **脱离会话后,detached result 的 notice 回退到 console**:fire-and-forget 路径经 `SessionInput.notify` 把结果送到触发会话的编辑器;会话拆除后,console 输出行是仅剩的呈现面。 diff --git a/packages/client/ui-layout/README.i18n.yaml b/packages/client/ui-layout/README.i18n.yaml index f021dbb3ca..8b5aff5db5 100644 --- a/packages/client/ui-layout/README.i18n.yaml +++ b/packages/client/ui-layout/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-layout/README.md -README.md: b01b35e9ec37e9b5b88046c3834c3aaeec70eb6e -README.zh.md: c3e4bf1e1b48feb87e68b3b7c73f7f9d11d7fea3 +README.md: 5cb8f01efb2e18109e917225dbce088ea77394af +README.zh.md: 6559fe595a6219b139fe46cf046906fa63636f64 diff --git a/packages/client/ui-layout/README.md b/packages/client/ui-layout/README.md index b01b35e9ec..5cb8f01efb 100644 --- a/packages/client/ui-layout/README.md +++ b/packages/client/ui-layout/README.md @@ -6,7 +6,7 @@ Shell plugin: three-column AppFrame (drag handles and concession chain) plus the AppFrame always mounts the conversation and details columns; a connected Session renders through `SessionProvider`. The transient layout store starts the sidebar at its default width and details closed, and it never reads or writes `localStorage`. Hero and other unselected states also derive a zero rendered details width without changing that stored preference. AppFrame retains the last non-blank Session id across those states: the first Session remains closed, an explicit details action opens the contract default width, returning to the same Session restores its unchanged width, and selecting a different Session closes details before paint. The conversation owner share is empty, while the sidebar owner share contains only `collapsed` and `width`; registrants obtain business data from standard hooks and actions from their own inject faces. -The `/client` export surface is the plugin body (`apply`/`inject`), `LayoutService`, and the four owner-share interfaces. AppFrame, the panel store, and the concession solver remain package-internal; tests import internals through `/src`. +The `/client` export surface is the plugin body (`apply`/`inject`), `LayoutService`, and the four owner-share interfaces. AppFrame, the panel store, and the concession solver remain package-internal. ## Model Experience diff --git a/packages/client/ui-layout/README.zh.md b/packages/client/ui-layout/README.zh.md index c3e4bf1e1b..6559fe595a 100644 --- a/packages/client/ui-layout/README.zh.md +++ b/packages/client/ui-layout/README.zh.md @@ -6,7 +6,7 @@ AppFrame 始终挂载会话栏和详情栏;已连接 Session 通过 `SessionProvider` 渲染。布局 store 是瞬时状态,侧边栏以默认宽度启动,详情栏则保持关闭,且该 store 从不读写 `localStorage`。hero 和其他未选中状态也会将详情栏的渲染宽度派生为零,但不会改变存储的宽度偏好。AppFrame 会跨越这些状态保留最后一个非 blank 会话 id:首个会话保持关闭;显式打开详情栏的操作会使用契约默认宽度;返回同一会话时恢复其未改变的宽度;选择不同会话时,详情栏会在绘制前关闭。会话 owner share 为空,侧边栏 owner share 只包含 `collapsed` 和 `width`;注册方通过标准钩子获取业务数据,并从各自的 inject 接口获取操作。 -`/client` 导出表层包含插件主体(`apply`/`inject`)、`LayoutService` 和四个 owner-share 接口。AppFrame、面板 store 与让步求解器仍属于包内部;测试通过 `/src` 导入内部实现。 +`/client` 导出表层包含插件主体(`apply`/`inject`)、`LayoutService` 和四个 owner-share 接口。AppFrame、面板 store 与让步求解器仍属于包内部。 ## 模型体验 diff --git a/packages/client/ui-model/README.i18n.yaml b/packages/client/ui-model/README.i18n.yaml index 9621610052..69ec84f418 100644 --- a/packages/client/ui-model/README.i18n.yaml +++ b/packages/client/ui-model/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-model/README.md -README.md: bbc834db9489941c171aea1cb4e6dadb6f24d211 -README.zh.md: 065a6b771dbd7eea87f0c632a6dd9f0fde6c0100 +README.md: 5f9fc65939eb747d916fa5609423d3186d1fefde +README.zh.md: 3ed8db3095d96e48813cf5b15a206ebf4c894950 diff --git a/packages/client/ui-model/README.md b/packages/client/ui-model/README.md index bbc834db94..5f9fc65939 100644 --- a/packages/client/ui-model/README.md +++ b/packages/client/ui-model/README.md @@ -2,7 +2,11 @@ English | [中文](README.zh.md) -Model selection plugin, browser half: TWO entries over ONE per-session directory owned by `ModelService` (`ctx.models`). For ordinary sessions, the `/model` popupSelect contribution (registered through `ctx.command`) and the composer's named `conversation.input.model` seat both load the session's advisory directory through `session.models` and submit through `session.selectModel` via the same `ModelDirectory` instance. The compact composer trigger opens a two-level Model/Effort menu: models stay provider-grouped, while the selected exact model supplies its adapter-owned effort names, descriptions, and default. The Host-reported provider/model/reasoning target is the single selection fact, but it is echoed only when the exact route remains in the advertised groups; removing that catalog row leaves the routable target intact while the trigger prompts `Select model`, no stale row is synthesized, and no Effort row is shown until the user picks an advertised model. `/model` applies the selected model's default effort, and the composer can then choose any advertised effort. Directory loads and selections share a generation counter so an older response never overwrites a newer one; a connection reset drops every resident projection and repulls the Host-restored target before display. Provider-local metadata failures list inline while usable groups stay selectable, and selection failures retain the prior target and directory. Directories are per-session, resolved lazily through `ctx.models.directoryFor(sessionId)`, and disposed with the session scope. Addressed subagent sessions expose neither entry, and their directory rejects loads, selections, and reconnect refreshes, because ordinary Agent-bound model RPCs would activate persisted child history outside the direct-parent continuation seam. +Model selection plugin, browser half: TWO entries over ONE per-session directory owned by `ModelService` (`ctx.models`). For ordinary sessions, the `/model` popupSelect contribution (registered through `ctx.command`) and the composer's named `conversation.input.model` seat both load the session's advisory directory through `session.models` and submit through `session.selectModel` via the same `ModelDirectory` instance. The compact composer trigger opens a two-level Model/Effort menu: models stay provider-grouped, while the selected exact model supplies its adapter-owned effort names, descriptions, and default. `/model` applies the selected model's default effort, and the composer can then choose any advertised effort. + +The Host-reported provider/model/reasoning target is the single selection fact, but it is echoed only when the exact route remains in the advertised groups; removing that catalog row leaves the routable target intact while the trigger prompts `Select model`, no stale row is synthesized, and no Effort row is shown until the user picks an advertised model. Directory loads and selections share a generation counter so an older response never overwrites a newer one; a connection reset drops every resident projection and repulls the Host-restored target before display. Provider-local metadata failures list inline while usable groups stay selectable, and selection failures retain the prior target and directory. + +Directories are per-session, resolved lazily through `ctx.models.directoryFor(sessionId)`, and disposed with the session scope. Addressed subagent sessions expose neither entry, and their directory rejects loads, selections, and reconnect refreshes, because ordinary Agent-bound model RPCs would activate persisted child history outside the direct-parent continuation seam. The `/client` export surface is the plugin body (`apply`/`inject`), `ModelService`, `ModelDirectory` with its state shape, and the seat's injected face type. diff --git a/packages/client/ui-model/README.zh.md b/packages/client/ui-model/README.zh.md index 065a6b771d..3ed8db3095 100644 --- a/packages/client/ui-model/README.zh.md +++ b/packages/client/ui-model/README.zh.md @@ -2,7 +2,11 @@ [English](README.md) | 中文 -模型选择插件(浏览器侧):**两个入口共用一份会话级目录**,由 `ModelService`(`ctx.models`)持有。对于普通会话,`/model` popupSelect 贡献项(经 `ctx.command` 注册)与 composer 的具名 `conversation.input.model` slot 都通过同一个 `ModelDirectory` 实例,经 `session.models` 加载会话的建议目录,并经 `session.selectModel` 提交。紧凑型 composer 触发器会打开两级 Model/Effort 菜单:模型仍按提供方分组,所选具体模型则提供由其适配器持有的推理强度名称、说明和默认值。Host 报告的提供方/模型/推理(reasoning)目标是唯一的选择事实,但只有当该精确路由仍在已公布分组中时才会回显;删除该目录行会保留仍可路由的目标,但触发器会提示 `Select model`,系统不会合成陈旧行,且在用户选择已公布的模型之前不会显示 Effort 行。`/model` 应用所选模型的默认推理强度,composer 随后可以选择任一已公布的推理强度。目录加载与选择共享一个代次计数器,旧响应不会覆盖新结果;连接重置会丢弃所有常驻目录投影,并在显示前重新拉取 Host 恢复的目标。各提供方的元数据获取失败会内联列出,同时可用分组仍可选择;选择失败会保留先前的目标和目录。目录按会话惰性解析(`ctx.models.directoryFor(sessionId)`),随会话作用域一并释放。已寻址 subagent 会话不公开任一入口,其目录会拒绝加载、选择与重新连接刷新,因为绑定到 agent(智能体)的普通模型 RPC 会在直接 parent 继续执行 seam 之外激活持久化 child 历史。 +模型选择插件(浏览器侧):**两个入口共用一份会话级目录**,由 `ModelService`(`ctx.models`)持有。对于普通会话,`/model` popupSelect 贡献项(经 `ctx.command` 注册)与 composer 的具名 `conversation.input.model` slot 都通过同一个 `ModelDirectory` 实例,经 `session.models` 加载会话的建议目录,并经 `session.selectModel` 提交。紧凑型 composer 触发器会打开两级 Model/Effort 菜单:模型仍按提供方分组,所选具体模型则提供由其适配器持有的推理强度名称、说明和默认值。`/model` 应用所选模型的默认推理强度,composer 随后可以选择任一已公布的推理强度。 + +Host 报告的提供方/模型/推理(reasoning)目标是唯一的选择事实,但只有当该精确路由仍在已公布分组中时才会回显;删除该目录行会保留仍可路由的目标,但触发器会提示 `Select model`,系统不会合成陈旧行,且在用户选择已公布的模型之前不会显示 Effort 行。目录加载与选择共享一个代次计数器,旧响应不会覆盖新结果;连接重置会丢弃所有常驻目录投影,并在显示前重新拉取 Host 恢复的目标。各提供方的元数据获取失败会内联列出,同时可用分组仍可选择;选择失败会保留先前的目标和目录。 + +目录按会话惰性解析(`ctx.models.directoryFor(sessionId)`),随会话作用域一并释放。已寻址 subagent 会话不公开任一入口,其目录会拒绝加载、选择与重新连接刷新,因为绑定到 agent(智能体)的普通模型 RPC 会在直接 parent 继续执行 seam 之外激活持久化 child 历史。 `/client` 导出面为插件本体(`apply`/`inject`)、`ModelService`、`ModelDirectory` 及其状态形状、slot 注入面类型。 diff --git a/packages/client/ui-sidebar/README.i18n.yaml b/packages/client/ui-sidebar/README.i18n.yaml index 609542a3b7..cc563215dc 100644 --- a/packages/client/ui-sidebar/README.i18n.yaml +++ b/packages/client/ui-sidebar/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-sidebar/README.md -README.md: f251a211153b2fb317947501a051030c431698a1 -README.zh.md: f22ebfa34e51a267a21a548570770ba51cedc116 +README.md: 45ae267d98b17bbc612cf932f5b95b42ba6ff4bf +README.zh.md: 436baf0b2d50934ce4813ac53c532006d9d0d4fb diff --git a/packages/client/ui-sidebar/README.md b/packages/client/ui-sidebar/README.md index f251a21115..45ae267d98 100644 --- a/packages/client/ui-sidebar/README.md +++ b/packages/client/ui-sidebar/README.md @@ -12,7 +12,7 @@ Scrollbars in the column are a pointer affordance: the shell rebinds ui-theme's The foot is the `sidebar.settings` seat: the sidebar renders only the bottom-pinned layout slot and shares its column state (`wide`); ui-settings registers the trigger row and settings panel there. -The `/client` export surface is the plugin body (`apply`/`inject`) plus the contract types only — SidebarRoot, the row components, and the tree derivation are internal (the slot registration closes over them; tests import src paths directly). +The `/client` export surface is the plugin body (`apply`/`inject`) plus the contract types only; SidebarRoot, the row components, and the tree derivation remain package-internal behind the slot registration. ## Model Experience diff --git a/packages/client/ui-sidebar/README.zh.md b/packages/client/ui-sidebar/README.zh.md index f22ebfa34e..436baf0b2d 100644 --- a/packages/client/ui-sidebar/README.zh.md +++ b/packages/client/ui-sidebar/README.zh.md @@ -12,7 +12,7 @@ New Session 会启动运行时的页面局部前端 Session Intent;真实 Work 页脚承载 `sidebar.settings`:侧边栏只渲染固定在底部的布局 slot,并共享其栏状态(`wide`);ui-settings 在此注册触发行和设置面板。 -`/client` 导出表层只包含插件主体(`apply`/`inject`)及契约类型:SidebarRoot、行组件和树派生均属于内部实现(slot 注册通过闭包引用它们;测试直接导入 src 路径)。 +`/client` 导出表层只包含插件主体(`apply`/`inject`)及契约类型;SidebarRoot、行组件和树派生仍由 slot 注册封装在包内。 ## 模型体验 diff --git a/packages/client/ui-workspace/README.i18n.yaml b/packages/client/ui-workspace/README.i18n.yaml index 1f3645727c..108c3e8127 100644 --- a/packages/client/ui-workspace/README.i18n.yaml +++ b/packages/client/ui-workspace/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-workspace/README.md -README.md: 7f17eb841368b5c04df0a8192716e2c9a3c6896a -README.zh.md: 574ac5b74e233d9366681991ede89a37c6fae79c +README.md: 3107793233d18c65b12a9a91a5be85d22acc733a +README.zh.md: 98912ac079a13fa62d5829d7d2469ff63c3da5bb diff --git a/packages/client/ui-workspace/README.md b/packages/client/ui-workspace/README.md index 7f17eb8413..3107793233 100644 --- a/packages/client/ui-workspace/README.md +++ b/packages/client/ui-workspace/README.md @@ -31,4 +31,4 @@ None; this package neither assembles nor sends a provider request. - **No fuzzy content search or event deep links** — the content backend uses literal token/phrase matching, and selecting a result opens the Session rather than the matching event. - **No Session deletion or unarchive control** — sessions can be archived, but archived sessions have no viewing or unarchive surface, and Workspace registration deletion does not delete Sessions. - **Pending user interaction is not aggregated into collapsed groups** — a waiting row inside a collapsed group lights no group-header indicator and becomes visible only after that group is expanded. -- **Native folder selection depends on the local Host carrier** — under the `-native` composition, fixture-only or remote browser deployments cannot open a local operating-system dialog; platform failures are shown in a retryable modal. Remote-capable picking is the `-browse` composition's in-app flow. +- **Native folder selection depends on the local Host carrier** — under the `-native` composition, in-process or remote browser deployments cannot open a local operating-system dialog; platform failures are shown in a retryable modal. Remote-capable picking is the `-browse` composition's in-app flow. diff --git a/packages/client/ui-workspace/README.zh.md b/packages/client/ui-workspace/README.zh.md index 574ac5b74e..98912ac079 100644 --- a/packages/client/ui-workspace/README.zh.md +++ b/packages/client/ui-workspace/README.zh.md @@ -31,4 +31,4 @@ Session 行渲染运行时的实时 `pendingInteraction` 分类:审批显示** - **没有模糊内容搜索或事件深链接**:内容后端采用字面 token/短语匹配,选择结果会打开 Session,而不是匹配的事件。 - **没有 Session 删除与取消归档控件**:会话可以归档,但已归档会话没有查看或取消归档入口;删除 Workspace 注册记录不会删除 Session。 - **待处理的用户交互不会聚合到折叠的分组上**:折叠分组内正在等待的行不会点亮分组头指示,只有展开该分组后才可见。 -- **原生文件夹选择依赖本地 Host 载体**:在 `-native` 组合下,仅使用 fixture(测试前置数据)的部署或远程浏览器部署无法打开本地操作系统对话框;模态框会显示平台故障,并允许重试。可远程的选取是 `-browse` 组合的应用内流程。 +- **原生文件夹选择依赖本地 Host 载体**:在 `-native` 组合下,进程内部署或远程浏览器部署无法打开本地操作系统对话框;模态框会显示平台故障,并允许重试。可远程的选取是 `-browse` 组合的应用内流程。 diff --git a/packages/client/web/README.i18n.yaml b/packages/client/web/README.i18n.yaml index 1e0c8b649b..9b652b3110 100644 --- a/packages/client/web/README.i18n.yaml +++ b/packages/client/web/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/web/README.md -README.md: 8425cb1b33db50a8b7e3667323b6b65bfb43ad32 -README.zh.md: 656cdcb1f2fc1edf0d8387ee9745b04476923797 +README.md: 74c481d573fb716e624e639c74b35c82e6894f63 +README.zh.md: 08c69665a00377ff4bb11eee90031a45b3901753 diff --git a/packages/client/web/README.md b/packages/client/web/README.md index 8425cb1b33..74c481d573 100644 --- a/packages/client/web/README.md +++ b/packages/client/web/README.md @@ -8,7 +8,7 @@ Shell self-sufficiency (web2 hard rule): the kernel value-imports no plugin pack `PLATFORM_MODULES` (src/platform.ts) is the single source of truth for the shared module surface: seed-table keys, tsdown client externals, and the vite alias set are its projections. -The optional `seams` parameter forwards the module system's `loadBundle` transport override (`BootSeams`); production callers omit it — it exists for test environments where external `