Commit Graph

260 Commits

Author SHA1 Message Date
Yichen Jiang
5870bc2e1a Merge origin/master into skill system branch
Use the new filesystem seam for skill file reads and system skill writes when ctx.fs is available, and include the skill tool in the generated tool catalog.
2026-07-03 12:26:58 +08:00
Yichen Jiang
077a5fe382 Merge remote-tracking branch 'origin/master' into codex/ask-user-question
# Conflicts:
#	packages/README.md
#	packages/ui/acp/README.md
#	pnpm-lock.yaml
2026-07-03 11:57:07 +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
6c88b380ea docs(tool-catalog): register dsh-tool-fs in the boot manifest
Master's new tool-schema catalog boots every tool-* package and hard-errors if
one is absent from the manifest. Add the dsh-tool-fs entry (boot dsh-fs-local to
satisfy the injected `fs`, harvest read/write/edit), note that dsh-fs-policy adds
the read-before-write/edit gate without changing schemas, and regenerate
docs/tool-catalog/tools.md. Update the collectToolCatalog test's expected tool
set to include the fs tools.
2026-07-02 09:30:04 +08:00
Tianyi Cui
eda2983b00 fix(docs): address Codex review — document the subagent_fork alias, harden dispose
Round-1 Codex review findings on the tool-schema catalog:

(A) The shipped coding-agent / acp-agent configs load dsh-tool-subagent twice —
as `subagent` (spawn backend) and `subagent_fork` (fork backend) — so the model
sees a `subagent_fork` tool the catalog never mentioned, while the intro claimed
to list "the exact name the model receives". The registered name is the plugin's
load-time `toolName` config, not a package fact, so rather than bake an
example-app config into a packages-scoped generator, add a per-package deployment
`note`: the subagent entry now records the `subagent_fork` alias and points at the
leaf configs. Intro and RFC scope reworded to state the unit is the package (at
its default config), with aliases noted — no longer overclaiming. A test asserts
the note names `subagent_fork`, covering the config-driven-name path.

(B) collectToolCatalog only disposed the context on the success path; a throw from
mount/schemas() after earlier plugins mounted would leak the fiber. Move
`ctx.fiber.dispose()` into a `finally` per the repo's dispose-to-quiescence rule.
2026-07-02 03:16:17 +08:00
Tianyi Cui
df0e7bd5f2 feat(docs): generate a tool-schema catalog by booting the tool plugins
Add docs/tool-catalog/tools.md, a generated reference of every model-facing
tool a shipped `packages/*/tool-*` plugin contributes (name, description,
JSON-Schema parameters) — the third generated catalog alongside the cordis
events/services and core-data-structures catalogs.

Unlike the cordis catalog (a pure source-AST pass), this generator BOOTS each
tool plugin on a real cordis Context and reads `ctx.tools.schemas()`, because a
tool schema is not statically knowable: `todo_write` builds its enum with a
runtime spread, descriptions are string-concatenated, `subagent`'s name is
config-driven, and MCP tools register raw JSON Schema without `defineTool`. A
completeness guard globs the on-disk `tool-*` packages and fails if any is
absent from the boot manifest, restoring the "nothing silently omitted"
property booting would otherwise lose. `verify-tool-catalog` runs inside
`doc-sync`, so the artifact cannot drift.

The boot-over-AST decision and the discovered-inventory / hand-written-recipe
split are recorded in a process RFC.
2026-07-02 02:20:24 +08:00
Tianyi Cui
3712f67bc6 fix(events): address review — core.md turn-only taxonomy, RFC mechanism names, post-execute content snapshot
- core-data-structures/core.md: the `agent/*` taxonomy said "turn/step
  boundaries", but the step-boundary mirror emits were dropped — `agent/*`
  mirrors only turn boundaries; step boundaries are durable `step/start`/
  `step/end` session events. Narrow the catalog so plugin authors aren't pointed
  at nonexistent `agent/*` step events.
- interception-seams RFC: replace stack-position phrasing ("a later stack PR",
  "the stack's first change", "the PR that makes...") with durable mechanism/RFC
  names (the hook bridge packages, the event-domain-semantics RFC).
- tools/post-execute snapshot: `dispatched.content` was the same array reference
  as `result.content`, so a listener's in-place `push`/`splice` leaked into the
  returned content while a reassignment was masked — the "protect from tampering"
  comment over-claimed. Copy content into a fresh array so the snapshot guards
  the array structure; comment now states it is not deep immutability. Regression
  extended to push a block in-place and assert it does not leak (proven red
  without the copy).
2026-07-01 15:39:08 +08:00
Tianyi Cui
483e0e5edf fix(events): address Codex review — protect post-execute result, purge stale tools/execute refs
Codex's PR-C review found two (A) blockers:

- tools/post-execute could corrupt the protected outcome. postExecute passed the
  mutable `result` to listeners and then read result.callId / spread result on the
  return paths, so a listener mutating the reference (flipping isError, rewriting
  callId, injecting an error) escaped the decision channel. Now the authoritative
  callId/isError/error are SNAPSHOT before the waterfall and the return value is
  rebuilt from the snapshot + the typed PostToolDecision — the decision is the only
  sanctioned way to change the outcome, and callId is always exec.callId. Added a
  regression test that mutates the result reference and asserts it has no effect;
  proven to fail red on the unfixed code.

- Public docs/JSDoc still advertised the removed `tools/execute` waterfall after the
  split. Swept every current-state reference to tools/pre-execute + tools/post-execute:
  the ToolRegistry class JSDoc (and the regenerated catalog), loop.ts's ASCII flow
  (also added the prompt-submit/session-start steps it was missing), the package-map
  READMEs (packages, core, agent-core), core-data-structures core.md/tools.md, the
  bash + acp + invariants src/READMEs (the deferred permission gate is the
  tools/pre-execute deny/ask seam now), the cookbook, and the implemented RFCs whose
  factual seam catalog drifted. codec.ts's totality prose now lists `rejected`.
  Proposed-RFC references are left as-is (frozen proposals, validated when built).
2026-06-30 20:25:30 +08:00
Tianyi Cui
dc95a7881d feat(events): interception seams — the typed-Decision surface for hooks
Reshape the agent's interception surface so every seam returns a small, typed
Decision union, and the set covers the hook points a CC/Codex bridge (and a
native plugin) needs. "Native hooks" are not a package — a native hook is just a
cordis plugin on these canonical events; the bridges (a later PR) only translate
an external protocol onto the same surface.

dsh-agent:
- NEW agent/session-start(agent, source) emit (once before turn 1; SessionStartSource
  startup|resume|clear|compact) — a pure notification, seeds context via inject().
- NEW agent/prompt-submit waterfall → PromptDecision (allow, optionally rewriting the
  prompt or attaching additionalContext, or block).
- RESHAPE agent/turn-continuation boolean → ContinuationDecision ({action:'stop'} |
  {action:'continue', reason?}; a continue reason is recorded as next-step steering).
- New HookContext envelope (required source — inject() would mislabel a missing one).

dsh-tools: split the single tools/execute waterfall into tools/pre-execute
(PreToolDecision allow/deny/ask gate) and tools/post-execute (PostToolDecision
accept/block, optionally replacing content or attaching additionalContext). Core
dispatch sits between as plain code; the tool body keeps its inner try/catch so a
thrown tool still reaches post-execute as an isError. ToolExecutionResult gains
additionalContext (ferried to the loop's per-step buffer). Input rewrite is
deliberately NOT offered (a proposed RFC designs it consistently).

dsh-session: new `rejected` TurnEndReason — a turn whose whole prompt batch was
blocked by prompt-submit.

agent-loop firing points: session-start emitted at create (source threaded —
startup for create/fork, resume for resume()); prompt-submit per drained message
with the always-open-turn rule (a fully-blocked batch is a zero-step rejected
turn); the continuation reshape; post-tool additionalContext buffered and appended
after all tool/results (adjacency). ACP codec maps rejected→cancelled.

A worked native-plugin example (interception.spec.ts) proves all four seams compose
end-to-end through the real loop with NO hook/* events (those belong to the bridge
lib). All existing tools/execute + turn-continuation tests migrated. The
tool-subagent abort test now aborts after a microtask so it still exercises the
live onAbort bridge (execute() awaits pre-execute before the body runs).

RFCs: implemented/feature/2026-06-30-interception-seams.md (the reshape) +
proposed/feature/2026-06-30-pre-tool-input-rewrite.md (the deferred rewrite design).
2026-06-30 17:11:18 +08:00
Tianyi Cui
d02e9f1bd6 Reorganize packages into a modular hierarchy
Move the 18 flat packages/<name> packages into role-grouped dirs:
core/, llm/, bash/, session-persistence/, ui/, support/. Group dirs are
pure containers; each package keeps its @deepseek-ai/dsh-* name.

Collapse the per-package tsconfig paths maps (base + typecheck) into one
@deepseek-ai/dsh-* wildcard with a candidate per group, and derive the
publint list from the hierarchy. Update all depth-coupled globs/configs
(workspace, tsdown, vitest, eslint, knip, tsconfig includes/refs,
per-package tsconfigs, generators, doc-script scopes, type-equiv manifest)
and the cross-package/script relative imports in tests.

Fix doc-typecheck's workspacePaths() to parse tsconfig JSONC via the
TypeScript API instead of a regex comment-strip, which corrupted the
new wildcard `/*/` path candidates.

WIP: doc cross-links and package/RFC docs still to update.
2026-06-20 22:55:20 +08:00