Commit Graph

415 Commits

Author SHA1 Message Date
Tianyi Cui 0efd65b10b Merge pull request #93 from deepseek-ai/codex/pr36-ts-specifier-declarations
Use explicit ts specifiers for NodeNext-safe declarations
2026-06-22 11:24:17 +08:00
imccyu fa9438bf16 docs: update rewriteRelativeImportExtensions to current rfc 2026-06-22 09:03:28 +08:00
imccyu 6731793859 revert: use rewriteRelativeImportExtensions for NextNode .d.ts resolve 2026-06-22 09:02:58 +08:00
Tianyi Cui 07f4047ff0 Use explicit ts specifiers for declarations
Restore explicit .ts relative specifiers in source and enable rewriteRelativeImportExtensions so emitted JS uses .js while declarations keep explicit .ts specifiers. Add a NodeNext declaration-consumer gate to prevent extensionless declaration regressions.
2026-06-22 06:11:00 +08:00
imccyu 94e7355449 docs: update packages hierarchy to current rfc 2026-06-22 01:38:44 +08:00
imccyu 732e121ff6 fix: make constraints, lint and md-links happy 2026-06-22 01:38:44 +08:00
imccyu 88b75181ad fix: apply ts-build-config adjustment to new packages 2026-06-22 01:38:39 +08:00
imccyu 74cdd9a2e5 Merge remote-tracking branch 'origin/master' into feat/adr0016-type-build-check 2026-06-22 00:35:51 +08:00
imccyu fd55d20548 revert: remove the non-branch changes introduced during the rebase 2026-06-21 23:57:00 +08:00
Tianyi Cui 6b4dc48fbd Merge pull request #89 from deepseek-ai/worktree-lessons-learned
docs(agents): stacked-PR orchestration + sharper real-entry-path lessons
2026-06-21 20:38:31 +08:00
Tianyi Cui a20e3b70b9 Merge remote-tracking branch 'origin/master' into worktree-lessons-learned 2026-06-21 20:37:26 +08:00
Tianyi Cui 5d3e7dd590 Merge pull request #88 from deepseek-ai/worktree-simplify-extract-examples
refactor(examples): extract the app spine into dsh-agent-core + app packages
2026-06-21 20:37:01 +08:00
Tianyi Cui 0b75598c91 Merge remote-tracking branch 'origin/master' into worktree-simplify-extract-examples 2026-06-21 20:36:05 +08:00
Tianyi Cui 7373941493 Merge pull request #87 from deepseek-ai/worktree-simplify-snapshot-goldens
test(snapshot): use session.jsonl as the only session-log artifact
2026-06-21 20:35:41 +08:00
Tianyi Cui c85713d11e Merge remote-tracking branch 'origin/master' into worktree-simplify-snapshot-goldens 2026-06-21 20:34:55 +08:00
Tianyi Cui e770e22486 Merge pull request #86 from deepseek-ai/worktree-simplify-trace-events
simplify(session): fold trace-only usage/error events into load-bearing events
2026-06-21 20:34:41 +08:00
Tianyi Cui 5d7af3be82 Merge remote-tracking branch 'origin/master' into worktree-simplify-trace-events 2026-06-21 20:33:53 +08:00
Tianyi Cui 9f13dd02f6 Merge pull request #85 from deepseek-ai/worktree-simplify-agent-stop
simplify(agent): drop unused public Agent.abort(), keep whenIdle()
2026-06-21 20:33:27 +08:00
Tianyi Cui 4ead67c06d Merge remote-tracking branch 'origin/master' into worktree-simplify-agent-stop 2026-06-21 20:32:21 +08:00
Tianyi Cui 8f50008d8b Merge pull request #84 from deepseek-ai/worktree-simplify-branded-ids
feat(types): brand the bash ids and stop brand erosion
2026-06-21 20:32:05 +08:00
Tianyi Cui 74344a8bd4 Merge remote-tracking branch 'origin/master' into worktree-simplify-branded-ids 2026-06-21 20:30:41 +08:00
Tianyi Cui 2d4c367e51 Merge pull request #83 from deepseek-ai/worktree-simplify-prune-seam
simplify(seams): prune dead methods from the persistence seam
2026-06-21 20:30:14 +08:00
Tianyi Cui fa5cf0afd7 Merge remote-tracking branch 'origin/master' into worktree-simplify-prune-seam 2026-06-21 20:27:19 +08:00
Tianyi Cui 89d9a6679b Merge pull request #82 from deepseek-ai/worktree-simplify-llm-surface
simplify(llm): drop unconsumed adapter-change event and assembled call surfaces
2026-06-21 20:27:04 +08:00
Tianyi Cui df4f8a4dea Merge remote-tracking branch 'origin/worktree-simplify-extract-examples' into worktree-lessons-learned 2026-06-21 20:22:47 +08:00
Tianyi Cui 6cb8f35adb Merge remote-tracking branch 'origin/worktree-simplify-snapshot-goldens' into worktree-simplify-extract-examples
# Conflicts:
#	AGENTS.md
2026-06-21 20:21:37 +08:00
Tianyi Cui 56a2351e08 Merge remote-tracking branch 'origin/worktree-simplify-trace-events' into worktree-simplify-snapshot-goldens 2026-06-21 20:19:16 +08:00
Tianyi Cui fd76791c6b Merge remote-tracking branch 'origin/worktree-simplify-agent-stop' into worktree-simplify-trace-events 2026-06-21 20:18:15 +08:00
Tianyi Cui a6a5892e8a Merge remote-tracking branch 'origin/worktree-simplify-branded-ids' into worktree-simplify-agent-stop
# Conflicts:
#	docs/rfc/README.md
2026-06-21 20:17:29 +08:00
Tianyi Cui 4661905714 Merge remote-tracking branch 'origin/worktree-simplify-prune-seam' into worktree-simplify-branded-ids
# Conflicts:
#	docs/rfc/implemented/simplification/2026-06-20-prune-dead-seam-methods.md
2026-06-21 20:15:21 +08:00
Tianyi Cui 5c0597e9de Merge branch 'worktree-simplify-extract-examples' into worktree-lessons-learned 2026-06-21 19:48:37 +08:00
Tianyi Cui 4d7726ecd3 fix review findings: don't treat disabled entries as load failures; scope the verify-package-paths lib skip to a real package root
assertEntriesLoaded() flagged ANY fiber-less entry as a failed import, but a
`disabled: true` entry settles without a fiber by design (Entry.refresh() skips
init() when disabled) — a valid "plugin off" config, not a broken import. Both
app bins now filter `fiber === undefined && !entry.disabled`. The stdio built-bin
smoke gains a disabled-(unresolvable)-entry config that must still boot.

The verify-package-paths lib-skip was unconditional and ran before the
moved-package check, so a stale group-less `packages/acp-agent/lib/bin.js` (the
exact drift this gate catches) was silently ignored just for containing `lib`.
Scope the skip: only exempt `lib` when it is the segment after an EXISTING
`packages/<group>/<pkg>` root, so a real-but-unbuilt `lib/bin.js` is still exempt
while a stale package path flags.
2026-06-21 19:47:22 +08:00
Tianyi Cui 64976df216 Merge branch 'worktree-simplify-extract-examples' into worktree-lessons-learned 2026-06-21 18:38:56 +08:00
Tianyi Cui 9f5e2b352c Merge branch 'worktree-simplify-snapshot-goldens' into worktree-simplify-extract-examples 2026-06-21 18:38:50 +08:00
Tianyi Cui ebe8c8e21f Merge branch 'worktree-simplify-trace-events' into worktree-simplify-snapshot-goldens 2026-06-21 18:38:40 +08:00
Tianyi Cui f09a88ecee Merge branch 'worktree-simplify-agent-stop' into worktree-simplify-trace-events 2026-06-21 18:38:29 +08:00
Tianyi Cui 2903f96548 fix review findings: RFC says session.jsonl is required for every snapshot scenario
The required-fixture-guard description still said session.jsonl was needed only
for model scenarios, but the harness passes <dir>/session.jsonl to llm-replay
unconditionally, so loadReplayScript() fails for a no-model scenario without it.
The code already requires it for all scenarios; align the RFC prose.
2026-06-21 18:36:13 +08:00
Tianyi Cui d0e1b02a7f fix review findings: correct whenIdle live-consumer claim in the stop-surface RFC
The retained-whenIdle paragraph claimed "live consumers (the ACP bridge's settle
points)", but `packages/ui/acp/src` has no whenIdle() call — the bridge owns its
agents and tears them down via AgentHandle.dispose(). whenIdle()'s live consumers
are ACP and agent TESTS awaiting settlement through the public seam. State that.
2026-06-21 18:36:04 +08:00
Tianyi Cui b7d018580e fix review findings: skip lib/ build-output refs in verify-package-paths; resolve acp built-bin npm deps from the declaring package
verify-package-paths flagged the new built-bin smokes' `lib/bin.js` citations
as stale-source drift, failing CI: doc-sync runs BEFORE build, so the build
output is absent at lint time. The gate targets moved SOURCE paths, so skip any
reference whose target goes through a `lib/` segment — mirroring how the file
scan already excludes `lib/`.

The acp built-bin smoke resolved `zod`/`@agentclientprotocol/sdk` via
`import.meta.resolve` from the test file's own context, but `acp-agent` does not
declare them — `dsh-acp` does. Under pnpm's strict layout they are not exposed
where the test resolves, so the new CI built-bin step failed with
"Cannot find package 'zod'". Resolve each from the `ui/acp` package URL (the one
that declares it) instead.
2026-06-21 18:35:46 +08:00
Tianyi Cui a96b1e3ae7 docs(agents): capture stacked-PR orchestration + sharper real-entry-path lessons
Three hard-won lessons from running a 7-PR simplification stack through two
waves of review feedback:

- New "## Orchestrating review feedback across a stacked PR chain" section: one
  worktree per branch; a fix belongs on the PR that introduced the issue then
  flows DOWN; review-fixes are separate commits never amends; delegated work is
  trust-but-verify (prove a regression guard FAILS on unfixed code); reply
  in-thread on the merits.
- "## Conventions" gains a "Never rewrite a pushed branch" rule next to the
  merge-commit rule: update a child by merging the parent down, never
  rebase/amend/force-push a pushed branch; a fix lands on its originating PR.
- Extended the "Line coverage is not behavior coverage" defensive-patterns
  bullet with two corollaries this stack re-taught: (1) a real-load-path test
  only GUARDS the export shape if a broken shape actually FAILS it — an
  inject-less composition plugin boots fine on a stray `export default`, so it
  needs an explicit no-default + unwrapExports assertion; (2) "real entry path"
  means the PUBLISHED artifact (built lib/bin.js under plain node), not the dev
  runtime (tsx), which masks boot settle-races, module-resolution differences,
  and a load failure that loader.await()'s Promise.allSettled swallows.
2026-06-21 17:17:00 +08:00
Tianyi Cui e63641bcfd Merge branch 'worktree-simplify-snapshot-goldens' into worktree-simplify-extract-examples 2026-06-21 15:48:33 +08:00
Tianyi Cui e04ca55374 fix review findings: require session.jsonl for every snapshot scenario
The per-kind fixture guard claimed no-model scenarios need no session.jsonl, but
the replay path requires one for ALL scenarios: runScenario() passes
`fixtureFile: <dir>/session.jsonl` unconditionally and llm-replay's
loadReplayScript() throws "fixture not found" when it is absent and no override
replaces it. A no-model scenario ships a header-only session.jsonl that derives
to an empty script. The guard + its comment now match that reality, so a future
no-model scenario following them won't fail at subprocess startup.
2026-06-21 15:46:38 +08:00
Tianyi Cui 3567808171 fix review findings: harden the app bins + built-bin smokes, arch-exception doc, snapshot fixture-guard
BLOCKER — the published lib/bin.js (stdio + acp) was exercised only via tsx
(demo:* / the src/bin.ts smokes); the built artifact under plain `node` was
unguarded. Root-cause on the BUILT bin:
  1. Settle race: boot() returned once loader.create() registered the include
     ENTRY, but the include loads its child plugins asynchronously — so boot()
     (and main()) resolved while the app plugins (stdin reader, agent loop, ACP
     bridge) were still mounting. A CLI with no attached handles yet exits 0
     silently, and a load error surfaces as an unhandled rejection AFTER boot.
     Fix: `await ctx.loader.await()` after create() — settle the whole tree.
  2. Config-path robustness: hand the include the config's ABSOLUTE file:// URL
     so resolution never depends on ctx.baseUrl / can never fall back to cwd.
Both bins fixed identically. NOTE: the cordis Loader resolves a config's bare
plugin specifiers via its internal module loader, active only under
`node --expose-internals`; the bin cannot add a node flag itself, so this is
documented in the bin JSDoc + both package READMEs (the demos already comply).
The repo `examples/*/cordis.yml` are tsx-only artifacts (workspace plugins
resolve through the tsconfig paths map, not node_modules), so they are not a
valid plain-node bin target — the smokes use a real-install-shaped temp dir.

Fail loud on a load failure: boot() previously exited 0 SILENTLY when a config
path's directory does not exist — the include plugin fails to IMPORT, the cordis
Loader catches+LOGS it and leaves the entry with no fiber (no rejection), and
`loader.await()` does not rethrow (EntryTree.await uses Promise.allSettled). Fix:
boot() now calls assertEntriesLoaded(ctx) after the tree settles and throws on
any entry with no fiber, so a typo'd config dir exits non-zero with a clear
message. main() also installs an unhandledRejection guard (installFailLoud) that
replaces Node's stack dump with a single labelled stderr line for the
companion case (a missing config FILE in a real dir, whose include-init throw
surfaces as a rejection Node already exits non-zero on). Regression tests added
to both built-bin smokes (missing dir + missing file → non-zero exit + stderr);
verified the missing-dir test fails on the pre-fix bin.

Built-bin smokes (the reviewer's ask): packages/ui/{stdio,acp}-agent/tests/
built-bin.e2e.ts run the REAL lib/bin.js under `node` (NOT tsx) in a temp
consumer dir, asserting the stdio echo round-trip / the acp initialize response
+ stdout purity, plus the fail-loud cases above. They build-gate (skip if lib/
absent) and run in a new ci.yml step after the build.

Issue 2 — packages/README.md + docs/architecture.md said "plugins depend on
interfaces, never on the concrete loop", but dsh-agent-core imports the concrete
dsh-agent-loop. Scope the rule to EXTENSION plugins and carve out the sanctioned
COMPOSITION/bundle exception (dsh-agent-core composes the concrete spine); note
it in the implemented RFC too.

Issue 3 — examples/acp-agent/tests/acp.snapshot.ts fixture-guard claimed
no-model scenarios need no session.jsonl, but runScenario() always boots
llm-replay with the session.jsonl path and loadReplayScript() throws when it is
absent. Require session.jsonl for ALL scenarios (no-model ones ship a
header-only fixture) and rewrite the comment to match reality.
2026-06-21 15:39:24 +08:00
Tianyi Cui 9e86fd995c fix(ci): repoint the demo smoke test at the dsh-stdio-agent bin
The CI "Demo smoke test" step booted the deleted examples/echo-agent/start.ts
(removed when the boot glue moved into the dsh-stdio-agent bin), so CI failed on
node 24 + 26 while the local gates and e2e passed. Invoke `pnpm run demo:echo`
instead of hardcoding the boot path — that routes through the canonical demo
script, so the smoke can never drift from it again. The output assertions and
the .sessions/_no-cwd/main-session-*.jsonl artifact check are unchanged
(verified the new bin produces identical output).
2026-06-21 14:23:19 +08:00
Tianyi Cui bfc5a9097d fix review findings: soften the last stdout-purity overclaim in the acp-agent module doc
The acp-agent README/RFC overclaim was already softened; this module doc comment
still said the footgun is "structurally unreachable from the leaf". A leaf
cordis.yml CAN still add a sibling @cordisjs/plugin-logger-console — the app does
not prevent it. Reword to the accurate claim: the app gives the default front
door no logger entry to misconfigure, and the "never add a stdout logger to an
ACP leaf" rule still stands.
2026-06-21 13:08:11 +08:00
Tianyi Cui 66e56bd395 fix review findings: stale coding-agent README, export-shape guards, overclaim wording
Codex review of PR #88 found three issues in the example-app extraction:

A1 — examples/coding-agent/README.md's plugin table still listed the OLD
direct-wired leaf entries (agent-loop, session-persistence, src/stdio-chat.ts —
the whole src/ dir is gone). Rewrite it to the four real leaf entries the
current cordis.yml loads (hmr, llm-deepseek, bash, stdio-agent), noting that
tool-bash/persistence/agent/loop now live inside the agent-core + stdio-agent
bundles.

A2 — the three new app/spine packages (agent-core, stdio-agent, acp-agent)
export NO `inject`, so a stray `export default apply` would let unwrapExports
collapse the module and silently DROP name/Config WITHOUT crashing — the
real-load-path smokes would stay green. agent-core is never Loader-unwrapped at
all. Add an explicit export-shape guard per package: assert no `default` export
and that the real Loader.unwrapExports leaves name/Config/apply intact. Verified
each fails when `export default apply` is added.

B — soften "structurally unreachable / cannot wire a stdout logger" overclaims
in the acp-agent/agent-core READMEs and the implemented RFC: a leaf CAN still add
a sibling logger entry; the accurate claim is the app omits one so the default
leaf has nothing to get wrong. Keep the safety directive (never add a stdout
logger to an ACP leaf).
2026-06-21 12:49:59 +08:00
Tianyi Cui 47afcb297f Merge PR6 (snapshot golden removal + PR5 trace-events) into PR7 2026-06-21 12:10:49 +08:00
Tianyi Cui e2bde2902c refactor(examples): extract the app spine into dsh-agent-core + app packages
Implements docs/rfc/.../2026-06-20-extract-example-app-packages.md. Each
example was thick — a hand-rolled start.ts, an infra preamble, nested
base.yml/base-core.yml/acp-tail.yml includes, and a coupled front-door
cluster enforced only by prose. This moves the composition into packages so
each example is a thin leaf cordis.yml: pick the swappable backends, load one
app package.

New packages:
  - @deepseek-ai/dsh-agent-core (packages/core/agent-core): one bundle plugin
    that loads the providerless/executor-less/UI-less spine (timer + llm +
    sessions + system-prompt + tools + agents + invariants + tool-bash +
    agent-loop) via ctx.plugin(...) inside apply(), and forwards agent-loop's
    `agents` list as its own Config (export const Config = AgentLoop.Config,
    default []).
  - @deepseek-ai/dsh-stdio-agent (packages/ui/stdio-agent): terminal chat APP —
    agent-core + console logger + readline UI + a pre-created `main` agent, with
    a bin. The demo:echo/coding front door.
  - @deepseek-ai/dsh-acp-agent (packages/ui/acp-agent): ACP server APP —
    agent-core + JSONL persistence + the acp bridge, NO stdout logger, with a
    bin. The stdout-purity footgun is structurally unreachable from the leaf.

Amendment to the RFC: hmr stays a LEAF cordis.yml entry, not baked into
dsh-stdio-agent. hmr is a Loader-only dev plugin (throws without
--expose-internals; the in-process test tier can't even import its decorator
form), so a package statically importing it could never carry the per-file
coverage gate. Unlike the console logger, a stray hmr is not a stdout-purity
footgun, so leaving it at the leaf costs no safety. With hmr out, all three new
packages carry in-process unit specs at 100%.

Boot glue (Loader tail, .env load, snapshot-mode selection, stdin-dispose
lifecycle) moves into each app's bin; start.ts and base.yml/base-core.yml/
acp-tail.yml are deleted. Each app package gets a keyless real-load-path test
that boots through its bin + the cordis Loader (guarding the unwrapExports
export-shape bug class, postmortem 0001). ACP snapshot replay stays green
against the existing committed goldens (pure boot restructuring). RFC moved
proposed->implemented with the amendment recorded; package/example/architecture
docs and the module graph updated.
2026-06-21 12:06:38 +08:00
Tianyi Cui ed2e230f20 Merge PR5 doc-sync fixes (b4471e5) into PR6 2026-06-21 11:34:46 +08:00
Tianyi Cui 2eb6ad3260 fix review findings: sync stale v1/removed-event doc references
Codex's re-confirmation pass verified both blocker fixes correct but found
doc/comment drift the fix commit missed:

- session/index.ts + session/README.md: "minimal v1 header" → "minimal header
  (stamped with the current SESSION_FORMAT_VERSION)" — the version is 0, not 1.
- session/index.ts deriveMessages comment listed "usage, and errors" as trace
  data — those standalone events no longer exist; only boundaries + chunks are.
- session-persistence RFC: "no v1 migration" → the pinned-v0 pre-release stance.
- collapse-trace-only RFC format-version note: reframed off the "bump the
  version and reject" wording (which now reads as the OTHER AGENTS.md stance)
  onto the pinned-0 unstable stance the session log actually uses.
- agent-loop/loop.ts finishError JSDoc: "with a logged `error` event" → the
  failure is recorded on turn/end.reason (no standalone error event).
- acp/acp-feature-support.md (two spots): usage is recorded on assistant/message
  now, not as standalone internal usage events.
- Regenerate the cordis catalog (finishError JSDoc line shift).
2026-06-21 11:33:41 +08:00