Commit Graph

86 Commits

Author SHA1 Message Date
Tianyi Cui
835156b038 fix(ui-trajectory): timing provenance on sub-span lanes; assembled snapshot for both views
Responding to ds-review-bot on #664:

- SubSpanLane gains a 'timing' discriminant (measured | running | unknown).
  A settle-only replay entry (callTime null, start outside the window) was
  previously indistinguishable from a measured 0 ms span; it now renders
  hollow with a 'duration unknown' hover title, and durationMs stays null
  for anything unmeasured. Pairs with the client-runtime fix that stopped
  fabricating callTime = settle time (826c3696a on the live-parallel PR).

- The built-client Code Mode fixture snapshot now switches to the
  Trajectory and Waterfall tabs and pins the assembled rendering: three
  Sub cells with real +0.8s durations and three measured lanes with their
  hover titles — product-visible coverage through the real bundle graph,
  not just package-level jsdom fixtures.

Agent Note (both languages) updated for the timing contract; pairing
re-recorded.
2026-07-26 14:20:59 +08:00
Tianyi Cui
202ef2fc89 Merge branch 'code-mode-ui/dispatch-spill' into code-mode-ui/shiki 2026-07-26 12:30:26 +08:00
Tianyi Cui
6333b51f2f Merge branch 'code-mode-ui/web-ui-v1' into code-mode-ui/live-parallel 2026-07-26 12:30:03 +08:00
Tianyi Cui
a786911293 fix: address review — non-vacuous error assertion, package contracts
ds-review-bot findings: the browser e2e's error-state check now requires
at least one error sub-row (was >= 0); the runtime README documents the
codeDispatches snapshot contract and the ui-conversation README the code
variant + nested sub-row semantics.
2026-07-26 10:36:35 +08:00
Tianyi Cui
bb3dc50a4b feat(web): shiki syntax highlighting for code surfaces
One highlighter for the client: a synchronous fine-grained shiki core
(JS regex engine, no WASM) in ui-primitives with an explicit grammar
allowlist (typescript, shellscript, json — aliases resolve, unknown
languages take a geometry-identical plain arm). The shared CodeBlock
component owns both arms; markdown fences, the run_code expanded
program body (typescript), and the details panel Input (json) all
route through it. Token colors live in a new ui-theme shiki.css sheet
as --shiki-* custom properties (light/dark blocks), wired through the
shell's base.css chain — tokens-only styling holds; shiki's generated
span tree is the sanctioned innerHTML path (static output, no user
HTML). jsdom specs pin token spans, aliases, both fallbacks, and the
fence route; the built-bundle snapshot asserts the highlighted program
under the code row.
2026-07-26 09:52:37 +08:00
Tianyi Cui
8a79679489 feat(tools): live dispatch lifecycle + native-contract parallel sub-calls in Code Mode
The bridge replaces its serialization queue with a pool that reuses the
native concurrency contract: submissions classify through
registry.executionMode (fail-closed isConcurrencySafe), start strictly in
submission order, overlap up to the validated maxParallelSubCalls config
(default 10; 1 restores serial), and exclusive calls drain the pool, run
alone, and bar later calls. Each started sub-call logs a
tool/code-dispatch-start event at pool entry; the existing
tool/code-dispatch settles the pair (started ⇔ settles exactly once;
abandoned queued calls log neither). SDK prompt guidance now states the
true Promise.all contract — re-recorded across every code/both-mode
snapshot (plus the stale cordis-dynamic-toolchain fixture gaining the
required description arg).

Client: CodeSubCall widens to RunningToolCall | ToolResultNode — starts
land the running shape (rows wear the native running ring), settles
replace in place preserving start order, callTime pairs to the start
time. Fixture emits start/settle pairs; jsdom pins the running sub-row;
runtime specs pin in-place settlement and out-of-order completion.
2026-07-26 06:02:36 +08:00
Tianyi Cui
13f7c62318 feat(web): render Code Mode sub-calls as native rows nested under the run_code row
The client indexes tool/code-dispatch events into
ConversationSnapshot.codeDispatches (parent callId -> ToolResultNode-shaped
sub-calls; live mux and history replay build the identical index). ChatView
renders each run_code parent as the new code variant (description summary,
program as the expanded monospace body) with its sub-dispatches as
always-visible indented rows — every sub-row dispatches through the SAME
keyed conversation.chat.toolview hole with the same GenericToolCard
fallback, so custom registrations (bash sample) take over sub-rows exactly
as top-level rows. The details panel resolves sub-callIds to full logged
args and complete output through the native path.

Evidence: fixture turn 64 + built-bundle jsdom snapshot, real-machinery
jsdom suites (nesting, error state, details, running parent, reference
stability), and a recorded code-mode browser e2e round (keyless replay +
aria golden). Scaffold gains a toolsMode patch knob.
2026-07-26 04:02:38 +08:00
Tianyi Cui
d60dea9f55 feat(tools): run_code description param + native-parity dispatch logging + web code-mode seam
run_code gains a required bash-style description parameter: presentCall
titles the card with it and moves the program to rawInput, so every
surface gets a readable label. tool/code-dispatch now logs each
sub-call's complete content/isError (the tool/result vocabulary),
replacing the bounded resultSummary and deleting the summarize/cwd
machinery — a UI renders sub-calls through the identical path as native
results. The dsh config tree mounts the worker code runtime and reads
DSH_TOOLS_MODE (temporary seam until per-session mode selection lands).

Session format stays v0 (pre-release churn). Code-mode ACP/TUI fixtures
re-recorded; TUI presenter pin refreshed; catalogs regenerated. Keyless
web smoke pins the code-mode wire contract (tools=[run_code] + SDK
prompt section).
2026-07-26 02:43:34 +08:00
Tianyi Cui
f1577d620e test(web): harden replay scaffold lifecycle 2026-07-26 00:33:46 +08:00
Tianyi Cui
882bd4a591 Merge origin/master into web-e2e-lane
# Conflicts:
#	tsconfig.host.json
2026-07-26 00:16:12 +08:00
Tianyi Cui
ee25ccf38b Merge remote-tracking branch 'origin/master' into web-e2e-lane
# Conflicts:
#	vitest.web.config.ts
2026-07-25 18:19:22 +08:00
Turtle
e996aabf47 Merge branch 'master' into worktree/dsh-arg-parser
Integrate the Commander adapter with master's `dsh web --workspace-root`
(workspace-aware session flow).

- args.ts: add `--workspace-root <path>` to the web subcommand; WebInvocation
  carries workspaceRoot.
- web.ts: keep the adapter-parsed signature, take (host, port, dev,
  workspaceRoot) and pass workspaceRoot through to AppCLIEntry (drop master's
  re-added parseArgs and CLI host/port validation — the schema owns those).
- bin.ts forwards invocation.workspaceRoot; args.spec + the Agent Note pair note
  the flag.
2026-07-25 18:05:39 +08:00
Turtle
2243023575 Merge branch 'master' into worktree/dsh-arg-parser
Integrate the Commander adapter with master's headless rework (dsh -p now boots
through AppCLIEntry — the same cordis.yml composition as dsh web, web-observable
while it runs — via toFetchHandler(ctx.apiProxy)).

- headless.ts: keep master's AppCLIEntry-based body but take the adapter's
  pre-parsed `task` (drop the re-added parseArgs). Old startHost path and the
  now-deleted packages/host/runtime dependency are gone.
- remove the stale packages/host/runtime dir left on disk by master's deletion
  (mirrors the earlier packages/ui/acp cleanup).
2026-07-25 17:48:21 +08:00
imccyu
c06cb2deec test(web): stabilize workspace snapshots 2026-07-25 17:46:05 +08:00
imccyu
9eb9c70a8a feat(web): add workspace-aware session flow 2026-07-25 17:45:26 +08:00
Turtle
5a06b9e926 fix(cli): reject default-surface flags leaked onto the web subcommand
ds-review-bot: `dsh web -p task`, `dsh web --resume s`, and `dsh --config c.yml
web` reached the web action with those values in program.opts() but the action
ignored them and served — silently dropping mode-specific inputs. The web
action now reads the parent opts and fails loud (exit 1) on a leaked
--config/-p/--resume, matching the root mode's mixing guard. Covered in
args.spec.ts.

Also (ds-review-bot): tui-demo/README documented the removed `dsh
[path-to-cordis.yml]` positional form; corrected to bare `dsh` / `dsh --config
<path>`. Agent Note + Chinese pair note the web-leak guard.
2026-07-25 17:24:39 +08:00
Tianyi Cui
3b4bb4436f Merge branch 'master' into worktree-webheadless 2026-07-25 17:15:08 +08:00
Turtle
9f6dbde7f6 refactor(cli): let the webserver schema own web --host/--port validation
The adapter no longer validates --host/--port or declares the allowed set:
LOOPBACK_HOST/ALL_INTERFACES_HOST leave args.ts. --host/--port are now
unvalidated pass-through overrides — the adapter only Number-coerces the port
string (the dsh-host-webserver schema wants a number). That schema
(host a 127.0.0.1/0.0.0.0 literal union, port a natural <= 65535) is the single
source of both the default (the shipped cordis.yml webserver row) and validity;
AppCLIEntry patches an explicit flag into that row, so a bad host/port fails
loud at the schema on boot (verified: `dsh web --host 9.9.9.9` and `--port abc`
both exit 1 with the schema's ValidationError).

web.ts keeps two display-only literals (the printed loopback URL, the
all-interfaces LAN-detection check), commented as mirrors of the schema, not a
source of truth. Agent Note + Chinese pair and README updated; the args spec
drops the host/port exit-code cases (now the schema's job, covered by the web
smoke on boot).
2026-07-25 16:19:02 +08:00
Turtle
6a8049879e docs(cli): trim bin.ts module comment to the non-obvious contract
Review (turtle1999): the opening narrated control flow. Drop the argv-parse/
switch narration; keep only the two non-obvious facts (per-mode dynamic imports,
and that the adapter exits so only a valid mode reaches the switch).
2026-07-25 15:50:10 +08:00
Turtle
fca2dda37d refactor(cli): unify the arg grammar — one program, --config flag, real web subcommand
Drop the bare `dsh <config>` positional in favor of a `--config <path>` flag.
Without a root positional, `web` can be a real Commander subcommand in one
program instead of the reserved-first-token dispatch to a second parser, so
`dsh --help` lists every mode natively (no hand-pasted command text) and the
second parser + reserved-token machinery are gone.

Grammar:
  dsh                       TUI (shipped tree + ~/.dsh overlay)
  dsh --config <path>       TUI, alternate tree (demos/tests only)
  dsh --resume <id>         TUI, resume a session
  dsh -p "task"             headless one-shot
  dsh web [--host --port --dev]

`dsh` is the product front door with no positional; `--config` exists only so
demo:cordis, demo:code-mode, and the keyless PTY smokes can point the shipped
bin at an example tree. Those three sites and the /resume re-exec argv move to
`--config <path>`. The `-p` + `--config`/`--resume` mode-mixing guard and the
cordis.yml-owns-host/port-default fix are preserved.

Agent Note + Chinese pair, README, tui.ts docs updated. All 13 PTY smokes
(including code-mode via --config and the exec-replace resume handoff) green.
2026-07-25 15:47:55 +08:00
Tianyi Cui
a5820f264c Merge origin/master into codex/trim-redundant-comments 2026-07-25 15:22:13 +08:00
Turtle
91d86f9b21 fix(cli): let cordis.yml own the web host/port default (single source)
The merge's "always pass adapter-resolved host/port to AppCLIEntry" made the
adapter's 127.0.0.1/3080 shadow apps/cli/cordis.yml's webserver row — editing
the yml port would have had no effect, a duplicated default.

The adapter now assigns no host/port default: an absent --host/--port leaves the
field undefined (WebInvocation.host?/port?), runWeb forwards each to AppCLIEntry
only when present, and AppCLIEntry patches the webserver row only for an
explicit flag. cordis.yml is the single source of the host/port default; the
adapter still validates a flag when given. Removes the now-unused
DEFAULT_WEB_PORT; LOOPBACK_HOST/ALL_INTERFACES_HOST stay as the allowed-value
vocabulary (validation + the printed URL/LAN line).
2026-07-25 15:03:17 +08:00
imccyu
f7d85bf9f5 fix(headless): CI green + review-bot findings for the shared composition
Coverage: api-proxy.ts joins the web-transport exclusion block (its
behavior specs moved here with it; the assembled-entry coverage lands with
the GUI test lane). Static: config-catalog regenerated after the log-round
revert shifted a source anchor. Prose brought current per review: the cli
README now describes the one shared composition (and its build
prerequisite), the apiproxy README points at the api-gateway row instead of
the deleted runtime package, and the config-tree agent note's headless
deferral paragraph records what actually landed (bilingual pair
re-recorded).
2026-07-25 14:40:45 +08:00
imccyu
0e287b582c feat(headless): boot dsh -p from the shared composition; retire dsh-host-runtime
dsh -p now runs AppCLIEntry over the same cordis.yml as dsh web — one
composition, no disabled rows, no layer marks. The only surface difference
is port 0 (parallel runs never collide), and the printed URL opens the live
headless session in a browser while it runs. The model face gains what web
already had (ask_user_question, workspace context, model titles) per the
unification ruling. InProcessApiClient now wraps toFetchHandler(ctx.apiProxy)
directly, so bootHost/startHost lose their last consumer and the
dsh-host-runtime package retires; its api-proxy behavior specs move to
dsh-host-apiproxy where the implementation lives.
2026-07-25 14:40:45 +08:00
imccyu
4a27da44cf test(web): real-composition webserver spec
Boots a test-only cordis.yml through the real Loader and asserts the route
service's behavior surface: exact/longest-prefix matching, tapIndex
transform order and unsubscription, traversal 403, non-GET 405, SPA-200
fallback, malformed-request 400 without process exit, duplicate-pattern
throw, dispose closing held connections with register/disposer symmetry,
and a listen-failure fail-loud case (EADDRINUSE -> FAILED fiber + late
rejection). Replaces the retired factory-era specs.
2026-07-25 14:40:45 +08:00
Turtle
2dfd8635e8 Merge branch 'master' into worktree/dsh-arg-parser
Integrate the Commander argument adapter and dsh-front-door work with master's
config-tree `dsh web` (#601: AppCLIEntry + apps/cli/cordis.yml) and the
packages/ui/acp → packages/acp/acp relocation.

- web.ts: keep master's AppCLIEntry-based boot, but take the adapter's parsed
  (host, port, dev) instead of an internal parseArgs. The adapter's host/port
  defaults (127.0.0.1/3080) match cordis.yml, so always passing them is
  behavior-equivalent to master's "undefined keeps the yml default".
- apps/cli/package.json: master's expanded config-tree dep set + commander.
- retire-readline Agent Note: point the TUI refusal proof at
  apps/cli/tests/built-bin.e2e.ts (both languages), re-record the pair.
- READMEs reconciled (demo-bin removal + master's ACP/channel rewording).
2026-07-25 14:37:57 +08:00
Tianyi Cui
5b98ba50b9 Merge remote-tracking branch 'origin/master' into web-e2e-lane 2026-07-25 14:21:40 +08:00
Tianyi Cui
fb17d5ec49 Merge remote-tracking branch 'origin/master' into web-e2e-lane
# Conflicts:
#	docs/config-catalog.md
#	docs/testing.i18n.yaml
#	docs/testing.md
#	docs/testing.zh.md
#	packages/host/runtime/src/start.ts
#	packages/support/acp-snapshot/README.md
2026-07-25 14:20:19 +08:00
Turtle
007e8fd92f refactor(cli): bail early in the arg adapter instead of returning errors as data
Address review and cut ceremony: the adapter no longer models help/version/
errors as DshInvocation members. Commander owns those under exitOverride — it
prints usage or the diagnostic and one try/catch in parseDshArgs turns the
thrown CommanderError into process.exit with the intended code. bin.ts drops its
help/version/error cases; the union is the three real modes.

Domain checks bail via command.error(print + exit 1): --prompt rejects an empty
task or a stray config/--resume, empty --resume= fails loud, and --host/--port
are validated. A repeated --resume or a flag captured as a value is Commander's
standard behavior, left alone (a bad id fails loud downstream). dsh --help
discloses web via addHelpText. Net: args.ts 185 -> 112 lines.

Also fixes review nits: built-bin e2e resolves on `close`; the /resume handoff
uses `dsh --resume=<id> -- <config>` so a config named `web` stays a positional;
and stale prose (cordis.yml comment, app-boot module doc + duplicate JSDoc,
ui/README, two feature notes, an agent-loop test name) tracks the shipped state.
Removes tui-demo's now-dead plugin-include dep and vendor/loader + app-boot
tsconfig references.
2026-07-25 14:15:25 +08:00
Tianyi Cui
76182a450c Merge origin/master into codex/pnpm-dsh-entrypoint 2026-07-25 13:40:14 +08:00
Turtle
89254b7c56 Merge remote-tracking branch 'origin/master' into codex/trim-redundant-comments
# Conflicts:
#	packages/client/connection/src/index.ts
#	packages/host/webserver/tests/web-plugins.spec.ts
2026-07-25 13:06:40 +08:00
Turtle
fac6c35e9a Trim redundant source comments 2026-07-25 13:02:37 +08:00
Turtle
0901140b3f test(cli): cover the dsh built-bin non-TTY refusal
Removing the dsh-tui-demo bin dropped the only test of the TUI's piped-launch
refusal. Add apps/cli/tests/built-bin.e2e.ts (apps/*/tests added to the e2e
vitest include) running the built lib/bin.js under plain Node with piped stdio,
and point the refusal message at `dsh -p "task"` for automation.
2026-07-25 13:01:45 +08:00
Turtle
870fb1cafa refactor(cli): make dsh the sole terminal front door, drop RESUME_SESSION_ID
Remove the redundant dsh-tui-demo bin and the RESUME_SESSION_ID environment
variable, leaving dsh as the one terminal entrypoint.

The dsh-tui-demo package was a plugin (the TUI app bundle mounted by dsh's
config) plus a bin that booted a leaf cordis.yml — the same job `dsh [config]`
does. The bin, its ./bin export, its built-bin.e2e.ts, the tsdown bin entry,
and the now-unused dsh-app-boot dependency are removed; the package keeps its
plugin and invariant. demo:cordis, demo:code-mode, and the tui-agent and
cordis-agent keyless PTY smokes now launch through apps/cli/src/bin.ts with the
config as the positional argument. cli-demo/acp-demo/jsonrpc-demo keep their
bins (distinct surfaces).

RESUME_SESSION_ID was the only bridge from --resume into the shipped config;
--resume now provides the id on the boot context via ctx.provide(
RESUME_SESSION_ID_KEY, id), and the four configs read it as a bare identifier
through a quoted typeof-guarded !!js expression. The TUI resumeCommand fixtures
and docs move to `dsh --resume {session}`.

Agent Note and its Chinese pair updated; config-catalog regenerated.
2026-07-25 12:43:59 +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
imccyu
2e27b8aeef chore(web): gate repairs for the config-tree boot round
Lint (bridge JSDoc params, service-class export shape, async invariant
listener form), regenerated doc catalogs/graphs with role classifications
for httpServer and clientModuleHost, catalog type-link exemptions for the
route/graph contracts, knip alignment (apps/cli composes via cordis.yml so
its yml-named deps are runtime edges knip cannot see; webserver's deleted
test dir), the zh side of the loading-model note brought along with its
pairing records, and coverage exclusions for the new web-transport halves
under the GUI test-lane TODO (real-composition harnesses land with that
lane).
2026-07-25 10:38:54 +08:00
imccyu
5466a81474 feat(web): boot dsh web from a config tree via AppCLIEntry
apps/cli/cordis.yml holds the whole composition flat — the host runtime
rows, the api-gateway row, the webserver row, and the ten dshClient rows.
AppCLIEntry is the pre-cordis glue: layered env (ambient > cwd .env >
$DSH_HOME/.env, fixing DSH_HOME=... dsh web not finding its key), patch
composition from the three non-yml sources (profile json through the static
PROFILE_MAPPINGS table, CLI flags, the resolved frontend distIndex), the
Loader include boot (--dev appends the hmr row before the settle), and the
fail-loud triple (assertEntriesLoaded + installFailLoud + an all-ACTIVE
sweep for PENDING fibers). web.ts shrinks to argv parsing + the URL line.
2026-07-25 10:25:02 +08:00
imccyu
80cd8f54b5 refactor(web): collapse the shell boot into the AppWebEntry class
The four free functions in boot.tsx become one kernel class holding what
must exist before cordis: the parsed BootManifest, the ClientModuleSystem
instance, and the loading-page handles. Context/Loader setup runs in
parallel with the immediately-tier prefetch, but entry creation awaits the
prefetch: materialization is tree.import's synchronous require, so
cross-package require edges (i18n -> runtime/client) need every
immediately-tier factory registered first — unbarriered creation raced
10-25% of boots. The kernel adopts the modules entry (writes the
__DSH_MODULES__ slot pre-cordis, creates the entry first, skips its graph
row), and provide('modules') now lives in the adoption apply. apps/web
drops its host-package edges (composition is apps/cli's job).
2026-07-25 10:23:40 +08:00
Tianyi Cui
6d3c25f494 refactor(web-e2e): rename harness -> scaffold; add interaction coverage
The shared test module was named harness.ts inside a repo whose product
IS a harness — hopelessly ambiguous. Renamed to scaffold.ts with
launchWebScaffold/WebScaffold; tsconfig plane-split entries, the seam
JSDoc/README mentions, and both Agent Note languages updated.

Both scenarios gain a Playwright interaction step over the settled
transcript (after the golden capture, so committed aria surfaces stay
untouched): replay-round-trip clicks the reasoning fold open/closed over
wire-delivered state; seeded-history expands a read tool row rebuilt from
the cold log and asserts the recorded result text appears (read rows are
expand-in-place — rowExpands routes the click to the inline fold, not the
details column). test:web 30 passed | 1 skipped.
2026-07-24 22:18:19 +08:00
Tianyi Cui
d08050ab69 chore(web-e2e): gate fixes — catalog, budgets, knip
Regenerate config-catalog for the llm-replay paceMs row; condense the
testing.md web-lane entry to pointer form and raise its ceiling 1020->1060
(the two-sentence tier entry for a genuinely new surface does not fit the
old ceiling after relocation-first trims); internalize two harness helpers
knip flagged (rawSessionLog/normalizeAria are module-internal).
2026-07-24 20:30:33 +08:00
Turtle
ee5132c1e1 refactor(cli): dispatch web as a reserved token, drop parse machinery
Simplify the Commander adapter now that behavior can change: dispatch a leading
`web` token to its own parser instead of a subcommand of the root program, and
read opts()/processedArgs after parse() instead of action closures with a
mutable holder.

This removes enablePositionalOptions(), the parent-option leak guard, both
action closures, and the --resume/--prompt argParser threading. Behavior
changes: `dsh -p x web` is a headless prompt (extra positional dropped),
`dsh web -p x` fails loud (web has no -p), and a repeated --resume is natural
last-wins. The two real fail-loud invariants stay as post-parse checks: an empty
--resume= id (agent-loop treats '' as no-resume) and an empty -p task.

Trims args.spec.ts to the routing/fail-loud/help behavior that matters; the
tui-agent keyless PTY smoke still covers bin.ts dispatch end to end. Net ~114
fewer lines across adapter and tests.
2026-07-24 20:01:38 +08:00
Tianyi Cui
2cd37e2765 docs(testing): web e2e lane docs + Agent Note to implemented
testing.md gains the web browser snapshot tier entry (divergent
DSH_SNAPSHOT=... test:web commands) and names apps/web/tests/snapshots/
as the web surface's snapshot home. The GUI testing note's tier map and
lane map gain the e2e scenarios (both languages, pair re-recorded) and
drop the stale verify-session-real references (those scripts left with
the missions/ tree). packages/client/AGENTS.md check ladder covers the
wire-carriage trigger and refresh/record commands. acp-snapshot README
stops claiming the whole package is ACP-specific — its normalizers are
transport-neutral with three consumers now. vitest.web.config.ts header
carries TODO(ci-browser) with the staged-reversal pointer.

The design-study Agent Note moves proposed/ -> implemented/ rewritten in
present tense: all review decisions recorded (llm:false seam over the
placeholder-key hack, providers-mode replay, whenIdle barrier stack,
single aria golden + anchors, TUI-style inline modes over a suite
factory, scrub-only header stance, CI deferral) with re-entry triggers
under Deferred.
2026-07-24 19:57:27 +08:00
Tianyi Cui
46b9a91e55 test(web): keyless browser e2e lane — replayed round trip + seeded cold resume
apps/web/tests/harness.ts boots the real web assembly in-process
(startHost llm:false -> installLlmReplay providers-mode -> mountWebPlugins
-> startWebServer) under DSH_SNAPSHOT replay/record/refresh. Barrier
stack: in-process turn/end -> agent.whenIdle (covers the persistence
flush) -> browser settled-poll. Seeding goes through the real persistence
API (semantic-checkpoint precedent); record harvests fixtures from live
session memory and tokenizes {{sessionId}}/{{cwd}}; refresh is the sole
golden writer. Console tripwires fail scenarios on reconnect/gap-repair
self-healing; harness close asserts full replay-fixture consumption.

Scenarios, each with fixtures recorded against THIS assembly via a live
model run: replay-round-trip (real composer -> real bash echo -> settled
markdown + aria golden + world-state event asserts) and seeded-history
(cold sidebar list -> implicit resume on open -> history tool cards from
the log, zero model calls). apps/web/tests are host-plane programs:
excluded from the client-registered apps/web project, included in
tsconfig.host.json (one program cannot hold both Context merge sides).
2026-07-24 19:49:02 +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
Turtle
c440217fde refactor(tui): defer cross-process resume locking 2026-07-24 01:29:35 -07:00
NI0317
2ae9f4fdf3 feat(tui): add safe session resume flow 2026-07-24 01:29:35 -07:00
Turtle
bcc041829c chore: expose dsh root script 2026-07-24 15:55:47 +08:00
imccyu
e903a864f7 fix(gui): reconcile the rebase — ui-question joins the client roster
ui-question landed on master as a full dshClient plugin package (composer
question flow); it enters the flat roster, apps/cli deps/refs, and the
smoke graph. Restore the workspace-context and user-interaction host deps
the conflict resolution had dropped.
2026-07-24 02:09:42 +08:00
imccyu
fb47f61a83 refactor(gui): host graph from dshClient discovery; webserver self-watches bundles for HMR
The registry scans mounted Loader entries' dshClient declarations and
composes __DSH_BOOT__ {rev, entries} — inject edges and the immediately
mark come from manifests, never hand-copied; malformed fields fail loud
at load. The composing app owns one flat roster plus the --dev switch
(hmr row and bundle watching are dev-graph decisions).

The rebuild signal is the webserver's own observation: in dev mode the
registry stat-polls each scanned bundle (fs.watchFile; polling because
network mounts deliver no inotify), re-hashes on change, and broadcasts
a rebuilt frame on the /plugins/events SSE channel only when the rev
actually changed. Watch membership follows the table across rescans;
dispose drops all watches; a torn read self-heals on the next tick.
The POST /plugins/rebuilt endpoint is gone — builders and the host
share zero protocol. dsh web --dev logs the watched bundle list and
each rebuilt id with its rev transition.
2026-07-24 02:09:42 +08:00
imccyu
b58f0989f9 refactor(gui): rebuild the client loading kernel as dsh-client-modules with a two-phase boot
The module system moves out of dsh-client-runtime (./loader retired) into
its own package: a lazy CJS table where executing a bundle only registers
its factory and materialization happens at first require, memoized, with
recursive requires self-ordering. ClientModuleSystem is a class; index.ts
keeps the types and a thin factory. Boot is two-phase: phase one prefetches
the immediately tier in parallel (registration only, failures deferred to
phase two's loud import); phase two mounts the vendored Loader with the
module system as internal, creates one entry per graph row plus the
app-shell pseudo-row the kernel appends itself, and settles on an
all-ACTIVE sweep. The shell kernel is self-sufficient: hand-rolled
loader-status stores, no plugin value imports, platform seed list single-
sourced in platform.ts.
2026-07-24 02:09:42 +08:00