Commit Graph
36 Commits
Author SHA1 Message Date
Turtle 8af3babaea docs: rescan rebased documentation hierarchy 2026-08-05 16:16:57 +08:00
Turtle b37c22bd0a Merge origin/master: combine boot-owned settlement with the fail-loud release
Conflicts: apps/cli/src/tui.ts (keep the release install over master's comment
rewording), packages/ui/app-boot/README* (master's new installFailLoud row
wording plus this branch's release and timeout rows).
2026-08-03 14:10:23 +08:00
Tianyi Cui c96f48e176 fix(review): tolerate mid-teardown personal-watcher registration
CI caught a startup race the PTY smoke stages naturally: a TUI /exit typed
while watchPersonalPatches is still opening its watcher disposes the tree,
and the HMR effect registration rejects with INACTIVE_EFFECT, crashing an
app that exited exactly as asked. Return a no-op disposer for that exact
code; every other registration failure still propagates (covered both ways).
2026-08-02 01:29:04 +08:00
Tianyi Cui 2448496803 feat: configure repository plugins from DSH home 2026-08-02 01:29:04 +08:00
Tianyi Cui fd4d369907 feat(cordis): add repository package cache 2026-08-02 01:25:02 +08:00
Tianyi Cui f941ba1b5f fix(review): label host-preparation failures and gate vendored lockfile links
ds-review-bot round 1: boot() now throws `host preparation failed` when
prepare() rejects before any config-tree entry mounts (the plugin-tree label
overstated), and the new hygiene gate verify-vendored-links pins the
linkWorkspacePackages fix — every vendored package name in pnpm-lock.yaml
must resolve to a workspace link with no registry copy alongside.
2026-08-02 01:25:01 +08:00
Tianyi Cui 2fe205f01a fix(cordis): clear omitted include patches 2026-08-02 01:25:01 +08:00
Tianyi Cui 195f7fa9af fix(cordis): make config reload transactional 2026-08-02 01:25:01 +08:00
Turtle cb1a6c74dc Merge remote-tracking branch 'origin/master' into fix/checkout-workdir-prompt
# Conflicts:
#	packages/ui/app-boot/README.i18n.yaml
#	packages/ui/app-boot/README.md
#	packages/ui/app-boot/README.zh.md
2026-08-01 00:04:49 +08:00
Turtle b35b06396d fix(app-boot): keep the fail-loud exit fatal while the terminal is released
Review of the previous commit found two defects in the release path, both
reproduced against the implementation:

- The timeout guarding a never-settling release was unref'ed. An
  unhandledRejection listener suppresses Node's default fatal exit, so with
  nothing else referenced the process reached an empty event loop and exited
  0 on the very failure it was reporting. Keep the timer referenced and clear
  it once the race settles.

- The handler uninstalled itself before awaiting the release. A second
  concurrent rejection then became uncaught and killed the process
  mid-teardown, stranding exactly the terminal state this restores. Replace
  the uninstall with a latch: the first rejection is the reported one, and
  later rejections (teardown's own included) fall through to the pending exit.

Add the PTY regression the fake-process tests cannot express: boot the shipped
tree over a fixture whose llm-pi-ai providers value is list-shaped, expect exit
1, and assert the captured bytes carry both the diagnostic and ESC[?2004l.
Against the pre-fix source the stream ends at ESC[?2004h ESC[>7u ESC[?u ESC[c
with no reset and the case fails, so it pins the actual bug.

Split the two-shape formatting test into one install per case; a latched
handler reports once by design.
2026-07-31 20:37:16 +08:00
Turtle 70f37206d2 fix(app-boot): release the terminal before a fatal load exit
A dsh launch whose config failed validation returned the user to a broken
shell: typing was invisible and the next command was mangled by a stray
Device Attributes reply (1;2;4cecho ...).

The Loader mounts entries concurrently, so ui-tui can already hold the
terminal (raw mode, bracketed paste, keyboard protocol, plus an in-flight
DA query) when a sibling entry rejects on its own config. installFailLoud
wrote its diagnostic and exited immediately, so nothing disposed the tree
and ProcessTerminal.stop() never ran.

Give installFailLoud an optional release teardown, awaited between the
diagnostic and the exit and bounded by FAIL_LOUD_RELEASE_TIMEOUT_MS. The
TUI launcher passes one that disposes the root context, reaching the same
shutdown() the /exit path already uses (drainInput() + ui.stop()). The
context is captured in boot()'s prepare hook because the rejection arrives
while boot() is still in flight.

Bins that pass no release keep the previous behavior exactly.
2026-07-31 20:37:16 +08:00
Turtle ef201014d0 Merge remote-tracking branch 'origin/master' into codex/dsh-home-path
# Conflicts:
#	packages/ui/app-boot/README.i18n.yaml
#	packages/ui/app-boot/README.md
#	packages/ui/app-boot/README.zh.md
#	packages/ui/app-boot/src/index.ts
2026-07-31 17:40:48 +08:00
Turtle 885f1ed1ac refactor(config): centralize DSH home paths 2026-07-31 16:49:55 +08:00
Turtle ce0aa90c1e feat(cli): dsh --dump-config / --dump-default-config print the composed tree
dsh --dump-config and dsh web --dump-config compose the shipped base,
the surface overlay, and the --config or personal overlay — exactly the
layers that surface boots — and print the entry list as YAML without
booting; --dump-default-config stops at the surface overlay so the two
outputs diff to precisely the user layer's effect.

The dump shares the mounting code: the vendored include exports its
patch algorithm as applyEntryPatches() and its !!js dialect as
entryListSchema (logged in vendor/README.md), dsh-app-boot's
renderConfigDump() composes and renders through both (and now imports
the dialect instead of duplicating it), and the CLI adds a thin
dump-config mode. !!js expressions print verbatim; unmatched patches
warn on stderr; boot-only flags are rejected alongside the dump flags.

(cherry picked from commit 1fdbebfa8a5dc7df840d53666320064a7e3dae59)
2026-07-31 16:20:54 +08:00
imccyu 05d73d6571 fix(app-boot): await only failed activation fibers 2026-07-31 03:20:20 +08:00
imccyu c92a6c3b76 fix(app-boot): preserve published loader composition 2026-07-31 03:20:20 +08:00
imccyu a691e2ab16 fix: preserve plugin activation errors 2026-07-31 03:20:20 +08:00
imccyu 296cb1c35f fix: app-boot report plugin error detail 2026-07-31 03:02:45 +08:00
Yichen Jiang 5251a13a25 fix(app-boot): survive a surface that exits before the tree settles
boot() asserts over ctx.loader after awaiting the Loader, but the TUI renders
as soon as its own fiber starts: an /exit typed before the last entry settles
runs disposeRootAndExit, which takes the Loader service with the tree. The
assertions then read undefined and crashed the process with a TypeError over
an app that exited exactly as asked. The keyless personal-overlay PTY smoke
lost this race in roughly two of three runs.
2026-07-30 22:09:03 +08:00
Turtle bcb049bef3 fix(prompt): distinguish checkout from workdir 2026-07-30 15:06:28 +08:00
Turtle a51143bded fix(cli): close shared config review gaps 2026-07-30 14:56:39 +08:00
Turtle cd100a81f1 fix(cli): harden launcher lifecycle 2026-07-30 11:14:18 +08:00
Turtle 9970f60969 test: cover overlays and refresh translation snapshot 2026-07-29 21:15:48 +08:00
Turtle f290a8b851 refactor(cli)!: one shared base config with per-surface overlays
`dsh` shipped two config trees that were 43 rows the same: apps/cli/cordis.yml
composed web as 74 flat rows, while the TUI booted examples/tui-agent/cordis.yml
whose single `@deepseek-ai/dsh-tui-demo` row mounted twelve plugins behind a
twenty-key pass-through Config. Neither file was what its location claimed —
apps/cli hardcoded the "example" as the product default and the "demo" bundle
was the application — and every capability change had to be made twice.

- apps/cli/base.cordis.yml holds the 43 shared rows; tui.cordis.yml and
  web.cordis.yml are patch lists stating only what differs per surface
- overlays apply as SIBLING patch lists at one include level, because include
  patches never cross an include boundary. Precedence: base < surface <
  (--config | personal ~/.dsh/config.yaml) < launcher flag/profile patches
- `--config` now applies an overlay INSTEAD OF the personal one, so a demo or
  test tree never inherits the user's route; new `--config-replace` boots a file
  as the entire tree (the old `--config` behaviour). Both survive /resume
- vendor/include: index each `insert`ed row as it is added so a later patch can
  configure or disable it. Upstream built the id index once before the patch
  loop, leaving every surface-only row — the whole TUI front door — silently
  unpatchable from user config. Logged as local modification 8
- session identity moves to dsh-agent-loop's CONFIGURED_AGENT_IDENTITIES_KEY;
  dsh-tui's MAIN_SESSION_ID_KEY is deleted (only the bundle read it)
- delete examples/tui-agent, examples/cordis-agent, packages/examples/tui-demo;
  TUI tests → apps/cli/tests, cordis e2e → packages/cordis/tool-cordis/tests,
  examples/code-mode survives as an overlay leaf
- `dsh web` gains --config, threaded into AppCLIEntry as an extra overlay

Three latent defects surfaced and are fixed here: the TUI captured the optional
sessionQuery service once at construction and could permanently disable /resume
when it won the mount race; the session-store root silently reverted to a
project-local ./.sessions; --config-replace was dropped by the resume handoff.

Verified by booting each tree through the real Loader (TUI 55 entries, web 75,
zero unsettled) rather than reading YAML. All eight terminal snapshots replay
byte-identically; 14/14 PTY smoke, 112/112 snapshots, 25/25 doc-sync, hygiene
and lint clean.
2026-07-29 21:15:42 +08:00
Turtle 6cd139a25b Merge branch 'master' into worktree/dsh-arg-parser
Integrate the Commander argument adapter with master's safe session-resume
feature and dsh web --dev flag.

- args.ts: add --dev to the web parser.
- tui.ts: keep master's process.execve in-place resume handoff, but take the
  adapter's parsed (config, resume); inject the resume id through boot's
  prepare(ctx) hook via ctx.provide(RESUME_SESSION_ID_KEY, id) instead of the
  RESUME_SESSION_ID env var; rebuild the re-exec argv as `dsh --resume <id>`.
- app-boot: drop master's replaceResumeArg (no longer needed) alongside the
  already-removed parseResumeArg; add RESUME_SESSION_ID_KEY.
- the four tui-agent/cordis configs read the ctx-provided resumeSessionId via a
  typeof-guarded !!js expression, so resume needs no env var.
- web.ts: keep master's client roster and --dev watch, take parsed host/port/dev.
2026-07-25 12:04:37 +08:00
Turtle 800bafda3b refactor(cli): parse dsh argv through one Commander adapter
Replace the dsh CLI's three hand-rolled parsing idioms (raw argv[0]/includes
dispatch in bin.ts, per-mode node:util parseArgs in headless.ts/web.ts, and the
bespoke parseResumeArg scanner in dsh-app-boot) with a single Commander adapter
in apps/cli/src/args.ts. parseDshArgs resolves argv into a discriminated
DshInvocation union; bin.ts switches on the mode and dynamic-imports the chosen
module, which now consumes already-parsed values.

- web is a real subcommand; --host uses choices and --port an argParser range
  check, moving validation into the parser.
- --resume rejects empty and repeated forms; --prompt rejects empty; a config
  positional after --prompt and a root flag placed before web fail loud.
- adds --help/--version; removes parseResumeArg from dsh-app-boot.
- new apps/cli/tests/args.spec.ts (apps/*/tests added to vitest include,
  apps/cli/tests to tsconfig.host.json); the tui-agent keyless PTY smoke covers
  bin.ts dispatch end to end unchanged.
2026-07-24 19:43:59 +08:00
NI0317 2ae9f4fdf3 feat(tui): add safe session resume flow 2026-07-24 01:29:35 -07:00
Turtle 55af920def fix(vendor/include): keep config reloads resilient 2026-07-22 17:50:45 +08:00
Tianyi Cui b72410b152 chore: extract vendored Cordis hot-reload fix 2026-07-22 17:30:45 +08:00
Turtle 7b46e1f73c fix: address PR #504 review warnings
- workspace-context: a transiently unavailable but still-effective candidate
  keeps its cached trimmed digest in the directory's dedup slot, so an
  identical later sibling is not emitted as a duplicate set until the next
  successful reconciliation
- app-boot: --resume rejects a following token that is itself resume syntax
  instead of accepting it as a session id
- tui: the queued-steering badge tracks per-entry sources and a drain removes
  one matching entry, so loop-authored steering (no agent/queued) cannot
  consume a pending user message's slot
2026-07-22 16:49:50 +08:00
Turtle 2464d6169f feat(app-boot): move personal config to the Harness home (~/.dsh)
Squashes feat/personal-config-dsh-home: personal config.yaml and .env move
from ~/.config/dsh to the Harness home (~/.dsh), plus the module-graph,
lockfile, and i18n pairing regeneration that followed.
2026-07-22 14:29:52 +08:00
Turtle 2a5dfb7d35 feat(tui): session resume — /resume command, exit hint, and dsh --resume <id>
Squashes feat/tui-resume-command, fix/tui-resume-desc, and
feat/tui-resume-flag.
2026-07-22 14:29:52 +08:00
Turtle 19f9a509aa feat(cli): tell the agent where its own source lives and invite it to extend dsh
Squashes feat/dsh-system-prompt-source-path and feat/dsh-source-extend-hint.
2026-07-22 14:29:28 +08:00
Turtle 6baa030594 feat(cli): dsh CLI with personal config overlays from ~/.config/dsh 2026-07-22 14:29:28 +08:00
Turtle a2f17d71ed fix(vendor/include): config hot-reload keeps the last good tree and its patches 2026-07-22 14:29:28 +08:00
Tianyi Cui 4a0941fb4b feat(ui): share the app bins' boot glue in @deepseek-ai/dsh-app-boot
The four near-twin helpers the two published bins carried — loadEnv,
installFailLoud, assertEntriesLoaded, boot — live once in
packages/ui/app-boot, parameterized by the bin's diagnostic prefix and
injectable at their side-effect seams (warn sink, process slice), so
every branch sits under the per-file 100% coverage gate: the unit suite
drives boot() in-process against the real Loader (relative-specifier
configs) through both the settled-tree path and the fiber-less-entry
rejection, and exercises the ENOENT/unloadable .env split, the
Error/non-Error/stackless fail-loud arms, and the disabled-entry
exclusion. resolveConfigPath (snapshot-aware) becomes the single path
resolver for both bins.

Each bin.ts is now a thin self-executing composition plus its
app-specific lifecycle (acp: replay env-skip + stdin-EOF dispose;
stdio: nothing extra), exports nothing, and stays coverage-excluded;
the built-bin smokes still prove both artifacts under plain node in the
node_modules-shaped temp dir (now symlinking ui/app-boot), including
the missing-config non-zero exit.

Implements docs/rfc/implemented/simplification/2026-07-04-share-app-bin-boot-glue.md
(moved from proposed/ and amended); the extract-example-app-packages
RFC's bin-ownership facts are amended in the same change.
2026-07-04 16:43:06 +08:00