Commit Graph

378 Commits

Author SHA1 Message Date
Tianyi Cui
aa36b3b36b feat(doc-standards): documentation tiers, budgets, and the ceiling gate
Standing docs accrete a paragraph per PR with nothing pushing back; the
root AGENTS.md reached 8,130 words in 50 commits with the same rule
stated two and three times. This encodes the counter-pressure:

- docs/AGENTS.md becomes the documentation standard: the tier taxonomy
  (one home per fact), target word budgets, and the slop checklist.
- verify-doc-budgets joins doc-sync: word ceilings for the six
  accretion-prone standing docs, manifest-driven, frozen at current
  sizes and ratcheted down as each doc is brought to target.
- .agents/skills/dsh-doc-standards: the thin placement/audit/red-gate
  workflow over the standard, mirroring the dsh-translate-docs split.
- RFC (implemented/process) records the decision, alternatives, and
  the first audit cycle's deferred work list.

The gate's first catch was the standard itself (1,057 > 1,000); it
ships condensed to 984 words rather than with a raised ceiling.
2026-07-04 13:53:43 +08:00
Tianyi Cui
1546c6060a Merge branch 'worktree-hooks-h-retro' into worktree-hooks-i-snapshots 2026-07-04 03:31:31 +08:00
Tianyi Cui
81c27bed36 Merge branch 'worktree-hooks-f-bridges' into worktree-hooks-g-stream-chunk
# Conflicts:
#	docs/rfc/README.md
2026-07-04 03:21:00 +08:00
Tianyi Cui
38cd1a150c Merge branch 'worktree-hooks-e-protocol' into worktree-hooks-f-bridges
# Conflicts:
#	examples/acp-agent/cordis.snapshot.yml
#	examples/acp-agent/cordis.yml
2026-07-04 03:09:37 +08:00
Tianyi Cui
7d69d759f6 Merge branch 'worktree-hooks-d-subagent' into worktree-hooks-e-protocol
# Conflicts:
#	docs/module-graph.md
#	pnpm-lock.yaml
2026-07-04 03:01:22 +08:00
Tianyi Cui
92be2724bd Merge branch 'worktree-hooks-c-interception' into worktree-hooks-d-subagent 2026-07-04 02:48:41 +08:00
Tianyi Cui
9033837081 Merge branch 'worktree-hooks-b-bash-seam' into worktree-hooks-c-interception
# Conflicts:
#	docs/architecture.md
#	docs/cordis-catalog/events-and-services.md
#	docs/core-data-structures/tools.md
#	packages/core/tools/README.md
#	packages/core/tools/src/index.ts
2026-07-04 02:44:26 +08:00
Tianyi Cui
89f3c484c1 Merge branch 'worktree-hooks-a-taxonomy' into worktree-hooks-b-bash-seam
# Conflicts:
#	docs/rfc/README.md
2026-07-04 02:26:19 +08:00
Tianyi Cui
c5ed5ff490 Merge remote-tracking branch 'origin/master' into worktree-hooks-a-taxonomy
# Conflicts:
#	docs/rfc/README.md
2026-07-04 02:15:52 +08:00
Tianyi Cui
ecd5ca6ce6 Merge branch 'master' into codex/fs-directory-listing 2026-07-04 01:27:42 +08:00
Tianyi Cui
571f5b383a Merge branch 'master' into docs/update-readme-for-sdk 2026-07-04 01:20:27 +08:00
Tianyi Cui
51640362b5 docs: rename coding demo to repl 2026-07-04 01:07:26 +08:00
Tianyi Cui
bf1966326e Merge remote-tracking branch 'origin/master' into codex/fs-directory-listing
# Conflicts:
#	docs/rfc/README.md
#	packages/fs/fs-local/src/index.ts
2026-07-04 00:56:17 +08:00
Tianyi Cui
74f61c89e3 test(hooks): snapshot the CC + Codex hook matrix end-to-end
Load both hook bridges in the ACP example (dsh-hooks-claude → ./hooks.json,
dsh-hooks-codex → ./codex-hooks.json) so the full-transcript snapshot tier can
exercise each dialect against the real app. An absent config file is a silent
no-op, so a scenario carries only the file it needs and the other bridge
vanishes — verified byte-identical against every pre-existing snapshot.

Add a scenario per hook point × its headline Decision outcome, both dialects:
UserPromptSubmit block (authored, keyless) + context-fold, PreToolUse deny/ask,
PostToolUse block/context, Stop force-continue. The mid-turn scenarios are
recorded against the real API with the hook active, so the model's reaction to
a denied/blocked/force-continued turn is part of the replayed transcript.

SessionStart and SubagentStart are deliberately excluded (detached best-effort
inject races the log position — a recorded golden fails 10/10 on its own
replay), as is SubagentStop (observe-only, zero transcript footprint — a golden
could never be proven to fail). Both stay on the bridges' unit coverage. See
docs/rfc/implemented/testing/2026-07-04-hook-snapshot-matrix.md.
2026-07-04 00:40:35 +08:00
Tianyi Cui
fb78a844af docs(tools): fix cross-module JSDoc links and the catalog source list
Codex review of the vocabulary relocation found two doc-accuracy issues:

- presentation.ts's JSDoc used {@link ToolDefinition...}, which the
  TypeScript language service cannot resolve because presentation.ts
  deliberately does not import index.ts (that would create the cycle the
  split avoids). Demote those three to plain `ToolDefinition` code text;
  same-file and imported @links (TerminalResultView, ContentBlock) stay.
- docs/core-data-structures/tools.md's source header listed only index.ts
  and schema.ts; add presentation.ts, which now owns the presentation
  vocabulary the page documents.
2026-07-04 00:39:09 +08:00
Tianyi Cui
539051b2c9 refactor(tools): move render-intent vocabulary to presentation.ts
The tool render-intent vocabulary (ToolCallView/ToolResultView + members,
FileLocation, FileDiff, ToolCallKind) is the UI-facing surface of
dsh-tools; it lived inline in index.ts alongside the registry and
execution core. Move it to its own presentation.ts module so index.ts is
the registry + execute waterfall and the presentation vocabulary is a
separate, one-directional dependency.

presentation.ts owns ONLY render-intent types and references none of the
execution types; index.ts imports the view types for ToolDefinition's
presentCall/presentResult signatures (clean acyclic index -> presentation).
The opaque `meta` presentation channel (ToolExecuteReturn, ToolResult,
ToolExecutionResult) is execution plumbing and stays in index.ts.

Public surface unchanged: index.ts re-exports the vocabulary, so consumers
(tool-fs/tool-bash/tool-web/tool-todo, the ACP bridge) keep importing from
@deepseek-ai/dsh-tools with zero churn. No producer/bridge/test edits; a
pure internal relocation with no observable-output change (snapshot goldens
untouched).
2026-07-04 00:20:06 +08:00
Tianyi Cui
19db3aa28a Merge branch 'master' into docs/i18n-translate-skill 2026-07-03 23:50:53 +08:00
Tianyi Cui
497ea15bdf fix(acp): relativize the completed diff card title
The result-time diff card sent view.title raw, so a completed edit/write
of an absolute in-workspace path flipped the card header back from the
relativized `Edit src/b.ts` to the absolute path — the pending card
relativizes, the result did not, and tool_call_update.title replaces the
header. Apply displayTitle to the result diff arm using the diff path,
mirroring the call-side card. Regression test proven red on the unfixed
arm.

Also record the overwrite diff-basis pre-read as a bounded follow-up
(TODO(overwrite-diff-bound) + RFC non-goal): overwriting a large file
reads the whole prior text into memory for a UI-only diff.
2026-07-03 23:14:22 +08:00
Tianyi Cui
9c6e09c0d8 Merge remote-tracking branch 'origin/master' into fs-acp-applied-hunk-diffs 2026-07-03 22:57:09 +08:00
Ziya
ec05295a0c docs: equal-authority pairing with sidecar consistency records
Redesign per review: neither language is canonical. A pair is three
sibling files — foo.md, foo.zh.md, foo.i18n.yaml — and either language
may be authored first (a Chinese-first RFC is as legitimate as an
English-first one). The sidecar record holds the FULL git blob hash of
both sides as of the last confirmed-consistent state, replacing the
in-file one-directional fingerprint; editing either side without
re-confirming the pair goes red. New --write mode re-records a pair
after both sides are brought in line, making the confirmation a
reviewable yaml diff. Pairs merge whole (completeness enforced).

- gate rewritten around pair anchors (union of .zh.md and .i18n.yaml
  remnants) so half-deleted pairs are caught from either side; red/green
  proven for en-only edit, zh-only edit, missing record, and a record
  for an excluded file
- verify-rfc-classification now skips .zh.md counterparts (same RFC,
  indexed via its English filename; the pairing gate owns consistency)
- docs/i18n/README.md + translation-rules.md reframed bidirectionally
  (terminology table binds both directions; typography section governs
  the Chinese side); zh counterparts updated; skill workflow updated
- RFC amended to the shipped design, records the English-canonical
  in-file-fingerprint model as considered-and-revised; RFC translated
  (docs/rfc/.../2026-07-02-bilingual-docs-and-pairing-gate.zh.md) and
  added to the required frontier
- generated docs stay excluded with the follow-up recorded: teach the
  generators to emit Chinese, then de-list
2026-07-03 07:41:24 -07:00
Tianyi Cui
53ebf6fc54 Merge remote-tracking branch 'origin/master' into fs-acp-render-intent-union 2026-07-03 22:41:10 +08:00
Tianyi Cui
e07886599c docs: finish the whole-file-diff sweep across comments and RFCs
Codex's stale-prose pass found seven more spots still describing the
result diff as ALWAYS an applied contextual hunk, or a create/binary
overwrite as rendering "only the call-time card": the DiffCallView JSDoc
and the acp bridge diff-arm comment, the FsWriteOutcome.before and
readTextForDiff JSDoc, and three RFC lines. All now say: the result diff
is the applied change — a contextual hunk when there is a before-image,
else a whole-file diff (create / undiffable binary) — and a successful
mutation always returns the result diff so the model-facing text can't
clobber it. Regenerate the cordis catalog (source line shift).
2026-07-03 21:50:52 +08:00
Tianyi Cui
da1d7f281d docs(tools): DiffResultView.diffs may be a whole-file diff, not only hunks
The write-diff-card fix made write's presentResult return an args-derived
whole-file diff (oldText:null) for a create / unchanged overwrite, but the
DiffResultView contract and its mirrored docs still said `diffs` is ALWAYS
the applied contextual hunks computed from before/after. Correct the type
JSDoc, the write execute-side comment, and the four mirrored surfaces
(tools.md, tools README, acp-feature-support, adding-a-tool cookbook) to
say: typically the applied hunks, or a whole-file diff when there is no
before-image (a create) — and that a mutation returns the diff result even
when it duplicates the call-time card, since a tool_call_update.content
replace would otherwise clobber the diff with the model-facing text.
Regenerate the cordis catalog (source line shift).
2026-07-03 21:26:50 +08:00
Tianyi Cui
53b215c646 fix(tool-fs): write always renders a diff card on the completed update
A Write CREATE rendered its completed tool_call_update as the model-facing
result TEXT (`<path>…</path>…Created file`), which — because an ACP
tool_call_update.content REPLACES the call's content — clobbered the
new-file diff the pending call installed. So Zed showed the diff, then
replaced it with raw XML-ish text; only overwrite/edit looked right
(their result re-sends a diff).

write's presentResult now ALWAYS returns a diff card for a successful
write: the applied contextual hunk from `meta` when there is one
(overwrite), else an args-derived whole-file diff (`oldText: null`) for a
create or an unchanged-content overwrite. This matches claude-agent-acp,
where the create diff rides on the update and no result text replaces it.
An error still falls through to generic rendering so its message shows.
edit is unchanged (it always has a hunk; no whole-file fallback).

Re-recorded fs-write / fs-write-overwrite goldens; the create's completed
update is now a {type:'diff'} block, not the XML result text.
2026-07-03 20:52:36 +08:00
Tianyi Cui
f0ae253670 Merge branch 'master' into codex/fs-directory-listing 2026-07-03 20:49:10 +08:00
Tianyi Cui
f86e0bedec docs(tools): sync ToolExecutionResult.meta comment to unknown
The pasted type-equiv block's JSDoc still said the meta payload is
`{@link JsonValue}`; the source comment is `unknown` (the meta channel is
opaque at the seam). verify-type-equiv compares type structure, not the
comment, so the drift slipped through — align the doc comment.
2026-07-03 19:05:46 +08:00
Tianyi Cui
dee2dee402 fix(tool-fs): CRLF-safe write diff, opaque meta, doc sync
Address the applied-hunk-diffs review:

- CRLF write overwrite emitted bogus every-line-changed hunks: write's
  `before` was LF-normalized but `after` kept the raw model content, so a
  CRLF rewrite of an LF file diffed every line. Normalize write's `after`
  to LF so both sides share the diff basis (edit already did). Regression
  test proves it fails on the raw-after path.
- The tool-private `meta` payload is now typed `unknown` (opaque) at every
  seam instead of `JsonValue`. This drops the `dsh-tools -> dsh-session`
  package edge that existed only to name the type, and removes the
  `FileDiff` index signature that had been widening the type solely for
  JsonValue-assignability. Serializability is still enforced at runtime by
  `Session.append`'s isJsonValue check, which was always the real guard.
- Sync the docs the new result/meta surface left stale: ToolResultView's
  diff card + ToolExecutionResult.meta in tools.md/session.md type-equiv
  blocks, the acp/tools READMEs, and the adding-a-tool cookbook; regenerate
  the cordis catalog and module graph.
2026-07-03 18:00:16 +08:00
Ziya
bb10931478 docs: apply translation-review findings across the four zh files
A four-way parallel review of every EN<->ZH pair (one reviewer per pair,
findings verified before applying) surfaced:

- terminology: stale-sense 过期 -> 陈旧 in development.zh.md (the table
  reserves 过期 for expired); GitHub workflow -> GitHub 工作流 per the
  table; dropped the invented 质量 in 'CI gates' -> CI 门禁; taxonomy ->
  分类体系 in translation-rules.zh.md; skill first-occurrence annotation
  added in the i18n README
- verbatim spans: <current-blob> restored inside an inline code span the
  translation had localized
- typography: full-width dashes normalized to no surrounding spaces
  across all four files (the rules' own MUST); one 顿号 between clauses
  -> comma; 顿号 before 以及 dropped
- fidelity/wording: must-not rendered 不得 (not 不应); local setup ->
  本地环境搭建; enforce -> 强制执行; verified surface -> 受验证的范围;
  batch-lands-before-neighbors nuance restored; 更新粘贴内容
2026-07-03 02:53:08 -07:00
Ziya
0282778126 docs: one more stale-sense 过期 -> 陈旧 in the i18n README translation 2026-07-03 02:33:42 -07:00
Ziya
f508ff2bf5 docs: freshness/stale renderings per MDN HTTP-caching zh precedent
freshness -> 新鲜度 confirmed against MDN's zh HTTP caching docs (freshness
lifetime -> 新鲜度生命周期); precedent now cited in the table. The same
source pairs stale with 陈旧, not 过期 (过期 maps to expired), so the stale
entry and the i18n README translation now say 陈旧译文.
2026-07-03 02:32:54 -07:00
Ziya
50a32cdcb1 docs: extend terminology table with i18n mechanism terms
Every new term the bilingual-docs work introduced, with precedented
renderings where precedent exists:

- orphan -> 孤立 (git's official zh l10n renders orphan as 孤立, e.g.
  孤立分支 — not 孤儿; the translations were corrected to match)
- info string -> 信息字符串 (CommonMark zh convention; corrected in the
  i18n README translation)
- fenced code block -> 围栏代码块 (MDN zh), staged -> 暂存 (git zh),
  event-sourced -> 事件溯源 (DDD convention), smoke test -> 冒烟测试,
  fail-fast -> 快速失败, plus fingerprint/pairing/freshness/stale/contract
- mechanism names coined by this repo, marked as such in the notes:
  language switcher -> 语言切换行, structural signature -> 结构签名,
  enforcement frontier -> 强制边界
- keep-English entries so future translators don't guess: backlog,
  blob hash, CI, doc-sync, e2e, monorepo, PR, worktree
2026-07-03 02:24:17 -07:00
Tianyi Cui
d8fd3225af feat(tool-fs): result-time applied-hunk diffs for write/edit
fs write/edit now emit a result-time contextual-diff tool_call_update
(the applied hunk with ±3 context lines, one hunk per replace_all site),
matching what claude-agent-acp sends and what makes an editor render the
change in place. The call-time snippet diff stays; the result hunk
supersedes it (ACP content-replace).

Mechanism:
- A persisted tool-private `meta` channel: execute may return
  `{ content, meta }`; `meta` (JsonValue) rides on the tool/result event
  and is handed back to presentResult, so the diff reproduces on replay
  (event-sourced). JsonValue is now exported from dsh-session.
- The backend returns raw before/after text (storage facts) on
  FsWriteOutcome/FsEditOutcome; the tool computes the hunk via the npm
  `diff` package's structuredPatch. A create has no before → no result
  diff; a failed/aborted mutation carries no meta.
- ToolResultView gains a DiffResultView; the bridge's result-side switch
  renders it as {type:'diff'} content blocks.

RFC: docs/rfc/implemented/architecture/2026-07-02-result-time-applied-hunk-diffs.md
(justifies the npm `diff` runtime dep over vendoring and the meta channel);
the render-intent-union RFC's Non-goal is updated to record this shipped.
All fs snapshot goldens re-recorded; edit/overwrite gain the contextual
result diff, create/read/policy-reject unchanged in structure.
2026-07-03 17:12:00 +08:00
Ziya
a899226397 docs: harden pairing gate per review — structural signature, not counts
Review findings addressed:

- The gate compared only heading and code-block COUNTS, understating the
  contract it claims to enforce. It now compares ordered structural
  signatures: heading depths, fenced code blocks verbatim (info string +
  content), table column counts, list kinds, and every link target except
  the language switcher. Proven red on a heading demotion, a reworded
  code-block comment, and a retargeted link; green on all existing pairs.
- Stated the gate's limit explicitly (header comment + docs/i18n/README.md
  both languages): green means fresh and structurally sound, NOT verified —
  translation quality is the reviewer's half of the contract.
- first-line extraction no longer silently drops the last character of a
  newline-less file (split with limit instead of indexOf slice).
- isExcluded documents the trailing-slash-is-the-boundary invariant.
- Rollout guidance: grow the required frontier at the pace translation
  review is resourced.
- dsh-code-review's doc-sync sublist is now the exhaustive chain.

docs/i18n/README.zh.md updated via the minimal-diff workflow and
re-fingerprinted.
2026-07-03 02:06:57 -07:00
Dudu-0223
005db3c610 Merge remote-tracking branch 'origin/master' into web-capability-seam
# Conflicts:
#	docs/architecture.md
#	docs/module-graph.md
#	docs/rfc/README.md
#	packages/README.md
#	tsconfig.build.json
#	tsconfig.json
2026-07-03 16:49:00 +08:00
Ziya
cd9d559805 docs: translate development.md to Chinese
First backlog item translated with the dsh-translate-docs skill: full-file
translation, terminology per docs/i18n/terminology.md, structure locked to
the source (11 headings, 10 byte-identical code blocks), fingerprinted and
added to the manifest's required list.
2026-07-03 01:27:34 -07:00
Dudu-0223
7441307251 fix(web): open WebError.code to string, aligning with other seams
The closed WebErrorCode union leaked fetch-transport details (redirect,
too-large, content-type) into the seam's shared vocabulary and made web
the only seam with a closed error-code union. Drop it and let WebError
carry an open code: string like LlmError/SubagentError; document the
codes grouped by owner (seam-neutral vs dsh-web-fetch-local transport).

Addresses tianyicui's leaky-abstraction review comment on WebErrorCode.
2026-07-03 15:56:47 +08:00
Yichen Jiang
6fc2cee837 fix(fs): translate listDir metadata failures 2026-07-03 15:12:40 +08:00
Yichen Jiang
803ed4bd95 feat(fs): add directory listing seam 2026-07-03 14:36:20 +08:00
Ziya
4d89bb3e74 docs: bilingual docs contract, translation skill, and pairing gate
Establish EN->ZH bilingual documentation for the README and docs tree:

- docs/i18n/README.md — the pairing contract: sibling foo.md <-> foo.zh.md,
  English canonical, blob-hash source fingerprints, language switchers,
  scope/exclusions, and a manifest-driven rollout ratchet.
- docs/i18n/translation-rules.md — how to translate: faithfulness, structure
  preservation, terminology discipline over docs/i18n/terminology.md, and
  typography rules grounded in MDN/K8s/Vue/clreq conventions.
- .agents/skills/dsh-translate-docs — the committed agent workflow, following
  the dsh-code-review pattern of deferring to docs as sources of truth.
- scripts/verify-translation-pairing.ts + manifest — a doc-sync gate: required
  pairs exist; every existing .zh.md is fresh (fingerprint = current source
  blob), switcher-linked, structure-matched, and non-orphaned; excluded
  (generated) docs stay unpaired. --list prints the translation work list.
- RFC (implemented/process) recording the decision and the alternatives.
- Dogfood: README.zh.md and the two i18n docs translated under their own rules.

Gates: doc-sync green including the new gate; red/green proven for stale
fingerprint, orphan, and excluded-file violations.
2026-07-02 23:12:25 -07:00
Tianyi Cui
1a57d67058 refactor(tools): tagged render-intent union for tool-call presentation
Replace the "bag of optional fields" tool-presentation types
(ToolCallPresentation / ToolResultPresentation / ToolTerminal) with a
card-tagged discriminated union — the standing FIXME(tool-presentation).
A tool declares one render intent per call/result and the ACP bridge
switches on `card`:

  ToolCallView   = generic | terminal | diff
  ToolResultView = generic | terminal

The `diff` card is new: fs write/edit now emit an ACP {type:'diff'}
content block (an editor's inline diff), which the old shapes could not
express. The bridge also relativizes a file card's title against the
session cwd (mirroring claude-agent-acp's toDisplayPath) while keeping
locations/diff paths raw, and derives the no-capability fenced console
fallback from a terminal result's output. read gains the window-in-title
(`Read foo.txt (5 - 8)`) and an always-set location line, matching the
reference adapter field-for-field.

Migrates all three producer families (tool-fs, tool-bash, tool-todo) and
the sole consumer (the ACP bridge) together — the source does not compile
piecewise. Adds snapshot coverage for the terminal _meta path (a new
capability-advertising scenario) and re-records the fs goldens to show the
diff cards. Applied-hunk (result-time, context-line) diffs need a new
result/event shape and are a follow-up.

RFC: docs/rfc/implemented/architecture/2026-07-02-tool-render-intent-union.md
2026-07-03 02:04:03 +08:00
Tianyi Cui
b84d4828a8 refactor(events): remove the agent/stream-chunk mirror of assistant/chunk
The loop recorded every model token delta as a durable `assistant/chunk`
session event AND emitted an identical live `agent/stream-chunk` Cordis event
one line later. Same StreamChunk, same turn/step; the emit added only the live
Agent handle, which the sole consumer discarded. This is the boundary-mirror
duplication the event-domain work removed for turn/step boundaries, applied to
the token stream — a follow-up the boundary RFC explicitly deferred.

The premise is settled: chunk persistence is authoritative (the proposal to
stop persisting chunks was rejected — replay/snapshots depend on it), so
`assistant/chunk` on `session/event` is the load-bearing token stream and
`agent/stream-chunk` is pure redundancy.

- Remove the `agent/stream-chunk` declaration + emit; drop the now-unused
  StreamChunk import from dsh-agent's types.
- Migrate `dsh-ui-stdio` (the only live consumer; ACP already reads
  assistant/chunk off session/event) to render assistant/chunk in its existing
  session/event listener. Consolidating to one listener also makes the
  inReasoning dim-SGR flag deterministic across chunk/boundary events (they no
  longer race across two listeners).
- Repoint the agent-loop tests (cancel/loop) and ui-stdio tests to the
  session/event assistant/chunk feed.
- New RFC (implemented/simplification/2026-07-02-remove-stream-chunk-mirror);
  amend the boundary RFC's retained-list entry to cross-link; update
  architecture, cookbook, event-domain-semantics, the ACP proposal, and the
  regenerated cordis catalog.

Snapshot goldens unchanged (ACP never used the mirror), confirming no
editor-facing transcript change.
2026-07-02 23:42:16 +08:00
Tianyi Cui
2a66b7c4e0 docs(hooks): correct fold description + drop history-narrating test comments
Codex convergence findings on the delegate-and-fold fix (code path verified
correct, prose only):

- The hook-bridges RFC claimed a downstream `block` "carries the bridge context
  too" for BOTH seams. True for `tools/post-execute` (PostToolDecision.block has
  an additionalContext field) but false for `agent/prompt-submit`
  (PromptDecision.block is `{kind,reason}` with no context field). The code is
  already correct — a blocked prompt drops the context, which is right since the
  prompt never reaches the model. Reworded the RFC to state the per-seam
  difference accurately.
- Two test comments narrated "Before the fix…", which the current-state-only
  doc rule forbids. Reworded to describe the behavior, not its history.
- Documented on concatContext (both bridges) why the merged block carries a
  single source: a HookContext holds one MessageSource and the seam cannot
  represent mixed provenance; rendering distinguishes only by source.kind, so a
  downstream plugin's text stays framed as plugin context.
2026-07-02 20:07:10 +08:00
Tianyi Cui
a334395f0c feat(coding-agent): wire the filesystem tools into the demo
Load dsh-fs-local + dsh-fs-policy + dsh-tool-fs after tool-todo (mirroring the
acp-agent wiring), and steer the system prompt to prefer read/write/edit for
file ops with bash for shell/tests/search. Update the welcome line and the
FIXME(config-comments) bash note.

Doc sweep now that both demos ship the fs tools and the seam resolves per-session
cwd: architecture.md and the event-gate RFC no longer say the demos do file ops
through bash / that no config wires the tools; the coding-agent + examples
READMEs and the AGENTS.md layout blurb list the fs tools; the acp-agent README
drops the launch-dir caveat (per-session cwd now works, so the server can launch
anywhere).

(stdio-agent is single-session, so fs-local's cwd = process.cwd() is the
workspace. Keyless boot smoke is blocked locally by an unrelated inotify
watcher-limit ENOSPC that also hits demo:echo; the config parses and the same fs
stack boots green in the acp-agent snapshot tier.)
2026-07-02 19:46:59 +08:00
Tianyi Cui
bd7fb31ae3 feat(tool-fs): editor-facing presentation for read/write/edit
The fs tools rendered as generic cards (title = tool name, raw file content) in
an ACP editor. Give them tool-owned presentation like bash/subagent have:

- read → title "Read <path>", kind read, offset/limit as rawInput
- write → title "Write <path>", kind edit
- edit → title "Edit <path>", kind edit, a clipped old→new rawInput summary

Add a provider-neutral `locations: { path, line? }[]` to ToolCallPresentation —
the files a call reads/modifies — so a capable editor can follow along / jump to
the file (read carries its offset as the line). The ACP bridge forwards it onto
the wire `tool_call` (ResolvedCallPresentation + call() + the tool_call build in
streamSessionEventUpdate). This flips the `locations` cell in the ACP feature
matrix to supported. The SDK already carries `tool_call.locations`
(ToolCallLocation `{ path, line? }`), so no ACP types leak into dsh-tools.

presentResult is intentionally omitted: it only receives `{ content, isError }`,
not the write/edit outcome, so titling by create-vs-overwrite or replacement
count would mean parsing the model-facing text — the static title stays.

Tests: pure presentCall assertions for all three tools incl. locations and the
edit rawInput clip; a bridge test drives the REAL fs tools through ToolPresenter
and asserts locations reaches the wire tool_call (proven to fail without the
forwarding line). New withFs harness option + dsh-fs devDeps on dsh-acp.
2026-07-02 19:36:17 +08:00
Tianyi Cui
4fc0d4833c Merge branch 'worktree-hooks-e-protocol' into worktree-hooks-f-bridges 2026-07-02 19:01:13 +08:00
Tianyi Cui
d72ceffd04 Merge branch 'worktree-hooks-d-subagent' into worktree-hooks-e-protocol 2026-07-02 18:43:40 +08:00
Tianyi Cui
8b701605b3 Merge branch 'worktree-hooks-c-interception' into worktree-hooks-d-subagent 2026-07-02 18:42:52 +08:00
Tianyi Cui
3422f8fa5c Merge branch 'worktree-hooks-b-bash-seam' into worktree-hooks-c-interception
# Conflicts:
#	docs/cordis-catalog/events-and-services.md
#	packages/core/agent/src/types.ts
2026-07-02 18:41:55 +08:00
Tianyi Cui
ef24ebf97c Merge branch 'worktree-hooks-a-taxonomy' into worktree-hooks-b-bash-seam 2026-07-02 18:36:38 +08:00
Tianyi Cui
9bc4df28c1 fix(hooks): delegate context-only hooks + default CLAUDE_PROJECT_DIR
Address review on the hook-bridges PR — two composability/compatibility bugs
in both the CC and Codex bridges:

1. A hook that only attaches additionalContext (no block/deny) returned
   `allow`/`accept` WITHOUT calling next(), short-circuiting every later
   agent/prompt-submit / tools/post-execute listener. A policy/sandbox plugin
   registered after the bridge never saw the prompt. Now the context-only path
   delegates via next() and folds its context onto the downstream decision
   (concatContext): a downstream block/deny still wins and carries the bridge
   context; a downstream allow/accept keeps its own content rewrite and gains
   the context. Only a real hook deny/block short-circuits.

2. CLAUDE_PROJECT_DIR was empty in the default ACP wiring (no projectDir
   configured), breaking common unmodified hooks that reference
   $CLAUDE_PROJECT_DIR. It now defaults per-run to the agent's session
   workspace (the same cwd the hook runs in); an explicit config.projectDir
   still wins.

Regression tests per bridge: a later listener blocks a prompt a context-only
hook allowed; both contexts survive when the downstream also adds one; the
default CLAUDE_PROJECT_DIR reaches the hook. Each proven red on the pre-fix
code.
2026-07-02 18:35:53 +08:00