Commit Graph
8 Commits
Author SHA1 Message Date
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 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
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
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 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 ca2207e26c Fix doc cross-links for the hierarchy; add package-path + shape gates
Merge brought in the RFC-classification reorg and two new doc gates;
rewrite every drifted packages/<name> cross-link (Markdown link targets,
moved-README relative depths, and .ts comment paths) to the grouped paths.

Add two doc-sync/hygiene gates so the manual checks this restructure
needed become automated:
- verify-package-paths.ts: flags a packages/<path> reference (in Markdown
  or a .ts comment/string) that does not resolve AND names a real package
  in a segment — i.e. a stale path to a MOVED package. A path naming a
  non-existent package (a forward-looking proposal) is left alone, so it
  applies uniformly across proposed/implemented/rejected.
- check-workspace-constraints: assert the packages/<group>/<pkg> depth-2
  shape (group dirs carry no package.json; no flat or over-nested
  packages). Group names stay open; only the shape is fixed.
2026-06-20 23:12:14 +08:00
Tianyi Cui 0f7abc9808 docs(core-data-structures): catalog the core data structures
A new docs/core-data-structures/ folder: a self-contained core.md defining what
counts as a "core" data structure (the agent-loop spine) and covering the spine
vocabulary, plus per-seam sub-pages (llm-streaming, session, persistence, tools,
bash). Type definitions are pasted verbatim via `ts type-equiv` blocks and
drift-checked by verify-type-equiv. Cross-linked from architecture.md; the
`ts type-equiv` mechanics are documented in development.md.
2026-06-20 16:24:56 +08:00